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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.flayignore9
-rw-r--r--.gitattributes1
-rw-r--r--.gitlab-ci.yml44
-rw-r--r--.gitlab/issue_templates/Feature proposal.md (renamed from .gitlab/issue_templates/Feature Proposal.md)0
-rw-r--r--.gitlab/issue_templates/Research proposal.md (renamed from .gitlab/issue_templates/Research Proposal.md)0
-rw-r--r--.gitlab/issue_templates/Security Developer Workflow.md70
-rw-r--r--.gitlab/issue_templates/Security developer workflow.md71
-rw-r--r--.gitlab/merge_request_templates/Database Changes.md50
-rw-r--r--.gitlab/merge_request_templates/Database changes.md50
-rw-r--r--.gitlab/merge_request_templates/Documentation.md14
-rw-r--r--.rubocop.yml6
-rw-r--r--.rubocop_todo.yml10
-rw-r--r--CHANGELOG.md338
-rw-r--r--CONTRIBUTING.md700
-rw-r--r--Dangerfile7
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile21
-rw-r--r--Gemfile.lock85
-rw-r--r--Gemfile.rails5.lock73
-rw-r--r--README.md12
-rwxr-xr-xRakefile4
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/api.js27
-rw-r--r--app/assets/javascripts/autosave.js4
-rw-r--r--app/assets/javascripts/awards_handler.js55
-rw-r--r--app/assets/javascripts/badges/components/badge.vue2
-rw-r--r--app/assets/javascripts/boards/components/board.js13
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue15
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue3
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js10
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.vue22
-rw-r--r--app/assets/javascripts/boards/components/modal/list.vue3
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue4
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.vue41
-rw-r--r--app/assets/javascripts/boards/filters/due_date_filters.js5
-rw-r--r--app/assets/javascripts/boards/index.js149
-rw-r--r--app/assets/javascripts/boards/models/issue.js1
-rw-r--r--app/assets/javascripts/boards/models/list.js62
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js25
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js8
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue45
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue6
-rw-r--r--app/assets/javascripts/clusters/constants.js15
-rw-r--r--app/assets/javascripts/commons/gitlab_ui.js4
-rw-r--r--app/assets/javascripts/commons/index.js1
-rw-r--r--app/assets/javascripts/commons/polyfills.js2
-rw-r--r--app/assets/javascripts/commons/polyfills/request_idle_callback.js17
-rw-r--r--app/assets/javascripts/confirm_danger_modal.js5
-rw-r--r--app/assets/javascripts/create_item_dropdown.js3
-rw-r--r--app/assets/javascripts/diffs/components/app.vue28
-rw-r--r--app/assets/javascripts/diffs/components/changed_files.vue45
-rw-r--r--app/assets/javascripts/diffs/components/changed_files_dropdown.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_discussions.vue5
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue56
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue50
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_gutter_content.vue30
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue50
-rw-r--r--app/assets/javascripts/diffs/components/diff_table_cell.vue55
-rw-r--r--app/assets/javascripts/diffs/components/diff_table_row.vue191
-rw-r--r--app/assets/javascripts/diffs/components/edit_button.vue8
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_comment_row.vue39
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue117
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue51
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue75
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue162
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue71
-rw-r--r--app/assets/javascripts/diffs/mixins/diff_content.js57
-rw-r--r--app/assets/javascripts/diffs/store/actions.js38
-rw-r--r--app/assets/javascripts/diffs/store/getters.js123
-rw-r--r--app/assets/javascripts/diffs/store/index.js11
-rw-r--r--app/assets/javascripts/diffs/store/modules/diff_state.js18
-rw-r--r--app/assets/javascripts/diffs/store/modules/index.js24
-rw-r--r--app/assets/javascripts/diffs/store/mutation_types.js1
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js21
-rw-r--r--app/assets/javascripts/diffs/store/utils.js44
-rw-r--r--app/assets/javascripts/due_date_select.js45
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.vue87
-rw-r--r--app/assets/javascripts/environments/components/environment_external_url.vue51
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue921
-rw-r--r--app/assets/javascripts/environments/components/environment_monitoring.vue49
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.vue94
-rw-r--r--app/assets/javascripts/environments/components/environment_stop.vue106
-rw-r--r--app/assets/javascripts/environments/components/environment_terminal_button.vue53
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue4
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue13
-rw-r--r--app/assets/javascripts/environments/components/stop_environment_modal.vue92
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.vue8
-rw-r--r--app/assets/javascripts/environments/mixins/environments_mixin.js22
-rw-r--r--app/assets/javascripts/environments/services/environments_service.js2
-rw-r--r--app/assets/javascripts/frequent_items/components/app.vue122
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list.vue78
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue117
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_mixin.js23
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue55
-rw-r--r--app/assets/javascripts/frequent_items/constants.js38
-rw-r--r--app/assets/javascripts/frequent_items/event_hub.js (renamed from app/assets/javascripts/projects_dropdown/event_hub.js)0
-rw-r--r--app/assets/javascripts/frequent_items/index.js69
-rw-r--r--app/assets/javascripts/frequent_items/store/actions.js81
-rw-r--r--app/assets/javascripts/frequent_items/store/getters.js4
-rw-r--r--app/assets/javascripts/frequent_items/store/index.js16
-rw-r--r--app/assets/javascripts/frequent_items/store/mutation_types.js9
-rw-r--r--app/assets/javascripts/frequent_items/store/mutations.js71
-rw-r--r--app/assets/javascripts/frequent_items/store/state.js8
-rw-r--r--app/assets/javascripts/frequent_items/utils.js49
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js3
-rw-r--r--app/assets/javascripts/gl_dropdown.js594
-rw-r--r--app/assets/javascripts/gl_form.js7
-rw-r--r--app/assets/javascripts/gpg_badges.js31
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue75
-rw-r--r--app/assets/javascripts/groups_select.js1
-rw-r--r--app/assets/javascripts/helpers/avatar_helper.js33
-rw-r--r--app/assets/javascripts/ide/components/activity_bar.vue21
-rw-r--r--app/assets/javascripts/ide/components/branches/item.vue60
-rw-r--r--app/assets/javascripts/ide/components/branches/search_list.vue111
-rw-r--r--app/assets/javascripts/ide/components/changed_file_icon.vue20
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/actions.vue3
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue10
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide.vue27
-rw-r--r--app/assets/javascripts/ide/components/ide_project_header.vue37
-rw-r--r--app/assets/javascripts/ide/components/ide_review.vue13
-rw-r--r--app/assets/javascripts/ide/components/ide_side_bar.vue163
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue21
-rw-r--r--app/assets/javascripts/ide/components/ide_tree.vue42
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue27
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail.vue3
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/dropdown.vue63
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/info.vue43
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/item.vue18
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/list.vue172
-rw-r--r--app/assets/javascripts/ide/components/nav_dropdown.vue59
-rw-r--r--app/assets/javascripts/ide/components/nav_dropdown_button.vue54
-rw-r--r--app/assets/javascripts/ide/components/nav_form.vue40
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/button.vue52
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue109
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue98
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/upload.vue124
-rw-r--r--app/assets/javascripts/ide/components/panes/right.vue58
-rw-r--r--app/assets/javascripts/ide/components/preview/clientside.vue171
-rw-r--r--app/assets/javascripts/ide/components/preview/navigator.vue147
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue17
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue25
-rw-r--r--app/assets/javascripts/ide/components/repo_tab.vue6
-rw-r--r--app/assets/javascripts/ide/components/shared/tokened_input.vue121
-rw-r--r--app/assets/javascripts/ide/constants.js24
-rw-r--r--app/assets/javascripts/ide/ide_router.js31
-rw-r--r--app/assets/javascripts/ide/index.js8
-rw-r--r--app/assets/javascripts/ide/lib/common/model.js2
-rw-r--r--app/assets/javascripts/ide/lib/themes/gl_theme.js1
-rw-r--r--app/assets/javascripts/ide/services/index.js10
-rw-r--r--app/assets/javascripts/ide/stores/actions.js47
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js30
-rw-r--r--app/assets/javascripts/ide/stores/actions/merge_request.js14
-rw-r--r--app/assets/javascripts/ide/stores/actions/tree.js16
-rw-r--r--app/assets/javascripts/ide/stores/getters.js8
-rw-r--r--app/assets/javascripts/ide/stores/index.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/branches/actions.js39
-rw-r--r--app/assets/javascripts/ide/stores/modules/branches/index.js10
-rw-r--r--app/assets/javascripts/ide/stores/modules/branches/mutation_types.js5
-rw-r--r--app/assets/javascripts/ide/stores/modules/branches/mutations.js21
-rw-r--r--app/assets/javascripts/ide/stores/modules/branches/state.js4
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js30
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/getters.js26
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/actions.js62
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/getters.js4
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/index.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js18
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/state.js10
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/actions.js61
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js6
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js84
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js46
-rw-r--r--app/assets/javascripts/ide/stores/mutations/tree.js11
-rw-r--r--app/assets/javascripts/ide/stores/state.js6
-rw-r--r--app/assets/javascripts/ide/stores/utils.js39
-rw-r--r--app/assets/javascripts/ide/utils.js17
-rw-r--r--app/assets/javascripts/importer_status.js17
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue6
-rw-r--r--app/assets/javascripts/issue_show/components/edited.vue2
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue6
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue6
-rw-r--r--app/assets/javascripts/issue_show/components/title.vue112
-rw-r--r--app/assets/javascripts/labels_select.js16
-rw-r--r--app/assets/javascripts/lazy_loader.js36
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js20
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js39
-rw-r--r--app/assets/javascripts/lib/utils/http_status.js27
-rw-r--r--app/assets/javascripts/lib/utils/poll.js14
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js14
-rw-r--r--app/assets/javascripts/main.js2
-rw-r--r--app/assets/javascripts/merge_request_tabs.js153
-rw-r--r--app/assets/javascripts/milestone_select.js3
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue54
-rw-r--r--app/assets/javascripts/monitoring/components/graph/flag.vue1
-rw-r--r--app/assets/javascripts/monitoring/services/monitoring_service.js19
-rw-r--r--app/assets/javascripts/monitoring/stores/monitoring_store.js12
-rw-r--r--app/assets/javascripts/notes.js4
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue6
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue13
-rw-r--r--app/assets/javascripts/notes/components/discussion_counter.vue28
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue1
-rw-r--r--app/assets/javascripts/notes/components/note_edited_text.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue10
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue3
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue73
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue8
-rw-r--r--app/assets/javascripts/notes/index.js3
-rw-r--r--app/assets/javascripts/notes/mixins/autosave.js17
-rw-r--r--app/assets/javascripts/notes/mixins/discussion_navigation.js29
-rw-r--r--app/assets/javascripts/notes/stores/actions.js11
-rw-r--r--app/assets/javascripts/notes/stores/getters.js89
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js7
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js6
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js12
-rw-r--r--app/assets/javascripts/pages/profiles/keys/index.js16
-rw-r--r--app/assets/javascripts/pages/profiles/show/emoji_menu.js18
-rw-r--r--app/assets/javascripts/pages/profiles/show/index.js49
-rw-r--r--app/assets/javascripts/pages/projects/blob/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/clusters/gcp/login/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/clusters/new/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/index.js14
-rw-r--r--app/assets/javascripts/pages/projects/jobs/terminal/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/new/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue14
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/builds/index.js9
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/failures/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js15
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue2
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/tree/show/index.js6
-rw-r--r--app/assets/javascripts/pages/search/show/search.js2
-rw-r--r--app/assets/javascripts/pages/snippets/form.js1
-rw-r--r--app/assets/javascripts/pages/users/activity_calendar.js5
-rw-r--r--app/assets/javascripts/performance_bar/components/performance_bar_app.vue27
-rw-r--r--app/assets/javascripts/performance_bar/index.js33
-rw-r--r--app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue5
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_component.vue7
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue3
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue1
-rw-r--r--app/assets/javascripts/preview_markdown.js14
-rw-r--r--app/assets/javascripts/profile/add_ssh_key_validation.js43
-rw-r--r--app/assets/javascripts/profile/profile.js4
-rw-r--r--app/assets/javascripts/project_select.js5
-rw-r--r--app/assets/javascripts/projects/project_new.js17
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue162
-rw-r--r--app/assets/javascripts/projects_dropdown/components/app.vue158
-rw-r--r--app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue57
-rw-r--r--app/assets/javascripts/projects_dropdown/components/projects_list_item.vue116
-rw-r--r--app/assets/javascripts/projects_dropdown/components/projects_list_search.vue63
-rw-r--r--app/assets/javascripts/projects_dropdown/components/search.vue65
-rw-r--r--app/assets/javascripts/projects_dropdown/constants.js10
-rw-r--r--app/assets/javascripts/projects_dropdown/index.js66
-rw-r--r--app/assets/javascripts/projects_dropdown/service/projects_service.js137
-rw-r--r--app/assets/javascripts/projects_dropdown/store/projects_store.js33
-rw-r--r--app/assets/javascripts/reports/components/grouped_test_reports_app.vue116
-rw-r--r--app/assets/javascripts/reports/components/issue_body.js9
-rw-r--r--app/assets/javascripts/reports/components/issue_status_icon.vue57
-rw-r--r--app/assets/javascripts/reports/components/issues_list.vue85
-rw-r--r--app/assets/javascripts/reports/components/modal.vue73
-rw-r--r--app/assets/javascripts/reports/components/modal_open_name.vue33
-rw-r--r--app/assets/javascripts/reports/components/report_issues.vue59
-rw-r--r--app/assets/javascripts/reports/components/report_link.vue29
-rw-r--r--app/assets/javascripts/reports/components/report_section.vue181
-rw-r--r--app/assets/javascripts/reports/components/summary_row.vue71
-rw-r--r--app/assets/javascripts/reports/components/test_issue_body.vue44
-rw-r--r--app/assets/javascripts/reports/constants.js18
-rw-r--r--app/assets/javascripts/reports/store/actions.js88
-rw-r--r--app/assets/javascripts/reports/store/getters.js16
-rw-r--r--app/assets/javascripts/reports/store/index.js15
-rw-r--r--app/assets/javascripts/reports/store/mutation_types.js7
-rw-r--r--app/assets/javascripts/reports/store/mutations.js49
-rw-r--r--app/assets/javascripts/reports/store/state.js61
-rw-r--r--app/assets/javascripts/reports/store/utils.js59
-rw-r--r--app/assets/javascripts/search_autocomplete.js269
-rw-r--r--app/assets/javascripts/shared/milestones/form.js3
-rw-r--r--app/assets/javascripts/shortcuts_navigation.js1
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue19
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/todo.vue98
-rw-r--r--app/assets/javascripts/terminal/index.js10
-rw-r--r--app/assets/javascripts/terminal/terminal.js103
-rw-r--r--app/assets/javascripts/users_select.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment.vue95
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue197
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue104
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue59
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js12
-rw-r--r--app/assets/javascripts/vue_shared/components/bar_chart.vue391
-rw-r--r--app/assets/javascripts/vue_shared/components/bar_chart_constants.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/clipboard_button.vue28
-rw-r--r--app/assets/javascripts/vue_shared/components/code_block.vue16
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue14
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_modal.vue14
-rw-r--r--app/assets/javascripts/vue_shared/components/header_ci_component.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/help_popover.vue53
-rw-r--r--app/assets/javascripts/vue_shared/components/icon.vue47
-rw-r--r--app/assets/javascripts/vue_shared/components/identicon.vue26
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue23
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/memory_graph.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/panel_resizer.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/project_avatar/default.vue47
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/svg_gradient.vue37
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue5
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss15
-rw-r--r--app/assets/stylesheets/framework/avatar.scss18
-rw-r--r--app/assets/stylesheets/framework/blocks.scss20
-rw-r--r--app/assets/stylesheets/framework/buttons.scss4
-rw-r--r--app/assets/stylesheets/framework/common.scss18
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss22
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss77
-rw-r--r--app/assets/stylesheets/framework/filters.scss5
-rw-r--r--app/assets/stylesheets/framework/forms.scss9
-rw-r--r--app/assets/stylesheets/framework/gfm.scss8
-rw-r--r--app/assets/stylesheets/framework/gitlab_theme.scss53
-rw-r--r--app/assets/stylesheets/framework/header.scss29
-rw-r--r--app/assets/stylesheets/framework/highlight.scss8
-rw-r--r--app/assets/stylesheets/framework/icons.scss24
-rw-r--r--app/assets/stylesheets/framework/images.scss2
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss5
-rw-r--r--app/assets/stylesheets/framework/jquery.scss2
-rw-r--r--app/assets/stylesheets/framework/lists.scss4
-rw-r--r--app/assets/stylesheets/framework/mixins.scss48
-rw-r--r--app/assets/stylesheets/framework/panels.scss1
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss7
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss12
-rw-r--r--app/assets/stylesheets/framework/stacked_progress_bar.scss2
-rw-r--r--app/assets/stylesheets/framework/typography.scss17
-rw-r--r--app/assets/stylesheets/framework/variables.scss39
-rw-r--r--app/assets/stylesheets/mailers/highlighted_diff_email.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss1497
-rw-r--r--app/assets/stylesheets/pages/boards.scss15
-rw-r--r--app/assets/stylesheets/pages/builds.scss22
-rw-r--r--app/assets/stylesheets/pages/clusters.scss48
-rw-r--r--app/assets/stylesheets/pages/commits.scss56
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss2
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss2
-rw-r--r--app/assets/stylesheets/pages/diff.scss15
-rw-r--r--app/assets/stylesheets/pages/editor.scss2
-rw-r--r--app/assets/stylesheets/pages/environments.scss63
-rw-r--r--app/assets/stylesheets/pages/graph.scss58
-rw-r--r--app/assets/stylesheets/pages/groups.scss52
-rw-r--r--app/assets/stylesheets/pages/issuable.scss39
-rw-r--r--app/assets/stylesheets/pages/issues.scss2
-rw-r--r--app/assets/stylesheets/pages/issues/issue_count_badge.scss26
-rw-r--r--app/assets/stylesheets/pages/labels.scss81
-rw-r--r--app/assets/stylesheets/pages/login.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_conflicts.scss210
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss232
-rw-r--r--app/assets/stylesheets/pages/milestone.scss36
-rw-r--r--app/assets/stylesheets/pages/note_form.scss2
-rw-r--r--app/assets/stylesheets/pages/notes.scss5
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss15
-rw-r--r--app/assets/stylesheets/pages/profile.scss20
-rw-r--r--app/assets/stylesheets/pages/projects.scss40
-rw-r--r--app/assets/stylesheets/pages/repo.scss1331
-rw-r--r--app/assets/stylesheets/pages/reports.scss155
-rw-r--r--app/assets/stylesheets/pages/search.scss72
-rw-r--r--app/assets/stylesheets/pages/settings.scss16
-rw-r--r--app/assets/stylesheets/pages/settings_ci_cd.scss5
-rw-r--r--app/assets/stylesheets/pages/todos.scss16
-rw-r--r--app/assets/stylesheets/pages/tree.scss18
-rw-r--r--app/assets/stylesheets/snippets.scss20
-rw-r--r--app/controllers/admin/cohorts_controller.rb11
-rw-r--r--app/controllers/admin/conversational_development_index_controller.rb5
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb4
-rw-r--r--app/controllers/admin/groups_controller.rb2
-rw-r--r--app/controllers/admin/hooks_controller.rb7
-rw-r--r--app/controllers/admin/identities_controller.rb2
-rw-r--r--app/controllers/admin/impersonations_controller.rb2
-rw-r--r--app/controllers/admin/jobs_controller.rb4
-rw-r--r--app/controllers/admin/runner_projects_controller.rb2
-rw-r--r--app/controllers/admin/runners_controller.rb2
-rw-r--r--app/controllers/admin/services_controller.rb6
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/controllers/application_controller.rb59
-rw-r--r--app/controllers/boards/issues_controller.rb17
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb4
-rw-r--r--app/controllers/concerns/group_tree.rb40
-rw-r--r--app/controllers/concerns/issuable_actions.rb2
-rw-r--r--app/controllers/concerns/lfs_request.rb21
-rw-r--r--app/controllers/concerns/members_presentation.rb8
-rw-r--r--app/controllers/concerns/membership_actions.rb2
-rw-r--r--app/controllers/concerns/notes_actions.rb4
-rw-r--r--app/controllers/concerns/preview_markdown.rb3
-rw-r--r--app/controllers/concerns/renders_notes.rb5
-rw-r--r--app/controllers/concerns/repository_settings_redirect.rb4
-rw-r--r--app/controllers/concerns/todos_actions.rb12
-rw-r--r--app/controllers/concerns/uploads_actions.rb10
-rw-r--r--app/controllers/dashboard/milestones_controller.rb13
-rw-r--r--app/controllers/dashboard/projects_controller.rb5
-rw-r--r--app/controllers/dashboard/todos_controller.rb2
-rw-r--r--app/controllers/groups/avatars_controller.rb2
-rw-r--r--app/controllers/groups/group_members_controller.rb2
-rw-r--r--app/controllers/groups/runners_controller.rb2
-rw-r--r--app/controllers/groups/uploads_controller.rb4
-rw-r--r--app/controllers/groups_controller.rb2
-rw-r--r--app/controllers/import/bitbucket_server_controller.rb131
-rw-r--r--app/controllers/import/gitlab_controller.rb5
-rw-r--r--app/controllers/import/manifest_controller.rb93
-rw-r--r--app/controllers/instance_statistics/application_controller.rb10
-rw-r--r--app/controllers/instance_statistics/cohorts_controller.rb13
-rw-r--r--app/controllers/instance_statistics/conversational_development_index_controller.rb7
-rw-r--r--app/controllers/jwt_controller.rb22
-rw-r--r--app/controllers/notification_settings_controller.rb4
-rw-r--r--app/controllers/profiles/active_sessions_controller.rb2
-rw-r--r--app/controllers/profiles/avatars_controller.rb2
-rw-r--r--app/controllers/profiles/chat_names_controller.rb2
-rw-r--r--app/controllers/profiles/emails_controller.rb2
-rw-r--r--app/controllers/profiles/gpg_keys_controller.rb4
-rw-r--r--app/controllers/profiles/keys_controller.rb2
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb2
-rw-r--r--app/controllers/profiles_controller.rb4
-rw-r--r--app/controllers/projects/application_controller.rb2
-rw-r--r--app/controllers/projects/autocomplete_sources_controller.rb2
-rw-r--r--app/controllers/projects/avatars_controller.rb2
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/clusters/gcp_controller.rb76
-rw-r--r--app/controllers/projects/clusters/user_controller.rb40
-rw-r--r--app/controllers/projects/clusters_controller.rb115
-rw-r--r--app/controllers/projects/commit_controller.rb9
-rw-r--r--app/controllers/projects/commits_controller.rb10
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb14
-rw-r--r--app/controllers/projects/deploy_tokens_controller.rb2
-rw-r--r--app/controllers/projects/environments_controller.rb18
-rw-r--r--app/controllers/projects/git_http_client_controller.rb4
-rw-r--r--app/controllers/projects/group_links_controller.rb4
-rw-r--r--app/controllers/projects/hooks_controller.rb7
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/jobs_controller.rb28
-rw-r--r--app/controllers/projects/labels_controller.rb20
-rw-r--r--app/controllers/projects/lfs_api_controller.rb16
-rw-r--r--app/controllers/projects/lfs_storage_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests/application_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb21
-rw-r--r--app/controllers/projects/milestones_controller.rb13
-rw-r--r--app/controllers/projects/mirrors_controller.rb10
-rw-r--r--app/controllers/projects/notes_controller.rb3
-rw-r--r--app/controllers/projects/pipeline_schedules_controller.rb2
-rw-r--r--app/controllers/projects/pipelines_controller.rb15
-rw-r--r--app/controllers/projects/protected_refs_controller.rb4
-rw-r--r--app/controllers/projects/raw_controller.rb3
-rw-r--r--app/controllers/projects/releases_controller.rb2
-rw-r--r--app/controllers/projects/repositories_controller.rb2
-rw-r--r--app/controllers/projects/runner_projects_controller.rb2
-rw-r--r--app/controllers/projects/runners_controller.rb8
-rw-r--r--app/controllers/projects/services_controller.rb2
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb2
-rw-r--r--app/controllers/projects/snippets_controller.rb4
-rw-r--r--app/controllers/projects/tags_controller.rb2
-rw-r--r--app/controllers/projects/templates_controller.rb2
-rw-r--r--app/controllers/projects/todos_controller.rb14
-rw-r--r--app/controllers/projects/triggers_controller.rb10
-rw-r--r--app/controllers/projects/uploads_controller.rb4
-rw-r--r--app/controllers/projects/wikis_controller.rb54
-rw-r--r--app/controllers/projects_controller.rb36
-rw-r--r--app/controllers/sessions_controller.rb38
-rw-r--r--app/controllers/sherlock/transactions_controller.rb2
-rw-r--r--app/controllers/snippets/notes_controller.rb2
-rw-r--r--app/controllers/snippets_controller.rb4
-rw-r--r--app/controllers/users_controller.rb6
-rw-r--r--app/finders/admin/projects_finder.rb4
-rw-r--r--app/finders/groups_finder.rb16
-rw-r--r--app/finders/issuable_finder.rb6
-rw-r--r--app/finders/labels_finder.rb11
-rw-r--r--app/finders/merge_requests_finder.rb2
-rw-r--r--app/finders/personal_projects_finder.rb23
-rw-r--r--app/finders/pipelines_finder.rb9
-rw-r--r--app/finders/projects_finder.rb20
-rw-r--r--app/finders/todos_finder.rb41
-rw-r--r--app/finders/user_recent_events_finder.rb3
-rw-r--r--app/graphql/gitlab_schema.rb5
-rw-r--r--app/graphql/mutations/base_mutation.rb13
-rw-r--r--app/graphql/mutations/concerns/mutations/resolves_project.rb13
-rw-r--r--app/graphql/mutations/merge_requests/base.rb32
-rw-r--r--app/graphql/mutations/merge_requests/set_wip.rb35
-rw-r--r--app/graphql/resolvers/concerns/resolves_pipelines.rb23
-rw-r--r--app/graphql/resolvers/merge_request_pipelines_resolver.rb16
-rw-r--r--app/graphql/resolvers/project_pipelines_resolver.rb11
-rw-r--r--app/graphql/types/ci/pipeline_status_enum.rb9
-rw-r--r--app/graphql/types/ci/pipeline_type.rb31
-rw-r--r--app/graphql/types/merge_request_type.rb6
-rw-r--r--app/graphql/types/mutation_type.rb6
-rw-r--r--app/graphql/types/permission_types/ci/pipeline.rb11
-rw-r--r--app/graphql/types/project_type.rb5
-rw-r--r--app/helpers/application_settings_helper.rb25
-rw-r--r--app/helpers/auth_helper.rb2
-rw-r--r--app/helpers/avatars_helper.rb62
-rw-r--r--app/helpers/blob_helper.rb31
-rw-r--r--app/helpers/button_helper.rb2
-rw-r--r--app/helpers/ci_status_helper.rb15
-rw-r--r--app/helpers/clusters_helper.rb1
-rw-r--r--app/helpers/commits_helper.rb9
-rw-r--r--app/helpers/environments_helper.rb19
-rw-r--r--app/helpers/groups_helper.rb11
-rw-r--r--app/helpers/hooks_helper.rb2
-rw-r--r--app/helpers/icons_helper.rb15
-rw-r--r--app/helpers/issuables_helper.rb20
-rw-r--r--app/helpers/markup_helper.rb4
-rw-r--r--app/helpers/merge_requests_helper.rb2
-rw-r--r--app/helpers/milestones_helper.rb8
-rw-r--r--app/helpers/namespaces_helper.rb45
-rw-r--r--app/helpers/notes_helper.rb2
-rw-r--r--app/helpers/pipeline_schedules_helper.rb2
-rw-r--r--app/helpers/projects_helper.rb19
-rw-r--r--app/helpers/repository_languages_helper.rb16
-rw-r--r--app/helpers/search_helper.rb26
-rw-r--r--app/helpers/snippets_helper.rb2
-rw-r--r--app/helpers/submodule_helper.rb8
-rw-r--r--app/helpers/time_helper.rb8
-rw-r--r--app/helpers/todos_helper.rb10
-rw-r--r--app/helpers/users_helper.rb34
-rw-r--r--app/helpers/visibility_level_helper.rb5
-rw-r--r--app/helpers/workhorse_helper.rb4
-rw-r--r--app/mailers/notify.rb4
-rw-r--r--app/mailers/previews/devise_mailer_preview.rb (renamed from spec/mailers/previews/devise_mailer_preview.rb)0
-rw-r--r--app/mailers/previews/email_rejection_mailer_preview.rb (renamed from spec/mailers/previews/email_rejection_mailer_preview.rb)0
-rw-r--r--app/mailers/previews/notify_preview.rb170
-rw-r--r--app/mailers/previews/repository_check_mailer_preview.rb (renamed from spec/mailers/previews/repository_check_mailer_preview.rb)0
-rw-r--r--app/models/ability.rb2
-rw-r--r--app/models/abuse_report.rb2
-rw-r--r--app/models/active_session.rb2
-rw-r--r--app/models/appearance.rb2
-rw-r--r--app/models/application_setting.rb44
-rw-r--r--app/models/application_setting/term.rb2
-rw-r--r--app/models/audit_event.rb2
-rw-r--r--app/models/award_emoji.rb2
-rw-r--r--app/models/badge.rb2
-rw-r--r--app/models/badges/group_badge.rb2
-rw-r--r--app/models/badges/project_badge.rb2
-rw-r--r--app/models/blob.rb2
-rw-r--r--app/models/blob_viewer/auxiliary.rb2
-rw-r--r--app/models/blob_viewer/balsamiq.rb2
-rw-r--r--app/models/blob_viewer/base.rb2
-rw-r--r--app/models/blob_viewer/binary_stl.rb2
-rw-r--r--app/models/blob_viewer/cartfile.rb2
-rw-r--r--app/models/blob_viewer/changelog.rb2
-rw-r--r--app/models/blob_viewer/client_side.rb2
-rw-r--r--app/models/blob_viewer/composer_json.rb2
-rw-r--r--app/models/blob_viewer/contributing.rb2
-rw-r--r--app/models/blob_viewer/dependency_manager.rb2
-rw-r--r--app/models/blob_viewer/download.rb2
-rw-r--r--app/models/blob_viewer/empty.rb2
-rw-r--r--app/models/blob_viewer/gemfile.rb2
-rw-r--r--app/models/blob_viewer/gemspec.rb2
-rw-r--r--app/models/blob_viewer/gitlab_ci_yml.rb2
-rw-r--r--app/models/blob_viewer/godeps_json.rb2
-rw-r--r--app/models/blob_viewer/image.rb2
-rw-r--r--app/models/blob_viewer/license.rb2
-rw-r--r--app/models/blob_viewer/markup.rb2
-rw-r--r--app/models/blob_viewer/notebook.rb2
-rw-r--r--app/models/blob_viewer/package_json.rb2
-rw-r--r--app/models/blob_viewer/pdf.rb2
-rw-r--r--app/models/blob_viewer/podfile.rb2
-rw-r--r--app/models/blob_viewer/podspec.rb2
-rw-r--r--app/models/blob_viewer/podspec_json.rb2
-rw-r--r--app/models/blob_viewer/readme.rb2
-rw-r--r--app/models/blob_viewer/requirements_txt.rb2
-rw-r--r--app/models/blob_viewer/rich.rb2
-rw-r--r--app/models/blob_viewer/route_map.rb2
-rw-r--r--app/models/blob_viewer/server_side.rb2
-rw-r--r--app/models/blob_viewer/simple.rb2
-rw-r--r--app/models/blob_viewer/sketch.rb2
-rw-r--r--app/models/blob_viewer/static.rb2
-rw-r--r--app/models/blob_viewer/svg.rb2
-rw-r--r--app/models/blob_viewer/text.rb2
-rw-r--r--app/models/blob_viewer/text_stl.rb2
-rw-r--r--app/models/blob_viewer/video.rb2
-rw-r--r--app/models/blob_viewer/yarn_lock.rb2
-rw-r--r--app/models/board.rb6
-rw-r--r--app/models/broadcast_message.rb2
-rw-r--r--app/models/chat_name.rb2
-rw-r--r--app/models/chat_team.rb2
-rw-r--r--app/models/ci/artifact_blob.rb2
-rw-r--r--app/models/ci/build.rb109
-rw-r--r--app/models/ci/build_metadata.rb2
-rw-r--r--app/models/ci/build_runner_session.rb27
-rw-r--r--app/models/ci/build_trace_chunk.rb151
-rw-r--r--app/models/ci/build_trace_chunks/database.rb31
-rw-r--r--app/models/ci/build_trace_chunks/fog.rb61
-rw-r--r--app/models/ci/build_trace_chunks/redis.rb53
-rw-r--r--app/models/ci/build_trace_section.rb2
-rw-r--r--app/models/ci/build_trace_section_name.rb2
-rw-r--r--app/models/ci/group.rb2
-rw-r--r--app/models/ci/group_variable.rb2
-rw-r--r--app/models/ci/job_artifact.rb49
-rw-r--r--app/models/ci/legacy_stage.rb2
-rw-r--r--app/models/ci/pipeline.rb14
-rw-r--r--app/models/ci/pipeline_schedule.rb2
-rw-r--r--app/models/ci/pipeline_schedule_variable.rb2
-rw-r--r--app/models/ci/pipeline_variable.rb2
-rw-r--r--app/models/ci/runner.rb36
-rw-r--r--app/models/ci/runner_namespace.rb2
-rw-r--r--app/models/ci/runner_project.rb2
-rw-r--r--app/models/ci/stage.rb2
-rw-r--r--app/models/ci/trigger.rb2
-rw-r--r--app/models/ci/trigger_request.rb2
-rw-r--r--app/models/ci/variable.rb2
-rw-r--r--app/models/clusters/applications/helm.rb51
-rw-r--r--app/models/clusters/applications/ingress.rb12
-rw-r--r--app/models/clusters/applications/jupyter.rb10
-rw-r--r--app/models/clusters/applications/prometheus.rb17
-rw-r--r--app/models/clusters/applications/runner.rb10
-rw-r--r--app/models/clusters/cluster.rb2
-rw-r--r--app/models/clusters/concerns/application_core.rb2
-rw-r--r--app/models/clusters/concerns/application_data.rb28
-rw-r--r--app/models/clusters/concerns/application_status.rb2
-rw-r--r--app/models/clusters/concerns/application_version.rb17
-rw-r--r--app/models/clusters/platforms/kubernetes.rb2
-rw-r--r--app/models/clusters/project.rb2
-rw-r--r--app/models/clusters/providers/gcp.rb2
-rw-r--r--app/models/commit.rb10
-rw-r--r--app/models/commit_range.rb2
-rw-r--r--app/models/commit_status.rb5
-rw-r--r--app/models/compare.rb2
-rw-r--r--app/models/concerns/access_requestable.rb2
-rw-r--r--app/models/concerns/artifact_migratable.rb2
-rw-r--r--app/models/concerns/atomic_internal_id.rb23
-rw-r--r--app/models/concerns/avatarable.rb12
-rw-r--r--app/models/concerns/awardable.rb2
-rw-r--r--app/models/concerns/batch_destroy_dependent_associations.rb2
-rw-r--r--app/models/concerns/blob_like.rb2
-rw-r--r--app/models/concerns/blocks_json_serialization.rb2
-rw-r--r--app/models/concerns/bulk_member_access_load.rb2
-rw-r--r--app/models/concerns/cache_markdown_field.rb24
-rw-r--r--app/models/concerns/cacheable_attributes.rb6
-rw-r--r--app/models/concerns/case_sensitivity.rb2
-rw-r--r--app/models/concerns/chronic_duration_attribute.rb2
-rw-r--r--app/models/concerns/created_at_filterable.rb2
-rw-r--r--app/models/concerns/deployment_platform.rb2
-rw-r--r--app/models/concerns/diff_file.rb2
-rw-r--r--app/models/concerns/discussion_on_diff.rb2
-rw-r--r--app/models/concerns/each_batch.rb2
-rw-r--r--app/models/concerns/editable.rb2
-rw-r--r--app/models/concerns/enum_with_nil.rb2
-rw-r--r--app/models/concerns/expirable.rb2
-rw-r--r--app/models/concerns/fast_destroy_all.rb2
-rw-r--r--app/models/concerns/faster_cache_keys.rb2
-rw-r--r--app/models/concerns/feature_gate.rb2
-rw-r--r--app/models/concerns/ghost_user.rb2
-rw-r--r--app/models/concerns/group_descendant.rb6
-rw-r--r--app/models/concerns/has_status.rb2
-rw-r--r--app/models/concerns/has_variable.rb2
-rw-r--r--app/models/concerns/ignorable_column.rb2
-rw-r--r--app/models/concerns/iid_routes.rb2
-rw-r--r--app/models/concerns/importable.rb2
-rw-r--r--app/models/concerns/issuable.rb10
-rw-r--r--app/models/concerns/label_eventable.rb16
-rw-r--r--app/models/concerns/loaded_in_group_list.rb2
-rw-r--r--app/models/concerns/manual_inverse_association.rb2
-rw-r--r--app/models/concerns/mentionable.rb2
-rw-r--r--app/models/concerns/mentionable/reference_regexes.rb2
-rw-r--r--app/models/concerns/milestoneish.rb2
-rw-r--r--app/models/concerns/note_on_diff.rb2
-rw-r--r--app/models/concerns/noteable.rb2
-rw-r--r--app/models/concerns/participable.rb2
-rw-r--r--app/models/concerns/presentable.rb2
-rw-r--r--app/models/concerns/project_features_compatibility.rb2
-rw-r--r--app/models/concerns/prometheus_adapter.rb17
-rw-r--r--app/models/concerns/protected_branch_access.rb2
-rw-r--r--app/models/concerns/protected_ref.rb4
-rw-r--r--app/models/concerns/protected_ref_access.rb14
-rw-r--r--app/models/concerns/protected_tag_access.rb2
-rw-r--r--app/models/concerns/reactive_caching.rb40
-rw-r--r--app/models/concerns/reactive_service.rb2
-rw-r--r--app/models/concerns/redis_cacheable.rb2
-rw-r--r--app/models/concerns/referable.rb2
-rw-r--r--app/models/concerns/relative_positioning.rb2
-rw-r--r--app/models/concerns/resolvable_discussion.rb2
-rw-r--r--app/models/concerns/resolvable_note.rb2
-rw-r--r--app/models/concerns/routable.rb46
-rw-r--r--app/models/concerns/select_for_project_authorization.rb9
-rw-r--r--app/models/concerns/sha_attribute.rb2
-rw-r--r--app/models/concerns/sortable.rb3
-rw-r--r--app/models/concerns/spammable.rb2
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb15
-rw-r--r--app/models/concerns/storage/legacy_project_wiki.rb2
-rw-r--r--app/models/concerns/storage/legacy_repository.rb2
-rw-r--r--app/models/concerns/strip_attribute.rb2
-rw-r--r--app/models/concerns/subscribable.rb2
-rw-r--r--app/models/concerns/taskable.rb2
-rw-r--r--app/models/concerns/throttled_touch.rb2
-rw-r--r--app/models/concerns/time_trackable.rb2
-rw-r--r--app/models/concerns/token_authenticatable.rb2
-rw-r--r--app/models/concerns/triggerable_hooks.rb2
-rw-r--r--app/models/concerns/uniquify.rb2
-rw-r--r--app/models/concerns/updated_at_filterable.rb2
-rw-r--r--app/models/concerns/valid_attribute.rb2
-rw-r--r--app/models/concerns/with_uploads.rb2
-rw-r--r--app/models/container_repository.rb2
-rw-r--r--app/models/conversational_development_index/card.rb2
-rw-r--r--app/models/conversational_development_index/idea_to_production_step.rb2
-rw-r--r--app/models/conversational_development_index/metric.rb2
-rw-r--r--app/models/cycle_analytics.rb2
-rw-r--r--app/models/dashboard_group_milestone.rb36
-rw-r--r--app/models/dashboard_milestone.rb2
-rw-r--r--app/models/deploy_key.rb2
-rw-r--r--app/models/deploy_keys_project.rb2
-rw-r--r--app/models/deploy_token.rb11
-rw-r--r--app/models/deployment.rb6
-rw-r--r--app/models/diff_discussion.rb2
-rw-r--r--app/models/diff_note.rb2
-rw-r--r--app/models/diff_viewer/added.rb2
-rw-r--r--app/models/diff_viewer/base.rb2
-rw-r--r--app/models/diff_viewer/client_side.rb2
-rw-r--r--app/models/diff_viewer/deleted.rb2
-rw-r--r--app/models/diff_viewer/image.rb2
-rw-r--r--app/models/diff_viewer/mode_changed.rb2
-rw-r--r--app/models/diff_viewer/no_preview.rb2
-rw-r--r--app/models/diff_viewer/not_diffable.rb2
-rw-r--r--app/models/diff_viewer/renamed.rb2
-rw-r--r--app/models/diff_viewer/rich.rb2
-rw-r--r--app/models/diff_viewer/server_side.rb2
-rw-r--r--app/models/diff_viewer/simple.rb2
-rw-r--r--app/models/diff_viewer/static.rb2
-rw-r--r--app/models/diff_viewer/text.rb2
-rw-r--r--app/models/directly_addressed_user.rb2
-rw-r--r--app/models/discussion.rb2
-rw-r--r--app/models/discussion_note.rb2
-rw-r--r--app/models/email.rb6
-rw-r--r--app/models/environment.rb6
-rw-r--r--app/models/epic.rb2
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/event_collection.rb2
-rw-r--r--app/models/external_issue.rb2
-rw-r--r--app/models/fork_network.rb2
-rw-r--r--app/models/fork_network_member.rb2
-rw-r--r--app/models/forked_project_link.rb2
-rw-r--r--app/models/generic_commit_status.rb2
-rw-r--r--app/models/global_label.rb2
-rw-r--r--app/models/global_milestone.rb2
-rw-r--r--app/models/gpg_key.rb2
-rw-r--r--app/models/gpg_key_subkey.rb2
-rw-r--r--app/models/gpg_signature.rb2
-rw-r--r--app/models/group.rb18
-rw-r--r--app/models/group_custom_attribute.rb2
-rw-r--r--app/models/group_label.rb2
-rw-r--r--app/models/group_milestone.rb2
-rw-r--r--app/models/guest.rb2
-rw-r--r--app/models/hooks/project_hook.rb2
-rw-r--r--app/models/hooks/service_hook.rb2
-rw-r--r--app/models/hooks/system_hook.rb2
-rw-r--r--app/models/hooks/web_hook.rb2
-rw-r--r--app/models/hooks/web_hook_log.rb7
-rw-r--r--app/models/identity.rb2
-rw-r--r--app/models/import_export_upload.rb16
-rw-r--r--app/models/individual_note_discussion.rb2
-rw-r--r--app/models/instance_configuration.rb2
-rw-r--r--app/models/internal_id.rb38
-rw-r--r--app/models/issue.rb7
-rw-r--r--app/models/issue/metrics.rb2
-rw-r--r--app/models/issue_assignee.rb2
-rw-r--r--app/models/issue_collection.rb2
-rw-r--r--app/models/key.rb2
-rw-r--r--app/models/label.rb14
-rw-r--r--app/models/label_link.rb2
-rw-r--r--app/models/label_priority.rb2
-rw-r--r--app/models/legacy_diff_discussion.rb2
-rw-r--r--app/models/legacy_diff_note.rb2
-rw-r--r--app/models/lfs_file_lock.rb2
-rw-r--r--app/models/lfs_object.rb2
-rw-r--r--app/models/lfs_objects_project.rb2
-rw-r--r--app/models/list.rb8
-rw-r--r--app/models/member.rb8
-rw-r--r--app/models/members/group_member.rb2
-rw-r--r--app/models/members/project_member.rb8
-rw-r--r--app/models/merge_request.rb112
-rw-r--r--app/models/merge_request/metrics.rb2
-rw-r--r--app/models/merge_request_diff.rb12
-rw-r--r--app/models/merge_request_diff_commit.rb2
-rw-r--r--app/models/merge_request_diff_file.rb2
-rw-r--r--app/models/merge_requests_closing_issues.rb2
-rw-r--r--app/models/milestone.rb36
-rw-r--r--app/models/namespace.rb4
-rw-r--r--app/models/network/commit.rb6
-rw-r--r--app/models/network/graph.rb2
-rw-r--r--app/models/note.rb10
-rw-r--r--app/models/note_diff_file.rb2
-rw-r--r--app/models/notification_reason.rb2
-rw-r--r--app/models/notification_recipient.rb2
-rw-r--r--app/models/notification_setting.rb3
-rw-r--r--app/models/oauth_access_grant.rb2
-rw-r--r--app/models/oauth_access_token.rb2
-rw-r--r--app/models/out_of_context_discussion.rb2
-rw-r--r--app/models/pages_domain.rb2
-rw-r--r--app/models/personal_access_token.rb2
-rw-r--r--app/models/personal_snippet.rb2
-rw-r--r--app/models/postgresql/replication_slot.rb32
-rw-r--r--app/models/programming_language.rb6
-rw-r--r--app/models/project.rb168
-rw-r--r--app/models/project_authorization.rb2
-rw-r--r--app/models/project_auto_devops.rb2
-rw-r--r--app/models/project_ci_cd_setting.rb2
-rw-r--r--app/models/project_custom_attribute.rb2
-rw-r--r--app/models/project_deploy_token.rb2
-rw-r--r--app/models/project_feature.rb28
-rw-r--r--app/models/project_group_link.rb5
-rw-r--r--app/models/project_import_data.rb2
-rw-r--r--app/models/project_import_state.rb2
-rw-r--r--app/models/project_label.rb2
-rw-r--r--app/models/project_services/asana_service.rb2
-rw-r--r--app/models/project_services/assembla_service.rb2
-rw-r--r--app/models/project_services/bamboo_service.rb24
-rw-r--r--app/models/project_services/bugzilla_service.rb2
-rw-r--r--app/models/project_services/buildkite_service.rb2
-rw-r--r--app/models/project_services/builds_email_service.rb2
-rw-r--r--app/models/project_services/campfire_service.rb6
-rw-r--r--app/models/project_services/chat_message/base_message.rb2
-rw-r--r--app/models/project_services/chat_message/issue_message.rb2
-rw-r--r--app/models/project_services/chat_message/merge_message.rb2
-rw-r--r--app/models/project_services/chat_message/note_message.rb2
-rw-r--r--app/models/project_services/chat_message/pipeline_message.rb2
-rw-r--r--app/models/project_services/chat_message/push_message.rb2
-rw-r--r--app/models/project_services/chat_message/wiki_page_message.rb2
-rw-r--r--app/models/project_services/chat_notification_service.rb2
-rw-r--r--app/models/project_services/ci_service.rb2
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/deployment_service.rb2
-rw-r--r--app/models/project_services/drone_ci_service.rb2
-rw-r--r--app/models/project_services/emails_on_push_service.rb2
-rw-r--r--app/models/project_services/external_wiki_service.rb2
-rw-r--r--app/models/project_services/flowdock_service.rb2
-rw-r--r--app/models/project_services/gemnasium_service.rb2
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/hangouts_chat_service.rb69
-rw-r--r--app/models/project_services/hipchat_service.rb22
-rw-r--r--app/models/project_services/irker_service.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb2
-rw-r--r--app/models/project_services/jira_service.rb20
-rw-r--r--app/models/project_services/kubernetes_service.rb4
-rw-r--r--app/models/project_services/mattermost_service.rb2
-rw-r--r--app/models/project_services/mattermost_slash_commands_service.rb2
-rw-r--r--app/models/project_services/microsoft_teams_service.rb2
-rw-r--r--app/models/project_services/mock_ci_service.rb2
-rw-r--r--app/models/project_services/mock_deployment_service.rb2
-rw-r--r--app/models/project_services/mock_monitoring_service.rb2
-rw-r--r--app/models/project_services/monitoring_service.rb2
-rw-r--r--app/models/project_services/packagist_service.rb2
-rw-r--r--app/models/project_services/pipelines_email_service.rb2
-rw-r--r--app/models/project_services/pivotaltracker_service.rb2
-rw-r--r--app/models/project_services/prometheus_service.rb2
-rw-r--r--app/models/project_services/pushover_service.rb4
-rw-r--r--app/models/project_services/redmine_service.rb2
-rw-r--r--app/models/project_services/slack_service.rb2
-rw-r--r--app/models/project_services/slack_slash_commands_service.rb2
-rw-r--r--app/models/project_services/slash_commands_service.rb2
-rw-r--r--app/models/project_services/teamcity_service.rb2
-rw-r--r--app/models/project_snippet.rb2
-rw-r--r--app/models/project_statistics.rb25
-rw-r--r--app/models/project_team.rb23
-rw-r--r--app/models/project_wiki.rb16
-rw-r--r--app/models/protectable_dropdown.rb2
-rw-r--r--app/models/protected_branch.rb2
-rw-r--r--app/models/protected_branch/merge_access_level.rb2
-rw-r--r--app/models/protected_branch/push_access_level.rb2
-rw-r--r--app/models/protected_ref_matcher.rb2
-rw-r--r--app/models/protected_tag.rb2
-rw-r--r--app/models/protected_tag/create_access_level.rb2
-rw-r--r--app/models/push_event.rb2
-rw-r--r--app/models/push_event_payload.rb2
-rw-r--r--app/models/readme_blob.rb2
-rw-r--r--app/models/redirect_route.rb2
-rw-r--r--app/models/release.rb2
-rw-r--r--app/models/remote_mirror.rb10
-rw-r--r--app/models/repository.rb51
-rw-r--r--app/models/repository_language.rb14
-rw-r--r--app/models/resource_label_event.rb35
-rw-r--r--app/models/route.rb2
-rw-r--r--app/models/security_event.rb2
-rw-r--r--app/models/sent_notification.rb2
-rw-r--r--app/models/service.rb5
-rw-r--r--app/models/site_statistic.rb76
-rw-r--r--app/models/snippet.rb3
-rw-r--r--app/models/snippet_blob.rb2
-rw-r--r--app/models/spam_log.rb2
-rw-r--r--app/models/storage/hashed_project.rb2
-rw-r--r--app/models/storage/legacy_project.rb2
-rw-r--r--app/models/subscription.rb2
-rw-r--r--app/models/system_note_metadata.rb2
-rw-r--r--app/models/term_agreement.rb2
-rw-r--r--app/models/timelog.rb2
-rw-r--r--app/models/todo.rb13
-rw-r--r--app/models/tree.rb2
-rw-r--r--app/models/trending_project.rb2
-rw-r--r--app/models/u2f_registration.rb2
-rw-r--r--app/models/upload.rb2
-rw-r--r--app/models/user.rb51
-rw-r--r--app/models/user_agent_detail.rb2
-rw-r--r--app/models/user_callout.rb2
-rw-r--r--app/models/user_custom_attribute.rb2
-rw-r--r--app/models/user_interacted_project.rb2
-rw-r--r--app/models/user_status.rb17
-rw-r--r--app/models/user_synced_attributes_metadata.rb2
-rw-r--r--app/models/users_star_project.rb2
-rw-r--r--app/models/wiki_directory.rb2
-rw-r--r--app/models/wiki_page.rb5
-rw-r--r--app/policies/application_setting/term_policy.rb2
-rw-r--r--app/policies/base_policy.rb2
-rw-r--r--app/policies/ci/build_policy.rb8
-rw-r--r--app/policies/ci/pipeline_policy.rb2
-rw-r--r--app/policies/ci/pipeline_schedule_policy.rb2
-rw-r--r--app/policies/ci/runner_policy.rb2
-rw-r--r--app/policies/ci/trigger_policy.rb2
-rw-r--r--app/policies/clusters/cluster_policy.rb4
-rw-r--r--app/policies/commit_status_policy.rb2
-rw-r--r--app/policies/concerns/policy_actor.rb36
-rw-r--r--app/policies/deploy_key_policy.rb2
-rw-r--r--app/policies/deploy_token_policy.rb6
-rw-r--r--app/policies/deployment_policy.rb2
-rw-r--r--app/policies/environment_policy.rb13
-rw-r--r--app/policies/external_issue_policy.rb2
-rw-r--r--app/policies/global_policy.rb7
-rw-r--r--app/policies/group_label_policy.rb2
-rw-r--r--app/policies/group_member_policy.rb2
-rw-r--r--app/policies/group_policy.rb19
-rw-r--r--app/policies/issuable_policy.rb2
-rw-r--r--app/policies/issue_policy.rb2
-rw-r--r--app/policies/merge_request_policy.rb2
-rw-r--r--app/policies/namespace_policy.rb2
-rw-r--r--app/policies/nil_policy.rb2
-rw-r--r--app/policies/note_policy.rb2
-rw-r--r--app/policies/personal_snippet_policy.rb2
-rw-r--r--app/policies/project_label_policy.rb2
-rw-r--r--app/policies/project_member_policy.rb2
-rw-r--r--app/policies/project_policy.rb10
-rw-r--r--app/policies/project_policy/class_methods.rb2
-rw-r--r--app/policies/project_snippet_policy.rb2
-rw-r--r--app/policies/protected_branch_policy.rb2
-rw-r--r--app/policies/user_policy.rb9
-rw-r--r--app/presenters/ci/build_metadata_presenter.rb2
-rw-r--r--app/presenters/ci/build_presenter.rb2
-rw-r--r--app/presenters/ci/build_runner_presenter.rb43
-rw-r--r--app/presenters/ci/group_variable_presenter.rb2
-rw-r--r--app/presenters/ci/pipeline_presenter.rb2
-rw-r--r--app/presenters/ci/variable_presenter.rb2
-rw-r--r--app/presenters/clusters/cluster_presenter.rb2
-rw-r--r--app/presenters/commit_status_presenter.rb16
-rw-r--r--app/presenters/conversational_development_index/metric_presenter.rb2
-rw-r--r--app/presenters/generic_commit_status_presenter.rb2
-rw-r--r--app/presenters/group_member_presenter.rb2
-rw-r--r--app/presenters/member_presenter.rb2
-rw-r--r--app/presenters/members_presenter.rb2
-rw-r--r--app/presenters/merge_request_presenter.rb4
-rw-r--r--app/presenters/project_member_presenter.rb2
-rw-r--r--app/presenters/project_presenter.rb7
-rw-r--r--app/presenters/projects/settings/deploy_keys_presenter.rb2
-rw-r--r--app/serializers/analytics_build_entity.rb2
-rw-r--r--app/serializers/analytics_build_serializer.rb2
-rw-r--r--app/serializers/analytics_commit_entity.rb2
-rw-r--r--app/serializers/analytics_commit_serializer.rb2
-rw-r--r--app/serializers/analytics_generic_serializer.rb2
-rw-r--r--app/serializers/analytics_issue_entity.rb2
-rw-r--r--app/serializers/analytics_issue_serializer.rb2
-rw-r--r--app/serializers/analytics_merge_request_entity.rb2
-rw-r--r--app/serializers/analytics_merge_request_serializer.rb2
-rw-r--r--app/serializers/analytics_stage_entity.rb2
-rw-r--r--app/serializers/analytics_stage_serializer.rb2
-rw-r--r--app/serializers/analytics_summary_entity.rb2
-rw-r--r--app/serializers/analytics_summary_serializer.rb2
-rw-r--r--app/serializers/award_emoji_entity.rb2
-rw-r--r--app/serializers/base_serializer.rb2
-rw-r--r--app/serializers/blob_entity.rb2
-rw-r--r--app/serializers/build_action_entity.rb2
-rw-r--r--app/serializers/build_artifact_entity.rb2
-rw-r--r--app/serializers/build_details_entity.rb4
-rw-r--r--app/serializers/build_metadata_entity.rb2
-rw-r--r--app/serializers/build_serializer.rb2
-rw-r--r--app/serializers/cluster_application_entity.rb2
-rw-r--r--app/serializers/cluster_entity.rb2
-rw-r--r--app/serializers/cluster_serializer.rb2
-rw-r--r--app/serializers/cohort_activity_month_entity.rb2
-rw-r--r--app/serializers/cohort_entity.rb2
-rw-r--r--app/serializers/cohorts_entity.rb2
-rw-r--r--app/serializers/cohorts_serializer.rb2
-rw-r--r--app/serializers/commit_entity.rb2
-rw-r--r--app/serializers/concerns/user_status_tooltip.rb19
-rw-r--r--app/serializers/concerns/with_pagination.rb2
-rw-r--r--app/serializers/container_repositories_serializer.rb2
-rw-r--r--app/serializers/container_repository_entity.rb2
-rw-r--r--app/serializers/container_tag_entity.rb2
-rw-r--r--app/serializers/container_tags_serializer.rb2
-rw-r--r--app/serializers/deploy_key_entity.rb2
-rw-r--r--app/serializers/deploy_key_serializer.rb2
-rw-r--r--app/serializers/deploy_keys_project_entity.rb2
-rw-r--r--app/serializers/deployment_entity.rb2
-rw-r--r--app/serializers/deployment_serializer.rb2
-rw-r--r--app/serializers/diff_file_entity.rb7
-rw-r--r--app/serializers/diffs_entity.rb2
-rw-r--r--app/serializers/diffs_serializer.rb2
-rw-r--r--app/serializers/discussion_entity.rb4
-rw-r--r--app/serializers/discussion_serializer.rb2
-rw-r--r--app/serializers/entity_date_helper.rb23
-rw-r--r--app/serializers/entity_request.rb2
-rw-r--r--app/serializers/environment_entity.rb14
-rw-r--r--app/serializers/environment_serializer.rb2
-rw-r--r--app/serializers/group_child_entity.rb2
-rw-r--r--app/serializers/group_child_serializer.rb2
-rw-r--r--app/serializers/group_entity.rb2
-rw-r--r--app/serializers/group_serializer.rb2
-rw-r--r--app/serializers/group_variable_entity.rb2
-rw-r--r--app/serializers/group_variable_serializer.rb2
-rw-r--r--app/serializers/issuable_entity.rb2
-rw-r--r--app/serializers/issuable_sidebar_entity.rb2
-rw-r--r--app/serializers/issue_entity.rb2
-rw-r--r--app/serializers/issue_serializer.rb2
-rw-r--r--app/serializers/issue_sidebar_entity.rb2
-rw-r--r--app/serializers/job_entity.rb2
-rw-r--r--app/serializers/job_group_entity.rb2
-rw-r--r--app/serializers/label_entity.rb2
-rw-r--r--app/serializers/label_serializer.rb2
-rw-r--r--app/serializers/lfs_file_lock_entity.rb2
-rw-r--r--app/serializers/lfs_file_lock_serializer.rb2
-rw-r--r--app/serializers/merge_request_basic_entity.rb2
-rw-r--r--app/serializers/merge_request_basic_serializer.rb2
-rw-r--r--app/serializers/merge_request_create_entity.rb2
-rw-r--r--app/serializers/merge_request_create_serializer.rb2
-rw-r--r--app/serializers/merge_request_diff_entity.rb2
-rw-r--r--app/serializers/merge_request_metrics_entity.rb2
-rw-r--r--app/serializers/merge_request_serializer.rb2
-rw-r--r--app/serializers/merge_request_user_entity.rb2
-rw-r--r--app/serializers/merge_request_widget_entity.rb18
-rw-r--r--app/serializers/note_attachment_entity.rb2
-rw-r--r--app/serializers/note_entity.rb4
-rw-r--r--app/serializers/note_user_entity.rb2
-rw-r--r--app/serializers/pipeline_details_entity.rb2
-rw-r--r--app/serializers/pipeline_entity.rb2
-rw-r--r--app/serializers/pipeline_serializer.rb11
-rw-r--r--app/serializers/project_entity.rb2
-rw-r--r--app/serializers/project_mirror_entity.rb2
-rw-r--r--app/serializers/project_note_entity.rb2
-rw-r--r--app/serializers/project_note_serializer.rb2
-rw-r--r--app/serializers/project_serializer.rb2
-rw-r--r--app/serializers/request_aware_entity.rb2
-rw-r--r--app/serializers/runner_entity.rb4
-rw-r--r--app/serializers/stage_entity.rb2
-rw-r--r--app/serializers/stage_serializer.rb2
-rw-r--r--app/serializers/status_entity.rb2
-rw-r--r--app/serializers/submodule_entity.rb2
-rw-r--r--app/serializers/test_case_entity.rb7
-rw-r--r--app/serializers/test_reports_comparer_entity.rb11
-rw-r--r--app/serializers/test_reports_comparer_serializer.rb3
-rw-r--r--app/serializers/test_suite_comparer_entity.rb14
-rw-r--r--app/serializers/time_trackable_entity.rb2
-rw-r--r--app/serializers/tree_entity.rb2
-rw-r--r--app/serializers/tree_root_entity.rb2
-rw-r--r--app/serializers/tree_serializer.rb2
-rw-r--r--app/serializers/user_entity.rb3
-rw-r--r--app/serializers/user_serializer.rb2
-rw-r--r--app/serializers/variable_entity.rb2
-rw-r--r--app/serializers/variable_serializer.rb2
-rw-r--r--app/services/access_token_validation_service.rb2
-rw-r--r--app/services/after_branch_delete_service.rb2
-rw-r--r--app/services/akismet_service.rb2
-rw-r--r--app/services/application_settings/base_service.rb2
-rw-r--r--app/services/application_settings/update_service.rb2
-rw-r--r--app/services/applications/create_service.rb2
-rw-r--r--app/services/audit_event_service.rb2
-rw-r--r--app/services/auth/container_registry_authentication_service.rb14
-rw-r--r--app/services/badges/base_service.rb2
-rw-r--r--app/services/badges/build_service.rb2
-rw-r--r--app/services/badges/create_service.rb2
-rw-r--r--app/services/badges/update_service.rb4
-rw-r--r--app/services/base_count_service.rb2
-rw-r--r--app/services/base_renderer.rb2
-rw-r--r--app/services/base_service.rb2
-rw-r--r--app/services/boards/base_service.rb2
-rw-r--r--app/services/boards/create_service.rb2
-rw-r--r--app/services/boards/issues/create_service.rb2
-rw-r--r--app/services/boards/issues/list_service.rb29
-rw-r--r--app/services/boards/issues/move_service.rb2
-rw-r--r--app/services/boards/list_service.rb2
-rw-r--r--app/services/boards/lists/create_service.rb2
-rw-r--r--app/services/boards/lists/destroy_service.rb2
-rw-r--r--app/services/boards/lists/generate_service.rb2
-rw-r--r--app/services/boards/lists/list_service.rb2
-rw-r--r--app/services/boards/lists/move_service.rb2
-rw-r--r--app/services/chat_names/authorize_user_service.rb2
-rw-r--r--app/services/chat_names/find_user_service.rb2
-rw-r--r--app/services/ci/compare_test_reports_service.rb37
-rw-r--r--app/services/ci/create_pipeline_schedule_service.rb2
-rw-r--r--app/services/ci/create_pipeline_service.rb2
-rw-r--r--app/services/ci/ensure_stage_service.rb2
-rw-r--r--app/services/ci/extract_sections_from_build_trace_service.rb2
-rw-r--r--app/services/ci/fetch_kubernetes_token_service.rb2
-rw-r--r--app/services/ci/pipeline_trigger_service.rb2
-rw-r--r--app/services/ci/play_build_service.rb2
-rw-r--r--app/services/ci/process_pipeline_service.rb2
-rw-r--r--app/services/ci/register_job_service.rb38
-rw-r--r--app/services/ci/retry_build_service.rb2
-rw-r--r--app/services/ci/retry_pipeline_service.rb2
-rw-r--r--app/services/ci/stop_environments_service.rb4
-rw-r--r--app/services/ci/update_build_queue_service.rb2
-rw-r--r--app/services/ci/update_runner_service.rb2
-rw-r--r--app/services/clusters/applications/base_helm_service.rb2
-rw-r--r--app/services/clusters/applications/check_ingress_ip_address_service.rb2
-rw-r--r--app/services/clusters/applications/check_installation_progress_service.rb10
-rw-r--r--app/services/clusters/applications/install_service.rb2
-rw-r--r--app/services/clusters/applications/schedule_installation_service.rb2
-rw-r--r--app/services/clusters/create_service.rb2
-rw-r--r--app/services/clusters/gcp/fetch_operation_service.rb2
-rw-r--r--app/services/clusters/gcp/finalize_creation_service.rb2
-rw-r--r--app/services/clusters/gcp/provision_service.rb2
-rw-r--r--app/services/clusters/gcp/verify_provision_status_service.rb2
-rw-r--r--app/services/clusters/update_service.rb2
-rw-r--r--app/services/cohorts_service.rb2
-rw-r--r--app/services/commits/change_service.rb4
-rw-r--r--app/services/commits/cherry_pick_service.rb2
-rw-r--r--app/services/commits/create_service.rb2
-rw-r--r--app/services/commits/revert_service.rb2
-rw-r--r--app/services/compare_service.rb2
-rw-r--r--app/services/concerns/exclusive_lease_guard.rb2
-rw-r--r--app/services/concerns/issues/resolve_discussions.rb2
-rw-r--r--app/services/concerns/update_visibility_level.rb2
-rw-r--r--app/services/concerns/users/new_user_notifier.rb2
-rw-r--r--app/services/concerns/users/participable_service.rb2
-rw-r--r--app/services/create_branch_service.rb2
-rw-r--r--app/services/create_deployment_service.rb2
-rw-r--r--app/services/create_release_service.rb2
-rw-r--r--app/services/create_snippet_service.rb2
-rw-r--r--app/services/delete_branch_service.rb2
-rw-r--r--app/services/delete_merged_branches_service.rb2
-rw-r--r--app/services/deploy_keys/create_service.rb2
-rw-r--r--app/services/deploy_tokens/create_service.rb2
-rw-r--r--app/services/discussions/base_service.rb2
-rw-r--r--app/services/discussions/resolve_service.rb2
-rw-r--r--app/services/discussions/update_diff_position_service.rb2
-rw-r--r--app/services/emails/base_service.rb2
-rw-r--r--app/services/emails/confirm_service.rb2
-rw-r--r--app/services/emails/create_service.rb2
-rw-r--r--app/services/emails/destroy_service.rb2
-rw-r--r--app/services/event_create_service.rb2
-rw-r--r--app/services/events/render_service.rb2
-rw-r--r--app/services/files/base_service.rb2
-rw-r--r--app/services/files/create_dir_service.rb2
-rw-r--r--app/services/files/create_service.rb2
-rw-r--r--app/services/files/delete_service.rb2
-rw-r--r--app/services/files/multi_service.rb2
-rw-r--r--app/services/files/update_service.rb2
-rw-r--r--app/services/git_push_service.rb97
-rw-r--r--app/services/git_tag_push_service.rb12
-rw-r--r--app/services/gpg_keys/create_service.rb2
-rw-r--r--app/services/gravatar_service.rb2
-rw-r--r--app/services/groups/base_service.rb2
-rw-r--r--app/services/groups/create_service.rb2
-rw-r--r--app/services/groups/destroy_service.rb2
-rw-r--r--app/services/groups/nested_create_service.rb14
-rw-r--r--app/services/groups/transfer_service.rb2
-rw-r--r--app/services/groups/update_service.rb13
-rw-r--r--app/services/ham_service.rb2
-rw-r--r--app/services/import_export_clean_up_service.rb13
-rw-r--r--app/services/issuable/bulk_update_service.rb2
-rw-r--r--app/services/issuable/common_system_notes_service.rb2
-rw-r--r--app/services/issuable/destroy_service.rb2
-rw-r--r--app/services/issuable_base_service.rb4
-rw-r--r--app/services/issues/base_service.rb7
-rw-r--r--app/services/issues/build_service.rb8
-rw-r--r--app/services/issues/close_service.rb2
-rw-r--r--app/services/issues/create_service.rb2
-rw-r--r--app/services/issues/duplicate_service.rb2
-rw-r--r--app/services/issues/fetch_referenced_merge_requests_service.rb2
-rw-r--r--app/services/issues/move_service.rb2
-rw-r--r--app/services/issues/reopen_service.rb2
-rw-r--r--app/services/issues/update_service.rb4
-rw-r--r--app/services/keys/base_service.rb2
-rw-r--r--app/services/keys/create_service.rb2
-rw-r--r--app/services/keys/destroy_service.rb2
-rw-r--r--app/services/keys/last_used_service.rb2
-rw-r--r--app/services/labels/base_service.rb2
-rw-r--r--app/services/labels/create_service.rb2
-rw-r--r--app/services/labels/find_or_create_service.rb10
-rw-r--r--app/services/labels/promote_service.rb2
-rw-r--r--app/services/labels/transfer_service.rb2
-rw-r--r--app/services/labels/update_service.rb2
-rw-r--r--app/services/lfs/file_transformer.rb2
-rw-r--r--app/services/lfs/lock_file_service.rb2
-rw-r--r--app/services/lfs/locks_finder_service.rb2
-rw-r--r--app/services/lfs/unlock_file_service.rb2
-rw-r--r--app/services/mattermost/create_team_service.rb2
-rw-r--r--app/services/members/approve_access_request_service.rb2
-rw-r--r--app/services/members/base_service.rb2
-rw-r--r--app/services/members/create_service.rb2
-rw-r--r--app/services/members/destroy_service.rb10
-rw-r--r--app/services/members/request_access_service.rb2
-rw-r--r--app/services/members/update_service.rb4
-rw-r--r--app/services/merge_request_metrics_service.rb2
-rw-r--r--app/services/merge_requests/add_todo_when_build_fails_service.rb2
-rw-r--r--app/services/merge_requests/assign_issues_service.rb2
-rw-r--r--app/services/merge_requests/base_service.rb2
-rw-r--r--app/services/merge_requests/build_service.rb11
-rw-r--r--app/services/merge_requests/close_service.rb2
-rw-r--r--app/services/merge_requests/conflicts/base_service.rb2
-rw-r--r--app/services/merge_requests/conflicts/list_service.rb2
-rw-r--r--app/services/merge_requests/conflicts/resolve_service.rb2
-rw-r--r--app/services/merge_requests/create_from_issue_service.rb2
-rw-r--r--app/services/merge_requests/create_service.rb2
-rw-r--r--app/services/merge_requests/delete_non_latest_diffs_service.rb2
-rw-r--r--app/services/merge_requests/ff_merge_service.rb2
-rw-r--r--app/services/merge_requests/get_urls_service.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb2
-rw-r--r--app/services/merge_requests/merge_when_pipeline_succeeds_service.rb2
-rw-r--r--app/services/merge_requests/post_merge_service.rb4
-rw-r--r--app/services/merge_requests/rebase_service.rb4
-rw-r--r--app/services/merge_requests/refresh_service.rb2
-rw-r--r--app/services/merge_requests/reload_diffs_service.rb2
-rw-r--r--app/services/merge_requests/reopen_service.rb3
-rw-r--r--app/services/merge_requests/resolved_discussion_notification_service.rb2
-rw-r--r--app/services/merge_requests/squash_service.rb2
-rw-r--r--app/services/merge_requests/update_service.rb2
-rw-r--r--app/services/merge_requests/working_copy_base_service.rb2
-rw-r--r--app/services/metrics_service.rb23
-rw-r--r--app/services/milestones/base_service.rb2
-rw-r--r--app/services/milestones/close_service.rb2
-rw-r--r--app/services/milestones/create_service.rb2
-rw-r--r--app/services/milestones/destroy_service.rb2
-rw-r--r--app/services/milestones/promote_service.rb2
-rw-r--r--app/services/milestones/reopen_service.rb2
-rw-r--r--app/services/milestones/update_service.rb4
-rw-r--r--app/services/note_summary.rb2
-rw-r--r--app/services/notes/build_service.rb2
-rw-r--r--app/services/notes/create_service.rb2
-rw-r--r--app/services/notes/destroy_service.rb2
-rw-r--r--app/services/notes/post_process_service.rb2
-rw-r--r--app/services/notes/quick_actions_service.rb2
-rw-r--r--app/services/notes/render_service.rb2
-rw-r--r--app/services/notes/resolve_service.rb2
-rw-r--r--app/services/notes/update_service.rb4
-rw-r--r--app/services/notification_recipient_service.rb15
-rw-r--r--app/services/notification_service.rb18
-rw-r--r--app/services/preview_markdown_service.rb9
-rw-r--r--app/services/projects/after_import_service.rb2
-rw-r--r--app/services/projects/autocomplete_service.rb36
-rw-r--r--app/services/projects/base_move_relations_service.rb2
-rw-r--r--app/services/projects/batch_count_service.rb2
-rw-r--r--app/services/projects/batch_forks_count_service.rb2
-rw-r--r--app/services/projects/batch_open_issues_count_service.rb2
-rw-r--r--app/services/projects/count_service.rb2
-rw-r--r--app/services/projects/create_from_template_service.rb14
-rw-r--r--app/services/projects/create_service.rb20
-rw-r--r--app/services/projects/destroy_service.rb4
-rw-r--r--app/services/projects/detect_repository_languages_service.rb52
-rw-r--r--app/services/projects/download_service.rb2
-rw-r--r--app/services/projects/enable_deploy_key_service.rb2
-rw-r--r--app/services/projects/fork_service.rb4
-rw-r--r--app/services/projects/forks_count_service.rb2
-rw-r--r--app/services/projects/gitlab_projects_import_service.rb65
-rw-r--r--app/services/projects/group_links/create_service.rb2
-rw-r--r--app/services/projects/group_links/destroy_service.rb2
-rw-r--r--app/services/projects/hashed_storage/migrate_attachments_service.rb32
-rw-r--r--app/services/projects/hashed_storage/migrate_repository_service.rb27
-rw-r--r--app/services/projects/hashed_storage_migration_service.rb13
-rw-r--r--app/services/projects/housekeeping_service.rb2
-rw-r--r--app/services/projects/import_export/export_service.rb2
-rw-r--r--app/services/projects/import_service.rb6
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_link_list_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_service.rb4
-rw-r--r--app/services/projects/lfs_pointers/lfs_import_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_link_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_list_service.rb2
-rw-r--r--app/services/projects/move_access_service.rb2
-rw-r--r--app/services/projects/move_deploy_keys_projects_service.rb2
-rw-r--r--app/services/projects/move_forks_service.rb2
-rw-r--r--app/services/projects/move_lfs_objects_projects_service.rb2
-rw-r--r--app/services/projects/move_notification_settings_service.rb2
-rw-r--r--app/services/projects/move_project_authorizations_service.rb2
-rw-r--r--app/services/projects/move_project_group_links_service.rb2
-rw-r--r--app/services/projects/move_project_members_service.rb2
-rw-r--r--app/services/projects/move_users_star_projects_service.rb2
-rw-r--r--app/services/projects/open_issues_count_service.rb2
-rw-r--r--app/services/projects/open_merge_requests_count_service.rb2
-rw-r--r--app/services/projects/overwrite_project_service.rb2
-rw-r--r--app/services/projects/participants_service.rb2
-rw-r--r--app/services/projects/propagate_service_template.rb2
-rw-r--r--app/services/projects/transfer_service.rb3
-rw-r--r--app/services/projects/unlink_fork_service.rb2
-rw-r--r--app/services/projects/update_pages_configuration_service.rb2
-rw-r--r--app/services/projects/update_pages_service.rb2
-rw-r--r--app/services/projects/update_remote_mirror_service.rb2
-rw-r--r--app/services/projects/update_service.rb74
-rw-r--r--app/services/prometheus/adapter_service.rb4
-rw-r--r--app/services/protected_branches/access_level_params.rb4
-rw-r--r--app/services/protected_branches/api_service.rb2
-rw-r--r--app/services/protected_branches/create_service.rb2
-rw-r--r--app/services/protected_branches/destroy_service.rb2
-rw-r--r--app/services/protected_branches/legacy_api_create_service.rb6
-rw-r--r--app/services/protected_branches/legacy_api_update_service.rb6
-rw-r--r--app/services/protected_branches/update_service.rb2
-rw-r--r--app/services/protected_tags/create_service.rb2
-rw-r--r--app/services/protected_tags/destroy_service.rb2
-rw-r--r--app/services/protected_tags/update_service.rb2
-rw-r--r--app/services/push_event_payload_service.rb2
-rw-r--r--app/services/quick_actions/interpret_service.rb2
-rw-r--r--app/services/repair_ldap_blocked_user_service.rb2
-rw-r--r--app/services/repository_archive_clean_up_service.rb2
-rw-r--r--app/services/reset_project_cache_service.rb2
-rw-r--r--app/services/resource_events/change_labels_service.rb43
-rw-r--r--app/services/search/global_service.rb2
-rw-r--r--app/services/search/group_service.rb2
-rw-r--r--app/services/search/project_service.rb2
-rw-r--r--app/services/search/snippet_service.rb2
-rw-r--r--app/services/search_service.rb2
-rw-r--r--app/services/spam_check_service.rb2
-rw-r--r--app/services/spam_service.rb2
-rw-r--r--app/services/submit_usage_ping_service.rb2
-rw-r--r--app/services/system_hooks_service.rb2
-rw-r--r--app/services/system_note_service.rb35
-rw-r--r--app/services/tags/create_service.rb2
-rw-r--r--app/services/tags/destroy_service.rb2
-rw-r--r--app/services/test_hooks/base_service.rb2
-rw-r--r--app/services/test_hooks/project_service.rb2
-rw-r--r--app/services/test_hooks/system_service.rb2
-rw-r--r--app/services/todo_service.rb32
-rw-r--r--app/services/todos/destroy/base_service.rb33
-rw-r--r--app/services/todos/destroy/confidential_issue_service.rb39
-rw-r--r--app/services/todos/destroy/entity_leave_service.rb101
-rw-r--r--app/services/todos/destroy/group_private_service.rb30
-rw-r--r--app/services/todos/destroy/private_features_service.rb40
-rw-r--r--app/services/todos/destroy/project_private_service.rb30
-rw-r--r--app/services/update_release_service.rb4
-rw-r--r--app/services/update_snippet_service.rb2
-rw-r--r--app/services/upload_service.rb8
-rw-r--r--app/services/user_agent_detail_service.rb2
-rw-r--r--app/services/user_project_access_changed_service.rb2
-rw-r--r--app/services/users/activity_service.rb23
-rw-r--r--app/services/users/build_service.rb5
-rw-r--r--app/services/users/create_service.rb2
-rw-r--r--app/services/users/destroy_service.rb2
-rw-r--r--app/services/users/last_push_event_service.rb2
-rw-r--r--app/services/users/migrate_to_ghost_user_service.rb2
-rw-r--r--app/services/users/refresh_authorized_projects_service.rb2
-rw-r--r--app/services/users/respond_to_terms_service.rb2
-rw-r--r--app/services/users/set_status_service.rb39
-rw-r--r--app/services/users/update_service.rb14
-rw-r--r--app/services/validate_new_branch_service.rb2
-rw-r--r--app/services/verify_pages_domain_service.rb2
-rw-r--r--app/services/web_hook_service.rb2
-rw-r--r--app/services/wiki_pages/base_service.rb2
-rw-r--r--app/services/wiki_pages/create_service.rb2
-rw-r--r--app/services/wiki_pages/destroy_service.rb2
-rw-r--r--app/services/wiki_pages/update_service.rb2
-rw-r--r--app/uploaders/attachment_uploader.rb2
-rw-r--r--app/uploaders/avatar_uploader.rb2
-rw-r--r--app/uploaders/favicon_uploader.rb2
-rw-r--r--app/uploaders/file_mover.rb2
-rw-r--r--app/uploaders/file_uploader.rb44
-rw-r--r--app/uploaders/gitlab_uploader.rb24
-rw-r--r--app/uploaders/import_export_uploader.rb17
-rw-r--r--app/uploaders/job_artifact_uploader.rb10
-rw-r--r--app/uploaders/legacy_artifact_uploader.rb2
-rw-r--r--app/uploaders/lfs_object_uploader.rb2
-rw-r--r--app/uploaders/namespace_file_uploader.rb2
-rw-r--r--app/uploaders/object_storage.rb2
-rw-r--r--app/uploaders/personal_file_uploader.rb2
-rw-r--r--app/uploaders/records_uploads.rb2
-rw-r--r--app/uploaders/uploader_helper.rb2
-rw-r--r--app/uploaders/workhorse.rb2
-rw-r--r--app/validators/abstract_path_validator.rb2
-rw-r--r--app/validators/certificate_fingerprint_validator.rb2
-rw-r--r--app/validators/certificate_key_validator.rb2
-rw-r--r--app/validators/certificate_validator.rb2
-rw-r--r--app/validators/cluster_name_validator.rb2
-rw-r--r--app/validators/color_validator.rb2
-rw-r--r--app/validators/cron_timezone_validator.rb2
-rw-r--r--app/validators/cron_validator.rb2
-rw-r--r--app/validators/duration_validator.rb2
-rw-r--r--app/validators/email_validator.rb2
-rw-r--r--app/validators/key_restriction_validator.rb2
-rw-r--r--app/validators/line_code_validator.rb2
-rw-r--r--app/validators/namespace_name_validator.rb2
-rw-r--r--app/validators/namespace_path_validator.rb2
-rw-r--r--app/validators/project_path_validator.rb2
-rw-r--r--app/validators/public_url_validator.rb2
-rw-r--r--app/validators/top_level_group_validator.rb2
-rw-r--r--app/validators/url_validator.rb2
-rw-r--r--app/validators/variable_duplicates_validator.rb6
-rw-r--r--app/views/admin/application_settings/_abuse.html.haml4
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml12
-rw-r--r--app/views/admin/application_settings/_background_jobs.html.haml6
-rw-r--r--app/views/admin/application_settings/_ci_cd.html.haml26
-rw-r--r--app/views/admin/application_settings/_email.html.haml2
-rw-r--r--app/views/admin/application_settings/_gitaly.html.haml8
-rw-r--r--app/views/admin/application_settings/_help_page.html.haml6
-rw-r--r--app/views/admin/application_settings/_influx.html.haml16
-rw-r--r--app/views/admin/application_settings/_ip_limits.html.haml14
-rw-r--r--app/views/admin/application_settings/_koding.html.haml4
-rw-r--r--app/views/admin/application_settings/_logging.html.haml6
-rw-r--r--app/views/admin/application_settings/_outbound.html.haml2
-rw-r--r--app/views/admin/application_settings/_pages.html.haml4
-rw-r--r--app/views/admin/application_settings/_performance.html.haml2
-rw-r--r--app/views/admin/application_settings/_performance_bar.html.haml4
-rw-r--r--app/views/admin/application_settings/_plantuml.html.haml4
-rw-r--r--app/views/admin/application_settings/_prometheus.html.haml2
-rw-r--r--app/views/admin/application_settings/_realtime.html.haml4
-rw-r--r--app/views/admin/application_settings/_registry.html.haml4
-rw-r--r--app/views/admin/application_settings/_repository_check.html.haml8
-rw-r--r--app/views/admin/application_settings/_repository_mirrors_form.html.haml4
-rw-r--r--app/views/admin/application_settings/_repository_storage.html.haml16
-rw-r--r--app/views/admin/application_settings/_signin.html.haml14
-rw-r--r--app/views/admin/application_settings/_signup.html.haml12
-rw-r--r--app/views/admin/application_settings/_spam.html.haml12
-rw-r--r--app/views/admin/application_settings/_terminal.html.haml4
-rw-r--r--app/views/admin/application_settings/_terms.html.haml2
-rw-r--r--app/views/admin/application_settings/_third_party_offers.html.haml13
-rw-r--r--app/views/admin/application_settings/_usage.html.haml7
-rw-r--r--app/views/admin/application_settings/_visibility_and_access.html.haml18
-rw-r--r--app/views/admin/application_settings/show.html.haml37
-rw-r--r--app/views/admin/dashboard/index.html.haml4
-rw-r--r--app/views/admin/groups/_form.html.haml10
-rw-r--r--app/views/admin/groups/_group.html.haml4
-rw-r--r--app/views/admin/groups/edit.html.haml4
-rw-r--r--app/views/admin/groups/index.html.haml4
-rw-r--r--app/views/admin/groups/new.html.haml4
-rw-r--r--app/views/admin/groups/show.html.haml56
-rw-r--r--app/views/admin/hooks/_form.html.haml8
-rw-r--r--app/views/admin/identities/edit.html.haml3
-rw-r--r--app/views/admin/identities/index.html.haml2
-rw-r--r--app/views/admin/identities/new.html.haml3
-rw-r--r--app/views/admin/impersonation_tokens/index.html.haml2
-rw-r--r--app/views/admin/labels/_label.html.haml14
-rw-r--r--app/views/admin/labels/index.html.haml5
-rw-r--r--app/views/admin/projects/_projects.html.haml4
-rw-r--r--app/views/admin/runners/_runner.html.haml4
-rw-r--r--app/views/admin/runners/show.html.haml5
-rw-r--r--app/views/admin/users/keys.html.haml2
-rw-r--r--app/views/admin/users/projects.html.haml2
-rw-r--r--app/views/admin/users/show.html.haml12
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml7
-rw-r--r--app/views/dashboard/_activities.html.haml2
-rw-r--r--app/views/dashboard/milestones/_milestone.html.haml2
-rw-r--r--app/views/dashboard/todos/index.html.haml48
-rw-r--r--app/views/doorkeeper/applications/_delete_form.html.haml6
-rw-r--r--app/views/doorkeeper/applications/_form.html.haml14
-rw-r--r--app/views/doorkeeper/applications/edit.html.haml4
-rw-r--r--app/views/doorkeeper/applications/index.html.haml35
-rw-r--r--app/views/doorkeeper/applications/new.html.haml4
-rw-r--r--app/views/doorkeeper/applications/show.html.haml14
-rw-r--r--app/views/doorkeeper/authorizations/error.html.haml2
-rw-r--r--app/views/doorkeeper/authorizations/new.html.haml28
-rw-r--r--app/views/doorkeeper/authorizations/show.html.haml2
-rw-r--r--app/views/doorkeeper/authorized_applications/_delete_form.html.haml2
-rw-r--r--app/views/doorkeeper/authorized_applications/index.html.haml6
-rw-r--r--app/views/explore/_head.html.haml4
-rw-r--r--app/views/explore/groups/_nav.html.haml2
-rw-r--r--app/views/explore/groups/index.html.haml12
-rw-r--r--app/views/explore/projects/_filter.html.haml6
-rw-r--r--app/views/explore/projects/_nav.html.haml6
-rw-r--r--app/views/explore/projects/index.html.haml4
-rw-r--r--app/views/explore/projects/starred.html.haml4
-rw-r--r--app/views/explore/projects/trending.html.haml4
-rw-r--r--app/views/groups/_activities.html.haml2
-rw-r--r--app/views/groups/issues.html.haml2
-rw-r--r--app/views/groups/merge_requests.html.haml2
-rw-r--r--app/views/groups/settings/_general.html.haml7
-rw-r--r--app/views/groups/settings/_permissions.html.haml1
-rw-r--r--app/views/help/_shortcuts.html.haml6
-rw-r--r--app/views/ide/index.html.haml8
-rw-r--r--app/views/import/_githubish_status.html.haml24
-rw-r--r--app/views/import/_project_status.html.haml11
-rw-r--r--app/views/import/bitbucket/deploy_key.js.haml2
-rw-r--r--app/views/import/bitbucket/status.html.haml37
-rw-r--r--app/views/import/bitbucket_server/new.html.haml26
-rw-r--r--app/views/import/bitbucket_server/status.html.haml87
-rw-r--r--app/views/import/fogbugz/new.html.haml18
-rw-r--r--app/views/import/fogbugz/new_user_map.html.haml34
-rw-r--r--app/views/import/fogbugz/status.html.haml27
-rw-r--r--app/views/import/gitea/new.html.haml17
-rw-r--r--app/views/import/gitea/status.html.haml6
-rw-r--r--app/views/import/github/new.html.haml2
-rw-r--r--app/views/import/github/status.html.haml2
-rw-r--r--app/views/import/gitlab/status.html.haml22
-rw-r--r--app/views/import/gitlab_projects/new.html.haml18
-rw-r--r--app/views/import/google_code/new.html.haml42
-rw-r--r--app/views/import/google_code/new_user_map.html.haml36
-rw-r--r--app/views/import/google_code/status.html.haml40
-rw-r--r--app/views/import/manifest/_form.html.haml23
-rw-r--r--app/views/import/manifest/new.html.haml12
-rw-r--r--app/views/import/manifest/status.html.haml42
-rw-r--r--app/views/instance_statistics/cohorts/_cohorts_table.html.haml (renamed from app/views/admin/cohorts/_cohorts_table.html.haml)0
-rw-r--r--app/views/instance_statistics/cohorts/_usage_ping.html.haml (renamed from app/views/admin/cohorts/_usage_ping.html.haml)0
-rw-r--r--app/views/instance_statistics/cohorts/index.html.haml (renamed from app/views/admin/cohorts/index.html.haml)0
-rw-r--r--app/views/instance_statistics/conversational_development_index/_callout.html.haml (renamed from app/views/admin/conversational_development_index/_callout.html.haml)0
-rw-r--r--app/views/instance_statistics/conversational_development_index/_card.html.haml (renamed from app/views/admin/conversational_development_index/_card.html.haml)0
-rw-r--r--app/views/instance_statistics/conversational_development_index/_disabled.html.haml (renamed from app/views/admin/conversational_development_index/_disabled.html.haml)0
-rw-r--r--app/views/instance_statistics/conversational_development_index/_no_data.html.haml (renamed from app/views/admin/conversational_development_index/_no_data.html.haml)0
-rw-r--r--app/views/instance_statistics/conversational_development_index/index.html.haml (renamed from app/views/admin/conversational_development_index/show.html.haml)0
-rw-r--r--app/views/layouts/_head.html.haml5
-rw-r--r--app/views/layouts/_recaptcha_verification.html.haml4
-rw-r--r--app/views/layouts/_search.html.haml14
-rw-r--r--app/views/layouts/admin.html.haml4
-rw-r--r--app/views/layouts/dashboard.html.haml4
-rw-r--r--app/views/layouts/devise.html.haml13
-rw-r--r--app/views/layouts/devise_empty.html.haml6
-rw-r--r--app/views/layouts/explore.html.haml4
-rw-r--r--app/views/layouts/group_settings.html.haml2
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml6
-rw-r--r--app/views/layouts/header/_default.html.haml22
-rw-r--r--app/views/layouts/header/_new_dropdown.haml24
-rw-r--r--app/views/layouts/help.html.haml6
-rw-r--r--app/views/layouts/instance_statistics.html.haml6
-rw-r--r--app/views/layouts/koding.html.haml6
-rw-r--r--app/views/layouts/mailer.text.erb2
-rw-r--r--app/views/layouts/mailer/devise.html.haml12
-rw-r--r--app/views/layouts/nav/_breadcrumbs.html.haml2
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml64
-rw-r--r--app/views/layouts/nav/_explore.html.haml14
-rw-r--r--app/views/layouts/nav/groups_dropdown/_show.html.haml12
-rw-r--r--app/views/layouts/nav/projects_dropdown/_show.html.haml6
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml111
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml54
-rw-r--r--app/views/layouts/nav/sidebar/_instance_statistics.html.haml33
-rw-r--r--app/views/layouts/nav/sidebar/_profile.html.haml56
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml73
-rw-r--r--app/views/layouts/notify.html.haml7
-rw-r--r--app/views/layouts/profile.html.haml4
-rw-r--r--app/views/layouts/project_settings.html.haml2
-rw-r--r--app/views/layouts/search.html.haml4
-rw-r--r--app/views/layouts/snippets.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml2
-rw-r--r--app/views/profiles/emails/index.html.haml2
-rw-r--r--app/views/profiles/gpg_keys/_form.html.haml2
-rw-r--r--app/views/profiles/keys/_form.html.haml17
-rw-r--r--app/views/profiles/notifications/show.html.haml4
-rw-r--r--app/views/profiles/passwords/edit.html.haml6
-rw-r--r--app/views/profiles/personal_access_tokens/index.html.haml4
-rw-r--r--app/views/profiles/preferences/show.html.haml6
-rw-r--r--app/views/profiles/show.html.haml38
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml10
-rw-r--r--app/views/projects/_activity.html.haml3
-rw-r--r--app/views/projects/_export.html.haml2
-rw-r--r--app/views/projects/_home_panel.html.haml5
-rw-r--r--app/views/projects/_import_project_pane.html.haml53
-rw-r--r--app/views/projects/_merge_request_merge_method_settings.html.haml2
-rw-r--r--app/views/projects/_new_project_fields.html.haml26
-rw-r--r--app/views/projects/_project_templates.html.haml31
-rw-r--r--app/views/projects/artifacts/file.html.haml2
-rw-r--r--app/views/projects/blame/show.html.haml2
-rw-r--r--app/views/projects/blob/_header.html.haml2
-rw-r--r--app/views/projects/blob/show.html.haml3
-rw-r--r--app/views/projects/buttons/_star.html.haml2
-rw-r--r--app/views/projects/clusters/_dropdown.html.haml12
-rw-r--r--app/views/projects/clusters/_gcp_signup_offer_banner.html.haml14
-rw-r--r--app/views/projects/clusters/gcp/_form.html.haml20
-rw-r--r--app/views/projects/clusters/gcp/_header.html.haml2
-rw-r--r--app/views/projects/clusters/gcp/login.html.haml21
-rw-r--r--app/views/projects/clusters/gcp/new.html.haml10
-rw-r--r--app/views/projects/clusters/new.html.haml35
-rw-r--r--app/views/projects/clusters/user/_form.html.haml18
-rw-r--r--app/views/projects/clusters/user/_header.html.haml2
-rw-r--r--app/views/projects/clusters/user/_show.html.haml10
-rw-r--r--app/views/projects/clusters/user/new.html.haml11
-rw-r--r--app/views/projects/commit/_change.html.haml4
-rw-r--r--app/views/projects/commit/_commit_box.html.haml5
-rw-r--r--app/views/projects/commits/_commit.html.haml4
-rw-r--r--app/views/projects/deploy_keys/_form.html.haml4
-rw-r--r--app/views/projects/deploy_keys/_index.html.haml2
-rw-r--r--app/views/projects/deploy_tokens/_form.html.haml24
-rw-r--r--app/views/projects/deploy_tokens/_revoke_modal.html.haml2
-rw-r--r--app/views/projects/deployments/_actions.haml7
-rw-r--r--app/views/projects/deployments/_rollback.haml7
-rw-r--r--app/views/projects/edit.html.haml21
-rw-r--r--app/views/projects/environments/_external_url.html.haml2
-rw-r--r--app/views/projects/environments/_form.html.haml4
-rw-r--r--app/views/projects/environments/_stop.html.haml5
-rw-r--r--app/views/projects/environments/empty.html.haml14
-rw-r--r--app/views/projects/environments/metrics.html.haml13
-rw-r--r--app/views/projects/environments/show.html.haml32
-rw-r--r--app/views/projects/forks/_fork_button.html.haml4
-rw-r--r--app/views/projects/imports/new.html.haml6
-rw-r--r--app/views/projects/issues/_form.html.haml4
-rw-r--r--app/views/projects/jobs/_sidebar.html.haml10
-rw-r--r--app/views/projects/jobs/show.html.haml2
-rw-r--r--app/views/projects/jobs/terminal.html.haml11
-rw-r--r--app/views/projects/labels/index.html.haml35
-rw-r--r--app/views/projects/merge_requests/_form.html.haml4
-rw-r--r--app/views/projects/merge_requests/_mr_box.html.haml2
-rw-r--r--app/views/projects/merge_requests/conflicts/_submit_form.html.haml2
-rw-r--r--app/views/projects/merge_requests/creations/_new_compare.html.haml2
-rw-r--r--app/views/projects/merge_requests/creations/_new_submit.html.haml37
-rw-r--r--app/views/projects/milestones/_form.html.haml10
-rw-r--r--app/views/projects/milestones/index.html.haml2
-rw-r--r--app/views/projects/milestones/show.html.haml2
-rw-r--r--app/views/projects/mirrors/_push.html.haml6
-rw-r--r--app/views/projects/new.html.haml13
-rw-r--r--app/views/projects/pages/_destroy.haml4
-rw-r--r--app/views/projects/pipeline_schedules/_form.html.haml12
-rw-r--r--app/views/projects/pipelines/_info.html.haml63
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml3
-rw-r--r--app/views/projects/pipelines/new.html.haml2
-rw-r--r--app/views/projects/pipelines/show.html.haml6
-rw-r--r--app/views/projects/project_members/_new_project_member.html.haml6
-rw-r--r--app/views/projects/project_members/_new_shared_group.html.haml6
-rw-r--r--app/views/projects/project_templates/_built_in_templates.html.haml17
-rw-r--r--app/views/projects/project_templates/_project_fields_form.html.haml12
-rw-r--r--app/views/projects/protected_branches/_update_protected_branch.html.haml2
-rw-r--r--app/views/projects/protected_branches/shared/_create_protected_branch.html.haml1
-rw-r--r--app/views/projects/protected_branches/shared/_index.html.haml2
-rw-r--r--app/views/projects/protected_branches/shared/_protected_branch.html.haml4
-rw-r--r--app/views/projects/protected_tags/_protected_tag.html.haml4
-rw-r--r--app/views/projects/protected_tags/_protected_tag_create_access_levels.haml8
-rw-r--r--app/views/projects/protected_tags/_update_protected_tag.haml5
-rw-r--r--app/views/projects/protected_tags/shared/_create_protected_tag.html.haml1
-rw-r--r--app/views/projects/protected_tags/shared/_protected_tag.html.haml2
-rw-r--r--app/views/projects/releases/edit.html.haml4
-rw-r--r--app/views/projects/runners/_runner.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_metrics.html.haml30
-rw-r--r--app/views/projects/services/prometheus/_show.html.haml33
-rw-r--r--app/views/projects/settings/ci_cd/_autodevops_form.html.haml41
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml53
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml30
-rw-r--r--app/views/projects/settings/integrations/_project_hook.html.haml8
-rw-r--r--app/views/projects/settings/integrations/show.html.haml4
-rw-r--r--app/views/projects/settings/members/show.html.haml2
-rw-r--r--app/views/projects/settings/repository/show.html.haml4
-rw-r--r--app/views/projects/show.html.haml9
-rw-r--r--app/views/projects/snippets/_actions.html.haml26
-rw-r--r--app/views/projects/snippets/edit.html.haml6
-rw-r--r--app/views/projects/snippets/index.html.haml4
-rw-r--r--app/views/projects/snippets/new.html.haml8
-rw-r--r--app/views/projects/snippets/show.html.haml4
-rw-r--r--app/views/projects/tags/index.html.haml2
-rw-r--r--app/views/projects/tree/show.html.haml3
-rw-r--r--app/views/projects/triggers/_form.html.haml4
-rw-r--r--app/views/projects/update.js.haml3
-rw-r--r--app/views/projects/wikis/_form.html.haml4
-rw-r--r--app/views/projects/wikis/_main_links.html.haml2
-rw-r--r--app/views/projects/wikis/_sidebar.html.haml8
-rw-r--r--app/views/shared/_event_filter.html.haml2
-rw-r--r--app/views/shared/_import_form.html.haml2
-rw-r--r--app/views/shared/_label.html.haml6
-rw-r--r--app/views/shared/_label_row.html.haml13
-rw-r--r--app/views/shared/_milestone_expired.html.haml9
-rw-r--r--app/views/shared/_new_project_item_select.html.haml4
-rw-r--r--app/views/shared/_personal_access_tokens_form.html.haml6
-rw-r--r--app/views/shared/_user_dropdown_contributing_link.html.haml5
-rw-r--r--app/views/shared/boards/_show.html.haml7
-rw-r--r--app/views/shared/boards/components/_board.html.haml29
-rw-r--r--app/views/shared/boards/components/_sidebar.html.haml3
-rw-r--r--app/views/shared/boards/components/sidebar/_labels.html.haml3
-rw-r--r--app/views/shared/builds/_build_output.html.haml3
-rw-r--r--app/views/shared/empty_states/_issues.html.haml2
-rw-r--r--app/views/shared/empty_states/_merge_requests.html.haml2
-rw-r--r--app/views/shared/hook_logs/_content.html.haml4
-rw-r--r--app/views/shared/issuable/_milestone_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml14
-rw-r--r--app/views/shared/issuable/form/_metadata.html.haml2
-rw-r--r--app/views/shared/issuable/form/_title.html.haml2
-rw-r--r--app/views/shared/members/_member.html.haml1
-rw-r--r--app/views/shared/milestones/_milestone.html.haml105
-rw-r--r--app/views/shared/notes/_form.html.haml2
-rw-r--r--app/views/shared/notes/_note.html.haml6
-rw-r--r--app/views/shared/notes/_notes_with_form.html.haml2
-rw-r--r--app/views/shared/projects/_project.html.haml4
-rw-r--r--app/views/shared/runners/show.html.haml2
-rw-r--r--app/views/shared/snippets/_embed.html.haml2
-rw-r--r--app/views/shared/snippets/_form.html.haml4
-rw-r--r--app/views/shared/snippets/_header.html.haml1
-rw-r--r--app/views/shared/tokens/_scopes_form.html.haml2
-rw-r--r--app/views/shared/web_hooks/_form.html.haml8
-rw-r--r--app/views/sherlock/queries/_general.html.haml4
-rw-r--r--app/views/users/show.html.haml79
-rw-r--r--app/workers/all_queues.yml11
-rw-r--r--app/workers/archive_trace_worker.rb2
-rw-r--r--app/workers/background_migration_worker.rb61
-rw-r--r--app/workers/ci/archive_traces_cron_worker.rb1
-rw-r--r--app/workers/ci/build_trace_chunk_flush_worker.rb2
-rw-r--r--app/workers/concerns/each_shard_worker.rb2
-rw-r--r--app/workers/concerns/gitlab/github_import/object_importer.rb2
-rw-r--r--app/workers/concerns/todos_destroyer_queue.rb12
-rw-r--r--app/workers/create_gpg_signature_worker.rb20
-rw-r--r--app/workers/delete_diff_files_worker.rb2
-rw-r--r--app/workers/detect_repository_languages_worker.rb33
-rw-r--r--app/workers/email_receiver_worker.rb8
-rw-r--r--app/workers/emails_on_push_worker.rb8
-rw-r--r--app/workers/git_garbage_collect_worker.rb4
-rw-r--r--app/workers/object_storage/migrate_uploads_worker.rb2
-rw-r--r--app/workers/object_storage_upload_worker.rb23
-rw-r--r--app/workers/process_commit_worker.rb5
-rw-r--r--app/workers/project_migrate_hashed_storage_worker.rb4
-rw-r--r--app/workers/prune_web_hook_logs_worker.rb26
-rw-r--r--app/workers/repository_check/batch_worker.rb20
-rw-r--r--app/workers/repository_check/dispatch_worker.rb15
-rw-r--r--app/workers/repository_fork_worker.rb4
-rw-r--r--app/workers/repository_import_worker.rb20
-rw-r--r--app/workers/schedule_update_user_activity_worker.rb12
-rw-r--r--app/workers/todos_destroyer/confidential_issue_worker.rb10
-rw-r--r--app/workers/todos_destroyer/entity_leave_worker.rb10
-rw-r--r--app/workers/todos_destroyer/group_private_worker.rb10
-rw-r--r--app/workers/todos_destroyer/private_features_worker.rb10
-rw-r--r--app/workers/todos_destroyer/project_private_worker.rb10
-rw-r--r--app/workers/update_user_activity_worker.rb27
-rwxr-xr-xbin/changelog14
-rw-r--r--changelogs/no-rm-rf-gitlab-basics.yml5
-rw-r--r--changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml4
-rw-r--r--changelogs/unreleased/1756-set-iid-via-api.yml5
-rw-r--r--changelogs/unreleased/19439-api-file-sha56-and-head.yml5
-rw-r--r--changelogs/unreleased/23705-add-single-file-download-in-repo.yml5
-rw-r--r--changelogs/unreleased/25990-improve-web-terminal.yml5
-rw-r--r--changelogs/unreleased/25990-interactive-web-terminals-authorization.yml5
-rw-r--r--changelogs/unreleased/27456-improve-feedback-when-dev-cannot-push-to-empty-repo.yml5
-rw-r--r--changelogs/unreleased/29278-commits-page-tooltips.yml5
-rw-r--r--changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml5
-rw-r--r--changelogs/unreleased/32783-api-all-members-with-ancestors.yml6
-rw-r--r--changelogs/unreleased/32821-better-error-message-add-invalid-user-to-project.yml5
-rw-r--r--changelogs/unreleased/34572-ssh-certificates.yml5
-rw-r--r--changelogs/unreleased/35158-snippets-api-visibility.yml5
-rw-r--r--changelogs/unreleased/35952-keep-admin-settings-open-after-submit.yml5
-rw-r--r--changelogs/unreleased/36409-frontend-for-clarifying-the-usefulness-of-the-search-bar.yml5
-rw-r--r--changelogs/unreleased/37561-add-id-settings.yml5
-rw-r--r--changelogs/unreleased/38604-add-private-profile.yml5
-rw-r--r--changelogs/unreleased/39604-update-top-right-avatar-after-changing-avatar.yml5
-rw-r--r--changelogs/unreleased/40005-u2f-unspported-browsers.yml5
-rw-r--r--changelogs/unreleased/40484-ordered-lists-copy-gfm.yml5
-rw-r--r--changelogs/unreleased/40973-disable-rack-attack-by-default.yml5
-rw-r--r--changelogs/unreleased/41416-making-instance-wide-data-tools-more-accessible.yml5
-rw-r--r--changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml5
-rw-r--r--changelogs/unreleased/41784-monitoring-graph-popovers.yml5
-rw-r--r--changelogs/unreleased/42342-teams-pipeline-notifications.yml5
-rw-r--r--changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml5
-rw-r--r--changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml5
-rw-r--r--changelogs/unreleased/43312-remove_user_activity_workers.yml5
-rw-r--r--changelogs/unreleased/43472-remove-environment-scope-field-on-cluster-creation-form-for-core-starter-plans.yml5
-rw-r--r--changelogs/unreleased/44127-board-label-edit-drop-down-is-showing-incorrect-selected-labels-summary.yml5
-rw-r--r--changelogs/unreleased/44674-use-one-column-form-layout-on-admin-area-settings-page.yml5
-rw-r--r--changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml5
-rw-r--r--changelogs/unreleased/45318-junit-FE.yml5
-rw-r--r--changelogs/unreleased/45318-vuex-store.yml5
-rw-r--r--changelogs/unreleased/45400-automatically-created-mr-uses-wrong-target-branch-when-branching-from-tag.yml5
-rw-r--r--changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml5
-rw-r--r--changelogs/unreleased/45487-slack-tag-push-notifs.yml5
-rw-r--r--changelogs/unreleased/45557-machine-type-help-links.yml6
-rw-r--r--changelogs/unreleased/45575-invalid-characters-signup.yml5
-rw-r--r--changelogs/unreleased/45703-open-web-ide-file-tree.yml5
-rw-r--r--changelogs/unreleased/45933-webide-fade-uneditable-area.yml5
-rw-r--r--changelogs/unreleased/46165-web-ide-branch-picker.yml5
-rw-r--r--changelogs/unreleased/46202-webide-file-states.yml5
-rw-r--r--changelogs/unreleased/46396-recognise-when-a-user-is-trying-to-validate-a-private-ssh-key-part-1.yml5
-rw-r--r--changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml5
-rw-r--r--changelogs/unreleased/46535-orphaned-uploads.yml5
-rw-r--r--changelogs/unreleased/46546-do-not-pre-select-previous-user-s-when-creating-protected-branches.yml5
-rw-r--r--changelogs/unreleased/46571-webhooks-nil-password.yml5
-rw-r--r--changelogs/unreleased/46703-group-dashboard-line-height-is-too-tall-for-group-names.yml5
-rw-r--r--changelogs/unreleased/46783-removed-omniauth-provider-causing-invalid-application-setting.yml5
-rw-r--r--changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml5
-rw-r--r--changelogs/unreleased/46861-issuable-title-with-longer-username.yml5
-rw-r--r--changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml5
-rw-r--r--changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml5
-rw-r--r--changelogs/unreleased/46940-hashed-storage-extend-enable-hashed-storage-for-all-new-projects-to-for-all-new-and-renamed-projects.yml5
-rw-r--r--changelogs/unreleased/47050-quick-actions-case-insensitive.yml5
-rw-r--r--changelogs/unreleased/47145-quick-actions-confidential.yml5
-rw-r--r--changelogs/unreleased/47156-improve-auto-devops-settings.yml5
-rw-r--r--changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml5
-rw-r--r--changelogs/unreleased/47274-help-users-find-our-contributing-page.yml5
-rw-r--r--changelogs/unreleased/47419-Fix-breadcrumbs.yml5
-rw-r--r--changelogs/unreleased/47516-pipe-scroll.yml5
-rw-r--r--changelogs/unreleased/47548-monospace-commit-messages.yml5
-rw-r--r--changelogs/unreleased/47631-operations-kubernetes-option-is-always-visible-when-repository-or-builds-are-disabled.yml5
-rw-r--r--changelogs/unreleased/47661-merge-request-box-disappearing-on-chrome.yml5
-rw-r--r--changelogs/unreleased/47728-mr-api-documentation-changes.yml5
-rw-r--r--changelogs/unreleased/47768-web-ide-redesign-header.yml5
-rw-r--r--changelogs/unreleased/47769-fix_ambiguous_due_date_for_issue_scopes.yml5
-rw-r--r--changelogs/unreleased/47794-environment-scope-cluster-page.yml6
-rw-r--r--changelogs/unreleased/47865-changelog-for-style-updates.yml5
-rw-r--r--changelogs/unreleased/48036-fix-web-ide-blob-crash.yml5
-rw-r--r--changelogs/unreleased/48050-add-full-commit-sha.yml5
-rw-r--r--changelogs/unreleased/48055-web-ide-resize-handles.yml5
-rw-r--r--changelogs/unreleased/48098-mutual-auth-cluster-applications.yml6
-rw-r--r--changelogs/unreleased/48100-fix-branch-not-shown.yml6
-rw-r--r--changelogs/unreleased/48153-date-selection-dialog-broken-when-creating-a-new-milestone.yml5
-rw-r--r--changelogs/unreleased/48246-osw-load-diffs-improvement.yml5
-rw-r--r--changelogs/unreleased/48378-avatar-upload.yml5
-rw-r--r--changelogs/unreleased/48419-charts-with-long-label-appear-oversized.yml5
-rw-r--r--changelogs/unreleased/48456-fix-system-level-labels-admin-ui.yml5
-rw-r--r--changelogs/unreleased/48461-search-dropdown-hides-shows-when-typing.yml5
-rw-r--r--changelogs/unreleased/48471-sidebar-on-jobs-and-wikis-is-missing-at-small-widths.yml5
-rw-r--r--changelogs/unreleased/48497-merge-request-refactor-displays-changes-dropdown-incorrectly.yml5
-rw-r--r--changelogs/unreleased/48515-sql-queries-are-not-shown-from-the-performance-bar-in-safari.yml5
-rw-r--r--changelogs/unreleased/48528-fix-mr-autocompletion.yml5
-rw-r--r--changelogs/unreleased/48537-update-avatar-only-via-api.yml5
-rw-r--r--changelogs/unreleased/48542-code-link.yml5
-rw-r--r--changelogs/unreleased/48549-markdown-header-code-does-not-have-the-correct-font-size.yml5
-rw-r--r--changelogs/unreleased/48603-merge-request-refactor-title-and-copy-to-clipboard-button-are-behind-the-action-buttons.yml5
-rw-r--r--changelogs/unreleased/48617-promoting-milestone.yml5
-rw-r--r--changelogs/unreleased/48636-new-mr-card-styles.yml5
-rw-r--r--changelogs/unreleased/48657-persist-auto-devops-banner-dismissal-per-user-cookie.yml5
-rw-r--r--changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml5
-rw-r--r--changelogs/unreleased/48804-redesign-gcp-banner.yml5
-rw-r--r--changelogs/unreleased/48823-copy-gfm.yml5
-rw-r--r--changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml6
-rw-r--r--changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml5
-rw-r--r--changelogs/unreleased/48934.yml5
-rw-r--r--changelogs/unreleased/48976-fix-sti-background-migration.yml6
-rw-r--r--changelogs/unreleased/49025-docs-kubernetes-tiller.yml5
-rw-r--r--changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml5
-rw-r--r--changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml5
-rw-r--r--changelogs/unreleased/49161-disable-toggle-comments.yml5
-rw-r--r--changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml5
-rw-r--r--changelogs/unreleased/49291-fix-memory-graph-component-typo.yml5
-rw-r--r--changelogs/unreleased/49324-add-support-for-tar-gz-autodevops-charts.yml5
-rw-r--r--changelogs/unreleased/49364-fix-broadcast-margin.yml5
-rw-r--r--changelogs/unreleased/49375-move-help-popover.yml5
-rw-r--r--changelogs/unreleased/49499-list-of-projects-not-loading-when-trying-to-create-an-issue-from-a-board-typeerror.yml5
-rw-r--r--changelogs/unreleased/49701-sorting-by-name-on-milestones-page-error.yml5
-rw-r--r--changelogs/unreleased/49747-update-poll-2xx.yml5
-rw-r--r--changelogs/unreleased/49776-pipeline-job-log-page-uses-too-much-cpu-for-loading-animation.yml5
-rw-r--r--changelogs/unreleased/49830-use-helm-272.yml5
-rw-r--r--changelogs/unreleased/49835-increase-width.yml5
-rw-r--r--changelogs/unreleased/49851-link-to-runners.yml6
-rw-r--r--changelogs/unreleased/49854-recover-mr-regression-fixes-safe-1.yml5
-rw-r--r--changelogs/unreleased/49854-recover-mr-regression-fixes-safe-2.yml5
-rw-r--r--changelogs/unreleased/49854-recover-mr-regression-fixes-safe-3.yml5
-rw-r--r--changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml5
-rw-r--r--changelogs/unreleased/49899-merge-request-e-mail-link-has-full-url.yml5
-rw-r--r--changelogs/unreleased/49966-improve-junit-fe.yml5
-rw-r--r--changelogs/unreleased/6860-FE-instance-level-project-templates.yml5
-rw-r--r--changelogs/unreleased/accept-rf3-2822-compliant-addresses.yml5
-rw-r--r--changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml5
-rw-r--r--changelogs/unreleased/add-homepage-link-to-status-pages.yml5
-rw-r--r--changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml5
-rw-r--r--changelogs/unreleased/add-missing-index-for-deployments.yml5
-rw-r--r--changelogs/unreleased/add-more-rebase-logging.yml5
-rw-r--r--changelogs/unreleased/add-total-time-flat-printer-for-profiling.yml6
-rw-r--r--changelogs/unreleased/api-minimal-access-level.yml5
-rw-r--r--changelogs/unreleased/artifact-format-v2-with-parser.yml5
-rw-r--r--changelogs/unreleased/artifact-format-v2.yml5
-rw-r--r--changelogs/unreleased/author-doc-fix.yml5
-rw-r--r--changelogs/unreleased/backstage-gb-stages-position-migration-clean-up.yml5
-rw-r--r--changelogs/unreleased/bjk-48176_ruby_gc.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-add-gemfile-rails5-lock-check.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-bump-grape-path-helpers-gem-to-1-0-5.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-fix-protect-from-forgery-in-application-controller.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-data-store-spec.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-found-new-routes-that-could-cause-conflicts-with-existing-namespaced-routes.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml6
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-set-request-format-in--commits-controller.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml5
-rw-r--r--changelogs/unreleased/bvl-fix-maintainer-push-rejected.yml6
-rw-r--r--changelogs/unreleased/bvl-graphql-nested-merge-request.yml5
-rw-r--r--changelogs/unreleased/bvl-graphql-permissions.yml5
-rw-r--r--changelogs/unreleased/bvl-graphql-wip-mutation.yml5
-rw-r--r--changelogs/unreleased/bvl-user-status-message-35463.yml5
-rw-r--r--changelogs/unreleased/bw-enable-commonmark.yml5
-rw-r--r--changelogs/unreleased/bw-fix-ee-dashboard.yml5
-rw-r--r--changelogs/unreleased/cache-doc-fix.yml5
-rw-r--r--changelogs/unreleased/ce-5024-filename-search.yml5
-rw-r--r--changelogs/unreleased/ce-5666-backport.yml5
-rw-r--r--changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml5
-rw-r--r--changelogs/unreleased/ci-builds-status-index.yml5
-rw-r--r--changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml5
-rw-r--r--changelogs/unreleased/commits_api_with_stats.yml5
-rw-r--r--changelogs/unreleased/cr-add-group-milestone-to-dashboard.yml5
-rw-r--r--changelogs/unreleased/cr-add-path-of-group-milestone.yml5
-rw-r--r--changelogs/unreleased/cr-keep-issue-labels.yml5
-rw-r--r--changelogs/unreleased/custom_wiki_sidebar.yml5
-rw-r--r--changelogs/unreleased/da-port-cte-to-ce.yml5
-rw-r--r--changelogs/unreleased/db-configure-after-drop-tables.yml5
-rw-r--r--changelogs/unreleased/dm-blockquote-trailing-whitespace.yml5
-rw-r--r--changelogs/unreleased/dm-branch-api-can-push.yml5
-rw-r--r--changelogs/unreleased/dm-favicon-asset-host.yml6
-rw-r--r--changelogs/unreleased/dm-label-reference-period.yml5
-rw-r--r--changelogs/unreleased/dz-labels-search.yml5
-rw-r--r--changelogs/unreleased/dz-manifest-import.yml5
-rw-r--r--changelogs/unreleased/existing-gcp-accounts.yml5
-rw-r--r--changelogs/unreleased/expose-ci-url.yml5
-rw-r--r--changelogs/unreleased/feat-add-default-avatar-to-group.yml5
-rw-r--r--changelogs/unreleased/feature-gb-email-delivery-metrics.yml5
-rw-r--r--changelogs/unreleased/feature-gb-login-activity-metrics.yml5
-rw-r--r--changelogs/unreleased/feature-oidc-subject-claim.yml5
-rw-r--r--changelogs/unreleased/features-show-project-id-on-home-panel.yml5
-rw-r--r--changelogs/unreleased/fix-boards-issue-highlight.yml5
-rw-r--r--changelogs/unreleased/fix-br-decode.yml5
-rw-r--r--changelogs/unreleased/fix-diff-note.yml5
-rw-r--r--changelogs/unreleased/fix-email-confirmation-addtional-email.yml5
-rw-r--r--changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml5
-rw-r--r--changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml5
-rw-r--r--changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml5
-rw-r--r--changelogs/unreleased/fix-groups-api-ordering.yml4
-rw-r--r--changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml5
-rw-r--r--changelogs/unreleased/fix-multiple-scopes.yml5
-rw-r--r--changelogs/unreleased/fix-paragraph-line-height-for-emoji.yml5
-rw-r--r--changelogs/unreleased/fix-project-api-archived.yml5
-rw-r--r--changelogs/unreleased/fix-prometheus-updated-status.yml5
-rw-r--r--changelogs/unreleased/fix-search-bar.yml5
-rw-r--r--changelogs/unreleased/fix-storage-size-for-artifacts-change.yml5
-rw-r--r--changelogs/unreleased/fix-tooltip-flicker.yml5
-rw-r--r--changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml5
-rw-r--r--changelogs/unreleased/fj-37736-improve-performance-post-receive-create-gpg-siganture-worker.yml5
-rw-r--r--changelogs/unreleased/fj-46278-apply-doorkeeper-scope-patch.yml5
-rw-r--r--changelogs/unreleased/fj-46278-enable-doorkeeper-reuse-access-token.yml6
-rw-r--r--changelogs/unreleased/fj-48123-fix-gitlab-import.yml5
-rw-r--r--changelogs/unreleased/fj-49014-wiki-search-error.yml5
-rw-r--r--changelogs/unreleased/fj-49512-fix-gitlab-git-pages-encoding.yml5
-rw-r--r--changelogs/unreleased/fj-49802-bug-api-set-http-headers.yml5
-rw-r--r--changelogs/unreleased/fj-bumping-gollum-lib-and-gollum-rugged-adapter.yml5
-rw-r--r--changelogs/unreleased/floating-avarage-commit-numbers.yml5
-rw-r--r--changelogs/unreleased/frozen-string-app-workers.yml5
-rw-r--r--changelogs/unreleased/frozen-string-danger.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-models-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-models.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-serializers.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-services.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-workers-2.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-apps-services-inner-even-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-apps-services-inner.yml5
-rw-r--r--changelogs/unreleased/frozen-string-vestigial.yml5
-rw-r--r--changelogs/unreleased/full-list-of-vulnerabilities-5239.yml5
-rw-r--r--changelogs/unreleased/git-rerere-link-doc-update.yml5
-rw-r--r--changelogs/unreleased/gitaly-commit-count-opt-out.yml5
-rw-r--r--changelogs/unreleased/gitaly-opt-out-branch-tag.yml5
-rw-r--r--changelogs/unreleased/hangouts_chat_integration.yml5
-rw-r--r--changelogs/unreleased/highlight-cluster-settings-message.yml5
-rw-r--r--changelogs/unreleased/ide-codesandbox-poc.yml5
-rw-r--r--changelogs/unreleased/ide-commit-actions-update.yml5
-rw-r--r--changelogs/unreleased/ide-delete-entries.yml5
-rw-r--r--changelogs/unreleased/ide-open-empty-merge-request.yml5
-rw-r--r--changelogs/unreleased/ide-pipeline-icon-open.yml5
-rw-r--r--changelogs/unreleased/ide-rename-files.yml5
-rw-r--r--changelogs/unreleased/ide-row-dropdown-design-update.yml5
-rw-r--r--changelogs/unreleased/ide-warn-staged-files.yml5
-rw-r--r--changelogs/unreleased/improve-junit-support-be.yml5
-rw-r--r--changelogs/unreleased/improve-metadata-access-performance.yml5
-rw-r--r--changelogs/unreleased/issue_43602.yml5
-rw-r--r--changelogs/unreleased/issue_44821.yml5
-rw-r--r--changelogs/unreleased/issue_47709.yml5
-rw-r--r--changelogs/unreleased/issue_47729.yml5
-rw-r--r--changelogs/unreleased/jprovazn-resource-events.yml5
-rw-r--r--changelogs/unreleased/jr-48133-web-ide-commit-ellipsis.yml5
-rw-r--r--changelogs/unreleased/jr-archive-hook.yml5
-rw-r--r--changelogs/unreleased/jupyter-image.yml5
-rw-r--r--changelogs/unreleased/kp-6927-epic-dates-from-milestone.yml5
-rw-r--r--changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml5
-rw-r--r--changelogs/unreleased/leipert-fix-pipelines-view.yml5
-rw-r--r--changelogs/unreleased/limit-metrics-content-type.yml5
-rw-r--r--changelogs/unreleased/mk-add-local-project-uploads-cleanup-task.yml5
-rw-r--r--changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml5
-rw-r--r--changelogs/unreleased/more-group-api-sorting-options.yml5
-rw-r--r--changelogs/unreleased/move-boards-modal-empty-state-vue-component.yml5
-rw-r--r--changelogs/unreleased/no-multi-assign-enable.yml5
-rw-r--r--changelogs/unreleased/no-multi-assign-follow-up.yml5
-rw-r--r--changelogs/unreleased/no-restricted-globals-enable.yml5
-rw-r--r--changelogs/unreleased/osw-delete-non-latest-mr-diff-files-migration.yml5
-rw-r--r--changelogs/unreleased/osw-delete-non-latest-mr-diff-files-upon-merge.yml5
-rw-r--r--changelogs/unreleased/osw-fix-missing-and-duplicated-milestones-on-list.yml5
-rw-r--r--changelogs/unreleased/osw-fix-n-plus-1-for-mrs-without-merge-info.yml5
-rw-r--r--changelogs/unreleased/osw-mark-as-merged-as-first-post-merge-action.yml5
-rw-r--r--changelogs/unreleased/pl-json-gon.yml5
-rw-r--r--changelogs/unreleased/pr-importer-io-extra-error-handling.yml5
-rw-r--r--changelogs/unreleased/prefer-destructuring-fix.yml5
-rw-r--r--changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml5
-rw-r--r--changelogs/unreleased/project-visibility-tooltip.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-46276.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47366.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47370.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47804.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47805.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-47835.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-47836.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-47960.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-48009.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-48012.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-48104.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-48140.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-48141.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-48142.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-48430.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-48432.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-48977.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-db-check.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-flaky-spec-user-uses-shortcuts.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-mysql-arel-from.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-pages-controller.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-revert-modal-spec.yml5
-rw-r--r--changelogs/unreleased/rails5-gpg-permit-concurrent.yml5
-rw-r--r--changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml5
-rw-r--r--changelogs/unreleased/rails5-mysql-rename-column.yml5
-rw-r--r--changelogs/unreleased/rails5-update-gemfile-lock-2.yml5
-rw-r--r--changelogs/unreleased/rails5-update-gemfile-lock.yml5
-rw-r--r--changelogs/unreleased/rails5-update-rouge.yml5
-rw-r--r--changelogs/unreleased/ravlen-deploy-tokens-display-update.yml5
-rw-r--r--changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml5
-rw-r--r--changelogs/unreleased/regen-2fa-codes.yml5
-rw-r--r--changelogs/unreleased/remove-allocations-gem.yml5
-rw-r--r--changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml5
-rw-r--r--changelogs/unreleased/remove-link-label-vertical-alignment-property.yml5
-rw-r--r--changelogs/unreleased/remove-small-container-width.yml5
-rw-r--r--changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml5
-rw-r--r--changelogs/unreleased/replace-snake-case-css-classes.yml5
-rw-r--r--changelogs/unreleased/revert-merge-request-discussion-buttons-padding.yml5
-rw-r--r--changelogs/unreleased/revert-merge-request-widget-button-height.yml5
-rw-r--r--changelogs/unreleased/rosulk-patch-12.yml5
-rw-r--r--changelogs/unreleased/rouge-3-2-0.yml5
-rw-r--r--changelogs/unreleased/runner-features.yml5
-rw-r--r--changelogs/unreleased/runners-max-timeout-param.yml5
-rw-r--r--changelogs/unreleased/safari-scrollbar-bug.yml5
-rw-r--r--changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml5
-rw-r--r--changelogs/unreleased/security-2682-fix-xss-for-markdown-toc.yml5
-rw-r--r--changelogs/unreleased/security-fj-bumping-sanitize-gem.yml5
-rw-r--r--changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml5
-rw-r--r--changelogs/unreleased/security-html_escape_branch_name.yml5
-rw-r--r--changelogs/unreleased/security-html_escape_usernames.yml5
-rw-r--r--changelogs/unreleased/security-ide-branch-name-xss.yml5
-rw-r--r--changelogs/unreleased/security-rd-do-not-show-internal-info-in-public-feed.yml5
-rw-r--r--changelogs/unreleased/sh-bump-gitaly-0-117.yml5
-rw-r--r--changelogs/unreleased/sh-bump-haml-5-0-4.yml5
-rw-r--r--changelogs/unreleased/sh-bump-rugged-0-27-2.yml5
-rw-r--r--changelogs/unreleased/sh-bump-sanitize-4-6-6.yml5
-rw-r--r--changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml5
-rw-r--r--changelogs/unreleased/sh-fix-admin-jobs-controller-timing-out.yml5
-rw-r--r--changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-47797-ce.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-49133.yml5
-rw-r--r--changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml5
-rw-r--r--changelogs/unreleased/sh-freeze-banzai-filter-strings.yml5
-rw-r--r--changelogs/unreleased/sh-handle-colons-in-url-passwords.yml5
-rw-r--r--changelogs/unreleased/sh-include-rbtrace.yml5
-rw-r--r--changelogs/unreleased/sh-lfs-fix-content-type.yml5
-rw-r--r--changelogs/unreleased/sh-limit-unauthenticated-session-times.yml5
-rw-r--r--changelogs/unreleased/sh-normalize-urls.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-locks-check-ce.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-wiki-empty-check.yml5
-rw-r--r--changelogs/unreleased/sh-remove-banzai-instrumentation.yml5
-rw-r--r--changelogs/unreleased/sh-simplify-liveness-check.yml5
-rw-r--r--changelogs/unreleased/sh-support-users-find-by-confirmed-emails.yml5
-rw-r--r--changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml5
-rw-r--r--changelogs/unreleased/stop-dynamic-routable-creation.yml5
-rw-r--r--changelogs/unreleased/straight-comparision-mode.yml5
-rw-r--r--changelogs/unreleased/tc-reorder-mail-notify-references.yml5
-rw-r--r--changelogs/unreleased/tc-repo-check-per-shard.yml5
-rw-r--r--changelogs/unreleased/text-expander-icon-update.yml5
-rw-r--r--changelogs/unreleased/todos-visibility-change.yml5
-rw-r--r--changelogs/unreleased/todos-visibility-migration.yml5
-rw-r--r--changelogs/unreleased/toggle-password-cluster.yml5
-rw-r--r--changelogs/unreleased/transfer_project_api_endpoint.yml5
-rw-r--r--changelogs/unreleased/tweak-sql-buckets.yml5
-rw-r--r--changelogs/unreleased/tz-diff-blob-image-viewer.yml5
-rw-r--r--changelogs/unreleased/tz-mr-port-memory-fixes.yml5
-rw-r--r--changelogs/unreleased/unify-views-search-results.yml5
-rw-r--r--changelogs/unreleased/update-card-body-style.yml5
-rw-r--r--changelogs/unreleased/update-environments-nav-controls.yml5
-rw-r--r--changelogs/unreleased/update-external-link-icon-in-header-user-dropdown.yml5
-rw-r--r--changelogs/unreleased/update-integrations-external-link-icons.yml5
-rw-r--r--changelogs/unreleased/update-issue-closing-pattern.yml5
-rw-r--r--changelogs/unreleased/update-pipeline-icon-in-web-ide-sidebar.yml5
-rw-r--r--changelogs/unreleased/update-specific-runners-help-url.yml5
-rw-r--r--changelogs/unreleased/upgrade-gitlab-markup.yml5
-rw-r--r--changelogs/unreleased/upgrade-hamlit-for-ruby25.yml5
-rw-r--r--changelogs/unreleased/use-backup-custom-hooks-gitaly.yml5
-rw-r--r--changelogs/unreleased/use-tooltip-component-in-mr-widget-author-time-component.yml5
-rw-r--r--changelogs/unreleased/winh-fix-gpg-regressions.yml5
-rw-r--r--changelogs/unreleased/winh-new-branch-url-encode.yml5
-rw-r--r--changelogs/unreleased/winh-restyle-user-status.yml5
-rw-r--r--changelogs/unreleased/winh-stop-all-environments.yml5
-rw-r--r--changelogs/unreleased/winh-tree-view-gpg.yml5
-rw-r--r--changelogs/unreleased/winh-upgrade-grape-path-helpers.yml5
-rw-r--r--changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml5
-rw-r--r--changelogs/unreleased/zj-gitaly-read-write-check.yml5
-rw-r--r--changelogs/unreleased/zj-remove-git-rake-tasks.yml5
-rw-r--r--changelogs/unreleased/zj-repository-languages.yml5
-rw-r--r--config/application.rb19
-rw-r--r--config/aws.yml.example22
-rw-r--r--config/boot.rb5
-rw-r--r--config/dependency_decisions.yml30
-rw-r--r--config/environment.rb2
-rw-r--r--config/environments/development.rb2
-rw-r--r--config/gitlab.yml.example21
-rw-r--r--config/initializers/1_settings.rb40
-rw-r--r--config/initializers/8_metrics.rb16
-rw-r--r--config/initializers/action_mailer_hooks.rb12
-rw-r--r--config/initializers/active_record_locking.rb4
-rw-r--r--config/initializers/active_record_table_definition.rb5
-rw-r--r--config/initializers/active_record_verbose_query_logs.rb54
-rw-r--r--config/initializers/additional_headers_interceptor.rb1
-rw-r--r--config/initializers/bootstrap_form.rb2
-rw-r--r--config/initializers/carrierwave.rb31
-rw-r--r--config/initializers/carrierwave_monkey_patch.rb69
-rw-r--r--config/initializers/devise.rb2
-rw-r--r--config/initializers/disable_email_interceptor.rb5
-rw-r--r--config/initializers/email_template_interceptor.rb2
-rw-r--r--config/initializers/omniauth.rb5
-rw-r--r--config/initializers/sidekiq.rb2
-rw-r--r--config/initializers/warden.rb44
-rw-r--r--config/object_store_settings.rb15
-rw-r--r--config/routes.rb3
-rw-r--r--config/routes/admin.rb6
-rw-r--r--config/routes/group.rb1
-rw-r--r--config/routes/import.rb13
-rw-r--r--config/routes/instance_statistics.rb8
-rw-r--r--config/routes/project.rb17
-rw-r--r--config/routes/repository.rb1
-rw-r--r--config/sidekiq_queues.yml4
-rw-r--r--config/unicorn.rb.example12
-rw-r--r--config/unicorn.rb.example.development17
-rw-r--r--danger/changelog/Dangerfile68
-rw-r--r--danger/changes_size/Dangerfile17
-rw-r--r--danger/database/Dangerfile83
-rw-r--r--danger/frozen_string/Dangerfile26
-rw-r--r--danger/gemfile/Dangerfile24
-rw-r--r--danger/metadata/Dangerfile25
-rw-r--r--danger/specs/Dangerfile18
-rw-r--r--db/fixtures/development/12_snippets.rb2
-rw-r--r--db/fixtures/development/19_environments.rb4
-rw-r--r--db/fixtures/development/20_nested_groups.rb28
-rw-r--r--db/fixtures/development/23_spam_logs.rb32
-rw-r--r--db/migrate/20140313092127_init_schema.rb9
-rw-r--r--db/migrate/20140407135544_fix_namespaces.rb1
-rw-r--r--db/migrate/20140415124820_limits_to_mysql.rb1
-rw-r--r--db/migrate/20140729145339_migrate_project_tags.rb1
-rw-r--r--db/migrate/20141121133009_add_timestamps_to_members.rb1
-rw-r--r--db/migrate/20141223135007_add_import_data_to_project_table.rb1
-rw-r--r--db/migrate/20150116234544_add_home_page_url_for_application_settings.rb1
-rw-r--r--db/migrate/20150116234545_add_gitlab_access_token_to_user.rb1
-rw-r--r--db/migrate/20150206222854_add_notification_email_to_user.rb1
-rw-r--r--db/migrate/20150211174341_allow_null_in_services_project_id.rb1
-rw-r--r--db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb1
-rw-r--r--db/migrate/20150223022001_set_missing_last_activity_at.rb1
-rw-r--r--db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb1
-rw-r--r--db/migrate/20150320234437_add_location_to_user.rb1
-rw-r--r--db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb1
-rw-r--r--db/migrate/20150327150017_add_import_data_to_project.rb1
-rw-r--r--db/migrate/20150327223628_add_devise_two_factor_to_users.rb1
-rw-r--r--db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb1
-rw-r--r--db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb1
-rw-r--r--db/migrate/20150411000035_fix_identities.rb1
-rw-r--r--db/migrate/20150411180045_rename_buildbox_service.rb1
-rw-r--r--db/migrate/20150417121913_create_project_import_data.rb1
-rw-r--r--db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb1
-rw-r--r--db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb1
-rw-r--r--db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb1
-rw-r--r--db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb1
-rw-r--r--db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb1
-rw-r--r--db/migrate/20150429002313_remove_abandoned_group_members_records.rb1
-rw-r--r--db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb1
-rw-r--r--db/migrate/20150509180749_convert_legacy_reference_notes.rb1
-rw-r--r--db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb1
-rw-r--r--db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb1
-rw-r--r--db/migrate/20150620233230_add_default_otp_required_for_login_value.rb1
-rw-r--r--db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb1
-rw-r--r--db/migrate/20150818213832_add_sent_notifications.rb1
-rw-r--r--db/migrate/20150915001905_enable_ssl_verification_by_default.rb1
-rw-r--r--db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb1
-rw-r--r--db/migrate/20150916114643_add_help_page_text_to_application_settings.rb1
-rw-r--r--db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb1
-rw-r--r--db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb1
-rw-r--r--db/migrate/20150920010715_add_consumed_timestep_to_users.rb1
-rw-r--r--db/migrate/20150920161119_add_line_code_to_sent_notification.rb1
-rw-r--r--db/migrate/20150924125150_add_project_id_to_ci_commit.rb1
-rw-r--r--db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb1
-rw-r--r--db/migrate/20150930001110_merge_request_error_field.rb1
-rw-r--r--db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb1
-rw-r--r--db/migrate/20151002112914_add_stage_idx_to_builds.rb1
-rw-r--r--db/migrate/20151002122929_add_ref_and_tag_to_builds.rb1
-rw-r--r--db/migrate/20151005075649_add_user_id_to_build.rb1
-rw-r--r--db/migrate/20151008143519_add_admin_notification_email_setting.rb1
-rw-r--r--db/migrate/20151013092124_add_artifacts_file_to_builds.rb1
-rw-r--r--db/migrate/20151019111551_fix_build_tags.rb1
-rw-r--r--db/migrate/20151019111703_fail_build_without_names.rb1
-rw-r--r--db/migrate/20151020173516_ci_limits_to_mysql.rb1
-rw-r--r--db/migrate/20151023112551_fail_build_with_empty_name.rb1
-rw-r--r--db/migrate/20151023144219_remove_satellites.rb1
-rw-r--r--db/migrate/20151103133339_add_shared_runners_setting.rb1
-rw-r--r--db/migrate/20151104105513_add_file_to_lfs_objects.rb1
-rw-r--r--db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb1
-rw-r--r--db/migrate/20151110125604_add_import_error_to_project.rb1
-rw-r--r--db/migrate/20151201203948_raise_hook_url_limit.rb1
-rw-r--r--db/migrate/20151209144329_migrate_ci_web_hooks.rb1
-rw-r--r--db/migrate/20151209145909_migrate_ci_emails.rb1
-rw-r--r--db/migrate/20151210030143_add_unlock_token_to_user.rb1
-rw-r--r--db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb1
-rw-r--r--db/migrate/20151210125232_migrate_ci_slack_service.rb1
-rw-r--r--db/migrate/20151210125927_migrate_ci_hip_chat_service.rb1
-rw-r--r--db/migrate/20151210125929_add_project_id_to_ci.rb1
-rw-r--r--db/migrate/20151210125930_migrate_ci_to_project.rb1
-rw-r--r--db/migrate/20151218154042_add_tfa_to_application_settings.rb1
-rw-r--r--db/migrate/20151221234414_add_tfa_additional_fields.rb1
-rw-r--r--db/migrate/20151224123230_rename_emojis.rb1
-rw-r--r--db/migrate/20151228175719_add_recaptcha_to_application_settings.rb1
-rw-r--r--db/migrate/20151229102248_influxdb_udp_port_setting.rb1
-rw-r--r--db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb1
-rw-r--r--db/migrate/20151231152326_add_akismet_to_application_settings.rb1
-rw-r--r--db/migrate/20160113111034_add_metrics_sample_interval.rb1
-rw-r--r--db/migrate/20160118155830_add_sentry_to_application_settings.rb1
-rw-r--r--db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb1
-rw-r--r--db/migrate/20160128233227_change_lfs_objects_size_column.rb1
-rw-r--r--db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb1
-rw-r--r--db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb1
-rw-r--r--db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb1
-rw-r--r--db/migrate/20160217100506_add_description_to_label.rb1
-rw-r--r--db/migrate/20160217174422_add_note_to_tasks.rb1
-rw-r--r--db/migrate/20160220123949_rename_tasks_to_todos.rb1
-rw-r--r--db/migrate/20160229193553_add_main_language_to_repository.rb1
-rw-r--r--db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb1
-rw-r--r--db/migrate/20160307221555_disallow_blank_line_code_on_note.rb1
-rw-r--r--db/migrate/20160316192622_change_target_id_to_null_on_todos.rb1
-rw-r--r--db/migrate/20160317092222_add_moved_to_to_issue.rb3
-rw-r--r--db/migrate/20160324020319_remove_todos_for_deleted_issues.rb1
-rw-r--r--db/migrate/20160328115649_migrate_new_notification_setting.rb1
-rw-r--r--db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb1
-rw-r--r--db/migrate/20160407120251_add_images_enabled_for_project.rb1
-rw-r--r--db/migrate/20160413115152_add_token_to_web_hooks.rb1
-rw-r--r--db/migrate/20160415062917_create_personal_access_tokens.rb1
-rw-r--r--db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb1
-rw-r--r--db/migrate/20160416182152_convert_award_note_to_emoji_award.rb1
-rw-r--r--db/migrate/20160419120017_add_metrics_packet_size.rb1
-rw-r--r--db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb1
-rw-r--r--db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb1
-rw-r--r--db/migrate/20160508215820_add_type_to_notes.rb1
-rw-r--r--db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb1
-rw-r--r--db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb1
-rw-r--r--db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb1
-rw-r--r--db/migrate/20160603180330_remove_duplicated_notification_settings.rb1
-rw-r--r--db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb1
-rw-r--r--db/migrate/20160610204157_add_deployments.rb2
-rw-r--r--db/migrate/20160610204158_add_environments.rb2
-rw-r--r--db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb1
-rw-r--r--db/migrate/20160616102642_remove_duplicated_keys.rb1
-rw-r--r--db/migrate/20160705054938_add_protected_branches_push_access.rb2
-rw-r--r--db/migrate/20160705054952_add_protected_branches_merge_access.rb2
-rw-r--r--db/migrate/20160824124900_add_table_issue_metrics.rb2
-rw-r--r--db/migrate/20160825052008_add_table_merge_request_metrics.rb2
-rw-r--r--db/migrate/20161113184239_create_user_chat_names_table.rb1
-rw-r--r--db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb54
-rw-r--r--db/migrate/20161226122833_remove_dot_git_from_usernames.rb12
-rw-r--r--db/migrate/20170130221926_create_uploads.rb1
-rw-r--r--db/migrate/20170222143317_drop_ci_projects.rb1
-rw-r--r--db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb1
-rw-r--r--db/migrate/20170329095907_create_ci_trigger_schedules.rb1
-rw-r--r--db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb2
-rw-r--r--db/migrate/20170425112128_create_pipeline_schedules_table.rb1
-rw-r--r--db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb1
-rw-r--r--db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb1
-rw-r--r--db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb1
-rw-r--r--db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb1
-rw-r--r--db/migrate/20180214093516_create_badges.rb1
-rw-r--r--db/migrate/20180227182112_add_group_id_to_boards_ce.rb2
-rw-r--r--db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb1
-rw-r--r--db/migrate/20180309160427_add_partial_indexes_on_todos.rb11
-rw-r--r--db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb1
-rw-r--r--db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb1
-rw-r--r--db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb1
-rw-r--r--db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb1
-rw-r--r--db/migrate/20180425075446_create_term_agreements.rb1
-rw-r--r--db/migrate/20180503131624_create_remote_mirrors.rb1
-rw-r--r--db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb1
-rw-r--r--db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb1
-rw-r--r--db/migrate/20180503193542_add_indexes_to_remote_mirror.rb1
-rw-r--r--db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb1
-rw-r--r--db/migrate/20180515005612_add_squash_to_merge_requests.rb1
-rw-r--r--db/migrate/20180515121227_create_notes_diff_files.rb1
-rw-r--r--db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb1
-rw-r--r--db/migrate/20180529093006_ensure_remote_mirror_columns.rb1
-rw-r--r--db/migrate/20180531185349_add_repository_languages.rb28
-rw-r--r--db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb4
-rw-r--r--db/migrate/20180608091413_add_group_to_todos.rb36
-rw-r--r--db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb1
-rw-r--r--db/migrate/20180612103626_add_columns_for_helm_tiller_certificates.rb12
-rw-r--r--db/migrate/20180613081317_create_ci_builds_runner_session.rb21
-rw-r--r--db/migrate/20180625113853_create_import_export_uploads.rb16
-rw-r--r--db/migrate/20180628124813_alter_web_hook_logs_indexes.rb28
-rw-r--r--db/migrate/20180629153018_create_site_statistics.rb18
-rw-r--r--db/migrate/20180702124358_remove_orphaned_routes.rb49
-rw-r--r--db/migrate/20180702134423_generate_missing_routes.rb143
-rw-r--r--db/migrate/20180704204006_add_hide_third_party_offers_to_application_settings.rb18
-rw-r--r--db/migrate/20180705160945_add_file_format_to_ci_job_artifacts.rb7
-rw-r--r--db/migrate/20180710162338_add_foreign_key_from_notification_settings_to_users.rb30
-rw-r--r--db/migrate/20180713092803_create_user_statuses.rb20
-rw-r--r--db/migrate/20180717125853_remove_restricted_todos.rb31
-rw-r--r--db/migrate/20180718005113_add_instance_statistics_visibility_to_application_setting.rb20
-rw-r--r--db/migrate/20180722103201_add_private_profile_to_users.rb10
-rw-r--r--db/migrate/20180723135214_add_web_ide_client_side_preview_enabled_to_application_settings.rb20
-rw-r--r--db/migrate/20180726172057_create_resource_label_events.rb18
-rw-r--r--db/migrate/20180807153545_remove_redundant_status_index_on_ci_builds.rb17
-rw-r--r--db/migrate/gpg_keys_limits_to_mysql.rb1
-rw-r--r--db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb2
-rw-r--r--db/migrate/limits_to_mysql.rb1
-rw-r--r--db/optional_migrations/composite_primary_keys.rb6
-rw-r--r--db/post_migrate/20161221153951_rename_reserved_project_names.rb2
-rw-r--r--db/post_migrate/20170313133418_rename_more_reserved_project_names.rb2
-rw-r--r--db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb1
-rw-r--r--db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb1
-rw-r--r--db/post_migrate/20170711145558_migrate_stages_statuses.rb1
-rw-r--r--db/post_migrate/20170830150306_drop_events_for_migration_table.rb1
-rw-r--r--db/post_migrate/20171106154015_remove_issues_branch_name.rb1
-rw-r--r--db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb1
-rw-r--r--db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb1
-rw-r--r--db/post_migrate/20180223124427_build_user_interacted_projects_table.rb3
-rw-r--r--db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb1
-rw-r--r--db/post_migrate/20180424151928_fill_file_store.rb2
-rw-r--r--db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb1
-rw-r--r--db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb1
-rw-r--r--db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb2
-rw-r--r--db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb1
-rw-r--r--db/post_migrate/20180619121030_enqueue_delete_diff_files_workers.rb52
-rw-r--r--db/post_migrate/20180629191052_add_partial_index_to_projects_for_last_repository_check_at.rb18
-rw-r--r--db/post_migrate/20180702120647_enqueue_fix_cross_project_label_links.rb30
-rw-r--r--db/post_migrate/20180704145007_update_project_indexes.rb23
-rw-r--r--db/post_migrate/20180706223200_populate_site_statistics.rb25
-rw-r--r--db/schema.rb89
-rw-r--r--doc/README.md5
-rw-r--r--doc/administration/gitaly/index.md50
-rw-r--r--doc/administration/high_availability/gitlab.md5
-rw-r--r--doc/administration/high_availability/nfs.md22
-rw-r--r--doc/administration/img/raketasks/check_repos_output.pngbin19153 -> 0 bytes
-rw-r--r--doc/administration/index.md4
-rw-r--r--doc/administration/job_artifacts.md14
-rw-r--r--doc/administration/job_traces.md155
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md4
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md5
-rw-r--r--doc/administration/operations/index.md5
-rw-r--r--doc/administration/operations/ssh_certificates.md165
-rw-r--r--doc/administration/pages/index.md65
-rw-r--r--doc/administration/raketasks/check.md79
-rw-r--r--doc/administration/raketasks/project_import_export.md7
-rw-r--r--doc/administration/repository_storage_types.md2
-rw-r--r--doc/administration/troubleshooting/debug.md18
-rw-r--r--doc/administration/uploads.md16
-rw-r--r--doc/api/README.md45
-rw-r--r--doc/api/groups.md37
-rw-r--r--doc/api/issues.md7
-rw-r--r--doc/api/jobs.md88
-rw-r--r--doc/api/members.md75
-rw-r--r--doc/api/merge_requests.md30
-rw-r--r--doc/api/notes.md2
-rw-r--r--doc/api/pipelines.md22
-rw-r--r--doc/api/project_import_export.md28
-rw-r--r--doc/api/projects.md98
-rw-r--r--doc/api/services.md48
-rw-r--r--doc/api/settings.md9
-rw-r--r--doc/api/todos.md1
-rw-r--r--doc/api/users.md126
-rw-r--r--doc/ci/docker/using_docker_build.md33
-rw-r--r--doc/ci/examples/README.md4
-rw-r--r--doc/ci/examples/code_climate.md53
-rw-r--r--doc/ci/examples/code_quality.md49
-rw-r--r--doc/ci/examples/container_scanning.md6
-rw-r--r--doc/ci/examples/dast.md2
-rw-r--r--doc/ci/examples/test-and-deploy-python-application-to-heroku.md2
-rw-r--r--doc/ci/triggers/README.md2
-rw-r--r--doc/ci/variables/README.md12
-rw-r--r--doc/ci/yaml/README.md42
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/api_graphql_styleguide.md262
-rw-r--r--doc/development/architecture.md4
-rw-r--r--doc/development/automatic_ce_ee_merge.md2
-rw-r--r--doc/development/background_migrations.md11
-rw-r--r--doc/development/contributing/design.md70
-rw-r--r--doc/development/contributing/index.md246
-rw-r--r--doc/development/contributing/issue_workflow.md357
-rw-r--r--doc/development/contributing/merge_request_workflow.md191
-rw-r--r--doc/development/documentation/styleguide.md14
-rw-r--r--doc/development/ee_features.md2
-rw-r--r--doc/development/emails.md4
-rw-r--r--doc/development/fe_guide/development_process.md6
-rw-r--r--doc/development/fe_guide/img/vue_arch.pngbin9848 -> 0 bytes
-rw-r--r--doc/development/fe_guide/performance.md14
-rw-r--r--doc/development/fe_guide/vue.md241
-rw-r--r--doc/development/i18n/externalization.md14
-rw-r--r--doc/development/i18n/proofreader.md2
-rw-r--r--doc/development/licensing.md22
-rw-r--r--doc/development/migration_style_guide.md28
-rw-r--r--doc/development/performance.md54
-rw-r--r--doc/development/profiling.md30
-rw-r--r--doc/development/pry_debugging.md130
-rw-r--r--doc/development/sql.md42
-rw-r--r--doc/development/testing_guide/best_practices.md2
-rw-r--r--doc/development/testing_guide/frontend_testing.md4
-rw-r--r--doc/development/ux_guide/copy.md2
-rw-r--r--doc/development/what_requires_downtime.md4
-rw-r--r--doc/gitlab-basics/create-project.md5
-rw-r--r--doc/gitlab-basics/img/create_new_project_info.pngbin75470 -> 77490 bytes
-rw-r--r--doc/install/installation.md21
-rw-r--r--doc/install/kubernetes/gitlab_chart.md12
-rw-r--r--doc/install/kubernetes/gitlab_omnibus.md2
-rw-r--r--doc/install/kubernetes/preparation/tiller.md18
-rw-r--r--doc/integration/bitbucket.md37
-rw-r--r--doc/integration/google.md4
-rw-r--r--doc/integration/oauth_provider.md4
-rw-r--r--doc/integration/saml.md172
-rw-r--r--doc/integration/shibboleth.md27
-rw-r--r--doc/public_access/public_access.md6
-rw-r--r--doc/raketasks/backup_restore.md25
-rw-r--r--doc/raketasks/cleanup.md60
-rw-r--r--doc/raketasks/user_management.md2
-rw-r--r--doc/security/rack_attack.md10
-rw-r--r--doc/ssh/README.md2
-rw-r--r--doc/topics/autodevops/index.md10
-rw-r--r--doc/university/high-availability/aws/README.md6
-rw-r--r--doc/update/10.6-to-10.7.md2
-rw-r--r--doc/update/11.0-to-11.1.md378
-rw-r--r--doc/update/11.1-to-11.2.md378
-rw-r--r--doc/update/mysql_to_postgresql.md18
-rw-r--r--doc/update/patch_versions.md16
-rw-r--r--doc/user/admin_area/monitoring/health_check.md23
-rw-r--r--doc/user/admin_area/settings/third_party_offers.md6
-rw-r--r--doc/user/gitlab_com/index.md3
-rw-r--r--doc/user/index.md2
-rw-r--r--doc/user/markdown.md4
-rw-r--r--doc/user/permissions.md2
-rw-r--r--doc/user/profile/account/two_factor_authentication.md2
-rw-r--r--doc/user/profile/index.md44
-rw-r--r--doc/user/profile/personal_access_tokens.md1
-rw-r--r--doc/user/project/bulk_editing.md22
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/index.md2
-rw-r--r--doc/user/project/clusters/index.md18
-rw-r--r--doc/user/project/img/bulk-editing.pngbin0 -> 197686 bytes
-rw-r--r--doc/user/project/img/labels_project_list_search.pngbin0 -> 175669 bytes
-rw-r--r--doc/user/project/import/bitbucket.md27
-rw-r--r--doc/user/project/import/bitbucket_server.md75
-rw-r--r--doc/user/project/import/img/bitbucket_import_new_project.pngbin1316 -> 0 bytes
-rw-r--r--doc/user/project/import/img/bitbucket_server_import_credentials.pngbin0 -> 40566 bytes
-rw-r--r--doc/user/project/import/img/bitbucket_server_import_select_project.pngbin0 -> 56750 bytes
-rw-r--r--doc/user/project/import/img/import_projects_from_new_project_page.pngbin36821 -> 81639 bytes
-rw-r--r--doc/user/project/import/img/manifest_status.pngbin0 -> 34878 bytes
-rw-r--r--doc/user/project/import/img/manifest_upload.pngbin0 -> 12079 bytes
-rw-r--r--doc/user/project/import/index.md1
-rw-r--r--doc/user/project/import/manifest.md49
-rw-r--r--doc/user/project/integrations/bamboo.md7
-rw-r--r--doc/user/project/integrations/emails_on_push.md2
-rw-r--r--doc/user/project/integrations/hangouts_chat.md27
-rw-r--r--doc/user/project/integrations/img/hangouts_chat_configuration.pngbin0 -> 101788 bytes
-rw-r--r--doc/user/project/integrations/jira.md2
-rw-r--r--doc/user/project/integrations/project_services.md1
-rw-r--r--doc/user/project/integrations/webhooks.md45
-rw-r--r--doc/user/project/issue_board.md18
-rw-r--r--doc/user/project/issues/index.md6
-rw-r--r--doc/user/project/issues/issues_functionalities.md2
-rw-r--r--doc/user/project/labels.md10
-rw-r--r--doc/user/project/merge_requests/img/merge_request.pngbin67228 -> 748131 bytes
-rw-r--r--doc/user/project/merge_requests/index.md6
-rw-r--r--doc/user/project/milestones/img/milestones_new_group_milestone.pngbin156704 -> 276831 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_new_project_milestone.pngbin173762 -> 257285 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_promote_milestone.pngbin350399 -> 76864 bytes
-rw-r--r--doc/user/project/repository/index.md6
-rw-r--r--doc/user/project/settings/index.md2
-rw-r--r--doc/user/project/web_ide/index.md13
-rw-r--r--doc/user/project/wiki/index.md7
-rw-r--r--doc/user/search/img/issues_mrs_shortcut.pngbin34115 -> 61888 bytes
-rw-r--r--doc/user/search/img/project_search.pngbin41900 -> 89002 bytes
-rw-r--r--doc/workflow/lfs/lfs_administration.md3
-rw-r--r--doc/workflow/todos.md1
-rw-r--r--generator_templates/active_record/migration/create_table_migration.rb2
-rw-r--r--generator_templates/active_record/migration/migration.rb2
-rw-r--r--generator_templates/rails/post_deployment_migration/migration.rb2
-rw-r--r--lib/additional_email_headers_interceptor.rb6
-rw-r--r--lib/api/boards.rb7
-rw-r--r--lib/api/boards_responses.rb16
-rw-r--r--lib/api/branches.rb1
-rw-r--r--lib/api/commits.rb16
-rw-r--r--lib/api/deploy_keys.rb6
-rw-r--r--lib/api/entities.rb70
-rw-r--r--lib/api/environments.rb3
-rw-r--r--lib/api/group_boards.rb6
-rw-r--r--lib/api/groups.rb10
-rw-r--r--lib/api/helpers.rb9
-rw-r--r--lib/api/helpers/headers_helpers.rb6
-rw-r--r--lib/api/helpers/members_helpers.rb25
-rw-r--r--lib/api/helpers/notes_helpers.rb2
-rw-r--r--lib/api/internal.rb55
-rw-r--r--lib/api/issues.rb6
-rw-r--r--lib/api/jobs.rb2
-rw-r--r--lib/api/keys.rb2
-rw-r--r--lib/api/members.rb22
-rw-r--r--lib/api/merge_requests.rb9
-rw-r--r--lib/api/pipelines.rb2
-rw-r--r--lib/api/project_export.rb10
-rw-r--r--lib/api/project_hooks.rb2
-rw-r--r--lib/api/projects.rb94
-rw-r--r--lib/api/runner.rb53
-rw-r--r--lib/api/runners.rb11
-rw-r--r--lib/api/services.rb13
-rw-r--r--lib/api/settings.rb150
-rw-r--r--lib/api/users.rb49
-rw-r--r--lib/backup/repository.rb171
-rw-r--r--lib/banzai/filter/absolute_link_filter.rb2
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb13
-rw-r--r--lib/banzai/filter/ascii_doc_post_processing_filter.rb2
-rw-r--r--lib/banzai/filter/autolink_filter.rb2
-rw-r--r--lib/banzai/filter/blockquote_fence_filter.rb24
-rw-r--r--lib/banzai/filter/color_filter.rb2
-rw-r--r--lib/banzai/filter/commit_range_reference_filter.rb2
-rw-r--r--lib/banzai/filter/commit_reference_filter.rb2
-rw-r--r--lib/banzai/filter/commit_trailers_filter.rb2
-rw-r--r--lib/banzai/filter/emoji_filter.rb6
-rw-r--r--lib/banzai/filter/epic_reference_filter.rb2
-rw-r--r--lib/banzai/filter/external_issue_reference_filter.rb2
-rw-r--r--lib/banzai/filter/external_link_filter.rb2
-rw-r--r--lib/banzai/filter/gollum_tags_filter.rb2
-rw-r--r--lib/banzai/filter/html_entity_filter.rb2
-rw-r--r--lib/banzai/filter/image_lazy_load_filter.rb4
-rw-r--r--lib/banzai/filter/image_link_filter.rb2
-rw-r--r--lib/banzai/filter/inline_diff_filter.rb2
-rw-r--r--lib/banzai/filter/issuable_reference_filter.rb2
-rw-r--r--lib/banzai/filter/issuable_state_filter.rb2
-rw-r--r--lib/banzai/filter/issue_reference_filter.rb2
-rw-r--r--lib/banzai/filter/label_reference_filter.rb2
-rw-r--r--lib/banzai/filter/markdown_engines/common_mark.rb2
-rw-r--r--lib/banzai/filter/markdown_filter.rb2
-rw-r--r--lib/banzai/filter/math_filter.rb2
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb7
-rw-r--r--lib/banzai/filter/mermaid_filter.rb2
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb2
-rw-r--r--lib/banzai/filter/plantuml_filter.rb2
-rw-r--r--lib/banzai/filter/redactor_filter.rb2
-rw-r--r--lib/banzai/filter/reference_filter.rb10
-rw-r--r--lib/banzai/filter/relative_link_filter.rb2
-rw-r--r--lib/banzai/filter/sanitization_filter.rb19
-rw-r--r--lib/banzai/filter/set_direction_filter.rb2
-rw-r--r--lib/banzai/filter/snippet_reference_filter.rb2
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb4
-rw-r--r--lib/banzai/filter/table_of_contents_filter.rb4
-rw-r--r--lib/banzai/filter/task_list_filter.rb2
-rw-r--r--lib/banzai/filter/user_reference_filter.rb2
-rw-r--r--lib/banzai/filter/video_link_filter.rb2
-rw-r--r--lib/banzai/filter/wiki_link_filter.rb2
-rw-r--r--lib/banzai/filter/yaml_front_matter_filter.rb2
-rw-r--r--lib/banzai/pipeline/emoji_pipeline.rb17
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb18
-rw-r--r--lib/banzai/pipeline/post_process_pipeline.rb12
-rw-r--r--lib/banzai/pipeline/single_line_pipeline.rb8
-rw-r--r--lib/bitbucket_server/client.rb71
-rw-r--r--lib/bitbucket_server/collection.rb23
-rw-r--r--lib/bitbucket_server/connection.rb122
-rw-r--r--lib/bitbucket_server/page.rb36
-rw-r--r--lib/bitbucket_server/paginator.rb38
-rw-r--r--lib/bitbucket_server/representation/activity.rb71
-rw-r--r--lib/bitbucket_server/representation/base.rb21
-rw-r--r--lib/bitbucket_server/representation/comment.rb130
-rw-r--r--lib/bitbucket_server/representation/pull_request.rb76
-rw-r--r--lib/bitbucket_server/representation/pull_request_comment.rb122
-rw-r--r--lib/bitbucket_server/representation/repo.rb69
-rw-r--r--lib/declarative_policy.rb12
-rw-r--r--lib/declarative_policy/base.rb10
-rw-r--r--lib/declarative_policy/delegate_dsl.rb6
-rw-r--r--lib/declarative_policy/policy_dsl.rb14
-rw-r--r--lib/declarative_policy/preferred_scope.rb10
-rw-r--r--lib/declarative_policy/rule.rb4
-rw-r--r--lib/declarative_policy/rule_dsl.rb10
-rw-r--r--lib/declarative_policy/runner.rb2
-rw-r--r--lib/disable_email_interceptor.rb7
-rw-r--r--lib/email_template_interceptor.rb11
-rw-r--r--lib/extracts_path.rb5
-rw-r--r--lib/feature.rb6
-rw-r--r--lib/gitaly/server.rb2
-rw-r--r--lib/gitlab.rb4
-rw-r--r--lib/gitlab/access.rb24
-rw-r--r--lib/gitlab/auth.rb19
-rw-r--r--lib/gitlab/auth/activity.rb78
-rw-r--r--lib/gitlab/auth/blocked_user_tracker.rb36
-rw-r--r--lib/gitlab/auth/o_auth/provider.rb2
-rw-r--r--lib/gitlab/auth/o_auth/user.rb2
-rw-r--r--lib/gitlab/background_migration.rb6
-rw-r--r--lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb1
-rw-r--r--lib/gitlab/background_migration/archive_legacy_traces.rb1
-rw-r--r--lib/gitlab/background_migration/create_fork_network_memberships_range.rb1
-rw-r--r--lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb1
-rw-r--r--lib/gitlab/background_migration/delete_diff_files.rb87
-rw-r--r--lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb1
-rw-r--r--lib/gitlab/background_migration/fill_file_store_job_artifact.rb1
-rw-r--r--lib/gitlab/background_migration/fill_file_store_lfs_object.rb1
-rw-r--r--lib/gitlab/background_migration/fill_store_upload.rb1
-rw-r--r--lib/gitlab/background_migration/fix_cross_project_label_links.rb140
-rw-r--r--lib/gitlab/background_migration/migrate_build_stage.rb1
-rw-r--r--lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb1
-rw-r--r--lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb1
-rw-r--r--lib/gitlab/background_migration/move_personal_snippet_files.rb1
-rw-r--r--lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb1
-rw-r--r--lib/gitlab/background_migration/populate_fork_networks_range.rb2
-rw-r--r--lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb3
-rw-r--r--lib/gitlab/background_migration/populate_untracked_uploads.rb2
-rw-r--r--lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb4
-rw-r--r--lib/gitlab/background_migration/prepare_untracked_uploads.rb2
-rw-r--r--lib/gitlab/background_migration/remove_restricted_todos.rb105
-rw-r--r--lib/gitlab/background_migration/schedule_diff_files_deletion.rb44
-rw-r--r--lib/gitlab/bare_repository_import/importer.rb36
-rw-r--r--lib/gitlab/bare_repository_import/repository.rb2
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb23
-rw-r--r--lib/gitlab/bitbucket_server_import/importer.rb327
-rw-r--r--lib/gitlab/bitbucket_server_import/project_creator.rb36
-rw-r--r--lib/gitlab/checks/change_access.rb25
-rw-r--r--lib/gitlab/checks/lfs_integrity.rb5
-rw-r--r--lib/gitlab/ci/ansi2html.rb118
-rw-r--r--lib/gitlab/ci/build/artifacts/gzip_file_adapter.rb46
-rw-r--r--lib/gitlab/ci/build/artifacts/metadata.rb19
-rw-r--r--lib/gitlab/ci/config/entry/artifacts.rb13
-rw-r--r--lib/gitlab/ci/config/entry/commands.rb13
-rw-r--r--lib/gitlab/ci/config/entry/configurable.rb2
-rw-r--r--lib/gitlab/ci/config/entry/reports.rb32
-rw-r--r--lib/gitlab/ci/config/entry/validators.rb14
-rw-r--r--lib/gitlab/ci/parsers.rb9
-rw-r--r--lib/gitlab/ci/parsers/junit.rb69
-rw-r--r--lib/gitlab/ci/reports/test_case.rb32
-rw-r--r--lib/gitlab/ci/reports/test_reports.rb39
-rw-r--r--lib/gitlab/ci/reports/test_reports_comparer.rb38
-rw-r--r--lib/gitlab/ci/reports/test_suite.rb54
-rw-r--r--lib/gitlab/ci/reports/test_suite_comparer.rb57
-rw-r--r--lib/gitlab/ci/status/build/failed.rb19
-rw-r--r--lib/gitlab/ci/trace.rb28
-rw-r--r--lib/gitlab/ci/trace/http_io.rb197
-rw-r--r--lib/gitlab/ci/trace/section_parser.rb12
-rw-r--r--lib/gitlab/ci/variables/collection/item.rb2
-rw-r--r--lib/gitlab/cleanup/project_upload_file_finder.rb66
-rw-r--r--lib/gitlab/cleanup/project_uploads.rb125
-rw-r--r--lib/gitlab/cleanup/remote_uploads.rb80
-rw-r--r--lib/gitlab/current_settings.rb2
-rw-r--r--lib/gitlab/data_builder/pipeline.rb2
-rw-r--r--lib/gitlab/database.rb41
-rw-r--r--lib/gitlab/database/migration_helpers.rb4
-rw-r--r--lib/gitlab/diff/file.rb3
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff.rb2
-rw-r--r--lib/gitlab/diff/image_point.rb6
-rw-r--r--lib/gitlab/diff/inline_diff.rb4
-rw-r--r--lib/gitlab/diff/line.rb8
-rw-r--r--lib/gitlab/ee_compat_check.rb28
-rw-r--r--lib/gitlab/email/handler/create_issue_handler.rb4
-rw-r--r--lib/gitlab/email/handler/create_merge_request_handler.rb4
-rw-r--r--lib/gitlab/email/handler/create_note_handler.rb4
-rw-r--r--lib/gitlab/email/handler/unsubscribe_handler.rb4
-rw-r--r--lib/gitlab/email/hook/additional_headers_interceptor.rb12
-rw-r--r--lib/gitlab/email/hook/delivery_metrics_observer.rb31
-rw-r--r--lib/gitlab/email/hook/disable_email_interceptor.rb13
-rw-r--r--lib/gitlab/email/hook/email_template_interceptor.rb18
-rw-r--r--lib/gitlab/encoding_helper.rb12
-rw-r--r--lib/gitlab/exclusive_lease_helpers.rb29
-rw-r--r--lib/gitlab/fogbugz_import/importer.rb22
-rw-r--r--lib/gitlab/gfm/uploads_rewriter.rb22
-rw-r--r--lib/gitlab/git/blob.rb108
-rw-r--r--lib/gitlab/git/commit.rb96
-rw-r--r--lib/gitlab/git/commit_stats.rb16
-rw-r--r--lib/gitlab/git/conflict/resolver.rb71
-rw-r--r--lib/gitlab/git/diff_collection.rb12
-rw-r--r--lib/gitlab/git/operation_service.rb2
-rw-r--r--lib/gitlab/git/popen.rb18
-rw-r--r--lib/gitlab/git/repository.rb1197
-rw-r--r--lib/gitlab/git/repository_mirroring.rb85
-rw-r--r--lib/gitlab/git/rev_list.rb73
-rwxr-xr-xlib/gitlab/git/support/format-git-cat-file-input21
-rw-r--r--lib/gitlab/git/tree.rb10
-rw-r--r--lib/gitlab/git/wiki.rb8
-rw-r--r--lib/gitlab/git_access.rb20
-rw-r--r--lib/gitlab/git_post_receive.rb12
-rw-r--r--lib/gitlab/git_ref_validator.rb8
-rw-r--r--lib/gitlab/gitaly_client.rb12
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb10
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb14
-rw-r--r--lib/gitlab/gitaly_client/conflicts_service.rb12
-rw-r--r--lib/gitlab/gitaly_client/namespace_service.rb12
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb38
-rw-r--r--lib/gitlab/gitaly_client/ref_service.rb81
-rw-r--r--lib/gitlab/gitaly_client/remote_service.rb4
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb77
-rw-r--r--lib/gitlab/gitaly_client/server_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/storage_settings.rb4
-rw-r--r--lib/gitlab/gitaly_client/wiki_service.rb14
-rw-r--r--lib/gitlab/github_import/importer/pull_requests_importer.rb2
-rw-r--r--lib/gitlab/gitlab_import/client.rb30
-rw-r--r--lib/gitlab/google_code_import/importer.rb22
-rw-r--r--lib/gitlab/gpg.rb12
-rw-r--r--lib/gitlab/gpg/commit.rb2
-rw-r--r--lib/gitlab/graphql/authorize.rb9
-rw-r--r--lib/gitlab/graphql/authorize/authorize_resource.rb46
-rw-r--r--lib/gitlab/graphql/connections.rb12
-rw-r--r--lib/gitlab/graphql/connections/keyset_connection.rb73
-rw-r--r--lib/gitlab/graphql/errors.rb9
-rw-r--r--lib/gitlab/graphql/mount_mutation.rb18
-rw-r--r--lib/gitlab/graphql/present/instrumentation.rb2
-rw-r--r--lib/gitlab/graphs/commits.rb2
-rw-r--r--lib/gitlab/hashed_storage/migrator.rb2
-rw-r--r--lib/gitlab/hook_data/base_builder.rb38
-rw-r--r--lib/gitlab/hook_data/issuable_builder.rb8
-rw-r--r--lib/gitlab/hook_data/issue_builder.rb9
-rw-r--r--lib/gitlab/hook_data/merge_request_builder.rb9
-rw-r--r--lib/gitlab/hook_data/note_builder.rb43
-rw-r--r--lib/gitlab/hook_data/wiki_page_builder.rb15
-rw-r--r--lib/gitlab/http_io.rb193
-rw-r--r--lib/gitlab/import_export.rb4
-rw-r--r--lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb17
-rw-r--r--lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb26
-rw-r--r--lib/gitlab/import_export/avatar_saver.rb11
-rw-r--r--lib/gitlab/import_export/command_line_util.rb15
-rw-r--r--lib/gitlab/import_export/file_importer.rb27
-rw-r--r--lib/gitlab/import_export/group_project_object_builder.rb90
-rw-r--r--lib/gitlab/import_export/import_export.yml1
-rw-r--r--lib/gitlab/import_export/importer.rb12
-rw-r--r--lib/gitlab/import_export/members_mapper.rb2
-rw-r--r--lib/gitlab/import_export/merge_request_parser.rb7
-rw-r--r--lib/gitlab/import_export/project_tree_restorer.rb29
-rw-r--r--lib/gitlab/import_export/relation_factory.rb74
-rw-r--r--lib/gitlab/import_export/saver.rb31
-rw-r--r--lib/gitlab/import_export/uploads_manager.rb98
-rw-r--r--lib/gitlab/import_export/uploads_restorer.rb21
-rw-r--r--lib/gitlab/import_export/uploads_saver.rb15
-rw-r--r--lib/gitlab/import_sources.rb30
-rw-r--r--lib/gitlab/json_logger.rb22
-rw-r--r--lib/gitlab/kubernetes.rb7
-rw-r--r--lib/gitlab/kubernetes/config_map.rb16
-rw-r--r--lib/gitlab/kubernetes/helm.rb2
-rw-r--r--lib/gitlab/kubernetes/helm/api.rb22
-rw-r--r--lib/gitlab/kubernetes/helm/base_command.rb34
-rw-r--r--lib/gitlab/kubernetes/helm/certificate.rb73
-rw-r--r--lib/gitlab/kubernetes/helm/init_command.rb18
-rw-r--r--lib/gitlab/kubernetes/helm/install_command.rb35
-rw-r--r--lib/gitlab/kubernetes/helm/pod.rb8
-rw-r--r--lib/gitlab/language_detection.rb68
-rw-r--r--lib/gitlab/logger.rb8
-rw-r--r--lib/gitlab/mail_room.rb2
-rw-r--r--lib/gitlab/manifest_import/manifest.rb81
-rw-r--r--lib/gitlab/manifest_import/project_creator.rb41
-rw-r--r--lib/gitlab/metrics/influx_db.rb2
-rw-r--r--lib/gitlab/metrics/method_call.rb2
-rw-r--r--lib/gitlab/metrics/subscribers/active_record.rb2
-rw-r--r--lib/gitlab/middleware/basic_health_check.rb43
-rw-r--r--lib/gitlab/middleware/multipart.rb16
-rw-r--r--lib/gitlab/middleware/read_only/controller.rb34
-rw-r--r--lib/gitlab/omniauth_initializer.rb25
-rw-r--r--lib/gitlab/popen.rb9
-rw-r--r--lib/gitlab/profiler.rb6
-rw-r--r--lib/gitlab/profiler/total_time_flat_printer.rb39
-rw-r--r--lib/gitlab/project_authorizations/with_nested_groups.rb2
-rw-r--r--lib/gitlab/project_authorizations/without_nested_groups.rb2
-rw-r--r--lib/gitlab/project_search_results.rb19
-rw-r--r--lib/gitlab/prometheus/metric.rb2
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb1
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb1
-rw-r--r--lib/gitlab/prometheus/queries/query_additional_metrics.rb8
-rw-r--r--lib/gitlab/regex.rb30
-rw-r--r--lib/gitlab/repository_cache_adapter.rb10
-rw-r--r--lib/gitlab/serializer/ci/variables.rb5
-rw-r--r--lib/gitlab/shell.rb59
-rw-r--r--lib/gitlab/template_helper.rb28
-rw-r--r--lib/gitlab/url_sanitizer.rb17
-rw-r--r--lib/gitlab/usage_data.rb2
-rw-r--r--lib/gitlab/user_activities.rb34
-rw-r--r--lib/gitlab/workhorse.rb63
-rw-r--r--lib/rouge/formatters/html_gitlab.rb2
-rw-r--r--lib/support/nginx/registry-ssl2
-rw-r--r--lib/tasks/gettext.rake36
-rw-r--r--lib/tasks/gitlab/bulk_add_permission.rake4
-rw-r--r--lib/tasks/gitlab/check.rake25
-rw-r--r--lib/tasks/gitlab/cleanup.rake58
-rw-r--r--lib/tasks/gitlab/git.rake85
-rw-r--r--lib/tasks/gitlab/info.rake4
-rw-r--r--lib/tasks/gitlab/uploads/migrate.rake2
-rw-r--r--lib/uploaded_file.rb17
-rw-r--r--locale/bg/gitlab.po2890
-rw-r--r--locale/cs_CZ/gitlab.po7910
-rw-r--r--locale/de/gitlab.po2896
-rw-r--r--locale/eo/gitlab.po2890
-rw-r--r--locale/es/gitlab.po3028
-rw-r--r--locale/fil_PH/gitlab.po2868
-rw-r--r--locale/fr/gitlab.po4518
-rw-r--r--locale/gitlab.pot1183
-rw-r--r--locale/gl_ES/gitlab.po7814
-rw-r--r--locale/id_ID/gitlab.po2851
-rw-r--r--locale/it/gitlab.po2918
-rw-r--r--locale/ja/gitlab.po4289
-rw-r--r--locale/ko/gitlab.po3433
-rw-r--r--locale/nl_NL/gitlab.po2876
-rw-r--r--locale/pl_PL/gitlab.po2904
-rw-r--r--locale/pt_BR/gitlab.po3928
-rw-r--r--locale/ru/gitlab.po3682
-rw-r--r--locale/tr_TR/gitlab.po2876
-rw-r--r--locale/uk/gitlab.po3458
-rw-r--r--locale/zh_CN/gitlab.po3497
-rw-r--r--locale/zh_HK/gitlab.po2879
-rw-r--r--locale/zh_TW/gitlab.po5023
-rw-r--r--package.json34
-rw-r--r--public/404.html4
-rw-r--r--public/422.html4
-rw-r--r--public/500.html4
-rw-r--r--public/502.html4
-rw-r--r--public/503.html4
-rw-r--r--qa/README.md2
-rw-r--r--qa/qa.rb41
-rw-r--r--qa/qa/factory/repository/project_push.rb2
-rw-r--r--qa/qa/factory/repository/push.rb16
-rw-r--r--qa/qa/factory/resource/branch.rb21
-rw-r--r--qa/qa/factory/resource/file.rb34
-rw-r--r--qa/qa/factory/resource/fork.rb24
-rw-r--r--qa/qa/factory/resource/group.rb2
-rw-r--r--qa/qa/factory/resource/kubernetes_cluster.rb8
-rw-r--r--qa/qa/factory/resource/merge_request.rb11
-rw-r--r--qa/qa/factory/resource/merge_request_from_fork.rb24
-rw-r--r--qa/qa/factory/resource/project.rb19
-rw-r--r--qa/qa/factory/resource/project_imported_from_github.rb37
-rw-r--r--qa/qa/factory/resource/project_milestone.rb36
-rw-r--r--qa/qa/factory/resource/sandbox.rb4
-rw-r--r--qa/qa/factory/resource/user.rb34
-rw-r--r--qa/qa/git/repository.rb2
-rw-r--r--qa/qa/page/admin/settings/repository_storage.rb4
-rw-r--r--qa/qa/page/component/dropzone.rb2
-rw-r--r--qa/qa/page/component/select2.rb11
-rw-r--r--qa/qa/page/file/form.rb40
-rw-r--r--qa/qa/page/file/shared/commit_message.rb19
-rw-r--r--qa/qa/page/file/show.rb30
-rw-r--r--qa/qa/page/issuable/sidebar.rb24
-rw-r--r--qa/qa/page/layout/banner.rb17
-rw-r--r--qa/qa/page/main/login.rb19
-rw-r--r--qa/qa/page/main/oauth.rb2
-rw-r--r--qa/qa/page/main/sign_up.rb27
-rw-r--r--qa/qa/page/menu/main.rb14
-rw-r--r--qa/qa/page/menu/side.rb21
-rw-r--r--qa/qa/page/merge_request/new.rb15
-rw-r--r--qa/qa/page/merge_request/show.rb44
-rw-r--r--qa/qa/page/project/fork/new.rb17
-rw-r--r--qa/qa/page/project/import/github.rb66
-rw-r--r--qa/qa/page/project/milestone/index.rb17
-rw-r--r--qa/qa/page/project/milestone/new.rb27
-rw-r--r--qa/qa/page/project/new.rb27
-rw-r--r--qa/qa/page/project/operations/kubernetes/add.rb4
-rw-r--r--qa/qa/page/project/operations/kubernetes/show.rb1
-rw-r--r--qa/qa/page/project/settings/ci_cd.rb8
-rw-r--r--qa/qa/page/project/settings/deploy_keys.rb2
-rw-r--r--qa/qa/page/project/settings/protected_branches.rb23
-rw-r--r--qa/qa/page/project/show.rb29
-rw-r--r--qa/qa/page/view.rb4
-rw-r--r--qa/qa/runtime/api/request.rb2
-rw-r--r--qa/qa/runtime/browser.rb13
-rw-r--r--qa/qa/runtime/env.rb15
-rw-r--r--qa/qa/runtime/key/base.rb4
-rw-r--r--qa/qa/runtime/namespace.rb4
-rw-r--r--qa/qa/runtime/release.rb4
-rw-r--r--qa/qa/scenario/test/instance.rb2
-rw-r--r--qa/qa/scenario/test/integration/github.rb18
-rw-r--r--qa/qa/service/kubernetes_cluster.rb10
-rw-r--r--qa/qa/specs/features/api/basics_spec.rb4
-rw-r--r--qa/qa/specs/features/api/users_spec.rb10
-rw-r--r--qa/qa/specs/features/login/ldap_spec.rb4
-rw-r--r--qa/qa/specs/features/login/standard_spec.rb15
-rw-r--r--qa/qa/specs/features/mattermost/group_create_spec.rb4
-rw-r--r--qa/qa/specs/features/mattermost/login_spec.rb4
-rw-r--r--qa/qa/specs/features/merge_request/create_spec.rb27
-rw-r--r--qa/qa/specs/features/merge_request/rebase_spec.rb4
-rw-r--r--qa/qa/specs/features/merge_request/squash_spec.rb6
-rw-r--r--qa/qa/specs/features/project/activity_spec.rb4
-rw-r--r--qa/qa/specs/features/project/add_deploy_key_spec.rb4
-rw-r--r--qa/qa/specs/features/project/add_secret_variable_spec.rb4
-rw-r--r--qa/qa/specs/features/project/auto_devops_spec.rb4
-rw-r--r--qa/qa/specs/features/project/create_issue_spec.rb4
-rw-r--r--qa/qa/specs/features/project/create_spec.rb4
-rw-r--r--qa/qa/specs/features/project/deploy_key_clone_spec.rb4
-rw-r--r--qa/qa/specs/features/project/file_spec.rb54
-rw-r--r--qa/qa/specs/features/project/fork_project_spec.rb23
-rw-r--r--qa/qa/specs/features/project/import_from_github_spec.rb106
-rw-r--r--qa/qa/specs/features/project/pipelines_spec.rb6
-rw-r--r--qa/qa/specs/features/project/wikis_spec.rb4
-rw-r--r--qa/qa/specs/features/repository/clone_spec.rb8
-rw-r--r--qa/qa/specs/features/repository/protected_branches_spec.rb41
-rw-r--r--qa/qa/specs/features/repository/push_spec.rb4
-rw-r--r--qa/spec/git/repository_spec.rb2
-rw-r--r--qa/spec/page/view_spec.rb4
-rw-r--r--qa/spec/runtime/env_spec.rb23
-rw-r--r--qa/spec/scenario/test/instance_spec.rb2
-rw-r--r--qa/spec/spec_helper.rb2
-rw-r--r--rubocop/cop/line_break_around_conditional_block.rb2
-rw-r--r--rubocop/cop/migration/add_reference.rb49
-rw-r--r--rubocop/rubocop.rb1
-rw-r--r--scripts/frontend/prettier.js4
-rwxr-xr-xscripts/lint-rugged5
-rwxr-xr-xscripts/trigger-build-docs14
-rw-r--r--spec/bin/changelog_spec.rb14
-rw-r--r--spec/config/object_store_settings_spec.rb29
-rw-r--r--spec/controllers/admin/services_controller_spec.rb2
-rw-r--r--spec/controllers/application_controller_spec.rb121
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb20
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb6
-rw-r--r--spec/controllers/boards/lists_controller_spec.rb2
-rw-r--r--spec/controllers/concerns/group_tree_spec.rb11
-rw-r--r--spec/controllers/dashboard/groups_controller_spec.rb4
-rw-r--r--spec/controllers/dashboard/milestones_controller_spec.rb21
-rw-r--r--spec/controllers/dashboard_controller_spec.rb2
-rw-r--r--spec/controllers/groups/boards_controller_spec.rb2
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/groups/runners_controller_spec.rb2
-rw-r--r--spec/controllers/groups/settings/ci_cd_controller_spec.rb2
-rw-r--r--spec/controllers/groups/uploads_controller_spec.rb8
-rw-r--r--spec/controllers/groups/variables_controller_spec.rb2
-rw-r--r--spec/controllers/groups_controller_spec.rb6
-rw-r--r--spec/controllers/import/bitbucket_server_controller_spec.rb154
-rw-r--r--spec/controllers/instance_statistics/cohorts_controller_spec.rb7
-rw-r--r--spec/controllers/instance_statistics/conversational_development_index_controller_spec.rb7
-rw-r--r--spec/controllers/metrics_controller_spec.rb49
-rw-r--r--spec/controllers/profiles_controller_spec.rb9
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb2
-rw-r--r--spec/controllers/projects/badges_controller_spec.rb2
-rw-r--r--spec/controllers/projects/blame_controller_spec.rb2
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb12
-rw-r--r--spec/controllers/projects/boards_controller_spec.rb2
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb2
-rw-r--r--spec/controllers/projects/clusters/applications_controller_spec.rb4
-rw-r--r--spec/controllers/projects/clusters/gcp_controller_spec.rb182
-rw-r--r--spec/controllers/projects/clusters/user_controller_spec.rb89
-rw-r--r--spec/controllers/projects/clusters_controller_spec.rb248
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb2
-rw-r--r--spec/controllers/projects/commits_controller_spec.rb14
-rw-r--r--spec/controllers/projects/compare_controller_spec.rb2
-rw-r--r--spec/controllers/projects/cycle_analytics_controller_spec.rb2
-rw-r--r--spec/controllers/projects/deploy_keys_controller_spec.rb4
-rw-r--r--spec/controllers/projects/deployments_controller_spec.rb2
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb21
-rw-r--r--spec/controllers/projects/find_file_controller_spec.rb2
-rw-r--r--spec/controllers/projects/forks_controller_spec.rb2
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/group_links_controller_spec.rb4
-rw-r--r--spec/controllers/projects/hooks_controller_spec.rb2
-rw-r--r--spec/controllers/projects/imports_controller_spec.rb10
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb27
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb124
-rw-r--r--spec/controllers/projects/labels_controller_spec.rb10
-rw-r--r--spec/controllers/projects/mattermosts_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests/creations_controller_spec.rb4
-rw-r--r--spec/controllers/projects/merge_requests/diffs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb101
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb45
-rw-r--r--spec/controllers/projects/mirrors_controller_spec.rb4
-rw-r--r--spec/controllers/projects/pages_controller_spec.rb2
-rw-r--r--spec/controllers/projects/pages_domains_controller_spec.rb2
-rw-r--r--spec/controllers/projects/pipeline_schedules_controller_spec.rb28
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb34
-rw-r--r--spec/controllers/projects/pipelines_settings_controller_spec.rb2
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb20
-rw-r--r--spec/controllers/projects/prometheus/metrics_controller_spec.rb2
-rw-r--r--spec/controllers/projects/protected_branches_controller_spec.rb8
-rw-r--r--spec/controllers/projects/protected_tags_controller_spec.rb2
-rw-r--r--spec/controllers/projects/runners_controller_spec.rb2
-rw-r--r--spec/controllers/projects/services_controller_spec.rb2
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb6
-rw-r--r--spec/controllers/projects/settings/integrations_controller_spec.rb2
-rw-r--r--spec/controllers/projects/settings/repository_controller_spec.rb2
-rw-r--r--spec/controllers/projects/snippets_controller_spec.rb6
-rw-r--r--spec/controllers/projects/templates_controller_spec.rb2
-rw-r--r--spec/controllers/projects/todos_controller_spec.rb133
-rw-r--r--spec/controllers/projects/tree_controller_spec.rb2
-rw-r--r--spec/controllers/projects/uploads_controller_spec.rb8
-rw-r--r--spec/controllers/projects/variables_controller_spec.rb2
-rw-r--r--spec/controllers/projects/wikis_controller_spec.rb124
-rw-r--r--spec/controllers/projects_controller_spec.rb91
-rw-r--r--spec/controllers/sessions_controller_spec.rb17
-rw-r--r--spec/controllers/uploads_controller_spec.rb8
-rw-r--r--spec/controllers/users_controller_spec.rb64
-rw-r--r--spec/factories/ci/build_trace_chunks.rb48
-rw-r--r--spec/factories/ci/builds.rb12
-rw-r--r--spec/factories/ci/job_artifacts.rb44
-rw-r--r--spec/factories/ci/pipelines.rb12
-rw-r--r--spec/factories/ci/runners.rb4
-rw-r--r--spec/factories/clusters/applications/helm.rb16
-rw-r--r--spec/factories/clusters/clusters.rb4
-rw-r--r--spec/factories/group_members.rb2
-rw-r--r--spec/factories/import_export_uploads.rb5
-rw-r--r--spec/factories/issues.rb2
-rw-r--r--spec/factories/merge_requests.rb18
-rw-r--r--spec/factories/milestones.rb5
-rw-r--r--spec/factories/notes.rb1
-rw-r--r--spec/factories/programming_languages.rb6
-rw-r--r--spec/factories/project_members.rb4
-rw-r--r--spec/factories/projects.rb20
-rw-r--r--spec/factories/protected_branches.rb8
-rw-r--r--spec/factories/protected_tags.rb6
-rw-r--r--spec/factories/repository_languages.rb7
-rw-r--r--spec/factories/resource_label_events.rb10
-rw-r--r--spec/factories/site_statistics.rb7
-rw-r--r--spec/factories/todos.rb4
-rw-r--r--spec/factories/uploads.rb7
-rw-r--r--spec/factories/user_statuses.rb9
-rw-r--r--spec/features/abuse_report_spec.rb4
-rw-r--r--spec/features/admin/admin_appearance_spec.rb18
-rw-r--r--spec/features/admin/admin_broadcast_messages_spec.rb12
-rw-r--r--spec/features/admin/admin_browse_spam_logs_spec.rb2
-rw-r--r--spec/features/admin/admin_cohorts_spec.rb15
-rw-r--r--spec/features/admin/admin_conversational_development_index_spec.rb40
-rw-r--r--spec/features/admin/admin_disables_git_access_protocol_spec.rb16
-rw-r--r--spec/features/admin/admin_disables_two_factor_spec.rb6
-rw-r--r--spec/features/admin/admin_groups_spec.rb14
-rw-r--r--spec/features/admin/admin_health_check_spec.rb2
-rw-r--r--spec/features/admin/admin_hook_logs_spec.rb8
-rw-r--r--spec/features/admin/admin_labels_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb4
-rw-r--r--spec/features/admin/admin_settings_spec.rb56
-rw-r--r--spec/features/admin/admin_users_spec.rb53
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb8
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb4
-rw-r--r--spec/features/atom/dashboard_spec.rb2
-rw-r--r--spec/features/atom/users_spec.rb2
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb2
-rw-r--r--spec/features/boards/boards_spec.rb4
-rw-r--r--spec/features/boards/issue_ordering_spec.rb2
-rw-r--r--spec/features/boards/modal_filter_spec.rb2
-rw-r--r--spec/features/boards/new_issue_spec.rb2
-rw-r--r--spec/features/boards/sidebar_spec.rb2
-rw-r--r--spec/features/boards/sub_group_project_spec.rb2
-rw-r--r--spec/features/calendar_spec.rb2
-rw-r--r--spec/features/commits_spec.rb8
-rw-r--r--spec/features/container_registry_spec.rb8
-rw-r--r--spec/features/cycle_analytics_spec.rb8
-rw-r--r--spec/features/dashboard/active_tab_spec.rb28
-rw-r--r--spec/features/dashboard/activity_spec.rb18
-rw-r--r--spec/features/dashboard/archived_projects_spec.rb4
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb6
-rw-r--r--spec/features/dashboard/groups_list_spec.rb2
-rw-r--r--spec/features/dashboard/instance_statistics_spec.rb60
-rw-r--r--spec/features/dashboard/issues_filter_spec.rb4
-rw-r--r--spec/features/dashboard/issues_spec.rb2
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb6
-rw-r--r--spec/features/dashboard/milestone_filter_spec.rb2
-rw-r--r--spec/features/dashboard/milestone_tabs_spec.rb2
-rw-r--r--spec/features/dashboard/milestones_spec.rb4
-rw-r--r--spec/features/dashboard/project_member_activity_index_spec.rb4
-rw-r--r--spec/features/dashboard/projects_spec.rb38
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb6
-rw-r--r--spec/features/dashboard/todos/target_state_spec.rb12
-rw-r--r--spec/features/dashboard/todos/todos_filtering_spec.rb2
-rw-r--r--spec/features/dashboard/todos/todos_sorting_spec.rb2
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb2
-rw-r--r--spec/features/dashboard/user_filters_projects_spec.rb2
-rw-r--r--spec/features/discussion_comments/commit_spec.rb2
-rw-r--r--spec/features/discussion_comments/issue_spec.rb2
-rw-r--r--spec/features/discussion_comments/merge_request_spec.rb2
-rw-r--r--spec/features/discussion_comments/snippets_spec.rb2
-rw-r--r--spec/features/expand_collapse_diffs_spec.rb2
-rw-r--r--spec/features/explore/groups_list_spec.rb4
-rw-r--r--spec/features/explore/new_menu_spec.rb32
-rw-r--r--spec/features/global_search_spec.rb4
-rw-r--r--spec/features/group_variables_spec.rb6
-rw-r--r--spec/features/groups/activity_spec.rb4
-rw-r--r--spec/features/groups/board_spec.rb35
-rw-r--r--spec/features/groups/empty_states_spec.rb34
-rw-r--r--spec/features/groups/group_settings_spec.rb32
-rw-r--r--spec/features/groups/issues_spec.rb27
-rw-r--r--spec/features/groups/labels/edit_spec.rb12
-rw-r--r--spec/features/groups/labels/index_spec.rb6
-rw-r--r--spec/features/groups/labels/subscription_spec.rb4
-rw-r--r--spec/features/groups/labels/user_sees_links_to_issuables.rb4
-rw-r--r--spec/features/groups/members/filter_members_spec.rb12
-rw-r--r--spec/features/groups/members/leave_group_spec.rb12
-rw-r--r--spec/features/groups/members/list_members_spec.rb22
-rw-r--r--spec/features/groups/members/manage_members_spec.rb18
-rw-r--r--spec/features/groups/members/master_manages_access_requests_spec.rb4
-rw-r--r--spec/features/groups/members/request_access_spec.rb20
-rw-r--r--spec/features/groups/members/sort_members_spec.rb22
-rw-r--r--spec/features/groups/merge_requests_spec.rb19
-rw-r--r--spec/features/groups/milestone_spec.rb17
-rw-r--r--spec/features/groups/milestones_sorting_spec.rb6
-rw-r--r--spec/features/groups/settings/group_badges_spec.rb2
-rw-r--r--spec/features/groups/share_lock_spec.rb20
-rw-r--r--spec/features/groups/show_spec.rb2
-rw-r--r--spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb2
-rw-r--r--spec/features/groups_spec.rb8
-rw-r--r--spec/features/ics/dashboard_issues_spec.rb2
-rw-r--r--spec/features/ide_spec.rb2
-rw-r--r--spec/features/import/manifest_import_spec.rb51
-rw-r--r--spec/features/instance_statistics/cohorts_spec.rb15
-rw-r--r--spec/features/instance_statistics/conversational_development_index_spec.rb40
-rw-r--r--spec/features/invites_spec.rb2
-rw-r--r--spec/features/issuables/close_reopen_report_toggle_spec.rb4
-rw-r--r--spec/features/issuables/shortcuts_issuable_spec.rb2
-rw-r--r--spec/features/issues/award_emoji_spec.rb2
-rw-r--r--spec/features/issues/award_spec.rb2
-rw-r--r--spec/features/issues/bulk_assignment_labels_spec.rb4
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb4
-rw-r--r--spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb4
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb8
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb8
-rw-r--r--spec/features/issues/filtered_search/dropdown_emoji_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_label_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_milestone_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/visual_tokens_spec.rb4
-rw-r--r--spec/features/issues/form_spec.rb6
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb4
-rw-r--r--spec/features/issues/issue_detail_spec.rb2
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb4
-rw-r--r--spec/features/issues/markdown_toolbar_spec.rb2
-rw-r--r--spec/features/issues/move_spec.rb24
-rw-r--r--spec/features/issues/note_polling_spec.rb2
-rw-r--r--spec/features/issues/spam_issues_spec.rb2
-rw-r--r--spec/features/issues/todo_spec.rb4
-rw-r--r--spec/features/issues/update_issues_spec.rb8
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb8
-rw-r--r--spec/features/issues_spec.rb2
-rw-r--r--spec/features/labels_hierarchy_spec.rb2
-rw-r--r--spec/features/markdown/gitlab_flavored_markdown_spec.rb2
-rw-r--r--spec/features/merge_request/maintainer_edits_fork_spec.rb2
-rw-r--r--spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb2
-rw-r--r--spec/features/merge_request/user_cherry_picks_spec.rb53
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_customizes_merge_commit_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_immediately_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb2
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb6
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb9
-rw-r--r--spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb71
-rw-r--r--spec/features/merge_request/user_sees_closing_issues_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_deleted_target_branch_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_discussions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_empty_state_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb232
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_wip_help_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb2
-rw-r--r--spec/features/merge_request/user_toggles_whitespace_changes_spec.rb2
-rw-r--r--spec/features/merge_request/user_uses_slash_commands_spec.rb4
-rw-r--r--spec/features/merge_requests/user_mass_updates_spec.rb6
-rw-r--r--spec/features/merge_requests/user_squashes_merge_request_spec.rb4
-rw-r--r--spec/features/milestone_spec.rb30
-rw-r--r--spec/features/milestones/user_deletes_milestone_spec.rb1
-rw-r--r--spec/features/milestones/user_edits_milestone_spec.rb22
-rw-r--r--spec/features/oauth_login_spec.rb2
-rw-r--r--spec/features/participants_autocomplete_spec.rb2
-rw-r--r--spec/features/password_reset_spec.rb2
-rw-r--r--spec/features/profiles/account_spec.rb24
-rw-r--r--spec/features/profiles/active_sessions_spec.rb6
-rw-r--r--spec/features/profiles/chat_names_spec.rb26
-rw-r--r--spec/features/profiles/emails_spec.rb14
-rw-r--r--spec/features/profiles/gpg_keys_spec.rb12
-rw-r--r--spec/features/profiles/keys_spec.rb28
-rw-r--r--spec/features/profiles/password_spec.rb2
-rw-r--r--spec/features/profiles/user_changes_notified_of_own_activity_spec.rb6
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb80
-rw-r--r--spec/features/profiles/user_visits_notifications_tab_spec.rb4
-rw-r--r--spec/features/profiles/user_visits_profile_spec.rb2
-rw-r--r--spec/features/project_variables_spec.rb2
-rw-r--r--spec/features/projects/activity/rss_spec.rb2
-rw-r--r--spec/features/projects/activity/user_sees_activity_spec.rb2
-rw-r--r--spec/features/projects/actve_tabs_spec.rb2
-rw-r--r--spec/features/projects/artifacts/file_spec.rb2
-rw-r--r--spec/features/projects/artifacts/raw_spec.rb2
-rw-r--r--spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb2
-rw-r--r--spec/features/projects/badges/coverage_spec.rb20
-rw-r--r--spec/features/projects/badges/list_spec.rb12
-rw-r--r--spec/features/projects/badges/pipeline_badge_spec.rb2
-rw-r--r--spec/features/projects/blobs/blob_line_permalink_updater_spec.rb2
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb43
-rw-r--r--spec/features/projects/blobs/edit_spec.rb6
-rw-r--r--spec/features/projects/blobs/shortcuts_blob_spec.rb2
-rw-r--r--spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb6
-rw-r--r--spec/features/projects/branches/download_buttons_spec.rb18
-rw-r--r--spec/features/projects/branches/new_branch_ref_dropdown_spec.rb2
-rw-r--r--spec/features/projects/branches_spec.rb4
-rw-r--r--spec/features/projects/clusters/applications_spec.rb24
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb16
-rw-r--r--spec/features/projects/clusters/interchangeability_spec.rb2
-rw-r--r--spec/features/projects/clusters/user_spec.rb6
-rw-r--r--spec/features/projects/clusters_spec.rb6
-rw-r--r--spec/features/projects/commit/builds_spec.rb12
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb2
-rw-r--r--spec/features/projects/commit/comments/user_adds_comment_spec.rb2
-rw-r--r--spec/features/projects/commit/diff_notes_spec.rb4
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/projects/commit/user_views_user_status_on_commit_spec.rb40
-rw-r--r--spec/features/projects/commits/rss_spec.rb2
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb3
-rw-r--r--spec/features/projects/compare_spec.rb2
-rw-r--r--spec/features/projects/deploy_keys_spec.rb2
-rw-r--r--spec/features/projects/diffs/diff_show_spec.rb4
-rw-r--r--spec/features/projects/environments/environment_metrics_spec.rb18
-rw-r--r--spec/features/projects/environments/environment_spec.rb93
-rw-r--r--spec/features/projects/environments/environments_spec.rb64
-rw-r--r--spec/features/projects/features_visibility_spec.rb4
-rw-r--r--spec/features/projects/files/project_owner_creates_license_file_spec.rb10
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb6
-rw-r--r--spec/features/projects/files/template_selector_menu_spec.rb12
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb5
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_deletes_files_spec.rb4
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb4
-rw-r--r--spec/features/projects/files/user_find_file_spec.rb2
-rw-r--r--spec/features/projects/files/user_reads_pipeline_status_spec.rb4
-rw-r--r--spec/features/projects/files/user_replaces_files_spec.rb4
-rw-r--r--spec/features/projects/files/user_uploads_files_spec.rb2
-rw-r--r--spec/features/projects/fork_spec.rb4
-rw-r--r--spec/features/projects/graph_spec.rb2
-rw-r--r--spec/features/projects/hook_logs/user_reads_log_spec.rb12
-rw-r--r--spec/features/projects/import_export/export_file_spec.rb7
-rw-r--r--spec/features/projects/import_export/import_file_object_storage_spec.rb103
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb11
-rw-r--r--spec/features/projects/import_export/namespace_export_file_spec.rb1
-rw-r--r--spec/features/projects/import_export/test_project_export.tar.gzbin343136 -> 3368 bytes
-rw-r--r--spec/features/projects/issuable_templates_spec.rb24
-rw-r--r--spec/features/projects/issues/rss_spec.rb2
-rw-r--r--spec/features/projects/issues/user_comments_on_issue_spec.rb8
-rw-r--r--spec/features/projects/issues/user_creates_issue_spec.rb3
-rw-r--r--spec/features/projects/issues/user_views_issue_spec.rb18
-rw-r--r--spec/features/projects/jobs/permissions_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_job_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_jobs_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb34
-rw-r--r--spec/features/projects/labels/issues_sorted_by_priority_spec.rb6
-rw-r--r--spec/features/projects/labels/search_labels_spec.rb80
-rw-r--r--spec/features/projects/labels/subscription_spec.rb4
-rw-r--r--spec/features/projects/labels/update_prioritization_spec.rb12
-rw-r--r--spec/features/projects/labels/user_creates_labels_spec.rb4
-rw-r--r--spec/features/projects/labels/user_edits_labels_spec.rb2
-rw-r--r--spec/features/projects/labels/user_removes_labels_spec.rb2
-rw-r--r--spec/features/projects/labels/user_sees_links_to_issuables.rb14
-rw-r--r--spec/features/projects/members/anonymous_user_sees_members_spec.rb8
-rw-r--r--spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb6
-rw-r--r--spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb14
-rw-r--r--spec/features/projects/members/group_members_spec.rb20
-rw-r--r--spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb6
-rw-r--r--spec/features/projects/members/groups_with_access_list_spec.rb16
-rw-r--r--spec/features/projects/members/list_spec.rb16
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb14
-rw-r--r--spec/features/projects/members/master_manages_access_requests_spec.rb4
-rw-r--r--spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb6
-rw-r--r--spec/features/projects/members/member_leaves_project_spec.rb6
-rw-r--r--spec/features/projects/members/owner_cannot_leave_project_spec.rb6
-rw-r--r--spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb6
-rw-r--r--spec/features/projects/members/share_with_group_spec.rb50
-rw-r--r--spec/features/projects/members/sorting_spec.rb46
-rw-r--r--spec/features/projects/members/user_requests_access_spec.rb20
-rw-r--r--spec/features/projects/merge_request_button_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_closes_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_comments_on_commit_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_comments_on_diff_spec.rb8
-rw-r--r--spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_creates_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_edits_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_manages_subscription_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_views_user_status_on_merge_request_spec.rb36
-rw-r--r--spec/features/projects/milestones/milestone_spec.rb2
-rw-r--r--spec/features/projects/milestones/milestones_sorting_spec.rb4
-rw-r--r--spec/features/projects/milestones/new_spec.rb6
-rw-r--r--spec/features/projects/milestones/user_interacts_with_labels_spec.rb2
-rw-r--r--spec/features/projects/new_project_spec.rb40
-rw-r--r--spec/features/projects/pages_spec.rb36
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb22
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb6
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb4
-rw-r--r--spec/features/projects/remote_mirror_spec.rb8
-rw-r--r--spec/features/projects/services/user_activates_asana_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_assembla_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_emails_on_push_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_flowdock_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_hipchat_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_irker_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_issue_tracker_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_jira_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_packagist_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_pivotaltracker_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_prometheus_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_pushover_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_slack_notifications_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_slack_slash_command_spec.rb12
-rw-r--r--spec/features/projects/services/user_views_services_spec.rb2
-rw-r--r--spec/features/projects/settings/forked_project_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/integration_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/lfs_settings_spec.rb6
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb64
-rw-r--r--spec/features/projects/settings/project_badges_spec.rb4
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb10
-rw-r--r--spec/features/projects/settings/user_archives_project_spec.rb2
-rw-r--r--spec/features/projects/settings/user_changes_avatar_spec.rb2
-rw-r--r--spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb4
-rw-r--r--spec/features/projects/settings/user_manages_group_links_spec.rb4
-rw-r--r--spec/features/projects/settings/user_manages_project_members_spec.rb6
-rw-r--r--spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb25
-rw-r--r--spec/features/projects/settings/user_transfers_a_project_spec.rb9
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb8
-rw-r--r--spec/features/projects/show/developer_views_empty_project_instructions_spec.rb14
-rw-r--r--spec/features/projects/show/download_buttons_spec.rb20
-rw-r--r--spec/features/projects/show/no_password_spec.rb2
-rw-r--r--spec/features/projects/show/rss_spec.rb2
-rw-r--r--spec/features/projects/show/user_manages_notifications_spec.rb32
-rw-r--r--spec/features/projects/show/user_sees_deletion_failure_message_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb68
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/show_spec.rb14
-rw-r--r--spec/features/projects/snippets/user_comments_on_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_deletes_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_updates_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_views_snippets_spec.rb2
-rw-r--r--spec/features/projects/sub_group_issuables_spec.rb2
-rw-r--r--spec/features/projects/tags/download_buttons_spec.rb20
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb12
-rw-r--r--spec/features/projects/tree/create_file_spec.rb8
-rw-r--r--spec/features/projects/tree/rss_spec.rb2
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb66
-rw-r--r--spec/features/projects/tree/upload_file_spec.rb8
-rw-r--r--spec/features/projects/user_creates_project_spec.rb2
-rw-r--r--spec/features/projects/user_uses_shortcuts_spec.rb12
-rw-r--r--spec/features/projects/user_views_empty_project_spec.rb4
-rw-r--r--spec/features/projects/view_on_env_spec.rb2
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb13
-rw-r--r--spec/features/projects/wiki/shortcuts_spec.rb4
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb52
-rw-r--r--spec/features/projects/wiki/user_deletes_wiki_page_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_git_access_wiki_page_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb6
-rw-r--r--spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb22
-rw-r--r--spec/features/projects_spec.rb57
-rw-r--r--spec/features/protected_branches_spec.rb6
-rw-r--r--spec/features/protected_tags_spec.rb2
-rw-r--r--spec/features/raven_js_spec.rb2
-rw-r--r--spec/features/reportable_note/commit_spec.rb2
-rw-r--r--spec/features/reportable_note/issue_spec.rb2
-rw-r--r--spec/features/reportable_note/merge_request_spec.rb2
-rw-r--r--spec/features/reportable_note/snippets_spec.rb2
-rw-r--r--spec/features/runners_spec.rb120
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_issues_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_merge_requests_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_milestones_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_wiki_pages_spec.rb2
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb4
-rw-r--r--spec/features/security/group/internal_access_spec.rb10
-rw-r--r--spec/features/security/group/private_access_spec.rb10
-rw-r--r--spec/features/security/group/public_access_spec.rb10
-rw-r--r--spec/features/security/project/internal_access_spec.rb68
-rw-r--r--spec/features/security/project/private_access_spec.rb62
-rw-r--r--spec/features/security/project/public_access_spec.rb68
-rw-r--r--spec/features/security/project/snippet/internal_access_spec.rb12
-rw-r--r--spec/features/security/project/snippet/private_access_spec.rb8
-rw-r--r--spec/features/security/project/snippet/public_access_spec.rb16
-rw-r--r--spec/features/signed_commits_spec.rb37
-rw-r--r--spec/features/snippets/explore_spec.rb8
-rw-r--r--spec/features/snippets/internal_snippet_spec.rb6
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb11
-rw-r--r--spec/features/snippets/public_snippets_spec.rb6
-rw-r--r--spec/features/snippets/search_snippets_spec.rb6
-rw-r--r--spec/features/snippets/show_spec.rb30
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb12
-rw-r--r--spec/features/snippets/user_deletes_snippet_spec.rb2
-rw-r--r--spec/features/snippets/user_edits_snippet_spec.rb2
-rw-r--r--spec/features/snippets/user_snippets_spec.rb12
-rw-r--r--spec/features/tags/master_creates_tag_spec.rb20
-rw-r--r--spec/features/tags/master_deletes_tag_spec.rb35
-rw-r--r--spec/features/tags/master_updates_tag_spec.rb12
-rw-r--r--spec/features/tags/master_views_tags_spec.rb16
-rw-r--r--spec/features/task_lists_spec.rb4
-rw-r--r--spec/features/triggers_spec.rb34
-rw-r--r--spec/features/u2f_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_group_spec.rb4
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_profile_spec.rb8
-rw-r--r--spec/features/uploads/user_uploads_file_to_note_spec.rb4
-rw-r--r--spec/features/user_sees_revert_modal_spec.rb28
-rw-r--r--spec/features/users/active_sessions_spec.rb10
-rw-r--r--spec/features/users/add_email_to_existing_account.rb15
-rw-r--r--spec/features/users/login_spec.rb243
-rw-r--r--spec/features/users/rss_spec.rb2
-rw-r--r--spec/features/users/show_spec.rb84
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb6
-rw-r--r--spec/finders/access_requests_finder_spec.rb4
-rw-r--r--spec/finders/admin/projects_finder_spec.rb2
-rw-r--r--spec/finders/concerns/finder_with_cross_project_access_spec.rb2
-rw-r--r--spec/finders/contributed_projects_finder_spec.rb4
-rw-r--r--spec/finders/environments_finder_spec.rb2
-rw-r--r--spec/finders/group_members_finder_spec.rb24
-rw-r--r--spec/finders/group_projects_finder_spec.rb18
-rw-r--r--spec/finders/issues_finder_spec.rb2
-rw-r--r--spec/finders/joined_groups_finder_spec.rb12
-rw-r--r--spec/finders/labels_finder_spec.rb18
-rw-r--r--spec/finders/members_finder_spec.rb12
-rw-r--r--spec/finders/merge_requests_finder_spec.rb18
-rw-r--r--spec/finders/move_to_project_finder_spec.rb22
-rw-r--r--spec/finders/notes_finder_spec.rb2
-rw-r--r--spec/finders/personal_projects_finder_spec.rb2
-rw-r--r--spec/finders/pipelines_finder_spec.rb29
-rw-r--r--spec/finders/projects_finder_spec.rb4
-rw-r--r--spec/finders/todos_finder_spec.rb38
-rw-r--r--spec/finders/user_recent_events_finder_spec.rb15
-rw-r--r--spec/fixtures/aosp_manifest.xml685
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json6
-rw-r--r--spec/fixtures/api/schemas/entities/test_case.json15
-rw-r--r--spec/fixtures/api/schemas/entities/test_reports_comparer.json26
-rw-r--r--spec/fixtures/api/schemas/entities/test_suite_comparer.json32
-rw-r--r--spec/fixtures/api/schemas/pipeline.json4
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json6
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/projects.json15
-rw-r--r--spec/fixtures/emails/commands_in_reply.eml2
-rw-r--r--spec/fixtures/emails/commands_only_reply.eml2
-rw-r--r--spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml2
-rw-r--r--spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml2
-rw-r--r--spec/fixtures/emails/update_commands_only_reply.eml2
-rw-r--r--spec/fixtures/emails/valid_reply.eml2
-rw-r--r--spec/fixtures/importers/bitbucket_server/activities.json1121
-rw-r--r--spec/fixtures/importers/bitbucket_server/pull_request.json146
-rw-r--r--spec/fixtures/junit/junit.xml32
-rw-r--r--spec/fixtures/junit/junit.xml.gzbin0 -> 568 bytes
-rw-r--r--spec/fixtures/junit/junit_ant.xml.gzbin0 -> 1659 bytes
-rw-r--r--spec/fixtures/junit/junit_with_corrupted_data.xml.gzbin0 -> 461 bytes
-rw-r--r--spec/fixtures/junit/junit_with_three_testsuites.xml.gzbin0 -> 10106 bytes
-rw-r--r--spec/fixtures/junit/junit_with_three_testsuites_1.xml8
-rw-r--r--spec/fixtures/junit/junit_with_three_testsuites_2.xml6010
-rw-r--r--spec/fixtures/junit/junit_with_three_testsuites_3.xml8
-rw-r--r--spec/fixtures/project_export.tar.gzbin0 -> 343091 bytes
-rw-r--r--spec/fixtures/trace/sample_trace32
-rw-r--r--spec/graphql/gitlab_schema_spec.rb8
-rw-r--r--spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb19
-rw-r--r--spec/graphql/mutations/merge_requests/set_wip_spec.rb51
-rw-r--r--spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb52
-rw-r--r--spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb30
-rw-r--r--spec/graphql/resolvers/project_pipelines_resolver_spec.rb22
-rw-r--r--spec/graphql/types/ci/pipeline_type_spec.rb7
-rw-r--r--spec/graphql/types/merge_request_type_spec.rb13
-rw-r--r--spec/graphql/types/mutation_type_spec.rb9
-rw-r--r--spec/graphql/types/project_type_spec.rb2
-rw-r--r--spec/helpers/avatars_helper_spec.rb65
-rw-r--r--spec/helpers/blob_helper_spec.rb4
-rw-r--r--spec/helpers/button_helper_spec.rb6
-rw-r--r--spec/helpers/groups_helper_spec.rb21
-rw-r--r--spec/helpers/icons_helper_spec.rb23
-rw-r--r--spec/helpers/issuables_helper_spec.rb22
-rw-r--r--spec/helpers/markup_helper_spec.rb23
-rw-r--r--spec/helpers/merge_requests_helper_spec.rb2
-rw-r--r--spec/helpers/namespaces_helper_spec.rb48
-rw-r--r--spec/helpers/notes_helper_spec.rb16
-rw-r--r--spec/helpers/projects_helper_spec.rb32
-rw-r--r--spec/helpers/search_helper_spec.rb14
-rw-r--r--spec/helpers/snippets_helper_spec.rb4
-rw-r--r--spec/helpers/submodule_helper_spec.rb21
-rw-r--r--spec/helpers/time_helper_spec.rb10
-rw-r--r--spec/helpers/users_helper_spec.rb16
-rw-r--r--spec/helpers/visibility_level_helper_spec.rb23
-rw-r--r--spec/javascripts/.eslintrc.yml1
-rw-r--r--spec/javascripts/api_spec.js2
-rw-r--r--spec/javascripts/autosave_spec.js10
-rw-r--r--spec/javascripts/avatar_helper_spec.js98
-rw-r--r--spec/javascripts/boards/boards_store_spec.js22
-rw-r--r--spec/javascripts/boards/issue_card_spec.js154
-rw-r--r--spec/javascripts/clusters/clusters_bundle_spec.js36
-rw-r--r--spec/javascripts/clusters/components/application_row_spec.js49
-rw-r--r--spec/javascripts/clusters/services/mock_data.js27
-rw-r--r--spec/javascripts/clusters/stores/clusters_store_spec.js4
-rw-r--r--spec/javascripts/datetime_utility_spec.js1
-rw-r--r--spec/javascripts/diffs/components/changed_files_spec.js91
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js192
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js6
-rw-r--r--spec/javascripts/diffs/components/diff_line_gutter_content_spec.js6
-rw-r--r--spec/javascripts/diffs/components/diff_line_note_form_spec.js43
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js2
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js44
-rw-r--r--spec/javascripts/diffs/store/getters_spec.js310
-rw-r--r--spec/javascripts/diffs/store/mutations_spec.js15
-rw-r--r--spec/javascripts/diffs/store/utils_spec.js31
-rw-r--r--spec/javascripts/environments/environment_item_spec.js2
-rw-r--r--spec/javascripts/environments/environment_rollback_spec.js4
-rw-r--r--spec/javascripts/environments/environment_stop_spec.js12
-rw-r--r--spec/javascripts/environments/mock_data.js12
-rw-r--r--spec/javascripts/fixtures/commit.rb2
-rw-r--r--spec/javascripts/fixtures/graph.html.haml1
-rw-r--r--spec/javascripts/fixtures/groups.rb2
-rw-r--r--spec/javascripts/fixtures/merge_requests.rb7
-rw-r--r--spec/javascripts/fixtures/projects.rb2
-rw-r--r--spec/javascripts/fixtures/search_autocomplete.html.haml6
-rw-r--r--spec/javascripts/frequent_items/components/app_spec.js251
-rw-r--r--spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js75
-rw-r--r--spec/javascripts/frequent_items/components/frequent_items_list_spec.js84
-rw-r--r--spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js77
-rw-r--r--spec/javascripts/frequent_items/mock_data.js168
-rw-r--r--spec/javascripts/frequent_items/store/actions_spec.js225
-rw-r--r--spec/javascripts/frequent_items/store/getters_spec.js24
-rw-r--r--spec/javascripts/frequent_items/store/mutations_spec.js117
-rw-r--r--spec/javascripts/frequent_items/utils_spec.js89
-rw-r--r--spec/javascripts/gpg_badges_spec.js76
-rw-r--r--spec/javascripts/helpers/init_vue_mr_page_helper.js12
-rw-r--r--spec/javascripts/helpers/vue_mount_component_helper.js10
-rw-r--r--spec/javascripts/helpers/vuex_action_helper.js112
-rw-r--r--spec/javascripts/helpers/vuex_action_helper_spec.js166
-rw-r--r--spec/javascripts/helpers/wait_for_promises.js1
-rw-r--r--spec/javascripts/ide/components/activity_bar_spec.js20
-rw-r--r--spec/javascripts/ide/components/branches/item_spec.js53
-rw-r--r--spec/javascripts/ide/components/branches/search_list_spec.js79
-rw-r--r--spec/javascripts/ide/components/changed_file_icon_spec.js8
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/actions_spec.js8
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/list_item_spec.js16
-rw-r--r--spec/javascripts/ide/components/ide_spec.js27
-rw-r--r--spec/javascripts/ide/components/ide_status_bar_spec.js26
-rw-r--r--spec/javascripts/ide/components/jobs/detail/description_spec.js2
-rw-r--r--spec/javascripts/ide/components/jobs/item_spec.js2
-rw-r--r--spec/javascripts/ide/components/merge_requests/dropdown_spec.js47
-rw-r--r--spec/javascripts/ide/components/merge_requests/info_spec.js51
-rw-r--r--spec/javascripts/ide/components/merge_requests/item_spec.js15
-rw-r--r--spec/javascripts/ide/components/merge_requests/list_spec.js112
-rw-r--r--spec/javascripts/ide/components/nav_dropdown_button_spec.js63
-rw-r--r--spec/javascripts/ide/components/nav_dropdown_spec.js50
-rw-r--r--spec/javascripts/ide/components/new_dropdown/button_spec.js49
-rw-r--r--spec/javascripts/ide/components/new_dropdown/index_spec.js61
-rw-r--r--spec/javascripts/ide/components/new_dropdown/modal_spec.js73
-rw-r--r--spec/javascripts/ide/components/new_dropdown/upload_spec.js3
-rw-r--r--spec/javascripts/ide/components/panes/right_spec.js85
-rw-r--r--spec/javascripts/ide/components/preview/clientside_spec.js362
-rw-r--r--spec/javascripts/ide/components/preview/navigator_spec.js185
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js3
-rw-r--r--spec/javascripts/ide/components/repo_file_spec.js19
-rw-r--r--spec/javascripts/ide/components/repo_tab_spec.js4
-rw-r--r--spec/javascripts/ide/components/shared/tokened_input_spec.js132
-rw-r--r--spec/javascripts/ide/helpers.js2
-rw-r--r--spec/javascripts/ide/ide_router_spec.js44
-rw-r--r--spec/javascripts/ide/mock_data.js41
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js22
-rw-r--r--spec/javascripts/ide/stores/actions/merge_request_spec.js4
-rw-r--r--spec/javascripts/ide/stores/actions/project_spec.js15
-rw-r--r--spec/javascripts/ide/stores/actions/tree_spec.js7
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js71
-rw-r--r--spec/javascripts/ide/stores/getters_spec.js10
-rw-r--r--spec/javascripts/ide/stores/modules/branches/actions_spec.js193
-rw-r--r--spec/javascripts/ide/stores/modules/branches/mutations_spec.js51
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js6
-rw-r--r--spec/javascripts/ide/stores/modules/commit/getters_spec.js16
-rw-r--r--spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js114
-rw-r--r--spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js19
-rw-r--r--spec/javascripts/ide/stores/modules/pipelines/actions_spec.js85
-rw-r--r--spec/javascripts/ide/stores/mutations/file_spec.js64
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js157
-rw-r--r--spec/javascripts/ide/stores/utils_spec.js62
-rw-r--r--spec/javascripts/issuable_time_tracker_spec.js201
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js4
-rw-r--r--spec/javascripts/issue_show/components/edited_spec.js6
-rw-r--r--spec/javascripts/job_spec.js27
-rw-r--r--spec/javascripts/jobs/header_spec.js2
-rw-r--r--spec/javascripts/jobs/mock_data.js4
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js19
-rw-r--r--spec/javascripts/lib/utils/poll_spec.js39
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js17
-rw-r--r--spec/javascripts/merge_request_spec.js23
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js1
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js45
-rw-r--r--spec/javascripts/monitoring/mock_data.js58
-rw-r--r--spec/javascripts/monitoring/monitoring_store_spec.js7
-rw-r--r--spec/javascripts/notes/components/discussion_counter_spec.js6
-rw-r--r--spec/javascripts/notes/components/noteable_discussion_spec.js71
-rw-r--r--spec/javascripts/notes/mock_data.js86
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js13
-rw-r--r--spec/javascripts/notes/stores/getters_spec.js173
-rw-r--r--spec/javascripts/notes/stores/mutation_spec.js14
-rw-r--r--spec/javascripts/pages/profiles/show/emoji_menu_spec.js117
-rw-r--r--spec/javascripts/pdf/page_spec.js2
-rw-r--r--spec/javascripts/performance_bar/components/performance_bar_app_spec.js61
-rw-r--r--spec/javascripts/performance_bar/index_spec.js83
-rw-r--r--spec/javascripts/pipelines/graph/dropdown_job_component_spec.js93
-rw-r--r--spec/javascripts/pipelines/graph/graph_component_spec.js43
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js34
-rw-r--r--spec/javascripts/pipelines/graph/job_name_component_spec.js2
-rw-r--r--spec/javascripts/pipelines/graph/mock_data.js20
-rw-r--r--spec/javascripts/pipelines/graph/stage_column_component_spec.js39
-rw-r--r--spec/javascripts/pipelines/header_component_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipeline_url_spec.js26
-rw-r--r--spec/javascripts/pipelines/stage_spec.js4
-rw-r--r--spec/javascripts/profile/add_ssh_key_validation_spec.js69
-rw-r--r--spec/javascripts/projects_dropdown/components/app_spec.js349
-rw-r--r--spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js72
-rw-r--r--spec/javascripts/projects_dropdown/components/projects_list_item_spec.js77
-rw-r--r--spec/javascripts/projects_dropdown/components/projects_list_search_spec.js84
-rw-r--r--spec/javascripts/projects_dropdown/components/search_spec.js100
-rw-r--r--spec/javascripts/projects_dropdown/mock_data.js96
-rw-r--r--spec/javascripts/projects_dropdown/service/projects_service_spec.js179
-rw-r--r--spec/javascripts/projects_dropdown/store/projects_store_spec.js41
-rw-r--r--spec/javascripts/reports/components/grouped_test_reports_app_spec.js199
-rw-r--r--spec/javascripts/reports/components/modal_open_name_spec.js45
-rw-r--r--spec/javascripts/reports/components/modal_spec.js45
-rw-r--r--spec/javascripts/reports/components/report_link_spec.js71
-rw-r--r--spec/javascripts/reports/components/report_section_spec.js197
-rw-r--r--spec/javascripts/reports/components/summary_row_spec.js37
-rw-r--r--spec/javascripts/reports/components/test_issue_body_spec.js71
-rw-r--r--spec/javascripts/reports/mock_data/mock_data.js8
-rw-r--r--spec/javascripts/reports/mock_data/new_and_fixed_failures_report.json1
-rw-r--r--spec/javascripts/reports/mock_data/new_failures_report.json1
-rw-r--r--spec/javascripts/reports/mock_data/no_failures_report.json1
-rw-r--r--spec/javascripts/reports/mock_data/resolved_failures.json37
-rw-r--r--spec/javascripts/reports/store/actions_spec.js171
-rw-r--r--spec/javascripts/reports/store/mutations_spec.js124
-rw-r--r--spec/javascripts/reports/store/utils_spec.js138
-rw-r--r--spec/javascripts/sidebar/assignees_spec.js8
-rw-r--r--spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js232
-rw-r--r--spec/javascripts/sidebar/todo_spec.js158
-rw-r--r--spec/javascripts/smart_interval_spec.js201
-rw-r--r--spec/javascripts/test_bundle.js32
-rw-r--r--spec/javascripts/vue_mr_widget/components/deployment_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js43
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js8
-rw-r--r--spec/javascripts/vue_shared/components/bar_chart_spec.js85
-rw-r--r--spec/javascripts/vue_shared/components/ci_icon_spec.js20
-rw-r--r--spec/javascripts/vue_shared/components/clipboard_button_spec.js54
-rw-r--r--spec/javascripts/vue_shared/components/code_block_spec.js33
-rw-r--r--spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js15
-rw-r--r--spec/javascripts/vue_shared/components/gl_modal_spec.js34
-rw-r--r--spec/javascripts/vue_shared/components/header_ci_component_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/icon_spec.js17
-rw-r--r--spec/javascripts/vue_shared/components/identicon_spec.js17
-rw-r--r--spec/javascripts/vue_shared/components/markdown/header_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/memory_graph_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/notes/system_note_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/panel_resizer_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/project_avatar/default_spec.js58
-rw-r--r--spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js14
-rw-r--r--spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js30
-rw-r--r--spec/lib/additional_email_headers_interceptor_spec.rb29
-rw-r--r--spec/lib/backup/repository_spec.rb40
-rw-r--r--spec/lib/banzai/filter/image_lazy_load_filter_spec.rb14
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb58
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb7
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/sanitization_filter_spec.rb12
-rw-r--r--spec/lib/banzai/pipeline/emoji_pipeline_spec.rb21
-rw-r--r--spec/lib/bitbucket_server/client_spec.rb88
-rw-r--r--spec/lib/bitbucket_server/connection_spec.rb68
-rw-r--r--spec/lib/bitbucket_server/page_spec.rb51
-rw-r--r--spec/lib/bitbucket_server/paginator_spec.rb35
-rw-r--r--spec/lib/bitbucket_server/representation/activity_spec.rb38
-rw-r--r--spec/lib/bitbucket_server/representation/comment_spec.rb55
-rw-r--r--spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb48
-rw-r--r--spec/lib/bitbucket_server/representation/pull_request_spec.rb79
-rw-r--r--spec/lib/bitbucket_server/representation/repo_spec.rb80
-rw-r--r--spec/lib/disable_email_interceptor_spec.rb24
-rw-r--r--spec/lib/extracts_path_spec.rb26
-rw-r--r--spec/lib/feature_spec.rb32
-rw-r--r--spec/lib/gitlab/auth/activity_spec.rb30
-rw-r--r--spec/lib/gitlab/auth/blocked_user_tracker_spec.rb68
-rw-r--r--spec/lib/gitlab/background_migration/delete_diff_files_spec.rb62
-rw-r--r--spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb22
-rw-r--r--spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb109
-rw-r--r--spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/remove_restricted_todos_spec.rb124
-rw-r--r--spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb43
-rw-r--r--spec/lib/gitlab/bare_repository_import/importer_spec.rb54
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb75
-rw-r--r--spec/lib/gitlab/bitbucket_server_import/importer_spec.rb291
-rw-r--r--spec/lib/gitlab/checks/change_access_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb56
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb24
-rw-r--r--spec/lib/gitlab/ci/config/entry/artifacts_spec.rb17
-rw-r--r--spec/lib/gitlab/ci/config/entry/commands_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports_spec.rb53
-rw-r--r--spec/lib/gitlab/ci/parsers/junit_spec.rb118
-rw-r--r--spec/lib/gitlab/ci/parsers_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/reports/test_case_spec.rb90
-rw-r--r--spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb134
-rw-r--r--spec/lib/gitlab/ci/reports/test_reports_spec.rb132
-rw-r--r--spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb225
-rw-r--r--spec/lib/gitlab/ci/reports/test_suite_spec.rb120
-rw-r--r--spec/lib/gitlab/ci/status/build/failed_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/status/build/play_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/stage/common_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/trace/http_io_spec.rb315
-rw-r--r--spec/lib/gitlab/ci/variables/collection/item_spec.rb8
-rw-r--r--spec/lib/gitlab/cleanup/project_uploads_spec.rb278
-rw-r--r--spec/lib/gitlab/cleanup/remote_uploads_spec.rb74
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb18
-rw-r--r--spec/lib/gitlab/cycle_analytics/permissions_spec.rb4
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb2
-rw-r--r--spec/lib/gitlab/database_spec.rb115
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb9
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb15
-rw-r--r--spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb29
-rw-r--r--spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb35
-rw-r--r--spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb24
-rw-r--r--spec/lib/gitlab/exclusive_lease_helpers_spec.rb76
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb64
-rw-r--r--spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/attributes_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blame_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_snippet_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb173
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb10
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb20
-rw-r--r--spec/lib/gitlab/git/committer_with_hooks_spec.rb2
-rw-r--r--spec/lib/gitlab/git/compare_spec.rb2
-rw-r--r--spec/lib/gitlab/git/diff_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb11
-rw-r--r--spec/lib/gitlab/git/hooks_service_spec.rb2
-rw-r--r--spec/lib/gitlab/git/index_spec.rb18
-rw-r--r--spec/lib/gitlab/git/popen_spec.rb30
-rw-r--r--spec/lib/gitlab/git/remote_repository_spec.rb2
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb729
-rw-r--r--spec/lib/gitlab/git/rev_list_spec.rb96
-rw-r--r--spec/lib/gitlab/git/tag_spec.rb2
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb2
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb25
-rw-r--r--spec/lib/gitlab/git_access_spec.rb64
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb175
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_service_spec.rb38
-rw-r--r--spec/lib/gitlab/gitaly_client/storage_service_spec.rb13
-rw-r--r--spec/lib/gitlab/gitaly_client/wiki_service_spec.rb22
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb7
-rw-r--r--spec/lib/gitlab/gitlab_import/client_spec.rb84
-rw-r--r--spec/lib/gitlab/google_code_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb4
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb103
-rw-r--r--spec/lib/gitlab/graphql/authorize_spec.rb20
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb112
-rw-r--r--spec/lib/gitlab/graphs/commits_spec.rb2
-rw-r--r--spec/lib/gitlab/hashed_storage/migrator_spec.rb2
-rw-r--r--spec/lib/gitlab/hook_data/base_builder_spec.rb64
-rw-r--r--spec/lib/gitlab/hook_data/issue_builder_spec.rb9
-rw-r--r--spec/lib/gitlab/hook_data/merge_request_builder_spec.rb9
-rw-r--r--spec/lib/gitlab/http_io_spec.rb318
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb105
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb31
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml6
-rw-r--r--spec/lib/gitlab/import_export/avatar_saver_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb89
-rw-r--r--spec/lib/gitlab/import_export/file_importer_spec.rb11
-rw-r--r--spec/lib/gitlab/import_export/group_project_object_builder_spec.rb52
-rw-r--r--spec/lib/gitlab/import_export/importer_object_storage_spec.rb115
-rw-r--r--spec/lib/gitlab/import_export/importer_spec.rb5
-rw-r--r--spec/lib/gitlab/import_export/members_mapper_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project.light.json8
-rw-r--r--spec/lib/gitlab/import_export/project.milestone-iid.json80
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb84
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/saver_spec.rb43
-rw-r--r--spec/lib/gitlab/import_export/uploads_manager_spec.rb80
-rw-r--r--spec/lib/gitlab/import_export/uploads_saver_spec.rb5
-rw-r--r--spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb33
-rw-r--r--spec/lib/gitlab/json_logger_spec.rb29
-rw-r--r--spec/lib/gitlab/kubernetes/config_map_spec.rb10
-rw-r--r--spec/lib/gitlab/kubernetes/helm/api_spec.rb14
-rw-r--r--spec/lib/gitlab/kubernetes/helm/base_command_spec.rb28
-rw-r--r--spec/lib/gitlab/kubernetes/helm/certificate_spec.rb28
-rw-r--r--spec/lib/gitlab/kubernetes/helm/init_command_spec.rb4
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb69
-rw-r--r--spec/lib/gitlab/kubernetes/helm/pod_spec.rb29
-rw-r--r--spec/lib/gitlab/kubernetes_spec.rb15
-rw-r--r--spec/lib/gitlab/language_detection_spec.rb85
-rw-r--r--spec/lib/gitlab/manifest_import/manifest_spec.rb46
-rw-r--r--spec/lib/gitlab/manifest_import/project_creator_spec.rb33
-rw-r--r--spec/lib/gitlab/middleware/basic_health_check_spec.rb57
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb2
-rw-r--r--spec/lib/gitlab/middleware/multipart_spec.rb86
-rw-r--r--spec/lib/gitlab/middleware/read_only_spec.rb77
-rw-r--r--spec/lib/gitlab/popen_spec.rb13
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb6
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb55
-rw-r--r--spec/lib/gitlab/repository_cache_adapter_spec.rb12
-rw-r--r--spec/lib/gitlab/sanitizers/svg_spec.rb4
-rw-r--r--spec/lib/gitlab/serializer/ci/variables_spec.rb6
-rw-r--r--spec/lib/gitlab/shell_spec.rb105
-rw-r--r--spec/lib/gitlab/slash_commands/issue_move_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_new_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_search_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_show_spec.rb2
-rw-r--r--spec/lib/gitlab/url_sanitizer_spec.rb7
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb2
-rw-r--r--spec/lib/gitlab/user_access_spec.rb40
-rw-r--r--spec/lib/gitlab/user_activities_spec.rb127
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb152
-rw-r--r--spec/lib/uploaded_file_spec.rb37
-rw-r--r--spec/mailers/notify_spec.rb33
-rw-r--r--spec/mailers/previews/notify_preview.rb170
-rw-r--r--spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb2
-rw-r--r--spec/migrations/add_foreign_key_from_notification_settings_to_users_spec.rb36
-rw-r--r--spec/migrations/enqueue_delete_diff_files_workers_spec.rb47
-rw-r--r--spec/migrations/generate_missing_routes_spec.rb84
-rw-r--r--spec/migrations/issues_moved_to_id_foreign_key_spec.rb2
-rw-r--r--spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb6
-rw-r--r--spec/migrations/migrate_process_commit_worker_jobs_spec.rb66
-rw-r--r--spec/migrations/migrate_stage_id_reference_in_background_spec.rb2
-rw-r--r--spec/migrations/migrate_stages_statuses_spec.rb2
-rw-r--r--spec/migrations/normalize_ldap_extern_uids_spec.rb10
-rw-r--r--spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb2
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_spec.rb2
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb2
-rw-r--r--spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb2
-rw-r--r--spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb2
-rw-r--r--spec/models/ci/build_metadata_spec.rb2
-rw-r--r--spec/models/ci/build_runner_session_spec.rb36
-rw-r--r--spec/models/ci/build_spec.rb418
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb446
-rw-r--r--spec/models/ci/build_trace_chunks/database_spec.rb105
-rw-r--r--spec/models/ci/build_trace_chunks/fog_spec.rb146
-rw-r--r--spec/models/ci/build_trace_chunks/redis_spec.rb132
-rw-r--r--spec/models/ci/job_artifact_spec.rb82
-rw-r--r--spec/models/ci/pipeline_spec.rb79
-rw-r--r--spec/models/ci/runner_spec.rb9
-rw-r--r--spec/models/clusters/applications/helm_spec.rb26
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb66
-rw-r--r--spec/models/clusters/applications/jupyter_spec.rb70
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb119
-rw-r--r--spec/models/clusters/applications/runner_spec.rb88
-rw-r--r--spec/models/concerns/avatarable_spec.rb4
-rw-r--r--spec/models/concerns/batch_destroy_dependent_associations_spec.rb4
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb16
-rw-r--r--spec/models/concerns/cacheable_attributes_spec.rb2
-rw-r--r--spec/models/concerns/issuable_spec.rb8
-rw-r--r--spec/models/concerns/mentionable_spec.rb6
-rw-r--r--spec/models/concerns/protected_ref_access_spec.rb10
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb15
-rw-r--r--spec/models/concerns/resolvable_discussion_spec.rb2
-rw-r--r--spec/models/concerns/routable_spec.rb35
-rw-r--r--spec/models/deploy_token_spec.rb17
-rw-r--r--spec/models/deployment_spec.rb18
-rw-r--r--spec/models/environment_spec.rb19
-rw-r--r--spec/models/group_spec.rb52
-rw-r--r--spec/models/hooks/system_hook_spec.rb8
-rw-r--r--spec/models/hooks/web_hook_log_spec.rb18
-rw-r--r--spec/models/import_export_upload_spec.rb25
-rw-r--r--spec/models/internal_id_spec.rb66
-rw-r--r--spec/models/issue_spec.rb2
-rw-r--r--spec/models/label_spec.rb16
-rw-r--r--spec/models/lfs_file_lock_spec.rb12
-rw-r--r--spec/models/member_spec.rb86
-rw-r--r--spec/models/members/group_member_spec.rb2
-rw-r--r--spec/models/members/project_member_spec.rb10
-rw-r--r--spec/models/merge_request_diff_spec.rb22
-rw-r--r--spec/models/merge_request_spec.rb277
-rw-r--r--spec/models/milestone_spec.rb20
-rw-r--r--spec/models/namespace_spec.rb42
-rw-r--r--spec/models/note_spec.rb6
-rw-r--r--spec/models/notification_setting_spec.rb8
-rw-r--r--spec/models/postgresql/replication_slot_spec.rb31
-rw-r--r--spec/models/programming_language_spec.rb11
-rw-r--r--spec/models/project_authorization_spec.rb6
-rw-r--r--spec/models/project_feature_spec.rb44
-rw-r--r--spec/models/project_services/bamboo_service_spec.rb22
-rw-r--r--spec/models/project_services/hangouts_chat_service_spec.rb246
-rw-r--r--spec/models/project_services/jira_service_spec.rb53
-rw-r--r--spec/models/project_spec.rb171
-rw-r--r--spec/models/project_statistics_spec.rb6
-rw-r--r--spec/models/project_team_spec.rb108
-rw-r--r--spec/models/project_wiki_spec.rb26
-rw-r--r--spec/models/protected_branch/merge_access_level_spec.rb2
-rw-r--r--spec/models/protected_branch/push_access_level_spec.rb2
-rw-r--r--spec/models/remote_mirror_spec.rb8
-rw-r--r--spec/models/repository_language_spec.rb16
-rw-r--r--spec/models/repository_spec.rb299
-rw-r--r--spec/models/resource_label_event_spec.rb48
-rw-r--r--spec/models/route_spec.rb14
-rw-r--r--spec/models/service_spec.rb4
-rw-r--r--spec/models/site_statistic_spec.rb83
-rw-r--r--spec/models/spam_log_spec.rb2
-rw-r--r--spec/models/todo_spec.rb1
-rw-r--r--spec/models/user_spec.rb122
-rw-r--r--spec/models/user_status_spec.rb20
-rw-r--r--spec/models/wiki_page_spec.rb10
-rw-r--r--spec/policies/ci/build_policy_spec.rb10
-rw-r--r--spec/policies/ci/pipeline_schedule_policy_spec.rb8
-rw-r--r--spec/policies/ci/trigger_policy_spec.rb12
-rw-r--r--spec/policies/clusters/cluster_policy_spec.rb4
-rw-r--r--spec/policies/concerns/policy_actor_spec.rb13
-rw-r--r--spec/policies/deploy_key_policy_spec.rb2
-rw-r--r--spec/policies/deploy_token_policy_spec.rb12
-rw-r--r--spec/policies/environment_policy_spec.rb106
-rw-r--r--spec/policies/global_policy_spec.rb38
-rw-r--r--spec/policies/group_policy_spec.rb48
-rw-r--r--spec/policies/project_policy_spec.rb30
-rw-r--r--spec/policies/protected_branch_policy_spec.rb4
-rw-r--r--spec/policies/user_policy_spec.rb4
-rw-r--r--spec/presenters/ci/build_runner_presenter_spec.rb86
-rw-r--r--spec/presenters/commit_status_presenter_spec.rb26
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb8
-rw-r--r--spec/presenters/project_presenter_spec.rb8
-rw-r--r--spec/requests/api/access_requests_spec.rb40
-rw-r--r--spec/requests/api/award_emoji_spec.rb2
-rw-r--r--spec/requests/api/badges_spec.rb54
-rw-r--r--spec/requests/api/branches_spec.rb16
-rw-r--r--spec/requests/api/commits_spec.rb64
-rw-r--r--spec/requests/api/deployments_spec.rb2
-rw-r--r--spec/requests/api/environments_spec.rb8
-rw-r--r--spec/requests/api/files_spec.rb30
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb68
-rw-r--r--spec/requests/api/graphql/project/merge_request_spec.rb24
-rw-r--r--spec/requests/api/graphql/project_query_spec.rb12
-rw-r--r--spec/requests/api/group_variables_spec.rb10
-rw-r--r--spec/requests/api/groups_spec.rb54
-rw-r--r--spec/requests/api/helpers_spec.rb2
-rw-r--r--spec/requests/api/internal_spec.rb83
-rw-r--r--spec/requests/api/issues_spec.rb34
-rw-r--r--spec/requests/api/jobs_spec.rb15
-rw-r--r--spec/requests/api/labels_spec.rb2
-rw-r--r--spec/requests/api/members_spec.rb197
-rw-r--r--spec/requests/api/merge_request_diffs_spec.rb2
-rw-r--r--spec/requests/api/merge_requests_spec.rb24
-rw-r--r--spec/requests/api/namespaces_spec.rb12
-rw-r--r--spec/requests/api/notes_spec.rb6
-rw-r--r--spec/requests/api/pages_domains_spec.rb28
-rw-r--r--spec/requests/api/pipeline_schedules_spec.rb24
-rw-r--r--spec/requests/api/pipelines_spec.rb5
-rw-r--r--spec/requests/api/project_export_spec.rb59
-rw-r--r--spec/requests/api/project_hooks_spec.rb4
-rw-r--r--spec/requests/api/project_import_spec.rb10
-rw-r--r--spec/requests/api/projects_spec.rb103
-rw-r--r--spec/requests/api/protected_branches_spec.rb34
-rw-r--r--spec/requests/api/repositories_spec.rb2
-rw-r--r--spec/requests/api/runner_spec.rb55
-rw-r--r--spec/requests/api/runners_spec.rb94
-rw-r--r--spec/requests/api/settings_spec.rb5
-rw-r--r--spec/requests/api/tags_spec.rb20
-rw-r--r--spec/requests/api/todos_spec.rb14
-rw-r--r--spec/requests/api/triggers_spec.rb2
-rw-r--r--spec/requests/api/users_spec.rb118
-rw-r--r--spec/requests/api/variables_spec.rb2
-rw-r--r--spec/requests/api/wikis_spec.rb62
-rw-r--r--spec/requests/git_http_spec.rb25
-rw-r--r--spec/requests/jwt_controller_spec.rb19
-rw-r--r--spec/requests/lfs_http_spec.rb58
-rw-r--r--spec/requests/lfs_locks_api_spec.rb6
-rw-r--r--spec/routing/admin_routing_spec.rb2
-rw-r--r--spec/routing/instance_statistics_routing_spec.rb11
-rw-r--r--spec/routing/project_routing_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/add_reference_spec.rb54
-rw-r--r--spec/serializers/deploy_key_entity_spec.rb4
-rw-r--r--spec/serializers/diff_file_entity_spec.rb14
-rw-r--r--spec/serializers/discussion_entity_spec.rb19
-rw-r--r--spec/serializers/environment_entity_spec.rb3
-rw-r--r--spec/serializers/environment_serializer_spec.rb10
-rw-r--r--spec/serializers/group_child_entity_spec.rb2
-rw-r--r--spec/serializers/merge_request_widget_entity_spec.rb15
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb4
-rw-r--r--spec/serializers/test_case_entity_spec.rb31
-rw-r--r--spec/serializers/test_reports_comparer_entity_spec.rb76
-rw-r--r--spec/serializers/test_reports_comparer_serializer_spec.rb62
-rw-r--r--spec/serializers/test_suite_comparer_entity_spec.rb86
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb144
-rw-r--r--spec/services/ci/compare_test_reports_service_spec.rb75
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb8
-rw-r--r--spec/services/ci/register_job_service_spec.rb50
-rw-r--r--spec/services/ci/retry_build_service_spec.rb20
-rw-r--r--spec/services/ci/retry_pipeline_service_spec.rb4
-rw-r--r--spec/services/ci/stop_environments_service_spec.rb2
-rw-r--r--spec/services/clusters/applications/check_installation_progress_service_spec.rb2
-rw-r--r--spec/services/clusters/applications/install_service_spec.rb2
-rw-r--r--spec/services/discussions/resolve_service_spec.rb2
-rw-r--r--spec/services/event_create_service_spec.rb4
-rw-r--r--spec/services/files/create_service_spec.rb2
-rw-r--r--spec/services/files/delete_service_spec.rb2
-rw-r--r--spec/services/files/multi_service_spec.rb2
-rw-r--r--spec/services/files/update_service_spec.rb14
-rw-r--r--spec/services/git_push_service_spec.rb28
-rw-r--r--spec/services/groups/destroy_service_spec.rb2
-rw-r--r--spec/services/groups/update_service_spec.rb32
-rw-r--r--spec/services/import_export_clean_up_service_spec.rb19
-rw-r--r--spec/services/issues/close_service_spec.rb2
-rw-r--r--spec/services/issues/create_service_spec.rb16
-rw-r--r--spec/services/issues/reopen_service_spec.rb4
-rw-r--r--spec/services/issues/update_service_spec.rb25
-rw-r--r--spec/services/labels/find_or_create_service_spec.rb20
-rw-r--r--spec/services/lfs/unlock_file_service_spec.rb6
-rw-r--r--spec/services/members/approve_access_request_service_spec.rb6
-rw-r--r--spec/services/members/create_service_spec.rb2
-rw-r--r--spec/services/members/destroy_service_spec.rb15
-rw-r--r--spec/services/members/update_service_spec.rb6
-rw-r--r--spec/services/merge_requests/close_service_spec.rb2
-rw-r--r--spec/services/merge_requests/conflicts/list_service_spec.rb24
-rw-r--r--spec/services/merge_requests/conflicts/resolve_service_spec.rb11
-rw-r--r--spec/services/merge_requests/create_service_spec.rb20
-rw-r--r--spec/services/merge_requests/ff_merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb5
-rw-r--r--spec/services/merge_requests/post_merge_service_spec.rb4
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb40
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb8
-rw-r--r--spec/services/merge_requests/squash_service_spec.rb45
-rw-r--r--spec/services/merge_requests/update_service_spec.rb2
-rw-r--r--spec/services/milestones/close_service_spec.rb2
-rw-r--r--spec/services/milestones/create_service_spec.rb2
-rw-r--r--spec/services/milestones/destroy_service_spec.rb2
-rw-r--r--spec/services/milestones/promote_service_spec.rb2
-rw-r--r--spec/services/milestones/update_service_spec.rb41
-rw-r--r--spec/services/notes/create_service_spec.rb6
-rw-r--r--spec/services/notes/post_process_service_spec.rb2
-rw-r--r--spec/services/notes/quick_actions_service_spec.rb20
-rw-r--r--spec/services/notes/update_service_spec.rb2
-rw-r--r--spec/services/notification_recipient_service_spec.rb8
-rw-r--r--spec/services/notification_service_spec.rb108
-rw-r--r--spec/services/preview_markdown_service_spec.rb12
-rw-r--r--spec/services/projects/autocomplete_service_spec.rb54
-rw-r--r--spec/services/projects/create_from_template_service_spec.rb35
-rw-r--r--spec/services/projects/create_service_spec.rb27
-rw-r--r--spec/services/projects/destroy_service_spec.rb14
-rw-r--r--spec/services/projects/detect_repository_languages_service_spec.rb54
-rw-r--r--spec/services/projects/fork_service_spec.rb2
-rw-r--r--spec/services/projects/gitlab_projects_import_service_spec.rb56
-rw-r--r--spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb17
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb3
-rw-r--r--spec/services/projects/hashed_storage_migration_service_spec.rb18
-rw-r--r--spec/services/projects/housekeeping_service_spec.rb2
-rw-r--r--spec/services/projects/import_service_spec.rb4
-rw-r--r--spec/services/projects/move_access_service_spec.rb12
-rw-r--r--spec/services/projects/move_project_authorizations_service_spec.rb8
-rw-r--r--spec/services/projects/move_project_group_links_service_spec.rb8
-rw-r--r--spec/services/projects/move_project_members_service_spec.rb8
-rw-r--r--spec/services/projects/overwrite_project_service_spec.rb8
-rw-r--r--spec/services/projects/transfer_service_spec.rb8
-rw-r--r--spec/services/projects/update_pages_service_spec.rb18
-rw-r--r--spec/services/projects/update_service_spec.rb51
-rw-r--r--spec/services/protected_branches/create_service_spec.rb8
-rw-r--r--spec/services/protected_tags/create_service_spec.rb4
-rw-r--r--spec/services/reset_project_cache_service_spec.rb2
-rw-r--r--spec/services/resource_events/change_labels_service_spec.rb53
-rw-r--r--spec/services/search/global_service_spec.rb2
-rw-r--r--spec/services/search_service_spec.rb2
-rw-r--r--spec/services/system_hooks_service_spec.rb2
-rw-r--r--spec/services/todos/destroy/confidential_issue_service_spec.rb43
-rw-r--r--spec/services/todos/destroy/entity_leave_service_spec.rb290
-rw-r--r--spec/services/todos/destroy/group_private_service_spec.rb69
-rw-r--r--spec/services/todos/destroy/private_features_service_spec.rb143
-rw-r--r--spec/services/todos/destroy/project_private_service_spec.rb43
-rw-r--r--spec/services/users/activity_service_spec.rb73
-rw-r--r--spec/services/users/destroy_service_spec.rb2
-rw-r--r--spec/services/users/refresh_authorized_projects_service_spec.rb30
-rw-r--r--spec/services/users/set_status_service_spec.rb58
-rw-r--r--spec/services/users/update_service_spec.rb21
-rw-r--r--spec/spec_helper.rb13
-rw-r--r--spec/support/api/repositories_shared_context.rb2
-rw-r--r--spec/support/api/time_tracking_shared_examples.rb8
-rw-r--r--spec/support/features/issuable_slash_commands_shared_examples.rb32
-rwxr-xr-xspec/support/generate-seed-repo-rb2
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb3
-rw-r--r--spec/support/helpers/graphql_helpers.rb81
-rw-r--r--spec/support/helpers/jira_service_helper.rb2
-rw-r--r--spec/support/helpers/key_generator_helper.rb2
-rw-r--r--spec/support/helpers/markdown_feature.rb2
-rw-r--r--spec/support/helpers/reactive_caching_helpers.rb4
-rw-r--r--spec/support/helpers/seed_helper.rb6
-rw-r--r--spec/support/helpers/stub_metrics.rb27
-rw-r--r--spec/support/helpers/stub_object_storage.rb5
-rw-r--r--spec/support/helpers/test_env.rb15
-rw-r--r--spec/support/helpers/user_activities_helpers.rb7
-rw-r--r--spec/support/helpers/wait_for_requests.rb4
-rw-r--r--spec/support/http_io/http_io_helpers.rb60
-rw-r--r--spec/support/import_export/export_file_helper.rb2
-rw-r--r--spec/support/matchers/access_matchers_for_controller.rb2
-rw-r--r--spec/support/matchers/disallow_request_matchers.rb15
-rw-r--r--spec/support/matchers/graphql_matchers.rb9
-rw-r--r--spec/support/matchers/metric_counter_matcher.rb11
-rw-r--r--spec/support/matchers/user_activity_matchers.rb5
-rw-r--r--spec/support/matchers/user_status_matcher.rb13
-rw-r--r--spec/support/prometheus/additional_metrics_shared_examples.rb7
-rw-r--r--spec/support/rspec.rb2
-rw-r--r--spec/support/services/issuable_create_service_slash_commands_shared_examples.rb4
-rw-r--r--spec/support/shared_contexts/merge_requests_allowing_collaboration.rb15
-rw-r--r--spec/support/shared_examples/ci_trace_shared_examples.rb106
-rw-r--r--spec/support/shared_examples/controllers/todos_shared_examples.rb43
-rw-r--r--spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb79
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/editable_merge_request_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb14
-rw-r--r--spec/support/shared_examples/helm_generated_script.rb4
-rw-r--r--spec/support/shared_examples/instance_statistics_controllers_shared_examples.rb37
-rw-r--r--spec/support/shared_examples/models/atomic_internal_id_spec.rb14
-rw-r--r--spec/support/shared_examples/models/members_notifications_shared_example.rb2
-rw-r--r--spec/support/shared_examples/notify_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/merge_requests_list.rb23
-rw-r--r--spec/support/shared_examples/requests/api/notes.rb1
-rw-r--r--spec/support/shared_examples/requests/graphql_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/boards/issues_list_service.rb10
-rw-r--r--spec/support/shared_examples/services/boards/issues_move_service.rb4
-rw-r--r--spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb54
-rw-r--r--spec/support/shared_examples/showing_user_status_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb18
-rw-r--r--spec/support/stored_repositories.rb4
-rw-r--r--spec/support/test_reports/test_reports_helper.rb93
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb21
-rw-r--r--spec/tasks/gitlab/cleanup_rake_spec.rb86
-rw-r--r--spec/tasks/gitlab/git_rake_spec.rb27
-rw-r--r--spec/uploaders/file_uploader_spec.rb99
-rw-r--r--spec/uploaders/gitlab_uploader_spec.rb62
-rw-r--r--spec/uploaders/import_export_uploader_spec.rb20
-rw-r--r--spec/uploaders/job_artifact_uploader_spec.rb37
-rw-r--r--spec/views/layouts/_head.html.haml_spec.rb33
-rw-r--r--spec/views/projects/_home_panel.html.haml_spec.rb121
-rw-r--r--spec/views/projects/imports/new.html.haml_spec.rb4
-rw-r--r--spec/views/projects/merge_requests/show.html.haml_spec.rb13
-rw-r--r--spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb6
-rw-r--r--spec/views/shared/notes/_form.html.haml_spec.rb2
-rw-r--r--spec/workers/background_migration_worker_spec.rb52
-rw-r--r--spec/workers/ci/archive_traces_cron_worker_spec.rb24
-rw-r--r--spec/workers/concerns/gitlab/github_import/object_importer_spec.rb1
-rw-r--r--spec/workers/concerns/waitable_worker_spec.rb4
-rw-r--r--spec/workers/create_gpg_signature_worker_spec.rb47
-rw-r--r--spec/workers/detect_repository_languages_worker_spec.rb32
-rw-r--r--spec/workers/emails_on_push_worker_spec.rb61
-rw-r--r--spec/workers/git_garbage_collect_worker_spec.rb13
-rw-r--r--spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb1
-rw-r--r--spec/workers/gitlab/github_import/import_issue_worker_spec.rb1
-rw-r--r--spec/workers/gitlab/github_import/import_note_worker_spec.rb1
-rw-r--r--spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb1
-rw-r--r--spec/workers/merge_worker_spec.rb2
-rw-r--r--spec/workers/object_storage_upload_worker_spec.rb108
-rw-r--r--spec/workers/pipeline_schedule_worker_spec.rb2
-rw-r--r--spec/workers/process_commit_worker_spec.rb40
-rw-r--r--spec/workers/project_migrate_hashed_storage_worker_spec.rb2
-rw-r--r--spec/workers/prune_web_hook_logs_worker_spec.rb22
-rw-r--r--spec/workers/repository_check/batch_worker_spec.rb8
-rw-r--r--spec/workers/repository_check/dispatch_worker_spec.rb8
-rw-r--r--spec/workers/repository_import_worker_spec.rb4
-rw-r--r--spec/workers/repository_update_remote_mirror_worker_spec.rb10
-rw-r--r--spec/workers/schedule_update_user_activity_worker_spec.rb25
-rw-r--r--spec/workers/storage_migrator_worker_spec.rb2
-rw-r--r--spec/workers/stuck_import_jobs_worker_spec.rb4
-rw-r--r--spec/workers/todos_destroyer/confidential_issue_worker_spec.rb12
-rw-r--r--spec/workers/todos_destroyer/entity_leave_worker_spec.rb12
-rw-r--r--spec/workers/todos_destroyer/group_private_worker_spec.rb12
-rw-r--r--spec/workers/todos_destroyer/private_features_worker_spec.rb12
-rw-r--r--spec/workers/todos_destroyer/project_private_worker_spec.rb12
-rw-r--r--spec/workers/update_user_activity_worker_spec.rb35
-rw-r--r--vendor/Dockerfile/Node-alpine.Dockerfile9
-rw-r--r--vendor/Dockerfile/Node.Dockerfile9
-rw-r--r--vendor/Dockerfile/Ruby-alpine.Dockerfile11
-rw-r--r--vendor/Dockerfile/Ruby.Dockerfile4
-rw-r--r--vendor/assets/javascripts/date.format.js132
-rw-r--r--vendor/assets/javascripts/xterm/encoding-indexes.js39
-rw-r--r--vendor/assets/javascripts/xterm/encoding.js3309
-rw-r--r--vendor/assets/javascripts/xterm/fit.js86
-rw-r--r--vendor/assets/javascripts/xterm/xterm.js2235
-rw-r--r--vendor/assets/stylesheets/xterm/xterm.css2206
-rw-r--r--vendor/gitignore/Autotools.gitignore5
-rw-r--r--vendor/gitignore/CraftCMS.gitignore5
-rw-r--r--vendor/gitignore/Delphi.gitignore2
-rw-r--r--vendor/gitignore/Eagle.gitignore1
-rw-r--r--vendor/gitignore/GWT.gitignore3
-rw-r--r--vendor/gitignore/Global/Backup.gitignore5
-rw-r--r--vendor/gitignore/Global/CodeKit.gitignore1
-rw-r--r--vendor/gitignore/Global/Eclipse.gitignore5
-rw-r--r--vendor/gitignore/Global/JetBrains.gitignore12
-rw-r--r--vendor/gitignore/Global/Matlab.gitignore15
-rw-r--r--vendor/gitignore/Global/Patch.gitignore2
-rw-r--r--vendor/gitignore/Global/SynopsysVCS.gitignore8
-rw-r--r--vendor/gitignore/Global/Vim.gitignore3
-rw-r--r--vendor/gitignore/LabVIEW.gitignore1
-rw-r--r--vendor/gitignore/Laravel.gitignore7
-rw-r--r--vendor/gitignore/Maven.gitignore4
-rw-r--r--vendor/gitignore/Node.gitignore6
-rw-r--r--vendor/gitignore/Objective-C.gitignore3
-rw-r--r--vendor/gitignore/Perl6.gitignore7
-rw-r--r--vendor/gitignore/Swift.gitignore3
-rw-r--r--vendor/gitignore/TeX.gitignore6
-rw-r--r--vendor/gitignore/Typo3.gitignore5
-rw-r--r--vendor/gitignore/Umbraco.gitignore2
-rw-r--r--vendor/gitignore/UnrealEngine.gitignore3
-rw-r--r--vendor/gitignore/VisualStudio.gitignore11
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml13
-rw-r--r--vendor/gitlab-ci-yml/Maven.gitlab-ci.yml2
-rw-r--r--vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml2
-rw-r--r--vendor/jupyter/values.yaml1
-rw-r--r--vendor/licenses.csv361
-rw-r--r--yarn.lock1696
4169 files changed, 142510 insertions, 41367 deletions
diff --git a/.flayignore b/.flayignore
index 3e5063674ff..4b6f7ba693a 100644
--- a/.flayignore
+++ b/.flayignore
@@ -1,11 +1,18 @@
*.erb
lib/gitlab/sanitizers/svg/whitelist.rb
lib/gitlab/diff/position_tracer.rb
+app/controllers/projects/approver_groups_controller.rb
+app/controllers/projects/approvers_controller.rb
+app/controllers/projects/protected_branches/merge_access_levels_controller.rb
+app/controllers/projects/protected_branches/push_access_levels_controller.rb
+app/controllers/projects/protected_tags/create_access_levels_controller.rb
app/policies/project_policy.rb
app/models/concerns/relative_positioning.rb
app/workers/stuck_merge_jobs_worker.rb
lib/gitlab/redis/*.rb
lib/gitlab/gitaly_client/operation_service.rb
+app/models/project_services/packagist_service.rb
+lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
lib/gitlab/background_migration/*
app/models/project_services/kubernetes_service.rb
lib/gitlab/workhorse.rb
@@ -19,6 +26,8 @@ ee/db/**/*
ee/app/serializers/ee/merge_request_widget_entity.rb
ee/lib/api/epics.rb
ee/lib/api/geo_nodes.rb
+ee/lib/ee/api/group_boards.rb
+ee/lib/ee/api/boards.rb
ee/lib/ee/gitlab/ldap/sync/admin_users.rb
ee/app/workers/geo/file_download_dispatch_worker/job_artifact_job_finder.rb
ee/app/workers/geo/file_download_dispatch_worker/lfs_object_job_finder.rb
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000000..f1c41c9bb76
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+Dangerfile gitlab-language=ruby
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8703ef6823a..fd02d72b4c2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
+image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
.dedicated-runner: &dedicated-runner
retry: 1
@@ -86,7 +86,9 @@ stages:
.rails5: &rails5
allow_failure: true
only:
- - /rails5/
+ variables:
+ - $CI_COMMIT_REF_NAME =~ /rails5/
+ - $RAILS5_ENABLED
variables:
BUNDLE_GEMFILE: "Gemfile.rails5"
RAILS5: "true"
@@ -269,10 +271,10 @@ package-and-qa:
<<: *single-script-job-variables
SCRIPT_NAME: trigger-build-docs
environment:
- name: review-docs/$CI_COMMIT_REF_NAME
+ name: review-docs/$CI_COMMIT_REF_SLUG
# DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are secret variables
# Discussion: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14236/diffs#note_40140693
- url: http://$DOCS_GITLAB_REPO_SUFFIX-$CI_ENVIRONMENT_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
+ url: http://$CI_ENVIRONMENT_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
on_stop: review-docs-cleanup
# Trigger a manual docs build in gitlab-docs only on non docs-only branches.
@@ -285,7 +287,8 @@ review-docs-deploy-manual:
- ./$SCRIPT_NAME deploy
when: manual
only:
- - branches
+ - branches@gitlab-org/gitlab-ce
+ - branches@gitlab-org/gitlab-ee
<<: *except-docs-and-qa
# Always trigger a docs build in gitlab-docs only on docs-only branches.
@@ -297,7 +300,8 @@ review-docs-deploy:
- gem install gitlab --no-ri --no-rdoc
- ./$SCRIPT_NAME deploy
only:
- - /(^docs[\/-].*|.*-docs$)/
+ - /(^docs[\/-].*|.*-docs$)/@gitlab-org/gitlab-ce
+ - /(^docs[\/-].*|.*-docs$)/@gitlab-org/gitlab-ee
<<: *except-qa
# Cleanup remote environment of gitlab-docs
@@ -305,7 +309,7 @@ review-docs-cleanup:
<<: *review-docs
stage: post-cleanup
environment:
- name: review-docs/$CI_COMMIT_REF_NAME
+ name: review-docs/$CI_COMMIT_REF_SLUG
action: stop
when: manual
script:
@@ -323,11 +327,9 @@ cloud-native-image:
variables:
GIT_DEPTH: "1"
cache: {}
- before_script:
- - gem install gitlab --no-rdoc --no-ri
- - chmod 755 ./scripts/trigger-build-cloud-native
script:
- - ./scripts/trigger-build-cloud-native
+ - gem install gitlab --no-ri --no-rdoc
+ - BUILD_TRIGGER_TOKEN=$CI_JOB_TOKEN scripts/trigger-build cng
only:
- tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee
@@ -436,6 +438,26 @@ setup-test-env:
- config/secrets.yml
- vendor/gitaly-ruby
+danger-review:
+ image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger
+ stage: test
+ allow_failure: true
+ cache: {}
+ dependencies: []
+ before_script: []
+ only:
+ variables:
+ - $DANGER_GITLAB_API_TOKEN
+ except:
+ refs:
+ - master
+ variables:
+ - $CI_COMMIT_REF_NAME =~ /^ce-to-ee-.*/
+ - $CI_COMMIT_REF_NAME =~ /.*-stable(-ee)?-prepare-.*/
+ script:
+ - git version
+ - danger --fail-on-errors=true
+
rspec-pg 0 30: *rspec-metadata-pg
rspec-pg 1 30: *rspec-metadata-pg
rspec-pg 2 30: *rspec-metadata-pg
diff --git a/.gitlab/issue_templates/Feature Proposal.md b/.gitlab/issue_templates/Feature proposal.md
index c4065d3c4ea..c4065d3c4ea 100644
--- a/.gitlab/issue_templates/Feature Proposal.md
+++ b/.gitlab/issue_templates/Feature proposal.md
diff --git a/.gitlab/issue_templates/Research Proposal.md b/.gitlab/issue_templates/Research proposal.md
index 5676656793d..5676656793d 100644
--- a/.gitlab/issue_templates/Research Proposal.md
+++ b/.gitlab/issue_templates/Research proposal.md
diff --git a/.gitlab/issue_templates/Security Developer Workflow.md b/.gitlab/issue_templates/Security Developer Workflow.md
deleted file mode 100644
index 0c878dbf64c..00000000000
--- a/.gitlab/issue_templates/Security Developer Workflow.md
+++ /dev/null
@@ -1,70 +0,0 @@
-<!--
-# Read me first!
-
-Create this issue under https://dev.gitlab.org/gitlab/gitlabhq
-
-Set the title to: `[Security] Description of the original issue`
--->
-
-### Prior to the security release
-
-- [ ] Read the [security process for developers] if you are not familiar with it.
-- [ ] Link to the original issue adding it to the [links section](#links)
-- [ ] Run `scripts/security-harness` in the CE, EE, and/or Omnibus to prevent pushing to any remote besides `dev.gitlab.org`
-- [ ] Create an MR targetting `org` `master`, prefixing your branch with `security-`
-- [ ] Label your MR with the ~security label, prefix the title with `WIP: [master]`
-- [ ] Add a link to the MR to the [links section](#links)
-- [ ] Add a link to an EE MR if required
-- [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**.
-- [ ] Assign the MR to a RM once is reviewed and ready to be merged. Check the [RM list] to see who to ping.
-
-#### Backports
-
-- [ ] Once the MR is ready to be merged, create MRs targetting the last 3 releases
- - [ ] At this point, it might be easy to squash the commits from the MR into one
- - You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [seckpick documentation]
- - [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable)
- - [ ] Create each MR targetting the security branch `security-X-Y`
- - [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR
-- [ ] Make sure all MRs have a link in the [links section](#links) and are assigned to a Release Manager.
-
-[seckpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script
-
-#### Documentation and final details
-
-- [ ] Check the topic on #security to see when the next release is going to happen and add a link to the [links section](#links)
-- [ ] Find out the versions affected (the Git history of the files affected may help you with this) and add them to the [details section](#details)
-- [ ] Fill in any upgrade notes that users may need to take into account in the [details section](#details)
-- [ ] Add Yes/No and further details if needed to the migration and settings columns in the [details section](#details)
-- [ ] Add the nickname of the external user who found the issue (and/or HackerOne profile) to the Thanks row in the [details section](#details)
-
-### Summary
-#### Links
-
-| Description | Link |
-| -------- | -------- |
-| Original issue | #TODO |
-| Security release issue | #TODO |
-| `master` MR | !TODO |
-| `master` MR (EE) | !TODO |
-| `Backport X.Y` MR | !TODO |
-| `Backport X.Y` MR | !TODO |
-| `Backport X.Y` MR | !TODO |
-| `Backport X.Y` MR (EE) | !TODO |
-| `Backport X.Y` MR (EE) | !TODO |
-| `Backport X.Y` MR (EE) | !TODO |
-
-#### Details
-
-| Description | Details | Further details|
-| -------- | -------- | -------- |
-| Versions affected | X.Y | |
-| Upgrade notes | | |
-| GitLab Settings updated | Yes/No| |
-| Migration required | Yes/No | |
-| Thanks | | |
-
-[security process for developers]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md
-[RM list]: https://about.gitlab.com/release-managers/
-
-/label ~security
diff --git a/.gitlab/issue_templates/Security developer workflow.md b/.gitlab/issue_templates/Security developer workflow.md
new file mode 100644
index 00000000000..c1f702e9385
--- /dev/null
+++ b/.gitlab/issue_templates/Security developer workflow.md
@@ -0,0 +1,71 @@
+<!--
+# Read me first!
+
+Create this issue under https://dev.gitlab.org/gitlab/gitlabhq
+
+Set the title to: `[Security] Description of the original issue`
+-->
+
+### Prior to the security release
+
+- [ ] Read the [security process for developers] if you are not familiar with it.
+- [ ] Link to the original issue adding it to the [links section](#links)
+- [ ] Run `scripts/security-harness` in the CE, EE, and/or Omnibus to prevent pushing to any remote besides `dev.gitlab.org`
+- [ ] Create an MR targetting `org` `master`, prefixing your branch with `security-`
+- [ ] Label your MR with the ~security label, prefix the title with `WIP: [master]`
+- [ ] Add a link to the MR to the [links section](#links)
+- [ ] Add a link to an EE MR if required
+- [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**.
+- [ ] Assign the MR to a RM once is reviewed and ready to be merged. Check the [RM list] to see who to ping.
+
+#### Backports
+
+- [ ] Once the MR is ready to be merged, create MRs targetting the last 3 releases
+ - [ ] At this point, it might be easy to squash the commits from the MR into one
+ - You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [seckpick documentation]
+ - [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable)
+ - [ ] Create each MR targetting the security branch `security-X-Y`
+ - [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR
+- [ ] Make sure all MRs have a link in the [links section](#links) and are assigned to a Release Manager.
+
+[seckpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script
+
+#### Documentation and final details
+
+- [ ] Check the topic on #security to see when the next release is going to happen and add a link to the [links section](#links)
+- [ ] Find out the versions affected (the Git history of the files affected may help you with this) and add them to the [details section](#details)
+- [ ] Fill in any upgrade notes that users may need to take into account in the [details section](#details)
+- [ ] Add Yes/No and further details if needed to the migration and settings columns in the [details section](#details)
+- [ ] Add the nickname of the external user who found the issue (and/or HackerOne profile) to the Thanks row in the [details section](#details)
+
+### Summary
+
+#### Links
+
+| Description | Link |
+| -------- | -------- |
+| Original issue | #TODO |
+| Security release issue | #TODO |
+| `master` MR | !TODO |
+| `master` MR (EE) | !TODO |
+| `Backport X.Y` MR | !TODO |
+| `Backport X.Y` MR | !TODO |
+| `Backport X.Y` MR | !TODO |
+| `Backport X.Y` MR (EE) | !TODO |
+| `Backport X.Y` MR (EE) | !TODO |
+| `Backport X.Y` MR (EE) | !TODO |
+
+#### Details
+
+| Description | Details | Further details|
+| -------- | -------- | -------- |
+| Versions affected | X.Y | |
+| Upgrade notes | | |
+| GitLab Settings updated | Yes/No| |
+| Migration required | Yes/No | |
+| Thanks | | |
+
+[security process for developers]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md
+[RM list]: https://about.gitlab.com/release-managers/
+
+/label ~security
diff --git a/.gitlab/merge_request_templates/Database Changes.md b/.gitlab/merge_request_templates/Database Changes.md
deleted file mode 100644
index 1c4f30d9320..00000000000
--- a/.gitlab/merge_request_templates/Database Changes.md
+++ /dev/null
@@ -1,50 +0,0 @@
-Add a description of your merge request here. Merge requests without an adequate
-description will not be reviewed until one is added.
-
-## Database Checklist
-
-When adding migrations:
-
-- [ ] Updated `db/schema.rb`
-- [ ] Added a `down` method so the migration can be reverted
-- [ ] Added the output of the migration(s) to the MR body
-- [ ] Added tests for the migration in `spec/migrations` if necessary (e.g. when migrating data)
-
-When adding or modifying queries to improve performance:
-
-- [ ] Included data that shows the performance improvement, preferably in the form of a benchmark
-- [ ] Included the output of `EXPLAIN (ANALYZE, BUFFERS)` of the relevant queries
-
-When adding foreign keys to existing tables:
-
-- [ ] Included a migration to remove orphaned rows in the source table before adding the foreign key
-- [ ] Removed any instances of `dependent: ...` that may no longer be necessary
-
-When adding tables:
-
-- [ ] Ordered columns based on the [Ordering Table Columns](https://docs.gitlab.com/ee/development/ordering_table_columns.html#ordering-table-columns) guidelines
-- [ ] Added foreign keys to any columns pointing to data in other tables
-- [ ] Added indexes for fields that are used in statements such as WHERE, ORDER BY, GROUP BY, and JOINs
-
-When removing columns, tables, indexes or other structures:
-
-- [ ] Removed these in a post-deployment migration
-- [ ] Made sure the application no longer uses (or ignores) these structures
-
-## General Checklist
-
-- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
-- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html)
-- [ ] API support added
-- [ ] Tests added for this feature/bug
-- Conform by the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
- - [ ] Has been reviewed by a Backend maintainer
- - [ ] Has been reviewed by a Database specialist
-- [ ] Conform by the [merge request performance guides](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
-- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
-- [ ] If you have multiple commits, please combine them into a few logically organized commits by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
-- [ ] Internationalization required/considered
-- [ ] If paid feature, have we considered GitLab.com plan and how it works for groups and is there a design for promoting it to users who aren't on the correct plan
-- [ ] End-to-end tests pass (`package-and-qa` manual pipeline job)
-
-/label ~database
diff --git a/.gitlab/merge_request_templates/Database changes.md b/.gitlab/merge_request_templates/Database changes.md
new file mode 100644
index 00000000000..e636ec313df
--- /dev/null
+++ b/.gitlab/merge_request_templates/Database changes.md
@@ -0,0 +1,50 @@
+Add a description of your merge request here. Merge requests without an adequate
+description will not be reviewed until one is added.
+
+## Database checklist
+
+When adding migrations:
+
+- [ ] Updated `db/schema.rb`
+- [ ] Added a `down` method so the migration can be reverted
+- [ ] Added the output of the migration(s) to the MR body
+- [ ] Added tests for the migration in `spec/migrations` if necessary (e.g. when migrating data)
+
+When adding or modifying queries to improve performance:
+
+- [ ] Included data that shows the performance improvement, preferably in the form of a benchmark
+- [ ] Included the output of `EXPLAIN (ANALYZE, BUFFERS)` of the relevant queries
+
+When adding foreign keys to existing tables:
+
+- [ ] Included a migration to remove orphaned rows in the source table before adding the foreign key
+- [ ] Removed any instances of `dependent: ...` that may no longer be necessary
+
+When adding tables:
+
+- [ ] Ordered columns based on the [Ordering Table Columns](https://docs.gitlab.com/ee/development/ordering_table_columns.html#ordering-table-columns) guidelines
+- [ ] Added foreign keys to any columns pointing to data in other tables
+- [ ] Added indexes for fields that are used in statements such as WHERE, ORDER BY, GROUP BY, and JOINs
+
+When removing columns, tables, indexes or other structures:
+
+- [ ] Removed these in a post-deployment migration
+- [ ] Made sure the application no longer uses (or ignores) these structures
+
+## General checklist
+
+- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
+- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/documentation/index.html#contributing-to-docs)
+- [ ] [API support added](https://docs.gitlab.com/ee/development/api_styleguide.html)
+- [ ] [Tests added for this feature/bug](https://docs.gitlab.com/ee/development/testing_guide/index.html)
+- Conforms to the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
+ - [ ] Has been reviewed by a Backend [maintainer](https://about.gitlab.com/handbook/engineering/#maintainer)
+ - [ ] Has been reviewed by a Database [specialist](https://about.gitlab.com/team/structure/#specialist)
+- [ ] Conforms to the [merge request performance guidelines](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
+- [ ] Conforms to the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
+- [ ] If you have multiple commits, please combine them into a few logically organized commits by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
+- [ ] [Internationalization required/considered](https://docs.gitlab.com/ee/development/i18n/index.html)
+- [ ] For a paid feature, have we considered GitLab.com plans, how it works for groups, and is there a design for promoting it to users who aren't on the correct plan?
+- [ ] [End-to-end tests](https://docs.gitlab.com/ee/development/testing_guide/end_to_end_tests.html#testing-code-in-merge-requests) pass (`package-and-qa` manual pipeline job)
+
+/label ~database
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index da38a703c3c..531035b3766 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -1,4 +1,4 @@
-<!--See the general Documentation guidelines https://docs.gitlab.com/ce/development/writing_documentation.html -->
+<!--See the general Documentation guidelines https://docs.gitlab.com/ee/development/documentation/index.html -->
## What does this MR do?
@@ -13,17 +13,17 @@ Closes
## Moving docs to a new location?
Read the guidelines:
-https://docs.gitlab.com/ce/development/writing_documentation.html#changing-document-location
+https://docs.gitlab.com/ee/development/documentation/#changing-document-location
- [ ] Make sure the old link is not removed and has its contents replaced with
a link to the new location.
- [ ] Make sure internal links pointing to the document in question are not broken.
-- [ ] Search and replace any links referring to old docs in GitLab Rails app,
- specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
-- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/writing_documentation.html#redirections-for-pages-with-disqus-comments)
+- [ ] Search and replace any links referring to the old docs in the GitLab Rails app,
+ specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
+- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ee/development/documentation/index.html#redirections-for-pages-with-disqus-comments)
to the new document if there are any Disqus comments on the old document thread.
-- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
- with the changes as well (https://docs.gitlab.com/ce/development/writing_documentation.html#cherry-picking-from-ce-to-ee).
+- [ ] If working on CE and the `ee-compat-check` jobs fails, [submit an MR to EE
+ with the changes](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee) as well.
- [ ] Ping one of the technical writers for review.
/label ~Documentation
diff --git a/.rubocop.yml b/.rubocop.yml
index 0582bfe8d70..c8b1ce327e2 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -10,9 +10,9 @@ AllCops:
Exclude:
- 'vendor/**/*'
- 'node_modules/**/*'
- - 'db/**/*'
- 'db/fixtures/**/*'
- - 'ee/db/**/*'
+ - 'db/schema.rb'
+ - 'ee/db/geo/schema.rb'
- 'tmp/**/*'
- 'bin/**/*'
- 'generator_templates/**/*'
@@ -34,6 +34,8 @@ Style/MutableConstant:
Naming/FileName:
ExpectMatchingDefinition: true
Exclude:
+ - 'db/**/*'
+ - 'ee/db/**/*'
- 'spec/**/*'
- 'features/**/*'
- 'ee/spec/**/*'
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index ccf301e6c78..8a1ca6747a8 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -10,10 +10,6 @@
Capybara/CurrentPathExpectation:
Enabled: false
-# Offense count: 956
-Capybara/FeatureMethods:
- Enabled: false
-
# Offense count: 23
FactoryBot/DynamicAttributeDefinedStatically:
Exclude:
@@ -203,12 +199,6 @@ Naming/HeredocDelimiterCase:
Naming/HeredocDelimiterNaming:
Enabled: false
-# Offense count: 27
-# Cop supports --auto-correct.
-# Configuration parameters: AutoCorrect.
-Performance/HashEachMethods:
- Enabled: false
-
# Offense count: 1
Performance/UnfreezeString:
Exclude:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9f38766392..7be28a9ac0e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,329 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.1.4 (2018-07-30)
+
+### Fixed (4 changes, 1 of them is from the community)
+
+- Rework some projects table indexes around repository_storage field. !20377
+- Don't overflow project/group dropdown results. !20704 (gfyoung)
+- Fixed IDE not opening JSON files. !20798
+- Disable Gitaly timeouts when creating or restoring backups. !20810
+
+## 11.1.3 (2018-07-27)
+
+- Not released.
+
+## 11.1.2 (2018-07-26)
+
+### Security (4 changes)
+
+- Adding CSRF protection to Hooks test action.
+- Don't expose project names in GitHub counters.
+- Don't expose project names in various counters.
+- Fixed XSS in branch name in Web IDE.
+
+### Fixed (1 change)
+
+- Escapes milestone and label's names on flash notice when promoting them.
+
+### Performance (1 change)
+
+- Fix slow Markdown rendering. !20820
+
+
+## 11.1.1 (2018-07-23)
+
+### Fixed (2 changes)
+
+- Add missing Gitaly branch_update nil checks. !20711
+- Fix filename for accelerated uploads.
+
+### Added (1 change)
+
+- Add uploader support to Import/Export uploads. !20484
+
+
+## 11.1.0 (2018-07-22)
+
+### Security (6 changes)
+
+- Fix XSS vulnerability for table of content generation.
+- Update sanitize gem to 4.6.5 to fix HTML injection vulnerability.
+- HTML escape branch name in project graphs page.
+- HTML escape the name of the user in ProjectsHelper#link_to_member.
+- Don't show events from internal projects for anonymous users in public feed.
+- Fix symlink vulnerability in project import.
+
+### Removed (1 change)
+
+- Remove deprecated object_storage_upload queue.
+
+### Fixed (98 changes, 52 of them are from the community)
+
+- Keep lists ordered when copying only list items. !18522 (Jan Beckmann)
+- Fix bug where maintainer would not be allowed to push to forks with merge requests that have `Allow maintainer edits` enabled. !18968
+- mergeError message has been binded using v-html directive. !19058 (Murat Dogan)
+- Set MR target branch to default branch if target branch is not valid. !19067
+- Fix CSS for buttons not to be hidden on issues/MR title. !19176 (Takuya Noguchi)
+- Use same gem versions for rails5 as for rails4 where possible. !19498 (Jasper Maes)
+- Fix extra blank line at start of rendered reStructuredText code block. !19596
+- Fix username validation order on signup, resolves #45575. !19610 (Jan Beckmann)
+- Make quick commands case insensitive. !19614 (Jan Beckmann)
+- Remove incorrect CI doc re: PowerShell. !19622 (gfyoung)
+- Fixes Microsoft Teams notifications for pipeline events. !19632 (Jeff Brown)
+- Fix branch name encoding for dropdown on issue page. !19634
+- Rails5 fix expected `issuable.reload.updated_at` to have changed. !19733 (Jasper Maes)
+- Rails5 fix stack level too deep. !19762 (Jasper Maes)
+- Rails5 ActionController::ParameterMissing: param is missing or the value is empty: application_setting. !19763 (Jasper Maes)
+- Invalidate cache with project details when repository is updated. !19774
+- Rails5 fix no implicit conversion of Hash into String. ActionController::Parameters no longer returns an hash in Rails 5. !19792 (Jasper Maes)
+- [Rails5] Fix snippets_finder arel queries. !19796 (@blackst0ne)
+- Fix fields for author & assignee in MR API docs. !19798 (gfyoung)
+- Remove scrollbar in Safari in repo settings page. !19809 (gfyoung)
+- Omits operartions and kubernetes item from project sidebar when repository or builds are disabled. !19835
+- Rails5 fix passing Group objects array into for_projects_and_groups milestone scope. !19863 (Jasper Maes)
+- Fix chat service tag notifications not sending when only default branch enabled. !19864
+- Only show new issue / new merge request on group page when issues / merge requests are enabled. !19869 (Jan Beckmann)
+- [Rails5] Explicitly set request.format for blob_controller. !19876 (@blackst0ne)
+- [Rails5] Fix optimistic lock value. !19878 (@blackst0ne)
+- Rails5 fix update_attribute usage not causing a save. !19881 (Jasper Maes)
+- Rails5 fix connection execute return integer instead of string. !19901 (Jasper Maes)
+- Rails5 fix format in uploads actions. !19907 (Jasper Maes)
+- [Rails5] Fix "-1 is not a valid data_store". !19917 (@blackst0ne)
+- [Rails5] Invalid single-table inheritance type: Group is not a subclass of Namespace. !19918 (@blackst0ne)
+- [Rails5] Fix pipeline_schedules_controller_spec. !19919 (@blackst0ne)
+- Rails5 fix passing Group objects array into for_projects_and_groups milestone scope. !19920 (Jasper Maes)
+- Rails5 update Gemfile.rails5.lock. !19921 (Jasper Maes)
+- [Rails5] Fix sessions_controller_spec. !19936 (@blackst0ne)
+- [Rails5] Set request.format for artifacts_controller. !19937 (@blackst0ne)
+- Fix webhook error when password is not present. !19945 (Jan Beckmann)
+- Fix label and milestone duplicated records and IID errors. !19961
+- Rails5 fix expected: 1 time with arguments: (97, anything, {"squash"=>false}) received: 0 times. !20004 (Jasper Maes)
+- Rails5 fix Projects::PagesController spec. !20007 (Jasper Maes)
+- [Rails5] Fix ActionCable '/cable' mountpoint conflict. !20015 (@blackst0ne)
+- Fix branches are not shown in Merge Request dropdown when preferred language is not English. !20016 (Hiroyuki Sato)
+- Rails5 fix Admin::HooksController. !20017 (Jasper Maes)
+- Rails5 fix expected: 0 times with any arguments received: 1 time with arguments: DashboardController. !20018 (Jasper Maes)
+- [Rails5] Set request.format in commits_controller. !20023 (@blackst0ne)
+- Keeps the label on an issue when the issue is moved. !20036
+- Cleanup Prometheus ruby metrics. !20039 (Ben Kochie)
+- Rails 5 fix Capybara::ElementNotFound: Unable to find visible css #modal-revert-commit and expected: "/bar" got: "/foo". !20044 (Jasper Maes)
+- [Rails5] Force the callback run first. !20055 (@blackst0ne)
+- Add readme button to non-empty project page. !20104
+- Fixed bug when editing a comment in an issue,the preview mode is toggled in the main textarea. !20112 (Constance Okoghenun)
+- Ignore unknown OAuth sources in ApplicationSetting. !20129
+- Fix paragraph line height for emoji. !20137 (George Tsiolis)
+- Fixes issue with uploading same image to Profile Avatar twice. !20161 (Chirag Bhatia)
+- Rails5 fix arel from in mysql_median_datetime_sql. !20167 (Jasper Maes)
+- Adds the `locked` state to the merge request API so that it can be used as a search filter. !20186
+- Enable Doorkeeper option to avoid generating new tokens when users login via oauth. !20200
+- Fix OAuth Application Authorization screen to appear with each access. !20216
+- Rails5 fix MySQL milliseconds problem in specs. !20221 (Jasper Maes)
+- Rails5 fix Mysql comparison failure caused by milliseconds problem. !20222 (Jasper Maes)
+- Updated last commit link color. !20234 (Constance Okoghenun)
+- Fixed Merge request changes dropdown displays incorrectly. !20237 (Constance Okoghenun)
+- Show jobs from same pipeline in sidebar in job details view. !20243
+- [Rails5] Fix milestone GROUP BY query. !20256 (@blackst0ne)
+- Line separator to the left of the 'Admin area' wrench icon had vanished. !20282 (bitsapien)
+- Check if archived trace exist before archive it. !20297
+- Load Devise with Omniauth when auto_sign_in_with_provider is configured. !20302
+- Fix link to job when creating a new issue from a failed job. !20328
+- Fix double "in" in time to artifact deletion message. !20357 (@bbodenmiller)
+- Fix wrong role badge displayed in projects dashboard. !20374
+- Stop relying on migrations in the CacheableAttributes cache key and cache attributes for 1 minute instead. !20389
+- Fixes toggle discussion button not expanding collapsed discussions. !20452
+- Resolve compatibility issues with node 6. !20461
+- Fixes base command used in Helm installations. !20471
+- Fix RSS button interaction on Dashboard, Project and Group activities. !20549
+- Use appropriate timeout on Gitaly server info checks, avoid error on timeout. !20552
+- Remove healthchecks from prometheus endpoint. !20565
+- Render MR page when diffs cannot be fetched from the database or the git repository. !20680
+- Expire correct method caches after HEAD changed.
+- Ensure MR diffs always exist in the PR importer.
+- Fix overlapping file title and file actions in MR changes tag.
+- Mark MR as merged regardless of errors when closing issues.
+- Fix performance bar modal visibility in Safari.
+- Prevent browser autocomplete for milestone date fields.
+- Limit the action suffixes in transaction metrics.
+- Add /uploads subdirectory to allowed upload paths.
+- Fix cross-project label references.
+- Invalidate merge request diffs cache if diff data change.
+- Don't show context button for diffs of deleted files.
+- Structure getters for diff Store properly and adds specs.
+- Bump rugged to 0.27.2.
+- Fix Bamboo CI status not showing for branch plans.
+- Fixed bug that allowed to remove other wiki pages if the title had wildcard characters.
+- Disabled Web IDE autocomplete suggestions for Markdown files. (Isaac Smith)
+- Fix merge request diffs when created with gitaly_diff_between enabled.
+- Properly detect label reference if followed by period or question mark.
+- Deactivate new KubernetesService created from active template to prevent project creation from failing.
+- Allow trailing whitespace on blockquote fence lines.
+
+### Deprecated (1 change)
+
+- Removes unused bootstrap 4 scss files. !19423
+
+### Changed (33 changes, 16 of them are from the community)
+
+- Change label link vertical alignment property. !18777 (George Tsiolis)
+- Updated the icon for expand buttons to ellipsis. !18793 (Constance Okoghenun)
+- Create new or add existing Kubernetes cluster from a single page. !18963
+- Use object storage as the first class persistable store for new live trace architecture. !19515
+- Hide project name if searching against a project. !19595
+- Allows you to create another deploy token dimmediately after creating one. !19639
+- Removes the environment scope field for users that cannot edit it. !19643
+- Don't hash user ID in OIDC subject claim. !19784 (Markus Koller)
+- Milestone page list redesign. !19832 (Constance Okoghenun)
+- Add environment dropdown for the metrics page. !19833
+- Allow querying a single merge request within a project. !19853
+- Update WebIDE to show file in tree on load. !19887
+- Remove small container width. !19893 (George Tsiolis)
+- Improve U2F workflow when using unsupported browsers. !19938 (Jan Beckmann)
+- Update Web IDE file tree styles. !19969
+- Highlight cluster settings message. !19996 (George Tsiolis)
+- Fade uneditable area in Web IDE. !20008
+- Update pipeline icon in web ide sidebar. !20058 (George Tsiolis)
+- Revert merge request discussion buttons padding. !20060 (George Tsiolis)
+- Fix boards issue highlight. !20063 (George Tsiolis)
+- Update external link icon in header user dropdown. !20150 (George Tsiolis)
+- Update external link icon in merge request widget. !20154 (George Tsiolis)
+- Update environments nav controls icons. !20199 (George Tsiolis)
+- Update integrations external link icons. !20205 (George Tsiolis)
+- Fixes an issue where migrations instead of schema loading were run. !20227
+- Add title placeholder for new issues. !20271 (George Tsiolis)
+- Close revoke deploy token modal on escape keypress. !20347 (George Tsiolis)
+- Change environment scope text depending on number of project clusters. Update form to only include form-groups.
+- Improve Web IDE commit flow.
+- Add machine type and pricing documentation links, add class to labels to make bold.
+- Remove remaining traces of the Allocations Gem.
+- Use one column form layout on Admin Area Settings page.
+- Add back copy for existing gcp accounts within offer banner.
+
+### Performance (16 changes, 4 of them are from the community)
+
+- Fully migrate pipeline stages position. !19369
+- Use Tooltip component in MrWidgetAuthorTime vue comonent. !19635 (George Tsiolis)
+- Move boards modal EmptyState vue component. !20068 (George Tsiolis)
+- Bump carrierwave gem verion to 1.2.3. !20287
+- Remove redundant query when removing trace. !20324
+- Improves performance of mr code, by fixing the state being mutated outside of the store in the util function trimFirstCharOfLineContent and in map operations. Avoids map operation in an empty array. Adds specs to the trimFirstCharOfLineContent function. !20380 (filipa)
+- Reduce the number of queries when searching for groups. !20398
+- Improve render performance of large wiki pages. !20465 (Peter Leitzen)
+- Improves performance on Merge Request diff tab by removing the scroll event listeners being added to every file.
+- Remove the ci_job_request_with_tags_matcher.
+- Updated Gitaly fail-fast timeout values.
+- Add index on deployable_type/id for deployments.
+- Eliminate N+1 queries in LFS file locks checks during a push.
+- Fix performance problem of accessing tag list for projects api endpoints.
+- Improve performance of listing users without projects.
+- Fixed pagination of web hook logs.
+
+### Added (29 changes, 9 of them are from the community)
+
+- Add dropdown to Groups link in top bar. !18280
+- Web IDE supports now Image + Download Diff Viewing. !18768
+- Use CommonMark syntax and rendering for new Markdown content. !19331
+- Add SHA256 and HEAD on File API. !19439 (ahmet2mir)
+- Add filename filtering to code search. !19509
+- Add CI_PIPELINE_URL and CI_JOB_URL. !19618
+- Expose visibility via Snippets API. !19620 (Jan Beckmann)
+- Fixed pagination of groups API. !19665 (Marko, Peter)
+- Added id sorting option to GET groups and subgroups API. !19665 (Marko, Peter)
+- Add a link to the contributing page in the user dropdown. !19708
+- Add Object Storage to project export. !20105
+- Change avatar image in the header when user updates their avatar. !20119 (Jamie Schembri)
+- Allow straight diff in Compare API. !20120 (Maciej Nowak)
+- Add transfer project API endpoint. !20122 (Aram Visser)
+- Expose permissions of the current user on resources in GraphQL. !20152
+- Run repository checks in parallel for each shard. !20179
+- Add pipeline lists to GraphQL. !20249
+- Add option to add README when creating a project. !20335
+- Add option to hide third party offers in admin application settings. !20379
+- Add /confidential quick action. (Jan Beckmann)
+- Support direct_upload for generic uploads.
+- Display merge request title & description in Web IDE.
+- Prune web hook logs older than 90 days.
+- Add Web Terminal for Ci Builds. (Vicky Chijwani)
+- Expose whether current user can push into a branch on branches API.
+- Present state indication on GFM preview.
+- migrate backup rake task to gitaly.
+- Add Gitlab::SQL:CTE for easily building CTE statements.
+- Added with_statsoption for GET /projects/:id/repository/commits.
+
+### Other (28 changes, 11 of them are from the community)
+
+- Move some Gitaly RPC's to opt-out. !19591
+- Bump grape-path-helpers to 1.0.5. !19604 (@blackst0ne)
+- Add CI job to check Gemfile.rails5.lock. !19605 (@blackst0ne)
+- Move Gitaly branch/tag/ref RPC's to opt-out. !19644
+- CE port gitlab-ee!6112. !19714
+- Enable no-multi-assignment in JS files. !19808 (gfyoung)
+- Enable no-restricted globals in JS files. !19877 (gfyoung)
+- Improve no-multi-assignment fixes after enabling rule. !19915 (gfyoung)
+- Enable prefer-structuring in JS files. !19943 (gfyoung)
+- Enable frozen string in app/workers/*.rb. !19944 (gfyoung)
+- Uses long sha version of the merged commit in MR widget copy to clipboard button. !19955
+- Update new group page to better explain what groups are. !19991
+- Update new SSH key page to improve copy. !19994
+- Update new SSH key page to improve key input validation. !19997
+- Gitaly metrics check for read/writeability. !20022
+- Add ellispsis to web ide commit button. !20030
+- Minor style changes to personal access token form and scope checkboxes. !20052
+- Finish enabling frozen string for app/workers/*.rb. !20197 (gfyoung)
+- Allows settings sections to expand by default when linking to them. !20211
+- Enable frozen string in apps/validators/*.rb. !20220 (gfyoung)
+- update bcrypt to also support libxcrypt. !20260 (muhammadn)
+- Enable frozen string in apps/validators/*.rb. !20382 (gfyoung)
+- Removes unused vuex code in mr refactor and removes unneeded dependencies. !20499
+- Delete non-latest merge request diff files upon merge.
+- Schedule workers to delete non-latest diffs in post-migration.
+- Remove the use of `is_shared` of `Ci::Runner`.
+- Add more detailed logging to githost.log when rebasing.
+- Use monospaced font for MR diff commit link ref on GFM.
+
+
+## 11.0.5 (2018-07-26)
+
+### Security (4 changes)
+
+- Don't expose project names in various counters.
+- Don't expose project names in GitHub counters.
+- Adding CSRF protection to Hooks test action.
+- Fixed XSS in branch name in Web IDE.
+
+### Fixed (1 change)
+
+- Escapes milestone and label's names on flash notice when promoting them.
+
+
+## 11.0.4 (2018-07-17)
+
+### Security (1 change)
+
+- Fix symlink vulnerability in project import.
+
+
+## 11.0.3 (2018-07-05)
+
+### Fixed (14 changes, 1 of them is from the community)
+
+- Revert merge request widget button max height. !20175 (George Tsiolis)
+- Implement upload copy when moving an issue with upload on object storage. !20191
+- Fix broken '!' support to autocomplete MRs in GFM fields. !20204
+- Restore showing Elasticsearch and Geo status on dashboard. !20276
+- Fix merge request page rendering error when its target/source branch is missing. !20280
+- Fix sidebar collapse breapoints for job and wiki pages.
+- fix size of code blocks in headings.
+- Fix loading screen for search autocomplete dropdown.
+- Fix ambiguous due_date column for Issue scopes.
+- Always serve favicon from main GitLab domain so that CI badge can be drawn over it.
+- Fix tooltip flickering bug.
+- Fix refreshing cache keys for open issues count.
+- Replace deprecated bs.affix in merge request tabs with sticky polyfill.
+- Prevent pipeline job tooltip from scrolling off dropdown container.
+
+
## 11.0.2 (2018-06-26)
### Fixed (8 changes, 1 of them is from the community)
@@ -275,6 +598,14 @@ entry.
- Workhorse to send raw diff and patch for commits.
+## 10.8.6 (2018-07-17)
+
+### Security (2 changes)
+
+- Fix symlink vulnerability in project import.
+- Merge branch 'fix-mr-widget-border' into 'master'.
+
+
## 10.8.5 (2018-06-21)
### Security (5 changes)
@@ -504,6 +835,13 @@ entry.
- Gitaly handles repository forks by default.
+## 10.7.7 (2018-07-17)
+
+### Security (1 change)
+
+- Fix symlink vulnerability in project import.
+
+
## 10.7.6 (2018-06-21)
### Security (6 changes)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fd4e769ecee..e68e3b9cab0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -21,40 +21,47 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+- [Contributing Documentation has been moved](#contributing-documentation-has-been-moved)
- [Contribute to GitLab](#contribute-to-gitlab)
- [Security vulnerability disclosure](#security-vulnerability-disclosure)
+- [Code of conduct](#code-of-conduct)
- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
- [Helping others](#helping-others)
- [I want to contribute!](#i-want-to-contribute)
+- [Contribution Flow](#contribution-flow)
- [Workflow labels](#workflow-labels)
- - [Type labels](#type-labels)
- - [Subject labels](#subject-labels)
- - [Team labels](#team-labels)
- - [Release Scoping labels](#release-scoping-labels)
- - [Bug Priority labels](#bug-priority-labels)
- - [Bug Severity labels](#bug-severity-labels)
- - [Severity impact guidance](#severity-impact-guidance)
- - [Label for community contributors](#label-for-community-contributors)
-- [Implement design & UI elements](#implement-design-ui-elements)
+ - [Type labels](#type-labels)
+ - [Subject labels](#subject-labels)
+ - [Team labels](#team-labels)
+ - [Release Scoping labels](#release-scoping-labels)
+ - [Priority labels](#priority-labels)
+ - [Severity labels](#severity-labels)
+ - [Severity impact guidance](#severity-impact-guidance)
+ - [Label for community contributors](#label-for-community-contributors)
+- [Implement design & UI elements](#implement-design--ui-elements)
- [Issue tracker](#issue-tracker)
- - [Issue triaging](#issue-triaging)
- - [Feature proposals](#feature-proposals)
- - [Issue tracker guidelines](#issue-tracker-guidelines)
- - [Issue weight](#issue-weight)
- - [Regression issues](#regression-issues)
- - [Technical and UX debt](#technical-and-ux-debt)
- - [Stewardship](#stewardship)
+ - [Issue triaging](#issue-triaging)
+ - [Feature proposals](#feature-proposals)
+ - [Issue tracker guidelines](#issue-tracker-guidelines)
+ - [Issue weight](#issue-weight)
+ - [Regression issues](#regression-issues)
+ - [Technical and UX debt](#technical-and-ux-debt)
+ - [Stewardship](#stewardship)
- [Merge requests](#merge-requests)
- - [Merge request guidelines](#merge-request-guidelines)
- - [Contribution acceptance criteria](#contribution-acceptance-criteria)
+ - [Merge request guidelines](#merge-request-guidelines)
+ - [Contribution acceptance criteria](#contribution-acceptance-criteria)
- [Definition of done](#definition-of-done)
- [Style guides](#style-guides)
-- [Code of conduct](#code-of-conduct)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
---
+## Contributing Documentation has been moved
+
+As of July 2018, all the documentation for contributing to the GitLab project has been moved to a new location.
+[view the new documentation](doc/development/contributing/index.md) to find the latest information.
+
## Contribute to GitLab
For a first-time step-by-step guide to the contribution process, see
@@ -83,6 +90,36 @@ Please report suspected security vulnerabilities in private to
Please do **NOT** create publicly viewable issues for suspected security
vulnerabilities.
+## 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/).
+
## Closing policy for issues and merge requests
GitLab is a popular open source project and the capacity to deal with issues
@@ -122,641 +159,164 @@ learn how to communicate with GitLab. If you're looking for a Gitter or Slack ch
please consider we favor
[asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real time communication. Thanks for your contribution!
-## Workflow labels
+## Contribution Flow
-To allow for asynchronous issue handling, we use [milestones][milestones-page]
-and [labels][labels-page]. Leads and product managers handle most of the
-scheduling into milestones. Labelling is a task for everyone.
+When contributing to GitLab, your merge request is subject to review by merge request maintainers of a particular specialty.
-Most issues will have labels for at least one of the following:
+When you submit code to GitLab, we really want it to get merged, but there will be times when it will not be merged.
-- Type: ~"feature proposal", ~bug, ~customer, etc.
-- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
-- Team: ~"CI/CD", ~Discussion, ~Quality, ~Platform, etc.
-- Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release"
-- Priority: ~P1, ~P2, ~P3, ~P4
-- Severity: ~S1, ~S2, ~S3, ~S4
+When maintainers are reading through a merge request they may request guidance from other maintainers. If merge request maintainers conclude that the code should not be merged, our reasons will be fully disclosed. If it has been decided that the code quality is not up to GitLab’s standards, the merge request maintainer will refer the author to our docs and code style guides, and provide some guidance.
-All labels, their meaning and priority are defined on the
-[labels page][labels-page].
+Sometimes style guides will be followed but the code will lack structural integrity, or the maintainer will have reservations about the code’s overall quality. When there is a reservation the maintainer will inform the author and provide some guidance. The author may then choose to update the merge request. Once the merge request has been updated and reassigned to the maintainer, they will review the code again. Once the code has been resubmitted any number of times, the maintainer may choose to close the merge request with a summary of why it will not be merged, as well as some guidance. If the merge request is closed the maintainer will be open to discussion as to how to improve the code so it can be approved in the future.
-If you come across an issue that has none of these, and you're allowed to set
-labels, you can _always_ add the team and type, and often also the subject.
+GitLab will do its best to review community contributions as quickly as possible. Specially appointed developers review community contributions daily. You may take a look at the [team page](https://about.gitlab.com/team/) for the merge request coach who specializes in the type of code you have written and mention them in the merge request. For example, if you have written some JavaScript in your code then you should mention the frontend merge request coach. If your code has multiple disciplines you may mention multiple merge request coaches.
-[milestones-page]: https://gitlab.com/gitlab-org/gitlab-ce/milestones
-[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels
+GitLab receives a lot of community contributions, so if your code has not been reviewed within 4 days of its initial submission feel free to re-mention the appropriate merge request coach.
-### Type labels
-
-Type labels are very important. They define what kind of issue this is. Every
-issue should have one or more.
-
-Examples of type labels are ~"feature proposal", ~bug, ~customer, ~security,
-and ~"direction".
+When submitting code to GitLab, you may feel that your contribution requires the aid of an external library. If your code includes an external library please provide a link to the library, as well as reasons for including it.
-A number of type labels have a priority assigned to them, which automatically
-makes them float to the top, depending on their importance.
+When your code contains more than 500 changes, any major breaking changes, or an external library, `@mention` a maintainer in the merge request. If you are not sure who to mention, the reviewer will add one early in the merge request process.
-Type labels are always lowercase, and can have any color, besides blue (which is
-already reserved for subject labels).
+[core team]: https://about.gitlab.com/core-team/
+[team]: https://about.gitlab.com/team/
+[getting-help]: https://about.gitlab.com/getting-help/
+[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
+[accepting-mrs-weight]: https://gitlab.com/gitlab-org/gitlab-ce/issues?assignee_id=0&label_name[]=Accepting%20Merge%20Requests&sort=weight_asc
+[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
+[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
+[google-group]: https://groups.google.com/forum/#!forum/gitlabhq
+[stackoverflow]: https://stackoverflow.com/questions/tagged/gitlab
+[fpl]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=feature+proposal
+[accepting-mrs-ce]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests
+[accepting-mrs-ee]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Accepting+Merge+Requests
+[gitlab-mr-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests
+[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit
+[git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
+[closed-merge-requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed
+[definition-of-done]: http://guide.agilealliance.org/guide/definition-of-done.html
+[contributor-covenant]: http://contributor-covenant.org
+[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout
+[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
+[changelog]: doc/development/changelog.md "Generate a changelog entry"
+[doc-guidelines]: doc/development/documentation/index.md "Documentation guidelines"
+[js-styleguide]: doc/development/fe_guide/style_guide_js.md "JavaScript styleguide"
+[scss-styleguide]: doc/development/fe_guide/style_guide_scss.md "SCSS styleguide"
+[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
+[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
+[license-finder-doc]: doc/development/licensing.md
+[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
+[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
+[testing]: doc/development/testing_guide/index.md
+[us-english]: https://en.wikipedia.org/wiki/American_English
-The descriptions on the [labels page][labels-page] explain what falls under each type label.
-### Subject labels
+## Workflow labels
-Subject labels are labels that define what area or feature of GitLab this issue
-hits. They are not always necessary, but very convenient.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Examples of subject labels are ~wiki, ~ldap, ~api,
-~issues, ~"merge requests", ~labels, and ~"container registry".
-If you are an expert in a particular area, it makes it easier to find issues to
-work on. You can also subscribe to those labels to receive an email each time an
-issue is labeled with a subject label corresponding to your expertise.
+### Type labels
-Subject labels are always all-lowercase.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-### Team labels
-Team labels specify what team is responsible for this issue.
-Assigning a team label makes sure issues get the attention of the appropriate
-people.
+### Subject labels
-The current team labels are:
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-- ~Configuration
-- ~"CI/CD"
-- ~Discussion
-- ~Distribution
-- ~Documentation
-- ~Geo
-- ~Gitaly
-- ~Monitoring
-- ~Platform
-- ~Quality
-- ~Release
-- ~"Security Products"
-- ~UX
-The descriptions on the [labels page][labels-page] explain what falls under the
-responsibility of each team.
+### Team labels
-Within those team labels, we also have the ~backend and ~frontend labels to
-indicate if an issue needs backend work, frontend work, or both.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Team labels are always capitalized so that they show up as the first label for
-any issue.
### Release Scoping labels
-Release Scoping labels help us clearly communicate expectations of the work for the
-release. There are three levels of Release Scoping labels:
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-- ~Deliverable: Issues that are expected to be delivered in the current
- milestone.
-- ~Stretch: Issues that are a stretch goal for delivering in the current
- milestone. If these issues are not done in the current release, they will
- strongly be considered for the next release.
-- ~"Next Patch Release": Issues to put in the next patch release. Work on these
- first, and add the "Pick Into X" label to the merge request, along with the
- appropriate milestone.
-Each issue scheduled for the current milestone should be labeled ~Deliverable
-or ~"Stretch". Any open issue for a previous milestone should be labeled
-~"Next Patch Release", or otherwise rescheduled to a different milestone.
+### Priority labels
-### Bug Priority labels
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Bug Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
-If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
-This label documents the planned timeline & urgency which is used to measure against our actual SLA on delivering ~bug fixes.
-| Label | Meaning | Estimate time to fix | Guidance |
-|-------|-----------------|------------------------------------------------------------------|----------|
-| ~P1 | Urgent Priority | The current release + potentially immediate hotfix to GitLab.com | |
-| ~P2 | High Priority | The next release | |
-| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) | |
-| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) | The issue is prominent but does not impact user workflow and a workaround is documented |
+### Severity labels
-### Bug Severity labels
-
-Severity labels help us clearly communicate the impact of a ~bug on users.
-
-| Label | Meaning | Impact of the defect | Example |
-|-------|-------------------|-------------------------------------------------------|---------|
-| ~S1 | Blocker | Outage, broken feature with no workaround | Unable to create an issue. Data corruption/loss. Security breach. |
-| ~S2 | Critical Severity | Broken Feature, workaround too complex & unacceptable | Can push commits, but only via the command line. |
-| ~S3 | Major Severity | Broken Feature, workaround acceptable | Can create merge requests only from the Merge Requests page, not through the Issue. |
-| ~S4 | Low Severity | Functionality inconvenience or cosmetic issue | Label colors are incorrect / not being displayed. |
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
#### Severity impact guidance
-| Label | Security Impact | Availability / Performance Impact |
-|-------|---------------------------------------------------------------------|--------------------------------------------------------------|
-| ~S1 | >50% users impacted (possible company extinction level event) | |
-| ~S2 | Many users or multiple paid customers impacted (but not apocalyptic)| The issue is (almost) guaranteed to occur in the near future |
-| ~S3 | A few users or a single paid customer impacted | The issue is likely to occur in the near future |
-| ~S4 | No paid users/customer impacted, or expected impact within 30 days | The issue _may_ occur but it's not likely |
-
-### Label for community contributors
-
-Issues that are beneficial to our users, 'nice to haves', that we currently do
-not have the capacity for or want to give the priority to, are labeled as
-~"Accepting Merge Requests", so the community can make a contribution.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Community contributors can submit merge requests for any issue they want, but
-the ~"Accepting Merge Requests" label has a special meaning. It points to
-changes that:
-1. We already agreed on,
-1. Are well-defined,
-1. Are likely to get accepted by a maintainer.
-
-We want to avoid a situation when a contributor picks an
-~"Accepting Merge Requests" issue and then their merge request gets closed,
-because we realize that it does not fit our vision, or we want to solve it in a
-different way.
-
-We add the ~"Accepting Merge Requests" label to:
-
-- Low priority ~bug issues (i.e. we do not add it to the bugs that we want to
-solve in the ~"Next Patch Release")
-- Small ~"feature proposal"
-- Small ~"technical debt" issues
-
-After adding the ~"Accepting Merge Requests" label, we try to estimate the
-[weight](#issue-weight) of the issue. We use issue weight to let contributors
-know how difficult the issue is. Additionally:
-
-- We advertise ["Accepting Merge Requests" issues with weight < 5][up-for-grabs]
- as suitable for people that have never contributed to GitLab before on the
- [Up For Grabs campaign](http://up-for-grabs.net)
-- We encourage people that have never contributed to any open source project to
- look for ["Accepting Merge Requests" issues with a weight of 1][firt-timers]
-
-If you've decided that you would like to work on an issue, please @-mention
-the [appropriate product manager](https://about.gitlab.com/handbook/product/#who-to-talk-to-for-what)
-as soon as possible. The product manager will then pull in appropriate GitLab team
-members to further discuss scope, design, and technical considerations. This will
-ensure that that your contribution is aligned with the GitLab product and minimize
-any rework and delay in getting it merged into master.
+### Label for community contributors
-GitLab team members who apply the ~"Accepting Merge Requests" label to an issue
-should update the issue description with a responsible product manager, inviting
-any potential community contributor to @-mention per above.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened
-[firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1
## Implement design & UI elements
-For guidance on UX implementation at GitLab, please refer to our [Design System](https://design.gitlab.com/).
-
-The UX team uses labels to manage their workflow.
-
-The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
-To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook.
-
-Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue.
-
-The UX team has a special type label called ~"design artifact". This label indicates that the final output
-for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone.
-Any issue labeled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is
-needed until the solution has been decided.
+This [documentation](doc/development/contributing/design.md) has been moved.
-~"design artifact" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone.
-
-To prevent the misunderstanding that a feature will be be delivered in the
-assigned milestone, when only UX design is planned for that milestone, the
-Product Manager should create a separate issue for the ~"design artifact",
-assign the ~UX, ~"design artifact" and ~"Deliverable" labels, add a milestone
-and use a title that makes it clear that the scheduled issue is design only
-(e.g. `Design exploration for XYZ`).
-
-When the ~"design artifact" issue has been completed, the UXer removes the ~UX
-label, adds the ~"UX ready" label and closes the issue. This indicates the
-design artifact is complete. The UXer will also copy the designs to related
-issues for implementation in an upcoming milestone.
## Issue tracker
-To get support for your particular problem please use the
-[getting help channels](https://about.gitlab.com/getting-help/).
-
-The [GitLab CE issue tracker on GitLab.com][ce-tracker] is for bugs concerning
-the latest GitLab release and [feature proposals](#feature-proposals).
-
-When submitting an issue please conform to the issue submission guidelines
-listed below. Not all issues will be addressed and your issue is more likely to
-be addressed if you submit a merge request which partially or fully solves
-the issue.
-
-If you're unsure where to post, post to the [mailing list][google-group] or
-[Stack Overflow][stackoverflow] first. There are a lot of helpful GitLab users
-there who may be able to help you quickly. If your particular issue turns out
-to be a bug, it will find its way from there.
-
-If it happens that you know the solution to an existing bug, please first
-open the issue in order to keep track of it and then open the relevant merge
-request that potentially fixes it.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
### Issue triaging
-Our issue triage policies are [described in our handbook]. You are very welcome
-to help the GitLab team triage issues. We also organize [issue bash events] once
-every quarter.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-The most important thing is making sure valid issues receive feedback from the
-development team. Therefore the priority is mentioning developers that can help
-on those issues. Please select someone with relevant experience from the
-[GitLab team][team]. If there is nobody mentioned with that expertise look in
-the commit history for the affected files to find someone.
-
-[described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
-[issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
### Feature proposals
-To create a feature proposal for CE, open an issue on the
-[issue tracker of CE][ce-tracker].
-
-For feature proposals for EE, open an issue on the
-[issue tracker of EE][ee-tracker].
-
-In order to help track the feature proposals, we have created a
-[`feature proposal`][fpl] label. For the time being, users that are not members
-of the project cannot add labels. You can instead ask one of the [core team]
-members to add the label ~"feature proposal" to the issue or add the following
-code snippet right after your description in a new line: `~"feature proposal"`.
-
-Please keep feature proposals as small and simple as possible, complex ones
-might be edited to make them small and simple.
-
-Please submit Feature Proposals using the ['Feature Proposal' issue template](.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker.
-
-For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
-be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
-need to ask one of the [core team] members to add the label, if you do not have permissions to do it by yourself.
-
-If you want to create something yourself, consider opening an issue first to
-discuss whether it is interesting to include this in GitLab.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
### Issue tracker guidelines
-**[Search the issue tracker][ce-tracker]** for similar entries before
-submitting your own, there's a good chance somebody else had the same issue or
-feature proposal. Show your support with an award emoji and/or join the
-discussion.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Please submit bugs using the ['Bug' issue template](.gitlab/issue_templates/Bug.md) provided on the issue tracker.
-The text in the parenthesis is there to help you with what to include. Omit it
-when submitting the actual issue. You can copy-paste it and then edit as you
-see fit.
### Issue weight
-Issue weight allows us to get an idea of the amount of work required to solve
-one or multiple issues. This makes it possible to schedule work more accurately.
-
-You are encouraged to set the weight of any issue. Following the guidelines
-below will make it easy to manage this, without unnecessary overhead.
-
-1. Set weight for any issue at the earliest possible convenience
-1. If you don't agree with a set weight, discuss with other developers until
-consensus is reached about the weight
-1. Issue weights are an abstract measurement of complexity of the issue. Do not
-relate issue weight directly to time. This is called [anchoring](https://en.wikipedia.org/wiki/Anchoring)
-and something you want to avoid.
-1. Something that has a weight of 1 (or no weight) is really small and simple.
-Something that is 9 is rewriting a large fundamental part of GitLab,
-which might lead to many hard problems to solve. Changing some text in GitLab
-is probably 1, adding a new Git Hook maybe 4 or 5, big features 7-9.
-1. If something is very large, it should probably be split up in multiple
-issues or chunks. You can simply not set the weight of a parent issue and set
-weights to children issues.
-
-### Regression issues
-
-Every monthly release has a corresponding issue on the CE issue tracker to keep
-track of functionality broken by that release and any fixes that need to be
-included in a patch release (see [8.3 Regressions] as an example).
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-As outlined in the issue description, the intended workflow is to post one note
-with a reference to an issue describing the regression, and then to update that
-note with a reference to the merge request that fixes it as it becomes available.
-If you're a contributor who doesn't have the required permissions to update
-other users' notes, please post a new note with a reference to both the issue
-and the merge request.
+### Regression issues
-The release manager will [update the notes] in the regression issue as fixes are
-addressed.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-[8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-ce/issues/4127
-[update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue
### 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][ce-tracker].
-For user experience improvements, we use the ~"UX debt" label.
-
-These labels should be added to issues that describe things that can be improved,
-shortcuts that have been taken, features that need additional attention, and all
-other things that have been left behind due to high velocity of development.
-For example, code that needs refactoring should use the ~"technical debt" label,
-user experience refinements should use the ~"UX debt" label.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Everyone can create an issue, though you may need to ask for adding a specific
-label, if you do not have permissions to do it by yourself. Additional labels
-can be combined with these labels, to make it easier to schedule
-the improvements for a release.
-
-Issues tagged with these labels have the same priority like issues
-that describe a new feature to be introduced in GitLab, and should be scheduled
-for a release by the appropriate person.
-
-Make sure to mention the merge request that the ~"technical debt" issue or
-~"UX debt" issue is associated with in the description of the issue.
### Stewardship
-For issues related to the open source stewardship of GitLab,
-there is the ~"stewardship" label.
-
-This label is to be used for issues in which the stewardship of GitLab
-is a topic of discussion. For instance if GitLab Inc. is planning to add
-features from GitLab EE to GitLab CE, related issues would be labelled with
-~"stewardship".
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-A recent example of this was the issue for
-[bringing the time tracking API to GitLab CE][time-tracking-issue].
-
-[time-tracking-issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/25517#note_20019084
## Merge requests
-We welcome merge requests with fixes and improvements to GitLab code, tests,
-and/or documentation. The issues that are specifically suitable for
-community contributions are listed with the label
-[`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce]
-and [EE][accepting-mrs-ee], but you are free to contribute to any other issue
-you want.
-
-Please note that if an issue is marked for the current milestone either before
-or while you are working on it, a team member may take over the merge request
-in order to ensure the work is finished before the release date.
+This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
-If you want to add a new feature that is not labeled it is best to first create
-a feedback issue (if there isn't one already) and leave a comment asking for it
-to be marked as `Accepting Merge Requests`. Please include screenshots or
-wireframes if the feature will also change the UI.
-
-Merge requests should be opened at [GitLab.com][gitlab-mr-tracker].
-
-If you are new to GitLab development (or web development in general), see the
-[I want to contribute!](#i-want-to-contribute) section to get you started with
-some potentially easy issues.
-
-To start with GitLab development download the [GitLab Development Kit][gdk] and
-see the [Development section](doc/development/README.md) for some guidelines.
### Merge request guidelines
-If you can, please submit a merge request with the fix or improvements
-including tests. If you don't know how to fix the issue but can write a test
-that exposes the issue we will accept that as well. In general bug fixes that
-include a regression test are merged quickly while new features without proper
-tests are least likely to receive timely feedback. The workflow to make a merge
-request is as follows:
-
-1. Fork the project into your personal space on GitLab.com
-1. Create a feature branch, branch away from `master`
-1. Write [tests](https://docs.gitlab.com/ee/development/rake_tasks.html#run-tests) and code
-1. [Generate a changelog entry with `bin/changelog`][changelog]
-1. If you are writing documentation, make sure to follow the
- [documentation guidelines][doc-guidelines]
-1. If you have multiple commits please combine them into a few logically
- organized commits by [squashing them][git-squash]
-1. Push the commit(s) to your fork
-1. Submit a merge request (MR) to the `master` branch
- 1. Your merge request needs at least 1 approval but feel free to require more.
- For instance if you're touching backend and frontend code, it's a good idea
- to require 2 approvals: 1 from a backend maintainer and 1 from a frontend
- maintainer
- 1. You don't have to select any approvers, but you can if you really want
- specific people to approve your merge request
-1. The MR title should describe the change you want to make
-1. The MR description should give a motive for your change and the method you
- used to achieve it.
- 1. If you are contributing code, fill in the template already provided in the
- "Description" field.
- 1. If you are contributing documentation, choose `Documentation` from the
- "Choose a template" menu and fill in the template.
- 1. Mention the issue(s) your merge request solves, using the `Solves #XXX` or
- `Closes #XXX` syntax to auto-close the issue(s) once the merge request will
- be merged.
-1. If you're allowed to, set a relevant milestone and labels
-1. If the MR changes the UI it should include *Before* and *After* screenshots
-1. If the MR changes CSS classes please include the list of affected pages,
- `grep css-class ./app -R`
-1. Be prepared to answer questions and incorporate feedback even if requests
- for this arrive weeks or months after your MR submission
- 1. If a discussion has been addressed, select the "Resolve discussion" button
- beneath it to mark it resolved.
-1. If your MR touches code that executes shell commands, reads or opens files or
- handles paths to files on disk, make sure it adheres to the
- [shell command guidelines](doc/development/shell_commands.md)
-1. If your code creates new files on disk please read the
- [shared files guidelines](doc/development/shared_files.md).
-1. When writing commit messages please follow
- [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
- [guidelines](http://chris.beams.io/posts/git-commit/).
-1. If your merge request adds one or more migrations, make sure to execute all
- migrations on a fresh database before the MR is reviewed. If the review leads
- to large changes in the MR, do this again once the review is complete.
-1. For more complex migrations, write tests.
-1. Merge requests **must** adhere to the [merge request performance
- guidelines](doc/development/merge_request_performance_guidelines.md).
-1. For tests that use Capybara or PhantomJS, see this [article on how
- to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
-
-Please keep the change in a single MR **as small as possible**. If you want to
-contribute a large feature think very hard what the minimum viable change is.
-Can you split the functionality? Can you only submit the backend/API code? Can
-you start with a very simple UI? Can you do part of the refactor? The increased
-reviewability of small MRs that leads to higher code quality is more important
-to us than having a minimal commit log. The smaller an MR is the more likely it
-is it will be merged (quickly). After that you can send more MRs to enhance it.
-The ['How to get faster PR reviews' document of Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md) also has some great points regarding this.
-
-For examples of feedback on merge requests please look at already
-[closed merge requests][closed-merge-requests]. If you would like quick feedback
-on your merge request feel free to mention someone from the [core team] or one
-of the [Merge request coaches][team].
-Please ensure that your merge request meets the contribution acceptance criteria.
-
-When having your code reviewed and when reviewing merge requests please take the
-[code review guidelines](doc/development/code_review.md) into account.
-
-### Contribution acceptance criteria
-
-1. The change is as small as possible
-1. Include proper tests and make all tests pass (unless it contains a test
- exposing a bug in existing code). Every new class should have corresponding
- unit tests, even if the class is exercised at a higher level, such as a feature test.
-1. If you suspect a failing CI build is unrelated to your contribution, you may
- try and restart the failing CI job or ask a developer to fix the
- aforementioned failing test
-1. Your MR initially contains a single commit (please use `git rebase -i` to
- squash commits)
-1. Your changes can merge without problems (if not please rebase if you're the
- only one working on your feature branch, otherwise, merge `master`)
-1. Does not break any existing functionality
-1. Fixes one specific issue or implements one specific feature (do not combine
- things, send separate merge requests if needed)
-1. Migrations should do only one thing (e.g., either create a table, move data
- to a new table or remove an old table) to aid retrying on failure
-1. Keeps the GitLab code base clean and well structured
-1. Contains functionality we think other users will benefit from too
-1. Doesn't add configuration options or settings options since they complicate
- making and testing future changes
-1. Changes do not adversely degrade performance.
- - Avoid repeated polling of endpoints that require a significant amount of overhead
- - Check for N+1 queries via the SQL log or [`QueryRecorder`](https://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
- - Avoid repeated access of filesystem
-1. If you need polling to support real-time features, please use
- [polling with ETag caching][polling-etag].
-1. Changes after submitting the merge request should be in separate commits
- (no squashing).
-1. It conforms to the [style guides](#style-guides) and the following:
- - If your change touches a line that does not follow the style, modify the
- entire line to follow it. This prevents linting tools from generating warnings.
- - Don't touch neighbouring lines. As an exception, automatic mass
- refactoring modifications may leave style non-compliant.
-1. If the merge request adds any new libraries (gems, JavaScript libraries,
- etc.), they should conform to our [Licensing guidelines][license-finder-doc].
- See the instructions in that document for help if your MR fails the
- "license-finder" test with a "Dependencies that need approval" error.
-1. The merge request meets the [definition of done](#definition-of-done).
+This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
-## Definition of done
-
-If you contribute to GitLab please know that changes involve more than just
-code. We have the following [definition of done][definition-of-done]. Please ensure you support
-the feature you contribute through all of these steps.
-
-1. Description explaining the relevancy (see following item)
-1. Working and clean code that is commented where needed
-1. [Unit, integration, and system tests][testing] that pass on the CI server
-1. Performance/scalability implications have been considered, addressed, and tested
-1. [Documented][doc-guidelines] in the `/doc` directory
-1. [Changelog entry added][changelog], if necessary
-1. Reviewed and any concerns are addressed
-1. Merged by a project maintainer
-1. Added to the release blog article, if relevant
-1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/), if relevant
-1. Community questions answered
-1. Answers to questions radiated (in docs/wiki/support etc.)
-
-If you add a dependency in GitLab (such as an operating system package) please
-consider updating the following and note the applicability of each in your
-merge request:
-
-1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/
-1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md
-1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool
-1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies
-1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit
-1. Test suite https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/prepare_build.sh
-1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab
-
-## Style guides
-
-1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
- Important sections include [Source Code Layout][rss-source] and
- [Naming][rss-naming]. Use:
- - multi-line method chaining style **Option A**: dot `.` on the second line
- - string literal quoting style **Option A**: single quoted by default
-1. [Rails](https://github.com/bbatsov/rails-style-guide)
-1. [Newlines styleguide][newlines-styleguide]
-1. [Testing][testing]
-1. [JavaScript styleguide][js-styleguide]
-1. [SCSS styleguide][scss-styleguide]
-1. [Shell commands](doc/development/shell_commands.md) created by GitLab
- contributors to enhance security
-1. [Database Migrations](doc/development/migration_style_guide.md)
-1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
-1. [Documentation styleguide](https://docs.gitlab.com/ee/development/documentation/styleguide.html)
-1. Interface text should be written subjectively instead of objectively. It
- should be the GitLab core team addressing a person. It should be written in
- present time and never use past tense (has been/was). For example instead
- of _prohibited this user from being saved due to the following errors:_ the
- text should be _sorry, we could not create your account because:_
-1. Code should be written in [US English][us-english]
-
-This is also the style used by linting tools such as
-[RuboCop](https://github.com/bbatsov/rubocop),
-[PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
-## Code of conduct
+### Contribution acceptance criteria
-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.
+This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
-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.
+## Definition of done
-This code of conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community.
+This [documentation](doc/development/contributing/merge_request_workflow.md)) has been moved.
-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/).
+## Style guides
-[core team]: https://about.gitlab.com/core-team/
-[team]: https://about.gitlab.com/team/
-[getting-help]: https://about.gitlab.com/getting-help/
-[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
-[accepting-mrs-weight]: https://gitlab.com/gitlab-org/gitlab-ce/issues?assignee_id=0&label_name[]=Accepting%20Merge%20Requests&sort=weight_asc
-[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
-[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
-[google-group]: https://groups.google.com/forum/#!forum/gitlabhq
-[stackoverflow]: https://stackoverflow.com/questions/tagged/gitlab
-[fpl]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=feature+proposal
-[accepting-mrs-ce]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests
-[accepting-mrs-ee]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Accepting+Merge+Requests
-[gitlab-mr-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests
-[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit
-[git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
-[closed-merge-requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed
-[definition-of-done]: http://guide.agilealliance.org/guide/definition-of-done.html
-[contributor-covenant]: http://contributor-covenant.org
-[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout
-[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
-[changelog]: doc/development/changelog.md "Generate a changelog entry"
-[doc-guidelines]: doc/development/documentation/index.md "Documentation guidelines"
-[js-styleguide]: doc/development/fe_guide/style_guide_js.md "JavaScript styleguide"
-[scss-styleguide]: doc/development/fe_guide/style_guide_scss.md "SCSS styleguide"
-[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
-[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
-[license-finder-doc]: doc/development/licensing.md
-[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
-[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
-[testing]: doc/development/testing_guide/index.md
-[us-english]: https://en.wikipedia.org/wiki/American_English
+This [documentation](doc/development/contributing/design.md) has been moved.
diff --git a/Dangerfile b/Dangerfile
new file mode 100644
index 00000000000..9217610da8b
--- /dev/null
+++ b/Dangerfile
@@ -0,0 +1,7 @@
+danger.import_dangerfile(path: 'danger/metadata')
+danger.import_dangerfile(path: 'danger/changes_size')
+danger.import_dangerfile(path: 'danger/changelog')
+danger.import_dangerfile(path: 'danger/specs')
+danger.import_dangerfile(path: 'danger/gemfile')
+danger.import_dangerfile(path: 'danger/database')
+danger.import_dangerfile(path: 'danger/frozen_string')
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 8b27ad70f93..a38b3bd31b1 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.109.0
+0.117.0
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index f374f6662e9..3eefcb9dd5b 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-0.9.1
+1.0.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index b7f8ee41e69..0e79152459e 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-7.1.4
+8.1.1
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index f77856a6f1a..831446cbd27 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-4.3.1
+5.1.0
diff --git a/Gemfile b/Gemfile
index 82559fa731c..d9066081f74 100644
--- a/Gemfile
+++ b/Gemfile
@@ -47,7 +47,7 @@ gem 'omniauth-google-oauth2', '~> 0.5.3'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-oauth2-generic', '~> 0.2.2'
gem 'omniauth-saml', '~> 1.10'
-gem 'omniauth-shibboleth', '~> 1.2.0'
+gem 'omniauth-shibboleth', '~> 1.3.0'
gem 'omniauth-twitter', '~> 1.4'
gem 'omniauth_crowd', '~> 2.2.0'
gem 'omniauth-authentiq', '~> 0.3.3'
@@ -104,7 +104,7 @@ gem 'hashie-forbidden_attributes'
gem 'kaminari', '~> 1.0'
# HAML
-gem 'hamlit', '~> 2.6.1'
+gem 'hamlit', '~> 2.8.8'
# Files attachments
gem 'carrierwave', '~> 1.2'
@@ -132,7 +132,7 @@ gem 'unf', '~> 0.1.4'
gem 'seed-fu', '~> 2.3.7'
# Markdown and HTML processing
-gem 'html-pipeline', '~> 2.7.1'
+gem 'html-pipeline', '~> 2.8'
gem 'deckar01-task_list', '2.0.0'
gem 'gitlab-markup', '~> 1.6.4'
gem 'redcarpet', '~> 3.4'
@@ -220,6 +220,9 @@ gem 'gemnasium-gitlab-service', '~> 0.2'
# Slack integration
gem 'slack-notifier', '~> 1.5.1'
+# Hangouts Chat integration
+gem 'hangouts-chat', '~> 0.0.5'
+
# Asana integration
gem 'asana', '~> 0.6.0'
@@ -230,7 +233,7 @@ gem 'ruby-fogbugz', '~> 0.2.1'
gem 'kubeclient', '~> 3.1.0'
# Sanitize user input
-gem 'sanitize', '~> 4.6.5'
+gem 'sanitize', '~> 4.6'
gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input
@@ -303,7 +306,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false
# Prometheus
- gem 'prometheus-client-mmap', '~> 0.9.3'
+ gem 'prometheus-client-mmap', '~> 0.9.4'
gem 'raindrops', '~> 0.18'
end
@@ -323,6 +326,7 @@ group :development do
end
group :development, :test do
+ gem 'bootsnap', '~> 1.3'
gem 'bullet', '~> 5.5.0', require: !!ENV['ENABLE_BULLET']
gem 'pry-byebug', '~> 3.4.1', platform: :mri
gem 'pry-rails', '~> 0.3.4'
@@ -351,9 +355,9 @@ group :development, :test do
gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4'
- gem 'gitlab-styles', '~> 2.3', require: false
+ gem 'gitlab-styles', '~> 2.4', require: false
# Pin these dependencies, otherwise a new rule could break the CI pipelines
- gem 'rubocop', '~> 0.52.1'
+ gem 'rubocop', '~> 0.54.0'
gem 'rubocop-rspec', '~> 1.22.1'
gem 'scss_lint', '~> 0.56.0', require: false
@@ -396,6 +400,7 @@ gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
gem 'ruby-prof', '~> 0.17.0'
+gem 'rbtrace', '~> 0.4', require: false
# OAuth
gem 'oauth2', '~> 1.4'
@@ -418,7 +423,7 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.103.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 0.112.0', require: 'gitaly'
gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed
diff --git a/Gemfile.lock b/Gemfile.lock
index 79e3888fa64..ee8eed07ed7 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -76,7 +76,7 @@ GEM
babosa (1.0.2)
base32 (0.3.2)
batch-loader (1.2.1)
- bcrypt (3.1.11)
+ bcrypt (3.1.12)
bcrypt_pbkdf (1.0.0)
benchmark-ips (2.3.0)
better_errors (2.1.1)
@@ -87,6 +87,8 @@ GEM
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
blankslate (2.1.2.4)
+ bootsnap (1.3.1)
+ msgpack (~> 1.0)
bootstrap_form (2.7.0)
brakeman (4.2.1)
browser (2.2.0)
@@ -108,7 +110,7 @@ GEM
capybara-screenshot (1.0.14)
capybara (>= 1.0, < 3)
launchy
- carrierwave (1.2.1)
+ carrierwave (1.2.3)
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
@@ -121,7 +123,7 @@ GEM
numerizer (~> 0.1.1)
chunky_png (1.3.5)
citrus (3.0.2)
- coderay (1.1.1)
+ coderay (1.1.2)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
commonmarker (0.17.8)
@@ -282,7 +284,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (0.103.0)
+ gitaly-proto (0.112.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@@ -312,8 +314,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
- gitlab-styles (2.3.2)
- rubocop (~> 0.51)
+ gitlab-styles (2.4.1)
+ rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
gitlab_omniauth-ldap (2.0.4)
@@ -359,7 +361,7 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
- grape-path-helpers (1.0.5)
+ grape-path-helpers (1.0.6)
activesupport (>= 4, < 5.1)
grape (~> 1.0)
rake (~> 12)
@@ -373,7 +375,8 @@ GEM
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
googleauth (>= 0.5.1, < 0.7)
- haml (4.0.7)
+ haml (5.0.4)
+ temple (>= 0.8.0)
tilt
haml_lint (0.26.0)
haml (>= 4.0, < 5.1)
@@ -381,10 +384,11 @@ GEM
rake (>= 10, < 13)
rubocop (>= 0.49.0)
sysexits (~> 1.1)
- hamlit (2.6.1)
- temple (~> 0.7.6)
+ hamlit (2.8.8)
+ temple (>= 0.8.0)
thor
tilt
+ hangouts-chat (0.0.5)
hashdiff (0.3.4)
hashie (3.5.7)
hashie-forbidden_attributes (0.1.1)
@@ -394,7 +398,7 @@ GEM
hipchat (1.5.2)
httparty
mimemagic
- html-pipeline (2.7.1)
+ html-pipeline (2.8.4)
activesupport (>= 2)
nokogiri (>= 1.4)
html2text (0.2.0)
@@ -490,7 +494,7 @@ GEM
memoist (0.16.0)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
- method_source (0.8.2)
+ method_source (0.9.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
@@ -500,6 +504,7 @@ GEM
mini_portile2 (2.3.0)
minitest (5.7.0)
mousetrap-rails (1.4.6)
+ msgpack (1.2.4)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
@@ -510,7 +515,7 @@ GEM
net-ldap (0.16.0)
net-ssh (5.0.1)
netrc (0.11.0)
- nokogiri (1.8.3)
+ nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
@@ -568,7 +573,7 @@ GEM
omniauth-saml (1.10.0)
omniauth (~> 1.3, >= 1.3.2)
ruby-saml (~> 1.7)
- omniauth-shibboleth (1.2.1)
+ omniauth-shibboleth (1.3.0)
omniauth (>= 1.0.0)
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
@@ -630,13 +635,12 @@ GEM
parser
unparser
procto (0.0.3)
- prometheus-client-mmap (0.9.3)
- pry (0.10.4)
+ prometheus-client-mmap (0.9.4)
+ pry (0.11.3)
coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- pry-byebug (3.4.2)
- byebug (~> 9.0)
+ method_source (~> 0.9.0)
+ pry-byebug (3.4.3)
+ byebug (>= 9.0, < 9.1)
pry (~> 0.10)
pry-rails (0.3.5)
pry (>= 0.9.10)
@@ -696,6 +700,10 @@ GEM
ffi (>= 0.5.0, < 2)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
+ rbtrace (0.4.10)
+ ffi (>= 1.0.6)
+ msgpack (>= 0.4.3)
+ trollop (>= 1.16.2)
rdoc (6.0.4)
re2 (1.1.1)
recaptcha (3.0.0)
@@ -736,7 +744,7 @@ GEM
retriable (3.1.1)
rinku (2.0.0)
rotp (2.1.2)
- rouge (3.1.1)
+ rouge (3.2.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
@@ -776,16 +784,16 @@ GEM
pg
rails
sqlite3
- rubocop (0.52.1)
+ rubocop (0.54.0)
parallel (~> 1.10)
- parser (>= 2.4.0.2, < 3.0)
+ parser (>= 2.5)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
- rubocop-rspec (1.22.1)
+ rubocop-rspec (1.22.2)
rubocop (>= 0.52.1)
ruby-enum (0.7.2)
i18n
@@ -804,7 +812,7 @@ GEM
et-orbi (~> 1.0)
rugged (0.27.2)
safe_yaml (1.0.4)
- sanitize (4.6.5)
+ sanitize (4.6.6)
crass (~> 1.0.2)
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4)
@@ -863,7 +871,6 @@ GEM
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
slack-notifier (1.5.1)
- slop (3.6.0)
spring (2.0.1)
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
@@ -889,7 +896,7 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
- temple (0.7.7)
+ temple (0.8.0)
test-prof (0.2.5)
test_after_commit (1.1.0)
activerecord (>= 3.2)
@@ -900,13 +907,14 @@ GEM
rack (>= 1, < 3)
thor (0.19.4)
thread_safe (0.3.6)
- tilt (2.0.6)
+ tilt (2.0.8)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
toml (0.1.2)
parslet (~> 1.5.0)
toml-rb (1.0.0)
citrus (~> 3.0, > 3.0)
+ trollop (2.1.3)
truncato (0.7.10)
htmlentities (~> 4.3.1)
nokogiri (~> 1.8.0, >= 1.7.0)
@@ -986,6 +994,7 @@ DEPENDENCIES
benchmark-ips (~> 2.3.0)
better_errors (~> 2.1.0)
binding_of_caller (~> 0.7.2)
+ bootsnap (~> 1.3)
bootstrap_form (~> 2.7.0)
brakeman (~> 4.2)
browser (~> 2.2)
@@ -1037,13 +1046,13 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.103.0)
+ gitaly-proto (~> 0.112.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
gitlab-markup (~> 1.6.4)
- gitlab-styles (~> 2.3)
+ gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
google-api-client (~> 0.19.8)
@@ -1057,11 +1066,12 @@ DEPENDENCIES
graphql (~> 1.8.0)
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
- hamlit (~> 2.6.1)
+ hamlit (~> 2.8.8)
+ hangouts-chat (~> 0.0.5)
hashie-forbidden_attributes
health_check (~> 2.6.0)
hipchat (~> 1.5.0)
- html-pipeline (~> 2.7.1)
+ html-pipeline (~> 2.8)
html2text
httparty (~> 0.13.3)
icalendar
@@ -1101,7 +1111,7 @@ DEPENDENCIES
omniauth-kerberos (~> 0.3.0)
omniauth-oauth2-generic (~> 0.2.2)
omniauth-saml (~> 1.10)
- omniauth-shibboleth (~> 1.2.0)
+ omniauth-shibboleth (~> 1.3.0)
omniauth-twitter (~> 1.4)
omniauth_crowd (~> 2.2.0)
org-ruby (~> 0.9.12)
@@ -1114,7 +1124,7 @@ DEPENDENCIES
peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2)
premailer-rails (~> 1.9.7)
- prometheus-client-mmap (~> 0.9.3)
+ prometheus-client-mmap (~> 0.9.4)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1)
@@ -1127,6 +1137,7 @@ DEPENDENCIES
rainbow (~> 2.2)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
+ rbtrace (~> 0.4)
rdoc (~> 6.0)
re2 (~> 1.1.1)
recaptcha (~> 3.0)
@@ -1143,7 +1154,7 @@ DEPENDENCIES
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
- rubocop (~> 0.52.1)
+ rubocop (~> 0.54.0)
rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.17.0)
@@ -1151,7 +1162,7 @@ DEPENDENCIES
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.27)
- sanitize (~> 4.6.5)
+ sanitize (~> 4.6)
sass-rails (~> 5.0.6)
scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7)
@@ -1194,4 +1205,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
- 1.16.2
+ 1.16.3
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index 3159942b4c5..39305927c0f 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -79,7 +79,7 @@ GEM
babosa (1.0.2)
base32 (0.3.2)
batch-loader (1.2.1)
- bcrypt (3.1.11)
+ bcrypt (3.1.12)
bcrypt_pbkdf (1.0.0)
benchmark-ips (2.3.0)
better_errors (2.1.1)
@@ -90,6 +90,8 @@ GEM
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
blankslate (2.1.2.4)
+ bootsnap (1.3.1)
+ msgpack (~> 1.0)
bootstrap_form (2.7.0)
brakeman (4.2.1)
browser (2.2.0)
@@ -111,7 +113,7 @@ GEM
capybara-screenshot (1.0.14)
capybara (>= 1.0, < 3)
launchy
- carrierwave (1.2.1)
+ carrierwave (1.2.3)
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
@@ -285,7 +287,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (0.103.0)
+ gitaly-proto (0.112.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@@ -315,8 +317,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
- gitlab-styles (2.3.2)
- rubocop (~> 0.51)
+ gitlab-styles (2.4.1)
+ rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
gitlab_omniauth-ldap (2.0.4)
@@ -362,7 +364,7 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
- grape-path-helpers (1.0.5)
+ grape-path-helpers (1.0.6)
activesupport (>= 4, < 5.1)
grape (~> 1.0)
rake (~> 12)
@@ -376,7 +378,8 @@ GEM
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
googleauth (>= 0.5.1, < 0.7)
- haml (4.0.7)
+ haml (5.0.4)
+ temple (>= 0.8.0)
tilt
haml_lint (0.26.0)
haml (>= 4.0, < 5.1)
@@ -384,10 +387,11 @@ GEM
rake (>= 10, < 13)
rubocop (>= 0.49.0)
sysexits (~> 1.1)
- hamlit (2.6.1)
- temple (~> 0.7.6)
+ hamlit (2.8.8)
+ temple (>= 0.8.0)
thor
tilt
+ hangouts-chat (0.0.5)
hashdiff (0.3.4)
hashie (3.5.7)
hashie-forbidden_attributes (0.1.1)
@@ -397,7 +401,7 @@ GEM
hipchat (1.5.2)
httparty
mimemagic
- html-pipeline (2.7.1)
+ html-pipeline (2.8.4)
activesupport (>= 2)
nokogiri (>= 1.4)
html2text (0.2.0)
@@ -503,6 +507,7 @@ GEM
mini_portile2 (2.3.0)
minitest (5.7.0)
mousetrap-rails (1.4.6)
+ msgpack (1.2.4)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
@@ -514,7 +519,7 @@ GEM
net-ssh (5.0.1)
netrc (0.11.0)
nio4r (2.3.1)
- nokogiri (1.8.2)
+ nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
@@ -572,7 +577,7 @@ GEM
omniauth-saml (1.10.0)
omniauth (~> 1.3, >= 1.3.2)
ruby-saml (~> 1.7)
- omniauth-shibboleth (1.2.1)
+ omniauth-shibboleth (1.3.0)
omniauth (>= 1.0.0)
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
@@ -634,7 +639,7 @@ GEM
parser
unparser
procto (0.0.3)
- prometheus-client-mmap (0.9.3)
+ prometheus-client-mmap (0.9.4)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
@@ -705,6 +710,10 @@ GEM
ffi (>= 0.5.0, < 2)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
+ rbtrace (0.4.10)
+ ffi (>= 1.0.6)
+ msgpack (>= 0.4.3)
+ trollop (>= 1.16.2)
rdoc (6.0.4)
re2 (1.1.1)
recaptcha (3.0.0)
@@ -745,7 +754,7 @@ GEM
retriable (3.1.1)
rinku (2.0.0)
rotp (2.1.2)
- rouge (3.1.1)
+ rouge (3.2.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
@@ -785,16 +794,16 @@ GEM
pg
rails
sqlite3
- rubocop (0.52.1)
+ rubocop (0.54.0)
parallel (~> 1.10)
- parser (>= 2.4.0.2, < 3.0)
+ parser (>= 2.5)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
- rubocop-rspec (1.22.1)
+ rubocop-rspec (1.22.2)
rubocop (>= 0.52.1)
ruby-enum (0.7.2)
i18n
@@ -811,9 +820,9 @@ GEM
rubyzip (1.2.1)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
- rugged (0.27.1)
+ rugged (0.27.2)
safe_yaml (1.0.4)
- sanitize (4.6.5)
+ sanitize (4.6.6)
crass (~> 1.0.2)
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4)
@@ -877,7 +886,7 @@ GEM
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
- sprockets (3.7.1)
+ sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
@@ -898,7 +907,7 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
- temple (0.7.7)
+ temple (0.8.0)
test-prof (0.2.5)
text (1.3.1)
thin (1.7.0)
@@ -907,13 +916,14 @@ GEM
rack (>= 1, < 3)
thor (0.19.4)
thread_safe (0.3.6)
- tilt (2.0.6)
+ tilt (2.0.8)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
toml (0.1.2)
parslet (~> 1.5.0)
toml-rb (1.0.0)
citrus (~> 3.0, > 3.0)
+ trollop (2.1.3)
truncato (0.7.10)
htmlentities (~> 4.3.1)
nokogiri (~> 1.8.0, >= 1.7.0)
@@ -996,6 +1006,7 @@ DEPENDENCIES
benchmark-ips (~> 2.3.0)
better_errors (~> 2.1.0)
binding_of_caller (~> 0.7.2)
+ bootsnap (~> 1.3)
bootstrap_form (~> 2.7.0)
brakeman (~> 4.2)
browser (~> 2.2)
@@ -1047,13 +1058,13 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.103.0)
+ gitaly-proto (~> 0.112.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
gitlab-markup (~> 1.6.4)
- gitlab-styles (~> 2.3)
+ gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
google-api-client (~> 0.19.8)
@@ -1067,11 +1078,12 @@ DEPENDENCIES
graphql (~> 1.8.0)
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
- hamlit (~> 2.6.1)
+ hamlit (~> 2.8.8)
+ hangouts-chat (~> 0.0.5)
hashie-forbidden_attributes
health_check (~> 2.6.0)
hipchat (~> 1.5.0)
- html-pipeline (~> 2.7.1)
+ html-pipeline (~> 2.8)
html2text
httparty (~> 0.13.3)
icalendar
@@ -1111,7 +1123,7 @@ DEPENDENCIES
omniauth-kerberos (~> 0.3.0)
omniauth-oauth2-generic (~> 0.2.2)
omniauth-saml (~> 1.10)
- omniauth-shibboleth (~> 1.2.0)
+ omniauth-shibboleth (~> 1.3.0)
omniauth-twitter (~> 1.4)
omniauth_crowd (~> 2.2.0)
org-ruby (~> 0.9.12)
@@ -1124,7 +1136,7 @@ DEPENDENCIES
peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2)
premailer-rails (~> 1.9.7)
- prometheus-client-mmap (~> 0.9.3)
+ prometheus-client-mmap (~> 0.9.4)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1)
@@ -1138,6 +1150,7 @@ DEPENDENCIES
rainbow (~> 2.2)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
+ rbtrace (~> 0.4)
rdoc (~> 6.0)
re2 (~> 1.1.1)
recaptcha (~> 3.0)
@@ -1154,7 +1167,7 @@ DEPENDENCIES
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
- rubocop (~> 0.52.1)
+ rubocop (~> 0.54.0)
rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.17.0)
@@ -1162,7 +1175,7 @@ DEPENDENCIES
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.27)
- sanitize (~> 4.6.5)
+ sanitize (~> 4.6)
sass-rails (~> 5.0.6)
scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7)
diff --git a/README.md b/README.md
index 8bd667b3dac..b6e1cc9a432 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,5 @@
# GitLab
-[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
-[![Overall test coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg)](https://gitlab.com/gitlab-org/gitlab-ce/pipelines)
-[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
-[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
-[![Gitter](https://badges.gitter.im/gitlabhq/gitlabhq.svg)](https://gitter.im/gitlabhq/gitlabhq?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-
## Test coverage
- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby
@@ -35,7 +29,7 @@ We're hiring developers, support people, and production engineers all the time,
There are two editions of GitLab:
- GitLab Community Edition (CE) is available freely under the MIT Expat license.
-- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/products/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/products/).
+- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/pricing/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
## Website
@@ -120,6 +114,10 @@ All documentation can be found on [docs.gitlab.com/ce/](https://docs.gitlab.com/
Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on our website for the many options to get help.
+## Why?
+
+[Read here](https://about.gitlab.com/why/)
+
## Is it any good?
[Yes](https://news.ycombinator.com/item?id=3067434)
diff --git a/Rakefile b/Rakefile
index 85fff2d51eb..de0d6695c7b 100755
--- a/Rakefile
+++ b/Rakefile
@@ -2,9 +2,9 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
-require File.expand_path('../config/application', __FILE__)
+require File.expand_path('config/application', __dir__)
-relative_url_conf = File.expand_path('../config/initializers/relative_url', __FILE__)
+relative_url_conf = File.expand_path('config/initializers/relative_url', __dir__)
require relative_url_conf if File.exist?("#{relative_url_conf}.rb")
Gitlab::Application.load_tasks
diff --git a/VERSION b/VERSION
index 0116f5d2c81..53906da50f8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-11.1.0-pre
+11.2.0-pre
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index d62fae99c6b..25fe2ae553e 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -100,12 +100,12 @@ const Api = {
},
// Return Merge Request for project
- mergeRequest(projectPath, mergeRequestId) {
+ mergeRequest(projectPath, mergeRequestId, params = {}) {
const url = Api.buildUrl(Api.mergeRequestPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId);
- return axios.get(url);
+ return axios.get(url, { params });
},
mergeRequests(params = {}) {
@@ -150,14 +150,15 @@ const Api = {
},
// Return group projects list. Filtered by query
- groupProjects(groupId, query, callback) {
+ groupProjects(groupId, query, options, callback) {
const url = Api.buildUrl(Api.groupProjectsPath).replace(':id', groupId);
+ const defaults = {
+ search: query,
+ per_page: 20,
+ };
return axios
.get(url, {
- params: {
- search: query,
- per_page: 20,
- },
+ params: Object.assign({}, defaults, options),
})
.then(({ data }) => callback(data));
},
@@ -243,6 +244,18 @@ const Api = {
});
},
+ branches(id, query = '', options = {}) {
+ const url = Api.buildUrl(this.createBranchPath).replace(':id', encodeURIComponent(id));
+
+ return axios.get(url, {
+ params: {
+ search: query,
+ per_page: 20,
+ ...options,
+ },
+ });
+ },
+
createBranch(id, { ref, branch }) {
const url = Api.buildUrl(this.createBranchPath).replace(':id', encodeURIComponent(id));
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js
index fa00a3cf386..e8c59fab609 100644
--- a/app/assets/javascripts/autosave.js
+++ b/app/assets/javascripts/autosave.js
@@ -53,4 +53,8 @@ export default class Autosave {
return window.localStorage.removeItem(this.key);
}
+
+ dispose() {
+ this.field.off('input');
+ }
}
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 70f20c5c7cf..e34db893989 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -33,19 +33,24 @@ const categoryLabelMap = {
const IS_VISIBLE = 'is-visible';
const IS_RENDERED = 'is-rendered';
-class AwardsHandler {
+export class AwardsHandler {
constructor(emoji) {
this.emoji = emoji;
this.eventListeners = [];
+ this.toggleButtonSelector = '.js-add-award';
+ this.menuClass = 'js-award-emoji-menu';
+ }
+
+ bindEvents() {
// If the user shows intent let's pre-build the menu
this.registerEventListener(
'one',
$(document),
'mouseenter focus',
- '.js-add-award',
+ this.toggleButtonSelector,
'mouseenter focus',
() => {
- const $menu = $('.emoji-menu');
+ const $menu = $(`.${this.menuClass}`);
if ($menu.length === 0) {
requestAnimationFrame(() => {
this.createEmojiMenu();
@@ -53,7 +58,7 @@ class AwardsHandler {
}
},
);
- this.registerEventListener('on', $(document), 'click', '.js-add-award', e => {
+ this.registerEventListener('on', $(document), 'click', this.toggleButtonSelector, e => {
e.stopPropagation();
e.preventDefault();
this.showEmojiMenu($(e.currentTarget));
@@ -61,15 +66,17 @@ class AwardsHandler {
this.registerEventListener('on', $('html'), 'click', e => {
const $target = $(e.target);
- if (!$target.closest('.emoji-menu').length) {
+ if (!$target.closest(`.${this.menuClass}`).length) {
$('.js-awards-block.current').removeClass('current');
- if ($('.emoji-menu').is(':visible')) {
- $('.js-add-award.is-active').removeClass('is-active');
- this.hideMenuElement($('.emoji-menu'));
+ if ($(`.${this.menuClass}`).is(':visible')) {
+ $(`${this.toggleButtonSelector}.is-active`).removeClass('is-active');
+ this.hideMenuElement($(`.${this.menuClass}`));
}
}
});
- this.registerEventListener('on', $(document), 'click', '.js-emoji-btn', e => {
+
+ const emojiButtonSelector = `.js-awards-block .js-emoji-btn, .${this.menuClass} .js-emoji-btn`;
+ this.registerEventListener('on', $(document), 'click', emojiButtonSelector, e => {
e.preventDefault();
const $target = $(e.currentTarget);
const $glEmojiElement = $target.find('gl-emoji');
@@ -101,7 +108,7 @@ class AwardsHandler {
$addBtn.closest('.js-awards-block').addClass('current');
}
- const $menu = $('.emoji-menu');
+ const $menu = $(`.${this.menuClass}`);
const $thumbsBtn = $menu.find('[data-name="thumbsup"], [data-name="thumbsdown"]').parent();
const $userAuthored = this.isUserAuthored($addBtn);
if ($menu.length) {
@@ -118,7 +125,7 @@ class AwardsHandler {
} else {
$addBtn.addClass('is-loading is-active');
this.createEmojiMenu(() => {
- const $createdMenu = $('.emoji-menu');
+ const $createdMenu = $(`.${this.menuClass}`);
$addBtn.removeClass('is-loading');
this.positionMenu($createdMenu, $addBtn);
return setTimeout(() => {
@@ -156,7 +163,7 @@ class AwardsHandler {
}
const emojiMenuMarkup = `
- <div class="emoji-menu">
+ <div class="emoji-menu ${this.menuClass}">
<input type="text" name="emoji-menu-search" value="" class="js-emoji-menu-search emoji-search search-input form-control" placeholder="Search emoji" />
<div class="emoji-menu-content">
@@ -185,7 +192,7 @@ class AwardsHandler {
// Avoid the jank and render the remaining categories separately
// This will take more time, but makes UI more responsive
- const menu = document.querySelector('.emoji-menu');
+ const menu = document.querySelector(`.${this.menuClass}`);
const emojiContentElement = menu.querySelector('.emoji-menu-content');
const remainingCategories = Object.keys(categoryMap).slice(1);
const allCategoriesAddedPromise = remainingCategories.reduce(
@@ -270,9 +277,9 @@ class AwardsHandler {
if (isInVueNoteablePage() && !isMainAwardsBlock) {
const id = votesBlock.attr('id').replace('note_', '');
- this.hideMenuElement($('.emoji-menu'));
+ this.hideMenuElement($(`.${this.menuClass}`));
- $('.js-add-award.is-active').removeClass('is-active');
+ $(`${this.toggleButtonSelector}.is-active`).removeClass('is-active');
const toggleAwardEvent = new CustomEvent('toggleAward', {
detail: {
awardName: emoji,
@@ -291,9 +298,9 @@ class AwardsHandler {
return typeof callback === 'function' ? callback() : undefined;
});
- this.hideMenuElement($('.emoji-menu'));
+ this.hideMenuElement($(`.${this.menuClass}`));
- return $('.js-add-award.is-active').removeClass('is-active');
+ return $(`${this.toggleButtonSelector}.is-active`).removeClass('is-active');
}
addAwardToEmojiBar(votesBlock, emoji, checkForMutuality) {
@@ -321,7 +328,7 @@ class AwardsHandler {
getVotesBlock() {
if (isInVueNoteablePage()) {
- const $el = $('.js-add-award.is-active').closest('.note.timeline-entry');
+ const $el = $(`${this.toggleButtonSelector}.is-active`).closest('.note.timeline-entry');
if ($el.length) {
return $el;
@@ -458,7 +465,7 @@ class AwardsHandler {
}
createEmoji(votesBlock, emoji) {
- if ($('.emoji-menu').length) {
+ if ($(`.${this.menuClass}`).length) {
this.createAwardButtonForVotesBlock(votesBlock, emoji);
}
this.createEmojiMenu(() => {
@@ -538,7 +545,7 @@ class AwardsHandler {
this.searchEmojis(term);
});
- const $menu = $('.emoji-menu');
+ const $menu = $(`.${this.menuClass}`);
this.registerEventListener('on', $menu, transitionEndEventString, e => {
if (e.target === e.currentTarget) {
// Clear the search
@@ -608,7 +615,7 @@ class AwardsHandler {
this.eventListeners.forEach(entry => {
entry.element.off.call(entry.element, ...entry.args);
});
- $('.emoji-menu').remove();
+ $(`.${this.menuClass}`).remove();
}
}
@@ -616,7 +623,11 @@ let awardsHandlerPromise = null;
export default function loadAwardsHandler(reload = false) {
if (!awardsHandlerPromise || reload) {
awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(
- Emoji => new AwardsHandler(Emoji),
+ Emoji => {
+ const awardsHandler = new AwardsHandler(Emoji);
+ awardsHandler.bindEvents();
+ return awardsHandler;
+ },
);
}
return awardsHandlerPromise;
diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue
index b4bfaee1d85..155c348286c 100644
--- a/app/assets/javascripts/badges/components/badge.vue
+++ b/app/assets/javascripts/badges/components/badge.vue
@@ -93,7 +93,7 @@ export default {
<icon
:size="16"
class="prepend-left-8 append-right-8"
- name="doc_image"
+ name="doc-image"
aria-hidden="true"
/>
</div>
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index a2355d7fd5c..9ad451fa375 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -2,6 +2,9 @@
import Sortable from 'sortablejs';
import Vue from 'vue';
+import { n__ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
+import Tooltip from '~/vue_shared/directives/tooltip';
import AccessorUtilities from '../../lib/utils/accessor';
import boardList from './board_list.vue';
import BoardBlankState from './board_blank_state.vue';
@@ -17,6 +20,10 @@ gl.issueBoards.Board = Vue.extend({
boardList,
'board-delete': gl.issueBoards.BoardDelete,
BoardBlankState,
+ Icon,
+ },
+ directives: {
+ Tooltip,
},
props: {
list: {
@@ -46,6 +53,12 @@ gl.issueBoards.Board = Vue.extend({
filter: Store.filter,
};
},
+ computed: {
+ counterTooltip() {
+ const { issuesSize } = this.list;
+ return `${n__('%d issue', '%d issues', issuesSize)}`;
+ },
+ },
watch: {
filter: {
handler() {
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 5c7565234d8..3e610a4088c 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -112,12 +112,20 @@ export default {
if (e.target) {
const containerEl = e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list');
const toBoardType = containerEl.dataset.boardType;
+ const cloneActions = {
+ label: ['milestone', 'assignee'],
+ assignee: ['milestone', 'label'],
+ milestone: ['label', 'assignee'],
+ };
if (toBoardType) {
const fromBoardType = this.list.type;
+ // For each list we check if the destination list is
+ // a the list were we should clone the issue
+ const shouldClone = Object.entries(cloneActions).some(entry => (
+ fromBoardType === entry[0] && entry[1].includes(toBoardType)));
- if ((fromBoardType === 'assignee' && toBoardType === 'label') ||
- (fromBoardType === 'label' && toBoardType === 'assignee')) {
+ if (shouldClone) {
return 'clone';
}
}
@@ -145,7 +153,8 @@ export default {
});
},
onUpdate: (e) => {
- const sortedArray = this.sortable.toArray().filter(id => id !== '-1');
+ const sortedArray = this.sortable.toArray()
+ .filter(id => id !== '-1');
gl.issueBoards.BoardsStore
.moveIssueInList(this.list, Store.moving.issue, e.oldIndex, e.newIndex, sortedArray);
},
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index ec23b1e7c11..1e3cd43d1f0 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -105,7 +105,7 @@ export default {
</div>
<label
:for="list.id + '-title'"
- class="label-light"
+ class="label-bold"
>
Title
</label>
@@ -115,6 +115,7 @@ export default {
:id="list.id + '-title'"
class="form-control"
type="text"
+ name="issue_title"
autocomplete="off"
/>
<project-select
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 371be109229..a9102743bf9 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -51,6 +51,16 @@ gl.issueBoards.BoardSidebar = Vue.extend({
canRemove() {
return !this.list.preset;
},
+ hasLabels() {
+ return this.issue.labels && this.issue.labels.length;
+ },
+ labelDropdownTitle() {
+ return this.hasLabels ?
+ `${this.issue.labels[0].title} ${this.issue.labels.length - 1}+ more` : 'Label';
+ },
+ selectedLabels() {
+ return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : '';
+ }
},
watch: {
detail: {
diff --git a/app/assets/javascripts/boards/components/modal/footer.vue b/app/assets/javascripts/boards/components/modal/footer.vue
index e0dac6003f1..d4affc8c3de 100644
--- a/app/assets/javascripts/boards/components/modal/footer.vue
+++ b/app/assets/javascripts/boards/components/modal/footer.vue
@@ -28,23 +28,29 @@ export default {
},
},
methods: {
+ buildUpdateRequest(list) {
+ return {
+ add_label_ids: [list.label.id],
+ };
+ },
addIssues() {
const firstListIndex = 1;
const list = this.modal.selectedList || this.state.lists[firstListIndex];
const selectedIssues = ModalStore.getSelectedIssues();
const issueIds = selectedIssues.map(issue => issue.id);
+ const req = this.buildUpdateRequest(list);
// Post the data to the backend
- gl.boardService.bulkUpdate(issueIds, {
- add_label_ids: [list.label.id],
- }).catch(() => {
- Flash(__('Failed to update issues, please try again.'));
+ gl.boardService
+ .bulkUpdate(issueIds, req)
+ .catch(() => {
+ Flash(__('Failed to update issues, please try again.'));
- selectedIssues.forEach((issue) => {
- list.removeIssue(issue);
- list.issuesSize -= 1;
+ selectedIssues.forEach((issue) => {
+ list.removeIssue(issue);
+ list.issuesSize -= 1;
+ });
});
- });
// Add the issues on the frontend
selectedIssues.forEach((issue) => {
diff --git a/app/assets/javascripts/boards/components/modal/list.vue b/app/assets/javascripts/boards/components/modal/list.vue
index 02ac36d7367..a58b5afe970 100644
--- a/app/assets/javascripts/boards/components/modal/list.vue
+++ b/app/assets/javascripts/boards/components/modal/list.vue
@@ -121,8 +121,7 @@
<div
v-if="issuesCount > 0 && issues.length === 0"
class="empty-state add-issues-empty-state-filter text-center">
- <div
- class="svg-content">
+ <div class="svg-content">
<img :src="emptyStateSvg" />
</div>
<div class="text-content">
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index eb335f352d3..ef9844d5562 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -46,7 +46,7 @@ export default {
selectable: true,
data: (term, callback) => {
this.loading = true;
- return Api.groupProjects(this.groupId, term, projects => {
+ return Api.groupProjects(this.groupId, term, {}, projects => {
this.loading = false;
callback(projects);
});
@@ -68,7 +68,7 @@ export default {
<template>
<div>
- <label class="label-light prepend-top-10">
+ <label class="label-bold prepend-top-10">
Project
</label>
<div
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
index 55278626ffc..90d4c710daf 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
@@ -5,7 +5,7 @@
const Store = gl.issueBoards.BoardsStore;
- export default {
+ export default Vue.extend({
props: {
issue: {
type: Object,
@@ -25,19 +25,16 @@
removeIssue() {
const { issue } = this;
const lists = issue.getLists();
- const listLabelIds = lists.map(list => list.label.id);
-
- let labelIds = issue.labels.map(label => label.id).filter(id => !listLabelIds.includes(id));
- if (labelIds.length === 0) {
- labelIds = [''];
- }
+ const req = this.buildPatchRequest(issue, lists);
const data = {
- issue: {
- label_ids: labelIds,
- },
+ issue: this.seedPatchRequest(issue, req),
};
+ if (data.issue.label_ids.length === 0) {
+ data.issue.label_ids = [''];
+ }
+
// Post the remove data
Vue.http.patch(this.updateUrl, data).catch(() => {
Flash(__('Failed to remove issue from board, please try again.'));
@@ -54,8 +51,30 @@
Store.detail.issue = {};
},
+ /**
+ * Build the default patch request.
+ */
+ buildPatchRequest(issue, lists) {
+ const listLabelIds = lists.map(list => list.label.id);
+
+ const labelIds = issue.labels
+ .map(label => label.id)
+ .filter(id => !listLabelIds.includes(id));
+
+ return {
+ label_ids: labelIds,
+ };
+ },
+ /**
+ * Seed the given patch request.
+ *
+ * (This is overridden in EE)
+ */
+ seedPatchRequest(issue, req) {
+ return req;
+ },
},
- };
+ });
</script>
<template>
<div
diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js b/app/assets/javascripts/boards/filters/due_date_filters.js
index 70132dbfa6f..9eaa0cd227d 100644
--- a/app/assets/javascripts/boards/filters/due_date_filters.js
+++ b/app/assets/javascripts/boards/filters/due_date_filters.js
@@ -1,8 +1,7 @@
-/* global dateFormat */
-
import Vue from 'vue';
+import dateFormat from 'dateformat';
-Vue.filter('due-date', (value) => {
+Vue.filter('due-date', value => {
const date = new Date(value);
return dateFormat(date, 'mmm d, yyyy', true);
});
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 200d1923635..bc263cbbfea 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -1,5 +1,3 @@
-/* eslint-disable quote-props, comma-dangle */
-
import $ from 'jquery';
import _ from 'underscore';
import Vue from 'vue';
@@ -47,7 +45,7 @@ export default () => {
gl.IssueBoardsApp = new Vue({
el: $boardApp,
components: {
- 'board': gl.issueBoards.Board,
+ board: gl.issueBoards.Board,
'board-sidebar': gl.issueBoards.BoardSidebar,
BoardAddIssuesModal,
},
@@ -65,11 +63,11 @@ export default () => {
defaultAvatar: $boardApp.dataset.defaultAvatar,
},
computed: {
- detailIssueVisible () {
+ detailIssueVisible() {
return Object.keys(this.detailIssue.issue).length;
},
},
- created () {
+ created() {
gl.boardService = new BoardService({
boardsEndpoint: this.boardsEndpoint,
listsEndpoint: this.listsEndpoint,
@@ -89,15 +87,16 @@ export default () => {
eventHub.$off('clearDetailIssue', this.clearDetailIssue);
sidebarEventHub.$off('toggleSubscription', this.toggleSubscription);
},
- mounted () {
+ mounted() {
this.filterManager = new FilteredSearchBoards(Store.filter, true, Store.cantEdit);
this.filterManager.setup();
Store.disabled = this.disabled;
- gl.boardService.all()
+ gl.boardService
+ .all()
.then(res => res.data)
- .then((data) => {
- data.forEach((board) => {
+ .then(data => {
+ data.forEach(board => {
const list = Store.addList(board, this.defaultAvatar);
if (list.type === 'closed') {
@@ -126,7 +125,7 @@ export default () => {
newIssue.setFetchingState('subscriptions', true);
BoardService.getIssueInfo(sidebarInfoEndpoint)
.then(res => res.data)
- .then((data) => {
+ .then(data => {
newIssue.setFetchingState('subscriptions', false);
newIssue.updateData({
subscribed: data.subscribed,
@@ -159,7 +158,7 @@ export default () => {
Flash(__('An error occurred when toggling the notification subscription'));
});
}
- }
+ },
},
});
@@ -168,77 +167,81 @@ export default () => {
data: {
filters: Store.state.filters,
},
- mounted () {
+ mounted() {
gl.issueBoards.newListDropdownInit();
},
});
- gl.IssueBoardsModalAddBtn = new Vue({
- el: document.getElementById('js-add-issues-btn'),
- mixins: [modalMixin],
- data() {
- return {
- modal: ModalStore.store,
- store: Store.state,
- canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
- };
- },
- computed: {
- disabled() {
- if (!this.store) {
- return true;
- }
- return !this.store.lists.filter(list => !list.preset).length;
+ const issueBoardsModal = document.getElementById('js-add-issues-btn');
+
+ if (issueBoardsModal) {
+ gl.IssueBoardsModalAddBtn = new Vue({
+ el: issueBoardsModal,
+ mixins: [modalMixin],
+ data() {
+ return {
+ modal: ModalStore.store,
+ store: Store.state,
+ canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
+ };
},
- tooltipTitle() {
- if (this.disabled) {
- return 'Please add a list to your board first';
- }
+ computed: {
+ disabled() {
+ if (!this.store) {
+ return true;
+ }
+ return !this.store.lists.filter(list => !list.preset).length;
+ },
+ tooltipTitle() {
+ if (this.disabled) {
+ return 'Please add a list to your board first';
+ }
- return '';
+ return '';
+ },
},
- },
- watch: {
- disabled() {
+ watch: {
+ disabled() {
+ this.updateTooltip();
+ },
+ },
+ mounted() {
this.updateTooltip();
},
- },
- mounted() {
- this.updateTooltip();
- },
- methods: {
- updateTooltip() {
- const $tooltip = $(this.$refs.addIssuesButton);
-
- this.$nextTick(() => {
- if (this.disabled) {
- $tooltip.tooltip();
- } else {
- $tooltip.tooltip('dispose');
+ methods: {
+ updateTooltip() {
+ const $tooltip = $(this.$refs.addIssuesButton);
+
+ this.$nextTick(() => {
+ if (this.disabled) {
+ $tooltip.tooltip();
+ } else {
+ $tooltip.tooltip('dispose');
+ }
+ });
+ },
+ openModal() {
+ if (!this.disabled) {
+ this.toggleModal(true);
}
- });
- },
- openModal() {
- if (!this.disabled) {
- this.toggleModal(true);
- }
+ },
},
- },
- template: `
- <div class="board-extra-actions">
- <button
- class="btn btn-create prepend-left-10"
- type="button"
- data-placement="bottom"
- ref="addIssuesButton"
- :class="{ 'disabled': disabled }"
- :title="tooltipTitle"
- :aria-disabled="disabled"
- v-if="canAdminList"
- @click="openModal">
- Add issues
- </button>
- </div>
- `,
- });
+ template: `
+ <div class="board-extra-actions">
+ <button
+ class="btn btn-create prepend-left-10"
+ type="button"
+ data-placement="bottom"
+ ref="addIssuesButton"
+ :class="{ 'disabled': disabled }"
+ :title="tooltipTitle"
+ :aria-disabled="disabled"
+ v-if="canAdminList"
+ @click="openModal">
+ Add issues
+ </button>
+ </div>
+ `,
+ });
+ }
};
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index b85266b6bc3..c7cfb72067c 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -4,6 +4,7 @@
/* global ListAssignee */
import Vue from 'vue';
+import '~/vue_shared/models/label';
import IssueProject from './project';
class ListIssue {
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index e35f277a865..050cbd8db48 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -7,6 +7,24 @@ import queryData from '../utils/query_data';
const PER_PAGE = 20;
+const TYPES = {
+ backlog: {
+ isPreset: true,
+ isExpandable: true,
+ isBlank: false,
+ },
+ closed: {
+ isPreset: true,
+ isExpandable: true,
+ isBlank: false,
+ },
+ blank: {
+ isPreset: true,
+ isExpandable: false,
+ isBlank: true,
+ },
+};
+
class List {
constructor(obj, defaultAvatar) {
this.id = obj.id;
@@ -14,8 +32,10 @@ class List {
this.position = obj.position;
this.title = obj.title;
this.type = obj.list_type;
- this.preset = ['backlog', 'closed', 'blank'].indexOf(this.type) > -1;
- this.isExpandable = ['backlog', 'closed'].indexOf(this.type) > -1;
+
+ const typeInfo = this.getTypeInfo(this.type);
+ this.preset = !!typeInfo.isPreset;
+ this.isExpandable = !!typeInfo.isExpandable;
this.isExpanded = true;
this.page = 1;
this.loading = true;
@@ -31,7 +51,7 @@ class List {
this.title = this.assignee.name;
}
- if (this.type !== 'blank' && this.id) {
+ if (!typeInfo.isBlank && this.id) {
this.getIssues().catch(() => {
// TODO: handle request error
});
@@ -107,7 +127,7 @@ class List {
return gl.boardService
.getIssuesForList(this.id, data)
.then(res => res.data)
- .then((data) => {
+ .then(data => {
this.loading = false;
this.issuesSize = data.size;
@@ -116,6 +136,8 @@ class List {
}
this.createIssues(data.issues);
+
+ return data;
});
}
@@ -126,18 +148,7 @@ class List {
return gl.boardService
.newIssue(this.id, issue)
.then(res => res.data)
- .then((data) => {
- issue.id = data.id;
- issue.iid = data.iid;
- issue.project = data.project;
- issue.path = data.real_path;
- issue.referencePath = data.reference_path;
-
- if (this.issuesSize > 1) {
- const moveBeforeId = this.issues[1].id;
- gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeId);
- }
- });
+ .then(data => this.onNewIssueResponse(issue, data));
}
createIssues(data) {
@@ -217,6 +228,25 @@ class List {
return !matchesRemove;
});
}
+
+ getTypeInfo (type) {
+ return TYPES[type] || {};
+ }
+
+ onNewIssueResponse (issue, data) {
+ issue.id = data.id;
+ issue.iid = data.iid;
+ issue.project = data.project;
+ issue.path = data.real_path;
+ issue.referencePath = data.reference_path;
+
+ if (this.issuesSize > 1) {
+ const moveBeforeId = this.issues[1].id;
+ gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeId);
+ }
+ }
}
window.List = List;
+
+export default List;
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 333338489bc..957114cf420 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -108,6 +108,16 @@ gl.issueBoards.BoardsStore = {
issue.findAssignee(listTo.assignee)) {
const targetIssue = listTo.findIssue(issue.id);
targetIssue.removeAssignee(listFrom.assignee);
+ } else if (listTo.type === 'milestone') {
+ const currentMilestone = issue.milestone;
+ const currentLists = this.state.lists
+ .filter(list => (list.type === 'milestone' && list.id !== listTo.id))
+ .filter(list => list.issues.some(listIssue => issue.id === listIssue.id));
+
+ issue.removeMilestone(currentMilestone);
+ issue.addMilestone(listTo.milestone);
+ currentLists.forEach(currentList => currentList.removeIssue(issue));
+ listTo.addIssue(issue, listFrom, newIndex);
} else {
// Add to new lists issues if it doesn't already exist
listTo.addIssue(issue, listFrom, newIndex);
@@ -125,11 +135,20 @@ gl.issueBoards.BoardsStore = {
} else if (listTo.type === 'backlog' && listFrom.type === 'assignee') {
issue.removeAssignee(listFrom.assignee);
listFrom.removeIssue(issue);
- } else if ((listTo.type !== 'label' && listFrom.type === 'assignee') ||
- (listTo.type !== 'assignee' && listFrom.type === 'label')) {
+ } else if (listTo.type === 'backlog' && listFrom.type === 'milestone') {
+ issue.removeMilestone(listFrom.milestone);
+ listFrom.removeIssue(issue);
+ } else if (this.shouldRemoveIssue(listFrom, listTo)) {
listFrom.removeIssue(issue);
}
},
+ shouldRemoveIssue(listFrom, listTo) {
+ return (
+ (listTo.type !== 'label' && listFrom.type === 'assignee') ||
+ (listTo.type !== 'assignee' && listFrom.type === 'label') ||
+ (listFrom.type === 'backlog')
+ );
+ },
moveIssueInList (list, issue, oldIndex, newIndex, idArray) {
const beforeId = parseInt(idArray[newIndex - 1], 10) || null;
const afterId = parseInt(idArray[newIndex + 1], 10) || null;
@@ -138,7 +157,7 @@ gl.issueBoards.BoardsStore = {
},
findList (key, val, type = 'label') {
const filteredList = this.state.lists.filter((list) => {
- const byType = type ? (list.type === type) || (list.type === 'assignee') : true;
+ const byType = type ? (list.type === type) || (list.type === 'assignee') || (list.type === 'milestone') : true;
return list[key] === val && byType;
});
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index 8139aa69fc7..0fdf0c7a389 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -6,7 +6,7 @@ import Poll from '../lib/utils/poll';
import initSettingsPanels from '../settings_panels';
import eventHub from './event_hub';
import {
- APPLICATION_INSTALLED,
+ APPLICATION_STATUS,
REQUEST_LOADING,
REQUEST_SUCCESS,
REQUEST_FAILURE,
@@ -162,8 +162,10 @@ export default class Clusters {
if (type === 'password') {
this.tokenField.setAttribute('type', 'text');
+ this.showTokenButton.textContent = s__('ClusterIntegration|Hide');
} else {
this.tokenField.setAttribute('type', 'password');
+ this.showTokenButton.textContent = s__('ClusterIntegration|Show');
}
}
@@ -175,8 +177,8 @@ export default class Clusters {
checkForNewInstalls(prevApplicationMap, newApplicationMap) {
const appTitles = Object.keys(newApplicationMap)
- .filter(appId => newApplicationMap[appId].status === APPLICATION_INSTALLED &&
- prevApplicationMap[appId].status !== APPLICATION_INSTALLED &&
+ .filter(appId => newApplicationMap[appId].status === APPLICATION_STATUS.INSTALLED &&
+ prevApplicationMap[appId].status !== APPLICATION_STATUS.INSTALLED &&
prevApplicationMap[appId].status !== null)
.map(appId => newApplicationMap[appId].title);
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index ec52fdfdf32..651f3b50236 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -4,12 +4,7 @@
import eventHub from '../event_hub';
import loadingButton from '../../vue_shared/components/loading_button.vue';
import {
- APPLICATION_NOT_INSTALLABLE,
- APPLICATION_SCHEDULED,
- APPLICATION_INSTALLABLE,
- APPLICATION_INSTALLING,
- APPLICATION_INSTALLED,
- APPLICATION_ERROR,
+ APPLICATION_STATUS,
REQUEST_LOADING,
REQUEST_SUCCESS,
REQUEST_FAILURE,
@@ -59,49 +54,57 @@
},
},
computed: {
+ isUnknownStatus() {
+ return !this.isKnownStatus && this.status !== null;
+ },
+ isKnownStatus() {
+ return Object.values(APPLICATION_STATUS).includes(this.status);
+ },
rowJsClass() {
return `js-cluster-application-row-${this.id}`;
},
installButtonLoading() {
return !this.status ||
- this.status === APPLICATION_SCHEDULED ||
- this.status === APPLICATION_INSTALLING ||
+ this.status === APPLICATION_STATUS.SCHEDULED ||
+ this.status === APPLICATION_STATUS.INSTALLING ||
this.requestStatus === REQUEST_LOADING;
},
installButtonDisabled() {
- // Avoid the potential for the real-time data to say APPLICATION_INSTALLABLE but
+ // Avoid the potential for the real-time data to say APPLICATION_STATUS.INSTALLABLE but
// we already made a request to install and are just waiting for the real-time
// to sync up.
- return (this.status !== APPLICATION_INSTALLABLE
- && this.status !== APPLICATION_ERROR) ||
+ return ((this.status !== APPLICATION_STATUS.INSTALLABLE
+ && this.status !== APPLICATION_STATUS.ERROR) ||
this.requestStatus === REQUEST_LOADING ||
- this.requestStatus === REQUEST_SUCCESS;
+ this.requestStatus === REQUEST_SUCCESS) && this.isKnownStatus;
},
installButtonLabel() {
let label;
if (
- this.status === APPLICATION_NOT_INSTALLABLE ||
- this.status === APPLICATION_INSTALLABLE ||
- this.status === APPLICATION_ERROR
+ this.status === APPLICATION_STATUS.NOT_INSTALLABLE ||
+ this.status === APPLICATION_STATUS.INSTALLABLE ||
+ this.status === APPLICATION_STATUS.ERROR ||
+ this.isUnknownStatus
) {
label = s__('ClusterIntegration|Install');
- } else if (this.status === APPLICATION_SCHEDULED ||
- this.status === APPLICATION_INSTALLING) {
+ } else if (this.status === APPLICATION_STATUS.SCHEDULED ||
+ this.status === APPLICATION_STATUS.INSTALLING) {
label = s__('ClusterIntegration|Installing');
- } else if (this.status === APPLICATION_INSTALLED) {
+ } else if (this.status === APPLICATION_STATUS.INSTALLED ||
+ this.status === APPLICATION_STATUS.UPDATED) {
label = s__('ClusterIntegration|Installed');
}
return label;
},
showManageButton() {
- return this.manageLink && this.status === APPLICATION_INSTALLED;
+ return this.manageLink && this.status === APPLICATION_STATUS.INSTALLED;
},
manageButtonLabel() {
return s__('ClusterIntegration|Manage');
},
hasError() {
- return this.status === APPLICATION_ERROR ||
+ return this.status === APPLICATION_STATUS.ERROR ||
this.requestStatus === REQUEST_FAILURE;
},
generalErrorDescription() {
@@ -182,7 +185,7 @@
</div>
</div>
<div
- v-if="hasError"
+ v-if="hasError || isUnknownStatus"
class="gl-responsive-table-row-layout"
role="row"
>
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 8ee7279e544..d708a9e595a 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -3,7 +3,7 @@ import _ from 'underscore';
import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
-import { APPLICATION_INSTALLED, INGRESS } from '../constants';
+import { APPLICATION_STATUS, INGRESS } from '../constants';
export default {
components: {
@@ -58,7 +58,7 @@ export default {
return INGRESS;
},
ingressInstalled() {
- return this.applications.ingress.status === APPLICATION_INSTALLED;
+ return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED;
},
ingressExternalIp() {
return this.applications.ingress.externalIp;
@@ -122,7 +122,7 @@ export default {
);
},
jupyterInstalled() {
- return this.applications.jupyter.status === APPLICATION_INSTALLED;
+ return this.applications.jupyter.status === APPLICATION_STATUS.INSTALLED;
},
jupyterHostname() {
return this.applications.jupyter.hostname;
diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js
index 371f71fde44..72fc9355d82 100644
--- a/app/assets/javascripts/clusters/constants.js
+++ b/app/assets/javascripts/clusters/constants.js
@@ -1,10 +1,13 @@
// These need to match what is returned from the server
-export const APPLICATION_NOT_INSTALLABLE = 'not_installable';
-export const APPLICATION_INSTALLABLE = 'installable';
-export const APPLICATION_SCHEDULED = 'scheduled';
-export const APPLICATION_INSTALLING = 'installing';
-export const APPLICATION_INSTALLED = 'installed';
-export const APPLICATION_ERROR = 'errored';
+export const APPLICATION_STATUS = {
+ NOT_INSTALLABLE: 'not_installable',
+ INSTALLABLE: 'installable',
+ SCHEDULED: 'scheduled',
+ INSTALLING: 'installing',
+ INSTALLED: 'installed',
+ UPDATED: 'updated',
+ ERROR: 'errored',
+};
// These are only used client-side
export const REQUEST_LOADING = 'request-loading';
diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js
new file mode 100644
index 00000000000..923c036f5a4
--- /dev/null
+++ b/app/assets/javascripts/commons/gitlab_ui.js
@@ -0,0 +1,4 @@
+import Vue from 'vue';
+import progressBar from '@gitlab-org/gitlab-ui/dist/base/progress_bar';
+
+Vue.component('gl-progress-bar', progressBar);
diff --git a/app/assets/javascripts/commons/index.js b/app/assets/javascripts/commons/index.js
index 0d2fe2925d8..ea945cd3fa5 100644
--- a/app/assets/javascripts/commons/index.js
+++ b/app/assets/javascripts/commons/index.js
@@ -3,4 +3,5 @@ import './polyfills';
import './jquery';
import './bootstrap';
import './vue';
+import './gitlab_ui';
import '../lib/utils/axios_utils';
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index d62d3c23654..589eeee9695 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -14,7 +14,9 @@ import 'core-js/es6/weak-map';
// Browser polyfills
import 'classlist-polyfill';
+import 'formdata-polyfill';
import './polyfills/custom_event';
import './polyfills/element';
import './polyfills/event';
import './polyfills/nodelist';
+import './polyfills/request_idle_callback';
diff --git a/app/assets/javascripts/commons/polyfills/request_idle_callback.js b/app/assets/javascripts/commons/polyfills/request_idle_callback.js
new file mode 100644
index 00000000000..2356569d06e
--- /dev/null
+++ b/app/assets/javascripts/commons/polyfills/request_idle_callback.js
@@ -0,0 +1,17 @@
+window.requestIdleCallback =
+ window.requestIdleCallback ||
+ function requestShim(cb) {
+ const start = Date.now();
+ return setTimeout(() => {
+ cb({
+ didTimeout: false,
+ timeRemaining: () => Math.max(0, 50 - (Date.now() - start)),
+ });
+ }, 1);
+ };
+
+window.cancelIdleCallback =
+ window.cancelIdleCallback ||
+ function cancelShim(id) {
+ clearTimeout(id);
+ };
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js
index 1638e09132b..b0c85c2572e 100644
--- a/app/assets/javascripts/confirm_danger_modal.js
+++ b/app/assets/javascripts/confirm_danger_modal.js
@@ -2,13 +2,16 @@ import $ from 'jquery';
import { rstrip } from './lib/utils/common_utils';
function openConfirmDangerModal($form, text) {
+ const $input = $('.js-confirm-danger-input');
+ $input.val('');
+
$('.js-confirm-text').text(text || '');
- $('.js-confirm-danger-input').val('');
$('#modal-confirm-danger').modal('show');
const confirmTextMatch = $('.js-confirm-danger-match').text();
const $submit = $('.js-confirm-danger-submit');
$submit.disable();
+ $input.focus();
$('.js-confirm-danger-input').off('input').on('input', function handleInput() {
const confirmText = rstrip($(this).val());
diff --git a/app/assets/javascripts/create_item_dropdown.js b/app/assets/javascripts/create_item_dropdown.js
index 42e9e568170..8ef9aa7f529 100644
--- a/app/assets/javascripts/create_item_dropdown.js
+++ b/app/assets/javascripts/create_item_dropdown.js
@@ -12,6 +12,7 @@ export default class CreateItemDropdown {
this.fieldName = options.fieldName;
this.onSelect = options.onSelect || (() => {});
this.getDataOption = options.getData;
+ this.getDataRemote = !!options.filterRemote;
this.createNewItemFromValueOption = options.createNewItemFromValue;
this.$dropdown = options.$dropdown;
this.$dropdownContainer = this.$dropdown.parent();
@@ -29,7 +30,7 @@ export default class CreateItemDropdown {
this.$dropdown.glDropdown({
data: this.getData.bind(this),
filterable: true,
- remote: false,
+ filterRemote: this.getDataRemote,
search: {
fields: ['text'],
},
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index e0aecda54f6..7cc4e6a2c3a 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -41,11 +41,6 @@ export default {
required: true,
},
},
- data() {
- return {
- activeFile: '',
- };
- },
computed: {
...mapState({
isLoading: state => state.diffs.isLoading,
@@ -63,7 +58,8 @@ export default {
plainDiffPath: state => state.diffs.plainDiffPath,
emailPatchPath: state => state.diffs.emailPatchPath,
}),
- ...mapGetters(['isParallelView', 'isNotesFetched']),
+ ...mapGetters('diffs', ['isParallelView']),
+ ...mapGetters(['isNotesFetched']),
targetBranch() {
return {
branchName: this.targetBranchName,
@@ -89,6 +85,9 @@ export default {
}
return __('Show latest version');
},
+ canCurrentUserFork() {
+ return this.currentUser.canFork === true && this.currentUser.canCreateMergeRequest;
+ },
},
watch: {
diffViewType() {
@@ -115,7 +114,7 @@ export default {
this.adjustView();
},
methods: {
- ...mapActions(['setBaseConfig', 'fetchDiffFiles']),
+ ...mapActions('diffs', ['setBaseConfig', 'fetchDiffFiles']),
fetchData() {
this.fetchDiffFiles().catch(() => {
createFlash(__('Something went wrong on our end. Please try again!'));
@@ -125,14 +124,6 @@ export default {
eventHub.$emit('fetchNotesData');
}
},
- setActive(filePath) {
- this.activeFile = filePath;
- },
- unsetActive(filePath) {
- if (this.activeFile === filePath) {
- this.activeFile = '';
- }
- },
adjustView() {
if (this.shouldShow && this.isParallelView) {
window.mrTabs.expandViewContainer();
@@ -145,7 +136,7 @@ export default {
</script>
<template>
- <div v-if="shouldShow">
+ <div v-show="shouldShow">
<div
v-if="isLoading"
class="loading"
@@ -194,7 +185,6 @@ export default {
<changed-files
:diff-files="diffFiles"
- :active-file="activeFile"
/>
<div
@@ -205,9 +195,7 @@ export default {
v-for="file in diffFiles"
:key="file.newPath"
:file="file"
- :current-user="currentUser"
- @setActive="setActive(file.filePath)"
- @unsetActive="unsetActive(file.filePath)"
+ :can-current-user-fork="canCurrentUserFork"
/>
</div>
<no-changes v-else />
diff --git a/app/assets/javascripts/diffs/components/changed_files.vue b/app/assets/javascripts/diffs/components/changed_files.vue
index c5ef9fefc2f..97751db1254 100644
--- a/app/assets/javascripts/diffs/components/changed_files.vue
+++ b/app/assets/javascripts/diffs/components/changed_files.vue
@@ -16,13 +16,6 @@ export default {
ClipboardButton,
},
mixins: [changedFilesMixin],
- props: {
- activeFile: {
- type: String,
- required: false,
- default: '',
- },
- },
data() {
return {
isStuck: false,
@@ -31,7 +24,7 @@ export default {
};
},
computed: {
- ...mapGetters(['isInlineView', 'isParallelView', 'areAllFilesCollapsed']),
+ ...mapGetters('diffs', ['isInlineView', 'isParallelView', 'areAllFilesCollapsed']),
sumAddedLines() {
return this.sumValues('addedLines');
},
@@ -66,11 +59,11 @@ export default {
document.removeEventListener('scroll', this.handleScroll);
},
methods: {
- ...mapActions(['setInlineDiffViewType', 'setParallelDiffViewType', 'expandAllFiles']),
+ ...mapActions('diffs', ['setInlineDiffViewType', 'setParallelDiffViewType', 'expandAllFiles']),
pluralize,
handleScroll() {
if (!this.updating) {
- requestAnimationFrame(this.updateIsStuck);
+ this.$nextTick(this.updateIsStuck);
this.updating = true;
}
},
@@ -148,25 +141,8 @@ export default {
/>
<span
- v-show="activeFile"
- class="prepend-left-5"
- >
- <strong class="prepend-right-5">
- {{ truncatedDiffPath(activeFile) }}
- </strong>
- <clipboard-button
- :text="activeFile"
- :title="s__('Copy file name to clipboard')"
- tooltip-placement="bottom"
- tooltip-container="body"
- class="btn btn-default btn-transparent btn-clipboard"
- />
- </span>
-
- <span
- v-show="!isStuck"
- id="diff-stats"
- class="diff-stats-additions-deletions-expanded"
+ class="js-diff-stats-additions-deletions-expanded
+ diff-stats-additions-deletions-expanded"
>
with
<strong class="cgreen">
@@ -177,6 +153,17 @@ export default {
{{ pluralize(`${sumRemovedLines} deletion`, sumRemovedLines) }}
</strong>
</span>
+ <div
+ class="js-diff-stats-additions-deletions-collapsed
+ diff-stats-additions-deletions-collapsed float-right d-sm-none"
+ >
+ <strong class="cgreen">
+ +{{ sumAddedLines }}
+ </strong>
+ <strong class="cred">
+ -{{ sumRemovedLines }}
+ </strong>
+ </div>
</div>
</div>
</div>
diff --git a/app/assets/javascripts/diffs/components/changed_files_dropdown.vue b/app/assets/javascripts/diffs/components/changed_files_dropdown.vue
index b38d217fbe3..045688a32bf 100644
--- a/app/assets/javascripts/diffs/components/changed_files_dropdown.vue
+++ b/app/assets/javascripts/diffs/components/changed_files_dropdown.vue
@@ -40,7 +40,7 @@ export default {
{{ n__('%d changed file', '%d changed files', diffFiles.length) }}
</span>
<icon
- :size="8"
+ class="caret-icon"
name="chevron-down"
/>
</button>
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 48ba967285f..02d5be1821b 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -22,7 +22,7 @@ export default {
projectPath: state => state.diffs.projectPath,
endpoint: state => state.diffs.endpoint,
}),
- ...mapGetters(['isInlineView', 'isParallelView']),
+ ...mapGetters('diffs', ['isInlineView', 'isParallelView']),
diffMode() {
const diffModeKey = Object.keys(diffModes).find(key => this.diffFile[`${key}File`]);
return diffModes[diffModeKey] || diffModes.replaced;
@@ -44,7 +44,7 @@ export default {
:diff-lines="diffFile.highlightedDiffLines || []"
/>
<parallel-diff-view
- v-else-if="isParallelView"
+ v-if="isParallelView"
:diff-file="diffFile"
:diff-lines="diffFile.parallelDiffLines || []"
/>
diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue
index 39d535036f6..e64d5511d78 100644
--- a/app/assets/javascripts/diffs/components/diff_discussions.vue
+++ b/app/assets/javascripts/diffs/components/diff_discussions.vue
@@ -15,9 +15,7 @@ export default {
</script>
<template>
- <div
- v-if="discussions.length"
- >
+ <div>
<div
v-for="discussion in discussions"
:key="discussion.id"
@@ -32,6 +30,7 @@ export default {
:render-header="false"
:render-diff-file="false"
:always-expanded="true"
+ :discussions-by-diff-order="true"
/>
</ul>
</div>
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 108eefdac5f..7e7058d8d08 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -18,22 +18,18 @@ export default {
type: Object,
required: true,
},
- currentUser: {
- type: Object,
+ canCurrentUserFork: {
+ type: Boolean,
required: true,
},
},
data() {
return {
- isActive: false,
isLoadingCollapsedDiff: false,
forkMessageVisible: false,
};
},
computed: {
- isDiscussionsExpanded() {
- return true; // TODO: @fatihacet - Fix this.
- },
isCollapsed() {
return this.file.collapsed || false;
},
@@ -47,15 +43,12 @@ export default {
false,
);
},
- },
- mounted() {
- document.addEventListener('scroll', this.handleScroll);
- },
- beforeDestroy() {
- document.removeEventListener('scroll', this.handleScroll);
+ showExpandMessage() {
+ return this.isCollapsed && !this.isLoadingCollapsedDiff && !this.file.tooLarge;
+ },
},
methods: {
- ...mapActions(['loadCollapsedDiff']),
+ ...mapActions('diffs', ['loadCollapsedDiff']),
handleToggle() {
const { collapsed, highlightedDiffLines, parallelDiffLines } = this.file;
@@ -65,36 +58,6 @@ export default {
this.file.collapsed = !this.file.collapsed;
}
},
- handleScroll() {
- if (!this.updating) {
- requestAnimationFrame(this.scrollUpdate.bind(this));
- this.updating = true;
- }
- },
- scrollUpdate() {
- const header = document.querySelector('.js-diff-files-changed');
- if (!header) {
- this.updating = false;
- return;
- }
-
- const { top, bottom } = this.$el.getBoundingClientRect();
- const { top: topOfFixedHeader, bottom: bottomOfFixedHeader } = header.getBoundingClientRect();
-
- const headerOverlapsContent = top < topOfFixedHeader && bottom > bottomOfFixedHeader;
- const fullyAboveHeader = bottom < bottomOfFixedHeader;
- const fullyBelowHeader = top > topOfFixedHeader;
-
- if (headerOverlapsContent && !this.isActive) {
- this.$emit('setActive');
- this.isActive = true;
- } else if (this.isActive && (fullyAboveHeader || fullyBelowHeader)) {
- this.$emit('unsetActive');
- this.isActive = false;
- }
-
- this.updating = false;
- },
handleLoadCollapsedDiff() {
this.isLoadingCollapsedDiff = true;
@@ -124,11 +87,10 @@ export default {
class="diff-file file-holder"
>
<diff-file-header
- :current-user="currentUser"
+ :can-current-user-fork="canCurrentUserFork"
:diff-file="file"
:collapsible="true"
:expanded="!isCollapsed"
- :discussions-expanded="isDiscussionsExpanded"
:add-merge-request-buttons="true"
class="js-file-title file-title"
@toggleFile="handleToggle"
@@ -159,7 +121,7 @@ export default {
</div>
<diff-content
- v-show="!isCollapsed"
+ v-if="!isCollapsed"
:class="{ hidden: isCollapsed || file.tooLarge }"
:diff-file="file"
/>
@@ -168,7 +130,7 @@ export default {
class="diff-content loading"
/>
<div
- v-show="isCollapsed && !isLoadingCollapsedDiff && !file.tooLarge"
+ v-if="showExpandMessage"
class="nothing-here-block diff-collapsed"
>
{{ __('This diff is collapsed.') }}
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index fba1d1af7cd..d3ffbe0415a 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -1,7 +1,9 @@
<script>
import _ from 'underscore';
+import { mapActions, mapGetters } from 'vuex';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import Icon from '~/vue_shared/components/icon.vue';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip';
import { truncateSha } from '~/lib/utils/text_utility';
import { __, s__, sprintf } from '~/locale';
@@ -12,6 +14,7 @@ export default {
ClipboardButton,
EditButton,
Icon,
+ FileIcon,
},
directives: {
Tooltip,
@@ -36,13 +39,8 @@ export default {
required: false,
default: true,
},
- discussionsExpanded: {
+ canCurrentUserFork: {
type: Boolean,
- required: false,
- default: true,
- },
- currentUser: {
- type: Object,
required: true,
},
},
@@ -52,6 +50,10 @@ export default {
};
},
computed: {
+ ...mapGetters('diffs', ['diffHasExpandedDiscussions', 'diffHasDiscussions']),
+ hasExpandedDiscussions() {
+ return this.diffHasExpandedDiscussions(this.diffFile);
+ },
icon() {
if (this.diffFile.submodule) {
return 'archive';
@@ -86,9 +88,6 @@ export default {
collapseIcon() {
return this.expanded ? 'chevron-down' : 'chevron-right';
},
- isDiscussionsExpanded() {
- return this.discussionsExpanded && this.expanded;
- },
viewFileButtonText() {
const truncatedContentSha = _.escape(truncateSha(this.diffFile.contentSha));
return sprintf(
@@ -109,9 +108,13 @@ export default {
false,
);
},
+ gfmCopyText() {
+ return `\`${this.diffFile.filePath}\``;
+ },
},
methods: {
- handleToggle(e, checkTarget) {
+ ...mapActions('diffs', ['toggleFileDiscussions']),
+ handleToggleFile(e, checkTarget) {
if (
!checkTarget ||
e.target === this.$refs.header ||
@@ -123,6 +126,9 @@ export default {
showForkMessage() {
this.$emit('showForkMessage');
},
+ handleToggleDiscussions() {
+ this.toggleFileDiscussions(this.diffFile);
+ },
},
};
</script>
@@ -131,7 +137,7 @@ export default {
<div
ref="header"
class="js-file-title file-title file-title-flex-parent"
- @click="handleToggle($event, true)"
+ @click="handleToggleFile($event, true)"
>
<div class="file-header-content">
<icon
@@ -139,18 +145,21 @@ export default {
:name="collapseIcon"
:size="16"
aria-hidden="true"
- class="diff-toggle-caret"
+ class="diff-toggle-caret append-right-5"
@click.stop="handleToggle"
/>
<a
+ v-once
ref="titleWrapper"
:href="titleLink"
+ class="append-right-4"
>
- <i
- :class="`fa-${icon}`"
- class="fa fa-fw"
+ <file-icon
+ :file-name="filePath"
+ :size="18"
aria-hidden="true"
- ></i>
+ css-classes="js-file-icon append-right-5"
+ />
<span v-if="diffFile.renamedFile">
<strong
v-tooltip
@@ -185,6 +194,7 @@ export default {
<clipboard-button
:title="__('Copy file path to clipboard')"
:text="diffFile.filePath"
+ :gfm="gfmCopyText"
css-class="btn-default btn-transparent btn-clipboard"
/>
@@ -211,17 +221,19 @@ export default {
v-if="diffFile.blob && diffFile.blob.readableText"
>
<button
- :class="{ active: isDiscussionsExpanded }"
+ :disabled="!diffHasDiscussions(diffFile)"
+ :class="{ active: hasExpandedDiscussions }"
:title="s__('MergeRequests|Toggle comments for this file')"
- class="btn js-toggle-diff-comments"
+ class="js-btn-vue-toggle-comments btn"
type="button"
+ @click="handleToggleDiscussions"
>
<icon name="comment" />
</button>
<edit-button
v-if="!diffFile.deletedFile"
- :current-user="currentUser"
+ :can-current-user-fork="canCurrentUserFork"
:edit-path="diffFile.editPath"
:can-modify-blob="diffFile.canModifyBlob"
@showForkMessage="showForkMessage"
diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
index a74ea4bfaaf..8ad1ea34245 100644
--- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
@@ -71,13 +71,23 @@ export default {
required: false,
default: false,
},
+ isHover: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ discussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
computed: {
...mapState({
diffViewType: state => state.diffs.diffViewType,
diffFiles: state => state.diffs.diffFiles,
}),
- ...mapGetters(['isLoggedIn', 'discussionsByLineCode']),
+ ...mapGetters(['isLoggedIn']),
lineHref() {
return this.lineCode ? `#${this.lineCode}` : '#';
},
@@ -85,30 +95,26 @@ export default {
return (
this.isLoggedIn &&
this.showCommentButton &&
+ this.isHover &&
!this.isMatchLine &&
!this.isContextLine &&
- !this.hasDiscussions &&
- !this.isMetaLine
+ !this.isMetaLine &&
+ !this.hasDiscussions
);
},
- discussions() {
- return this.discussionsByLineCode[this.lineCode] || [];
- },
hasDiscussions() {
return this.discussions.length > 0;
},
shouldShowAvatarsOnGutter() {
- let render = this.hasDiscussions && this.showCommentButton;
-
if (!this.lineType && this.linePosition === LINE_POSITION_RIGHT) {
- render = false;
+ return false;
}
- return render;
+ return this.showCommentButton && this.hasDiscussions;
},
},
methods: {
- ...mapActions(['loadMoreLines', 'showCommentForm']),
+ ...mapActions('diffs', ['loadMoreLines', 'showCommentForm']),
handleCommentButton() {
this.showCommentForm({ lineCode: this.lineCode });
},
@@ -176,7 +182,7 @@ export default {
v-else
>
<button
- v-show="shouldShowCommentButton"
+ v-if="shouldShowCommentButton"
type="button"
class="add-diff-note js-add-diff-note-button"
title="Add a comment to this line"
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 6943b462e86..cbe4551d06b 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -1,24 +1,20 @@
<script>
-import $ from 'jquery';
import { mapState, mapGetters, mapActions } from 'vuex';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import noteForm from '../../notes/components/note_form.vue';
import { getNoteFormData } from '../store/utils';
-import Autosave from '../../autosave';
-import { DIFF_NOTE_TYPE, NOTE_TYPE } from '../constants';
+import autosave from '../../notes/mixins/autosave';
+import { DIFF_NOTE_TYPE } from '../constants';
export default {
components: {
noteForm,
},
+ mixins: [autosave],
props: {
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
+ diffFileHash: {
+ type: String,
required: true,
},
line: {
@@ -40,48 +36,58 @@ export default {
noteableData: state => state.notes.noteableData,
diffViewType: state => state.diffs.diffViewType,
}),
+ ...mapGetters('diffs', ['getDiffFileByHash']),
...mapGetters(['isLoggedIn', 'noteableType', 'getNoteableData', 'getNotesDataByProp']),
},
mounted() {
if (this.isLoggedIn) {
- const noteableData = this.getNoteableData;
const keys = [
- NOTE_TYPE,
- this.noteableType,
- noteableData.id,
- noteableData.diff_head_sha,
+ this.noteableData.diff_head_sha,
DIFF_NOTE_TYPE,
- noteableData.source_project_id,
+ this.noteableData.source_project_id,
this.line.lineCode,
];
- this.autosave = new Autosave($(this.$refs.noteForm.$refs.textarea), keys);
+ this.initAutoSave(this.noteableData, keys);
}
},
methods: {
- ...mapActions(['cancelCommentForm', 'saveNote', 'fetchDiscussions']),
- handleCancelCommentForm() {
- this.autosave.reset();
+ ...mapActions('diffs', ['cancelCommentForm']),
+ ...mapActions(['saveNote', 'refetchDiscussionById']),
+ handleCancelCommentForm(shouldConfirm, isDirty) {
+ if (shouldConfirm && isDirty) {
+ const msg = s__('Notes|Are you sure you want to cancel creating this comment?');
+
+ // eslint-disable-next-line no-alert
+ if (!window.confirm(msg)) {
+ return;
+ }
+ }
+
this.cancelCommentForm({
lineCode: this.line.lineCode,
});
+ this.$nextTick(() => {
+ this.resetAutoSave();
+ });
},
handleSaveNote(note) {
+ const selectedDiffFile = this.getDiffFileByHash(this.diffFileHash);
const postData = getNoteFormData({
note,
noteableData: this.noteableData,
noteableType: this.noteableType,
noteTargetLine: this.noteTargetLine,
diffViewType: this.diffViewType,
- diffFile: this.diffFile,
+ diffFile: selectedDiffFile,
linePosition: this.position,
});
this.saveNote(postData)
- .then(() => {
+ .then(result => {
const endpoint = this.getNotesDataByProp('discussionsPath');
- this.fetchDiscussions(endpoint)
+ this.refetchDiscussionById({ path: endpoint, discussionId: result.discussion_id })
.then(() => {
this.handleCancelCommentForm();
})
diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue
index 68fe6787f9b..33bc8d9971e 100644
--- a/app/assets/javascripts/diffs/components/diff_table_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue
@@ -10,6 +10,9 @@ import {
NEW_NO_NEW_LINE_TYPE,
LINE_HOVER_CLASS_NAME,
LINE_UNFOLD_CLASS_NAME,
+ INLINE_DIFF_VIEW_TYPE,
+ LINE_POSITION_LEFT,
+ LINE_POSITION_RIGHT,
} from '../constants';
export default {
@@ -21,10 +24,19 @@ export default {
type: Object,
required: true,
},
- diffFile: {
- type: Object,
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
required: true,
},
+ diffViewType: {
+ type: String,
+ required: false,
+ default: INLINE_DIFF_VIEW_TYPE,
+ },
showCommentButton: {
type: Boolean,
required: false,
@@ -55,15 +67,26 @@ export default {
required: false,
default: false,
},
+ discussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
computed: {
- ...mapGetters(['isLoggedIn', 'isInlineView']),
+ ...mapGetters(['isLoggedIn']),
normalizedLine() {
- if (this.isInlineView) {
- return this.line;
+ let normalizedLine;
+
+ if (this.diffViewType === INLINE_DIFF_VIEW_TYPE) {
+ normalizedLine = this.line;
+ } else if (this.linePosition === LINE_POSITION_LEFT) {
+ normalizedLine = this.line.left;
+ } else if (this.linePosition === LINE_POSITION_RIGHT) {
+ normalizedLine = this.line.right;
}
- return this.lineType === OLD_LINE_TYPE ? this.line.left : this.line.right;
+ return normalizedLine;
},
isMatchLine() {
return this.normalizedLine.type === MATCH_LINE_TYPE;
@@ -72,10 +95,10 @@ export default {
return this.normalizedLine.type === CONTEXT_LINE_TYPE;
},
isMetaLine() {
+ const { type } = this.normalizedLine;
+
return (
- this.normalizedLine.type === OLD_NO_NEW_LINE_TYPE ||
- this.normalizedLine.type === NEW_NO_NEW_LINE_TYPE ||
- this.normalizedLine.type === EMPTY_CELL_TYPE
+ type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE
);
},
classNameMap() {
@@ -103,29 +126,23 @@ export default {
<template>
<td
- v-if="isContentLine"
- :class="lineType"
- class="line_content"
- v-html="normalizedLine.richText"
- >
- </td>
- <td
- v-else
:class="classNameMap"
>
<diff-line-gutter-content
- :file-hash="diffFile.fileHash"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line-type="normalizedLine.type"
:line-code="normalizedLine.lineCode"
:line-position="linePosition"
:line-number="lineNumber"
:meta-data="normalizedLine.metaData"
:show-comment-button="showCommentButton"
- :context-lines-path="diffFile.contextLinesPath"
+ :is-hover="isHover"
:is-bottom="isBottom"
:is-match-line="isMatchLine"
:is-context-line="isContentLine"
:is-meta-line="isMetaLine"
+ :discussions="discussions"
/>
</td>
</template>
diff --git a/app/assets/javascripts/diffs/components/diff_table_row.vue b/app/assets/javascripts/diffs/components/diff_table_row.vue
deleted file mode 100644
index 8716fdaf44d..00000000000
--- a/app/assets/javascripts/diffs/components/diff_table_row.vue
+++ /dev/null
@@ -1,191 +0,0 @@
-<script>
-import $ from 'jquery';
-import { mapGetters } from 'vuex';
-import DiffTableCell from './diff_table_cell.vue';
-import {
- NEW_LINE_TYPE,
- OLD_LINE_TYPE,
- CONTEXT_LINE_TYPE,
- CONTEXT_LINE_CLASS_NAME,
- OLD_NO_NEW_LINE_TYPE,
- PARALLEL_DIFF_VIEW_TYPE,
- NEW_NO_NEW_LINE_TYPE,
- LINE_POSITION_LEFT,
- LINE_POSITION_RIGHT,
-} from '../constants';
-
-export default {
- components: {
- DiffTableCell,
- },
- props: {
- diffFile: {
- type: Object,
- required: true,
- },
- line: {
- type: Object,
- required: true,
- },
- isBottom: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return {
- isHover: false,
- isLeftHover: false,
- isRightHover: false,
- };
- },
- computed: {
- ...mapGetters(['isInlineView', 'isParallelView']),
- isContextLine() {
- return this.line.left
- ? this.line.left.type === CONTEXT_LINE_TYPE
- : this.line.type === CONTEXT_LINE_TYPE;
- },
- classNameMap() {
- return {
- [this.line.type]: this.line.type,
- [CONTEXT_LINE_CLASS_NAME]: this.isContextLine,
- [PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView,
- };
- },
- inlineRowId() {
- const { lineCode, oldLine, newLine } = this.line;
-
- return lineCode || `${this.diffFile.fileHash}_${oldLine}_${newLine}`;
- },
- parallelViewLeftLineType() {
- if (this.line.right.type === NEW_NO_NEW_LINE_TYPE) {
- return OLD_NO_NEW_LINE_TYPE;
- }
-
- return this.line.left.type;
- },
- },
- created() {
- this.newLineType = NEW_LINE_TYPE;
- this.oldLineType = OLD_LINE_TYPE;
- this.linePositionLeft = LINE_POSITION_LEFT;
- this.linePositionRight = LINE_POSITION_RIGHT;
- },
- methods: {
- handleMouseMove(e) {
- const isHover = e.type === 'mouseover';
-
- if (this.isInlineView) {
- this.isHover = isHover;
- } else {
- const hoveringCell = e.target.closest('td');
- const allCellsInHoveringRow = Array.from(e.currentTarget.children);
- const hoverIndex = allCellsInHoveringRow.indexOf(hoveringCell);
-
- if (hoverIndex >= 2) {
- this.isRightHover = isHover;
- } else {
- this.isLeftHover = isHover;
- }
- }
- },
- // Prevent text selecting on both sides of parallel diff view
- // Backport of the same code from legacy diff notes.
- handleParallelLineMouseDown(e) {
- const line = $(e.currentTarget);
- const table = line.closest('table');
-
- table.removeClass('left-side-selected right-side-selected');
- const [lineClass] = ['left-side', 'right-side'].filter(name => line.hasClass(name));
-
- if (lineClass) {
- table.addClass(`${lineClass}-selected`);
- }
- },
- },
-};
-</script>
-
-<template>
- <tr
- v-if="isInlineView"
- :id="inlineRowId"
- :class="classNameMap"
- class="line_holder"
- @mouseover="handleMouseMove"
- @mouseout="handleMouseMove"
- >
- <diff-table-cell
- :diff-file="diffFile"
- :line="line"
- :line-type="oldLineType"
- :is-bottom="isBottom"
- :is-hover="isHover"
- :show-comment-button="true"
- class="diff-line-num old_line"
- />
- <diff-table-cell
- :diff-file="diffFile"
- :line="line"
- :line-type="newLineType"
- :is-bottom="isBottom"
- :is-hover="isHover"
- class="diff-line-num new_line"
- />
- <diff-table-cell
- :class="line.type"
- :diff-file="diffFile"
- :line="line"
- :is-content-line="true"
- />
- </tr>
-
- <tr
- v-else
- :class="classNameMap"
- class="line_holder"
- @mouseover="handleMouseMove"
- @mouseout="handleMouseMove"
- >
- <diff-table-cell
- :diff-file="diffFile"
- :line="line"
- :line-type="oldLineType"
- :line-position="linePositionLeft"
- :is-bottom="isBottom"
- :is-hover="isLeftHover"
- :show-comment-button="true"
- class="diff-line-num old_line"
- />
- <diff-table-cell
- :id="line.left.lineCode"
- :diff-file="diffFile"
- :line="line"
- :is-content-line="true"
- :line-type="parallelViewLeftLineType"
- class="line_content parallel left-side"
- @mousedown.native="handleParallelLineMouseDown"
- />
- <diff-table-cell
- :diff-file="diffFile"
- :line="line"
- :line-type="newLineType"
- :line-position="linePositionRight"
- :is-bottom="isBottom"
- :is-hover="isRightHover"
- :show-comment-button="true"
- class="diff-line-num new_line"
- />
- <diff-table-cell
- :id="line.right.lineCode"
- :diff-file="diffFile"
- :line="line"
- :is-content-line="true"
- :line-type="line.right.type"
- class="line_content parallel right-side"
- @mousedown.native="handleParallelLineMouseDown"
- />
- </tr>
-</template>
diff --git a/app/assets/javascripts/diffs/components/edit_button.vue b/app/assets/javascripts/diffs/components/edit_button.vue
index ebf90631d76..2fb85ca2f07 100644
--- a/app/assets/javascripts/diffs/components/edit_button.vue
+++ b/app/assets/javascripts/diffs/components/edit_button.vue
@@ -5,8 +5,8 @@ export default {
type: String,
required: true,
},
- currentUser: {
- type: Object,
+ canCurrentUserFork: {
+ type: Boolean,
required: true,
},
canModifyBlob: {
@@ -17,12 +17,12 @@ export default {
},
methods: {
handleEditClick(evt) {
- if (!this.currentUser || this.canModifyBlob) {
+ if (!this.canCurrentUserFork || this.canModifyBlob) {
// if we can Edit, do default Edit button behavior
return;
}
- if (this.currentUser.canFork && this.currentUser.canCreateMergeRequest) {
+ if (this.canCurrentUserFork) {
evt.preventDefault();
this.$emit('showForkMessage');
}
diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
index 0e935f1d68e..caf84dc9573 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
@@ -1,5 +1,5 @@
<script>
-import { mapState, mapGetters } from 'vuex';
+import { mapState } from 'vuex';
import diffDiscussions from './diff_discussions.vue';
import diffLineNoteForm from './diff_line_note_form.vue';
@@ -13,40 +13,24 @@ export default {
type: Object,
required: true,
},
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
+ diffFileHash: {
+ type: String,
required: true,
},
lineIndex: {
type: Number,
required: true,
},
+ discussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
computed: {
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
- ...mapGetters(['discussionsByLineCode']),
- isDiscussionExpanded() {
- if (!this.discussions.length) {
- return false;
- }
-
- return this.discussions.every(discussion => discussion.expanded);
- },
- hasCommentForm() {
- return this.diffLineCommentForms[this.line.lineCode];
- },
- discussions() {
- return this.discussionsByLineCode[this.line.lineCode] || [];
- },
- shouldRender() {
- return this.isDiscussionExpanded || this.hasCommentForm;
- },
className() {
return this.discussions.length ? '' : 'js-temp-notes-holder';
},
@@ -56,7 +40,6 @@ export default {
<template>
<tr
- v-if="shouldRender"
:class="className"
class="notes_holder"
>
@@ -67,14 +50,14 @@ export default {
<td class="notes_content">
<div class="content">
<diff-discussions
+ v-if="discussions.length"
:discussions="discussions"
/>
<diff-line-note-form
v-if="diffLineCommentForms[line.lineCode]"
- :diff-file="diffFile"
- :diff-lines="diffLines"
+ :diff-file-hash="diffFileHash"
:line="line"
- :note-target-line="diffLines[lineIndex]"
+ :note-target-line="line"
/>
</div>
</td>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
new file mode 100644
index 00000000000..32d65ff994f
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -0,0 +1,117 @@
+<script>
+import { mapGetters } from 'vuex';
+import DiffTableCell from './diff_table_cell.vue';
+import {
+ NEW_LINE_TYPE,
+ OLD_LINE_TYPE,
+ CONTEXT_LINE_TYPE,
+ CONTEXT_LINE_CLASS_NAME,
+ PARALLEL_DIFF_VIEW_TYPE,
+ LINE_POSITION_LEFT,
+ LINE_POSITION_RIGHT,
+} from '../constants';
+
+export default {
+ components: {
+ DiffTableCell,
+ },
+ props: {
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
+ required: true,
+ },
+ line: {
+ type: Object,
+ required: true,
+ },
+ isBottom: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ discussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ data() {
+ return {
+ isHover: false,
+ };
+ },
+ computed: {
+ ...mapGetters('diffs', ['isInlineView']),
+ isContextLine() {
+ return this.line.type === CONTEXT_LINE_TYPE;
+ },
+ classNameMap() {
+ return {
+ [this.line.type]: this.line.type,
+ [CONTEXT_LINE_CLASS_NAME]: this.isContextLine,
+ [PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView,
+ };
+ },
+ inlineRowId() {
+ const { lineCode, oldLine, newLine } = this.line;
+
+ return lineCode || `${this.fileHash}_${oldLine}_${newLine}`;
+ },
+ },
+ created() {
+ this.newLineType = NEW_LINE_TYPE;
+ this.oldLineType = OLD_LINE_TYPE;
+ this.linePositionLeft = LINE_POSITION_LEFT;
+ this.linePositionRight = LINE_POSITION_RIGHT;
+ },
+ methods: {
+ handleMouseMove(e) {
+ // To show the comment icon on the gutter we need to know if we hover the line.
+ // Current table structure doesn't allow us to do this with CSS in both of the diff view types
+ this.isHover = e.type === 'mouseover';
+ },
+ },
+};
+</script>
+
+<template>
+ <tr
+ :id="inlineRowId"
+ :class="classNameMap"
+ class="line_holder"
+ @mouseover="handleMouseMove"
+ @mouseout="handleMouseMove"
+ >
+ <diff-table-cell
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
+ :line="line"
+ :line-type="oldLineType"
+ :is-bottom="isBottom"
+ :is-hover="isHover"
+ :show-comment-button="true"
+ :discussions="discussions"
+ class="diff-line-num old_line"
+ />
+ <diff-table-cell
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
+ :line="line"
+ :line-type="newLineType"
+ :is-bottom="isBottom"
+ :is-hover="isHover"
+ :discussions="discussions"
+ class="diff-line-num new_line"
+ />
+ <td
+ :class="line.type"
+ class="line_content"
+ v-html="line.richText"
+ >
+ </td>
+ </tr>
+</template>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index e72f85df77a..e7d789734c3 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -1,12 +1,48 @@
<script>
-import diffContentMixin from '../mixins/diff_content';
+import { mapGetters, mapState } from 'vuex';
+import inlineDiffTableRow from './inline_diff_table_row.vue';
import inlineDiffCommentRow from './inline_diff_comment_row.vue';
+import { trimFirstCharOfLineContent } from '../store/utils';
export default {
components: {
inlineDiffCommentRow,
+ inlineDiffTableRow,
+ },
+ props: {
+ diffFile: {
+ type: Object,
+ required: true,
+ },
+ diffLines: {
+ type: Array,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapGetters('diffs', [
+ 'commitId',
+ 'shouldRenderInlineCommentRow',
+ 'singleDiscussionByLineCode',
+ ]),
+ ...mapState({
+ diffLineCommentForms: state => state.diffs.diffLineCommentForms,
+ }),
+ normalizedDiffLines() {
+ return this.diffLines.map(line => (line.richText ? trimFirstCharOfLineContent(line) : line));
+ },
+ diffLinesLength() {
+ return this.normalizedDiffLines.length;
+ },
+ userColorScheme() {
+ return window.gon.user_color_scheme;
+ },
+ },
+ methods: {
+ discussionsList(line) {
+ return line.lineCode !== undefined ? this.singleDiscussionByLineCode(line.lineCode) : [];
+ },
},
- mixins: [diffContentMixin],
};
</script>
@@ -19,18 +55,21 @@ export default {
<template
v-for="(line, index) in normalizedDiffLines"
>
- <diff-table-row
- :diff-file="diffFile"
+ <inline-diff-table-row
+ :file-hash="diffFile.fileHash"
+ :context-lines-path="diffFile.contextLinesPath"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:key="line.lineCode"
+ :discussions="discussionsList(line)"
/>
<inline-diff-comment-row
- :diff-file="diffFile"
- :diff-lines="normalizedDiffLines"
+ v-if="shouldRenderInlineCommentRow(line)"
+ :diff-file-hash="diffFile.fileHash"
:line="line"
:line-index="index"
:key="index"
+ :discussions="discussionsList(line)"
/>
</template>
</tbody>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
index 5f33ec7a3c2..48b8feeb0b4 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -1,5 +1,5 @@
<script>
-import { mapState, mapGetters } from 'vuex';
+import { mapState } from 'vuex';
import diffDiscussions from './diff_discussions.vue';
import diffLineNoteForm from './diff_line_note_form.vue';
@@ -13,67 +13,61 @@ export default {
type: Object,
required: true,
},
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
+ diffFileHash: {
+ type: String,
required: true,
},
lineIndex: {
type: Number,
required: true,
},
+ leftDiscussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ rightDiscussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
computed: {
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
- ...mapGetters(['discussionsByLineCode']),
leftLineCode() {
return this.line.left.lineCode;
},
rightLineCode() {
return this.line.right.lineCode;
},
- hasDiscussion() {
- const discussions = this.discussionsByLineCode;
-
- return discussions[this.leftLineCode] || discussions[this.rightLineCode];
- },
hasExpandedDiscussionOnLeft() {
- const discussions = this.discussionsByLineCode[this.leftLineCode];
+ const discussions = this.leftDiscussions;
return discussions ? discussions.every(discussion => discussion.expanded) : false;
},
hasExpandedDiscussionOnRight() {
- const discussions = this.discussionsByLineCode[this.rightLineCode];
+ const discussions = this.rightDiscussions;
return discussions ? discussions.every(discussion => discussion.expanded) : false;
},
hasAnyExpandedDiscussion() {
return this.hasExpandedDiscussionOnLeft || this.hasExpandedDiscussionOnRight;
},
- shouldRenderDiscussionsRow() {
- const hasDiscussion = this.hasDiscussion && this.hasAnyExpandedDiscussion;
- const hasCommentFormOnLeft = this.diffLineCommentForms[this.leftLineCode];
- const hasCommentFormOnRight = this.diffLineCommentForms[this.rightLineCode];
-
- return hasDiscussion || hasCommentFormOnLeft || hasCommentFormOnRight;
- },
shouldRenderDiscussionsOnLeft() {
- return this.discussionsByLineCode[this.leftLineCode] && this.hasExpandedDiscussionOnLeft;
+ return this.leftDiscussions && this.hasExpandedDiscussionOnLeft;
},
shouldRenderDiscussionsOnRight() {
- return (
- this.discussionsByLineCode[this.rightLineCode] &&
- this.hasExpandedDiscussionOnRight &&
- this.line.right.type
- );
+ return this.rightDiscussions && this.hasExpandedDiscussionOnRight && this.line.right.type;
+ },
+ showRightSideCommentForm() {
+ return this.line.right.type && this.diffLineCommentForms[this.rightLineCode];
},
className() {
- return this.hasDiscussion ? '' : 'js-temp-notes-holder';
+ return this.leftDiscussions.length > 0 || this.rightDiscussions.length > 0
+ ? ''
+ : 'js-temp-notes-holder';
},
},
};
@@ -81,7 +75,6 @@ export default {
<template>
<tr
- v-if="shouldRenderDiscussionsRow"
:class="className"
class="notes_holder"
>
@@ -92,16 +85,15 @@ export default {
class="content"
>
<diff-discussions
- :discussions="discussionsByLineCode[leftLineCode]"
+ v-if="leftDiscussions.length"
+ :discussions="leftDiscussions"
/>
</div>
<diff-line-note-form
- v-if="diffLineCommentForms[leftLineCode] &&
- diffLineCommentForms[leftLineCode]"
- :diff-file="diffFile"
- :diff-lines="diffLines"
+ v-if="diffLineCommentForms[leftLineCode]"
+ :diff-file-hash="diffFileHash"
:line="line.left"
- :note-target-line="diffLines[lineIndex].left"
+ :note-target-line="line.left"
position="left"
/>
</td>
@@ -112,16 +104,15 @@ export default {
class="content"
>
<diff-discussions
- :discussions="discussionsByLineCode[rightLineCode]"
+ v-if="rightDiscussions.length"
+ :discussions="rightDiscussions"
/>
</div>
<diff-line-note-form
- v-if="diffLineCommentForms[rightLineCode] &&
- diffLineCommentForms[rightLineCode] && line.right.type"
- :diff-file="diffFile"
- :diff-lines="diffLines"
+ v-if="showRightSideCommentForm"
+ :diff-file-hash="diffFileHash"
:line="line.right"
- :note-target-line="diffLines[lineIndex].right"
+ :note-target-line="line.right"
position="right"
/>
</td>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
new file mode 100644
index 00000000000..d4e54c2bd00
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -0,0 +1,162 @@
+<script>
+import $ from 'jquery';
+import { mapGetters } from 'vuex';
+import DiffTableCell from './diff_table_cell.vue';
+import {
+ NEW_LINE_TYPE,
+ OLD_LINE_TYPE,
+ CONTEXT_LINE_TYPE,
+ CONTEXT_LINE_CLASS_NAME,
+ OLD_NO_NEW_LINE_TYPE,
+ PARALLEL_DIFF_VIEW_TYPE,
+ NEW_NO_NEW_LINE_TYPE,
+ LINE_POSITION_LEFT,
+ LINE_POSITION_RIGHT,
+} from '../constants';
+
+export default {
+ components: {
+ DiffTableCell,
+ },
+ props: {
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
+ required: true,
+ },
+ line: {
+ type: Object,
+ required: true,
+ },
+ isBottom: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ leftDiscussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ rightDiscussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ data() {
+ return {
+ isLeftHover: false,
+ isRightHover: false,
+ };
+ },
+ computed: {
+ ...mapGetters('diffs', ['isParallelView']),
+ isContextLine() {
+ return this.line.left.type === CONTEXT_LINE_TYPE;
+ },
+ classNameMap() {
+ return {
+ [CONTEXT_LINE_CLASS_NAME]: this.isContextLine,
+ [PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView,
+ };
+ },
+ parallelViewLeftLineType() {
+ if (this.line.right.type === NEW_NO_NEW_LINE_TYPE) {
+ return OLD_NO_NEW_LINE_TYPE;
+ }
+
+ return this.line.left.type;
+ },
+ },
+ created() {
+ this.newLineType = NEW_LINE_TYPE;
+ this.oldLineType = OLD_LINE_TYPE;
+ this.linePositionLeft = LINE_POSITION_LEFT;
+ this.linePositionRight = LINE_POSITION_RIGHT;
+ this.parallelDiffViewType = PARALLEL_DIFF_VIEW_TYPE;
+ },
+ methods: {
+ handleMouseMove(e) {
+ const isHover = e.type === 'mouseover';
+ const hoveringCell = e.target.closest('td');
+ const allCellsInHoveringRow = Array.from(e.currentTarget.children);
+ const hoverIndex = allCellsInHoveringRow.indexOf(hoveringCell);
+
+ if (hoverIndex >= 2) {
+ this.isRightHover = isHover;
+ } else {
+ this.isLeftHover = isHover;
+ }
+ },
+ // Prevent text selecting on both sides of parallel diff view
+ // Backport of the same code from legacy diff notes.
+ handleParallelLineMouseDown(e) {
+ const line = $(e.currentTarget);
+ const table = line.closest('table');
+
+ table.removeClass('left-side-selected right-side-selected');
+ const [lineClass] = ['left-side', 'right-side'].filter(name => line.hasClass(name));
+
+ if (lineClass) {
+ table.addClass(`${lineClass}-selected`);
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <tr
+ :class="classNameMap"
+ class="line_holder"
+ @mouseover="handleMouseMove"
+ @mouseout="handleMouseMove"
+ >
+ <diff-table-cell
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
+ :line="line"
+ :line-type="oldLineType"
+ :line-position="linePositionLeft"
+ :is-bottom="isBottom"
+ :is-hover="isLeftHover"
+ :show-comment-button="true"
+ :diff-view-type="parallelDiffViewType"
+ :discussions="leftDiscussions"
+ class="diff-line-num old_line"
+ />
+ <td
+ :id="line.left.lineCode"
+ :class="parallelViewLeftLineType"
+ class="line_content parallel left-side"
+ @mousedown.native="handleParallelLineMouseDown"
+ v-html="line.left.richText"
+ >
+ </td>
+ <diff-table-cell
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
+ :line="line"
+ :line-type="newLineType"
+ :line-position="linePositionRight"
+ :is-bottom="isBottom"
+ :is-hover="isRightHover"
+ :show-comment-button="true"
+ :diff-view-type="parallelDiffViewType"
+ :discussions="rightDiscussions"
+ class="diff-line-num new_line"
+ />
+ <td
+ :id="line.right.lineCode"
+ :class="line.right.type"
+ class="line_content parallel right-side"
+ @mousedown.native="handleParallelLineMouseDown"
+ v-html="line.right.richText"
+ >
+ </td>
+ </tr>
+</template>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index ed92b4ee249..24ceb52a04a 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -1,25 +1,65 @@
<script>
-import diffContentMixin from '../mixins/diff_content';
+import { mapState, mapGetters } from 'vuex';
+import parallelDiffTableRow from './parallel_diff_table_row.vue';
import parallelDiffCommentRow from './parallel_diff_comment_row.vue';
import { EMPTY_CELL_TYPE } from '../constants';
+import { trimFirstCharOfLineContent } from '../store/utils';
export default {
components: {
+ parallelDiffTableRow,
parallelDiffCommentRow,
},
- mixins: [diffContentMixin],
+ props: {
+ diffFile: {
+ type: Object,
+ required: true,
+ },
+ diffLines: {
+ type: Array,
+ required: true,
+ },
+ },
computed: {
+ ...mapGetters('diffs', [
+ 'commitId',
+ 'singleDiscussionByLineCode',
+ 'shouldRenderParallelCommentRow',
+ ]),
+ ...mapState({
+ diffLineCommentForms: state => state.diffs.diffLineCommentForms,
+ }),
parallelDiffLines() {
- return this.normalizedDiffLines.map(line => {
- if (!line.left) {
- Object.assign(line, { left: { type: EMPTY_CELL_TYPE } });
- } else if (!line.right) {
- Object.assign(line, { right: { type: EMPTY_CELL_TYPE } });
+ return this.diffLines.map(line => {
+ const parallelLine = Object.assign({}, line);
+
+ if (line.left) {
+ parallelLine.left = trimFirstCharOfLineContent(line.left);
+ } else {
+ parallelLine.left = { type: EMPTY_CELL_TYPE };
+ }
+
+ if (line.right) {
+ parallelLine.right = trimFirstCharOfLineContent(line.right);
+ } else {
+ parallelLine.right = { type: EMPTY_CELL_TYPE };
}
- return line;
+ return parallelLine;
});
},
+ diffLinesLength() {
+ return this.parallelDiffLines.length;
+ },
+ userColorScheme() {
+ return window.gon.user_color_scheme;
+ },
+ },
+ methods: {
+ discussionsByLine(line, leftOrRight) {
+ return line[leftOrRight] && line[leftOrRight].lineCode !== undefined ?
+ this.singleDiscussionByLineCode(line[leftOrRight].lineCode) : [];
+ },
},
};
</script>
@@ -35,18 +75,23 @@ export default {
<template
v-for="(line, index) in parallelDiffLines"
>
- <diff-table-row
- :diff-file="diffFile"
+ <parallel-diff-table-row
+ :file-hash="diffFile.fileHash"
+ :context-lines-path="diffFile.contextLinesPath"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:key="index"
+ :left-discussions="discussionsByLine(line, 'left')"
+ :right-discussions="discussionsByLine(line, 'right')"
/>
<parallel-diff-comment-row
- :key="line.left.lineCode || line.right.lineCode"
+ v-if="shouldRenderParallelCommentRow(line)"
+ :key="`dcr-${index}`"
:line="line"
- :diff-file="diffFile"
- :diff-lines="parallelDiffLines"
+ :diff-file-hash="diffFile.fileHash"
:line-index="index"
+ :left-discussions="discussionsByLine(line, 'left')"
+ :right-discussions="discussionsByLine(line, 'right')"
/>
</template>
</tbody>
diff --git a/app/assets/javascripts/diffs/mixins/diff_content.js b/app/assets/javascripts/diffs/mixins/diff_content.js
deleted file mode 100644
index ebb511d3a7e..00000000000
--- a/app/assets/javascripts/diffs/mixins/diff_content.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { mapGetters } from 'vuex';
-import diffDiscussions from '../components/diff_discussions.vue';
-import diffLineGutterContent from '../components/diff_line_gutter_content.vue';
-import diffLineNoteForm from '../components/diff_line_note_form.vue';
-import diffTableRow from '../components/diff_table_row.vue';
-import { trimFirstCharOfLineContent } from '../store/utils';
-
-export default {
- props: {
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
- required: true,
- },
- },
- components: {
- diffDiscussions,
- diffTableRow,
- diffLineNoteForm,
- diffLineGutterContent,
- },
- computed: {
- ...mapGetters(['commit']),
- commitId() {
- return this.commit && this.commit.id;
- },
- userColorScheme() {
- return window.gon.user_color_scheme;
- },
- normalizedDiffLines() {
- return this.diffLines.map(line => {
- if (line.richText) {
- return trimFirstCharOfLineContent(line);
- }
-
- if (line.left) {
- Object.assign(line, { left: trimFirstCharOfLineContent(line.left) });
- }
-
- if (line.right) {
- Object.assign(line, { right: trimFirstCharOfLineContent(line.right) });
- }
-
- return line;
- });
- },
- diffLinesLength() {
- return this.normalizedDiffLines.length;
- },
- fileHash() {
- return this.diffFile.fileHash;
- },
- },
-};
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 5e0fd5109bb..27001142257 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -82,14 +82,32 @@ export const expandAllFiles = ({ commit }) => {
commit(types.EXPAND_ALL_FILES);
};
-export default {
- setBaseConfig,
- fetchDiffFiles,
- setInlineDiffViewType,
- setParallelDiffViewType,
- showCommentForm,
- cancelCommentForm,
- loadMoreLines,
- loadCollapsedDiff,
- expandAllFiles,
+/**
+ * Toggles the file discussions after user clicked on the toggle discussions button.
+ *
+ * Gets the discussions for the provided diff.
+ *
+ * If all discussions are expanded, it will collapse all of them
+ * If all discussions are collapsed, it will expand all of them
+ * If some discussions are open and others closed, it will expand the closed ones.
+ *
+ * @param {Object} diff
+ */
+export const toggleFileDiscussions = ({ getters, dispatch }, diff) => {
+ const discussions = getters.getDiffFileDiscussions(diff);
+ const shouldCloseAll = getters.diffHasAllExpandedDiscussions(diff);
+ const shouldExpandAll = getters.diffHasAllCollpasedDiscussions(diff);
+
+ discussions.forEach(discussion => {
+ const data = { discussionId: discussion.id };
+
+ if (shouldCloseAll) {
+ dispatch('collapseDiscussion', data, { root: true });
+ } else if (shouldExpandAll || (!shouldCloseAll && !shouldExpandAll && !discussion.expanded)) {
+ dispatch('expandDiscussion', data, { root: true });
+ }
+ });
};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index 66d0f47d102..4a47646d7fa 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -1,16 +1,113 @@
+import _ from 'underscore';
import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '../constants';
-export default {
- isParallelView(state) {
- return state.diffViewType === PARALLEL_DIFF_VIEW_TYPE;
- },
- isInlineView(state) {
- return state.diffViewType === INLINE_DIFF_VIEW_TYPE;
- },
- areAllFilesCollapsed(state) {
- return state.diffFiles.every(file => file.collapsed);
- },
- commit(state) {
- return state.commit;
- },
+export const isParallelView = state => state.diffViewType === PARALLEL_DIFF_VIEW_TYPE;
+
+export const isInlineView = state => state.diffViewType === INLINE_DIFF_VIEW_TYPE;
+
+export const areAllFilesCollapsed = state => state.diffFiles.every(file => file.collapsed);
+
+export const commitId = state => (state.commit && state.commit.id ? state.commit.id : null);
+
+/**
+ * Checks if the diff has all discussions expanded
+ * @param {Object} diff
+ * @returns {Boolean}
+ */
+export const diffHasAllExpandedDiscussions = (state, getters) => diff => {
+ const discussions = getters.getDiffFileDiscussions(diff);
+
+ return (discussions.length && discussions.every(discussion => discussion.expanded)) || false;
+};
+
+/**
+ * Checks if the diff has all discussions collpased
+ * @param {Object} diff
+ * @returns {Boolean}
+ */
+export const diffHasAllCollpasedDiscussions = (state, getters) => diff => {
+ const discussions = getters.getDiffFileDiscussions(diff);
+
+ return (discussions.length && discussions.every(discussion => !discussion.expanded)) || false;
+};
+
+/**
+ * Checks if the diff has any open discussions
+ * @param {Object} diff
+ * @returns {Boolean}
+ */
+export const diffHasExpandedDiscussions = (state, getters) => diff => {
+ const discussions = getters.getDiffFileDiscussions(diff);
+
+ return (
+ (discussions.length && discussions.find(discussion => discussion.expanded) !== undefined) ||
+ false
+ );
};
+
+/**
+ * Checks if the diff has any discussion
+ * @param {Boolean} diff
+ * @returns {Boolean}
+ */
+export const diffHasDiscussions = (state, getters) => diff =>
+ getters.getDiffFileDiscussions(diff).length > 0;
+
+/**
+ * Returns an array with the discussions of the given diff
+ * @param {Object} diff
+ * @returns {Array}
+ */
+export const getDiffFileDiscussions = (state, getters, rootState, rootGetters) => diff =>
+ rootGetters.discussions.filter(
+ discussion =>
+ discussion.diff_discussion && _.isEqual(discussion.diff_file.file_hash, diff.fileHash),
+ ) || [];
+
+export const singleDiscussionByLineCode = (state, getters, rootState, rootGetters) => lineCode => {
+ if (!lineCode || lineCode === undefined) return [];
+ const discussions = rootGetters.discussionsByLineCode;
+ return discussions[lineCode] || [];
+};
+
+export const shouldRenderParallelCommentRow = (state, getters) => line => {
+ const leftLineCode = line.left.lineCode;
+ const rightLineCode = line.right.lineCode;
+ const leftDiscussions = getters.singleDiscussionByLineCode(leftLineCode);
+ const rightDiscussions = getters.singleDiscussionByLineCode(rightLineCode);
+ const hasDiscussion = leftDiscussions.length || rightDiscussions.length;
+
+ const hasExpandedDiscussionOnLeft = leftDiscussions.length
+ ? leftDiscussions.every(discussion => discussion.expanded)
+ : false;
+ const hasExpandedDiscussionOnRight = rightDiscussions.length
+ ? rightDiscussions.every(discussion => discussion.expanded)
+ : false;
+
+ if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) {
+ return true;
+ }
+
+ const hasCommentFormOnLeft = state.diffLineCommentForms[leftLineCode];
+ const hasCommentFormOnRight = state.diffLineCommentForms[rightLineCode];
+
+ return hasCommentFormOnLeft || hasCommentFormOnRight;
+};
+
+export const shouldRenderInlineCommentRow = (state, getters) => line => {
+ if (state.diffLineCommentForms[line.lineCode]) return true;
+
+ const lineDiscussions = getters.singleDiscussionByLineCode(line.lineCode);
+ if (lineDiscussions.length === 0) {
+ return false;
+ }
+
+ return lineDiscussions.every(discussion => discussion.expanded);
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma∂ tests
+export const getDiffFileByHash = state => fileHash =>
+ state.diffFiles.find(file => file.fileHash === fileHash);
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/diffs/store/index.js b/app/assets/javascripts/diffs/store/index.js
deleted file mode 100644
index e6aa8f5b12a..00000000000
--- a/app/assets/javascripts/diffs/store/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
-import diffsModule from './modules';
-
-Vue.use(Vuex);
-
-export default new Vuex.Store({
- modules: {
- diffs: diffsModule,
- },
-});
diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js
new file mode 100644
index 00000000000..39d90a64aab
--- /dev/null
+++ b/app/assets/javascripts/diffs/store/modules/diff_state.js
@@ -0,0 +1,18 @@
+import Cookies from 'js-cookie';
+import { getParameterValues } from '~/lib/utils/url_utility';
+import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME } from '../../constants';
+
+const viewTypeFromQueryString = getParameterValues('view')[0];
+const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME);
+const defaultViewType = INLINE_DIFF_VIEW_TYPE;
+
+export default () => ({
+ isLoading: true,
+ endpoint: '',
+ basePath: '',
+ commit: null,
+ diffFiles: [],
+ mergeRequestDiffs: [],
+ diffLineCommentForms: {},
+ diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
+});
diff --git a/app/assets/javascripts/diffs/store/modules/index.js b/app/assets/javascripts/diffs/store/modules/index.js
index 94caa131506..20d1ebbe049 100644
--- a/app/assets/javascripts/diffs/store/modules/index.js
+++ b/app/assets/javascripts/diffs/store/modules/index.js
@@ -1,25 +1,11 @@
-import Cookies from 'js-cookie';
-import { getParameterValues } from '~/lib/utils/url_utility';
-import actions from '../actions';
-import getters from '../getters';
+import * as actions from '../actions';
+import * as getters from '../getters';
import mutations from '../mutations';
-import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME } from '../../constants';
-
-const viewTypeFromQueryString = getParameterValues('view')[0];
-const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME);
-const defaultViewType = INLINE_DIFF_VIEW_TYPE;
+import createState from './diff_state';
export default {
- state: {
- isLoading: true,
- endpoint: '',
- basePath: '',
- commit: null,
- diffFiles: [],
- mergeRequestDiffs: [],
- diffLineCommentForms: {},
- diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
- },
+ namespaced: true,
+ state: createState(),
getters,
actions,
mutations,
diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js
index 63e9239dce4..2c8e1a1466f 100644
--- a/app/assets/javascripts/diffs/store/mutation_types.js
+++ b/app/assets/javascripts/diffs/store/mutation_types.js
@@ -1,7 +1,6 @@
export const SET_BASE_CONFIG = 'SET_BASE_CONFIG';
export const SET_LOADING = 'SET_LOADING';
export const SET_DIFF_DATA = 'SET_DIFF_DATA';
-export const SET_DIFF_FILES = 'SET_DIFF_FILES';
export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE';
export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS';
export const ADD_COMMENT_FORM_LINE = 'ADD_COMMENT_FORM_LINE';
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 339a33f8b71..a98b2be89a3 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -20,12 +20,6 @@ export default {
});
},
- [types.SET_DIFF_FILES](state, diffFiles) {
- Object.assign(state, {
- diffFiles: convertObjectPropsToCamelCase(diffFiles, { deep: true }),
- });
- },
-
[types.SET_MERGE_REQUEST_DIFFS](state, mergeRequestDiffs) {
Object.assign(state, {
mergeRequestDiffs: convertObjectPropsToCamelCase(mergeRequestDiffs, { deep: true }),
@@ -72,15 +66,10 @@ export default {
},
[types.EXPAND_ALL_FILES](state) {
- const diffFiles = [];
-
- state.diffFiles.forEach(file => {
- diffFiles.push({
- ...file,
- collapsed: false,
- });
- });
-
- Object.assign(state, { diffFiles });
+ // eslint-disable-next-line no-param-reassign
+ state.diffFiles = state.diffFiles.map(file => ({
+ ...file,
+ collapsed: false,
+ }));
},
};
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index da7ae16aaf1..82082ac508a 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -155,18 +155,42 @@ export function addContextLines(options) {
}
}
-export function trimFirstCharOfLineContent(line) {
- if (!line.richText) {
- return line;
+/**
+ * Trims the first char of the `richText` property when it's either a space or a diff symbol.
+ * @param {Object} line
+ * @returns {Object}
+ */
+export function trimFirstCharOfLineContent(line = {}) {
+ const parsedLine = Object.assign({}, line);
+
+ if (line.richText) {
+ const firstChar = parsedLine.richText.charAt(0);
+
+ if (firstChar === ' ' || firstChar === '+' || firstChar === '-') {
+ parsedLine.richText = line.richText.substring(1);
+ }
}
- const firstChar = line.richText.charAt(0);
+ return parsedLine;
+}
- if (firstChar === ' ' || firstChar === '+' || firstChar === '-') {
- Object.assign(line, {
- richText: line.richText.substring(1),
- });
- }
+export function getDiffRefsByLineCode(diffFiles) {
+ return diffFiles.reduce((acc, diffFile) => {
+ const { baseSha, headSha, startSha } = diffFile.diffRefs;
+ const { newPath, oldPath } = diffFile;
+
+ // We can only use highlightedDiffLines to create the map of diff lines because
+ // highlightedDiffLines will also include every parallel diff line in it.
+ if (diffFile.highlightedDiffLines) {
+ diffFile.highlightedDiffLines.forEach(line => {
+ const { lineCode, oldLine, newLine } = line;
+
+ if (lineCode) {
+ acc[lineCode] = { baseSha, headSha, startSha, newPath, oldPath, oldLine, newLine };
+ }
+ });
+ }
- return line;
+ return acc;
+ }, {});
}
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index 4164149dd06..8abd8bc581a 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -1,7 +1,6 @@
-/* global dateFormat */
-
import $ from 'jquery';
import Pikaday from 'pikaday';
+import dateFormat from 'dateformat';
import { __ } from '~/locale';
import axios from './lib/utils/axios_utils';
import { timeFor } from './lib/utils/datetime_utility';
@@ -55,7 +54,7 @@ class DueDateSelect {
format: 'yyyy-mm-dd',
parse: dateString => parsePikadayDate(dateString),
toString: date => pikadayToString(date),
- onSelect: (dateText) => {
+ onSelect: dateText => {
$dueDateInput.val(calendar.toString(dateText));
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
@@ -73,7 +72,7 @@ class DueDateSelect {
}
initRemoveDueDate() {
- this.$block.on('click', '.js-remove-due-date', (e) => {
+ this.$block.on('click', '.js-remove-due-date', e => {
const calendar = this.$datePicker.data('pikaday');
e.preventDefault();
@@ -124,7 +123,8 @@ class DueDateSelect {
this.$loading.fadeOut();
};
- gl.issueBoards.BoardsStore.detail.issue.update(this.$dropdown.attr('data-issue-update'))
+ gl.issueBoards.BoardsStore.detail.issue
+ .update(this.$dropdown.attr('data-issue-update'))
.then(fadeOutLoader)
.catch(fadeOutLoader);
}
@@ -147,17 +147,18 @@ class DueDateSelect {
$('.js-remove-due-date-holder').toggleClass('hidden', selectedDateValue.length);
- return axios.put(this.issueUpdateURL, this.datePayload)
- .then(() => {
- const tooltipText = hasDueDate ? `${__('Due date')}<br />${selectedDateValue} (${timeFor(selectedDateValue)})` : __('Due date');
- if (isDropdown) {
- this.$dropdown.trigger('loaded.gl.dropdown');
- this.$dropdown.dropdown('toggle');
- }
- this.$sidebarCollapsedValue.attr('data-original-title', tooltipText);
+ return axios.put(this.issueUpdateURL, this.datePayload).then(() => {
+ const tooltipText = hasDueDate
+ ? `${__('Due date')}<br />${selectedDateValue} (${timeFor(selectedDateValue)})`
+ : __('Due date');
+ if (isDropdown) {
+ this.$dropdown.trigger('loaded.gl.dropdown');
+ this.$dropdown.dropdown('toggle');
+ }
+ this.$sidebarCollapsedValue.attr('data-original-title', tooltipText);
- return this.$loading.fadeOut();
- });
+ return this.$loading.fadeOut();
+ });
}
}
@@ -170,6 +171,8 @@ export default class DueDateSelectors {
initMilestoneDatePicker() {
$('.datepicker').each(function initPikadayMilestone() {
const $datePicker = $(this);
+ const datePickerVal = $datePicker.val();
+
const calendar = new Pikaday({
field: $datePicker.get(0),
theme: 'gitlab-theme animate-picker',
@@ -182,20 +185,24 @@ export default class DueDateSelectors {
},
});
- calendar.setDate(parsePikadayDate($datePicker.val()));
+ calendar.setDate(parsePikadayDate(datePickerVal));
$datePicker.data('pikaday', calendar);
});
- $('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
+ $('.js-clear-due-date,.js-clear-start-date').on('click', e => {
e.preventDefault();
- const calendar = $(e.target).siblings('.datepicker').data('pikaday');
+ const calendar = $(e.target)
+ .siblings('.datepicker')
+ .data('pikaday');
calendar.setDate(null);
});
}
// eslint-disable-next-line class-methods-use-this
initIssuableSelect() {
- const $loading = $('.js-issuable-update .due_date').find('.block-loading').hide();
+ const $loading = $('.js-issuable-update .due_date')
+ .find('.block-loading')
+ .hide();
$('.js-due-date-select').each((i, dropdown) => {
const $dropdown = $(dropdown);
diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue
index e3652fe739e..63d83e307ee 100644
--- a/app/assets/javascripts/environments/components/environment_actions.vue
+++ b/app/assets/javascripts/environments/components/environment_actions.vue
@@ -1,50 +1,50 @@
<script>
- import Icon from '~/vue_shared/components/icon.vue';
- import eventHub from '../event_hub';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
+import Icon from '~/vue_shared/components/icon.vue';
+import eventHub from '../event_hub';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ loadingIcon,
+ Icon,
+ },
+ props: {
+ actions: {
+ type: Array,
+ required: false,
+ default: () => [],
},
- components: {
- loadingIcon,
- Icon,
+ },
+ data() {
+ return {
+ isLoading: false,
+ };
+ },
+ computed: {
+ title() {
+ return 'Deploy to...';
},
- props: {
- actions: {
- type: Array,
- required: false,
- default: () => [],
- },
- },
- data() {
- return {
- isLoading: false,
- };
- },
- computed: {
- title() {
- return 'Deploy to...';
- },
- },
- methods: {
- onClickAction(endpoint) {
- this.isLoading = true;
+ },
+ methods: {
+ onClickAction(endpoint) {
+ this.isLoading = true;
- eventHub.$emit('postAction', endpoint);
- },
+ eventHub.$emit('postAction', { endpoint });
+ },
- isActionDisabled(action) {
- if (action.playable === undefined) {
- return false;
- }
+ isActionDisabled(action) {
+ if (action.playable === undefined) {
+ return false;
+ }
- return !action.playable;
- },
+ return !action.playable;
},
- };
+ },
+};
</script>
<template>
<div
@@ -61,10 +61,7 @@
data-toggle="dropdown"
>
<span>
- <icon
- :size="12"
- name="play"
- />
+ <icon name="play" />
<i
class="fa fa-caret-down"
aria-hidden="true"
@@ -85,10 +82,6 @@
class="js-manual-action-link no-btn btn"
@click="onClickAction(action.play_path)"
>
- <icon
- :size="12"
- name="play"
- />
<span>
{{ action.name }}
</span>
diff --git a/app/assets/javascripts/environments/components/environment_external_url.vue b/app/assets/javascripts/environments/components/environment_external_url.vue
index 68195225d50..7446196de13 100644
--- a/app/assets/javascripts/environments/components/environment_external_url.vue
+++ b/app/assets/javascripts/environments/components/environment_external_url.vue
@@ -1,30 +1,30 @@
<script>
- import Icon from '~/vue_shared/components/icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
- import { s__ } from '../../locale';
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
+import { s__ } from '../../locale';
- /**
- * Renders the external url link in environments table.
- */
- export default {
- components: {
- Icon,
+/**
+ * Renders the external url link in environments table.
+ */
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ externalUrl: {
+ type: String,
+ required: true,
},
- directives: {
- tooltip,
+ },
+ computed: {
+ title() {
+ return s__('Environments|Open live environment');
},
- props: {
- externalUrl: {
- type: String,
- required: true,
- },
- },
- computed: {
- title() {
- return s__('Environments|Open');
- },
- },
- };
+ },
+};
</script>
<template>
<a
@@ -37,9 +37,6 @@
target="_blank"
rel="noopener noreferrer nofollow"
>
- <icon
- :size="12"
- name="external-link"
- />
+ <icon name="external-link" />
</a>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index 5ecdccf63ad..11e3b781e5a 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -1,429 +1,461 @@
<script>
- import Timeago from 'timeago.js';
- import _ from 'underscore';
- import tooltip from '~/vue_shared/directives/tooltip';
- import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
- import { humanize } from '~/lib/utils/text_utility';
- import ActionsComponent from './environment_actions.vue';
- import ExternalUrlComponent from './environment_external_url.vue';
- import StopComponent from './environment_stop.vue';
- import RollbackComponent from './environment_rollback.vue';
- import TerminalButtonComponent from './environment_terminal_button.vue';
- import MonitoringButtonComponent from './environment_monitoring.vue';
- import CommitComponent from '../../vue_shared/components/commit.vue';
- import eventHub from '../event_hub';
-
- /**
- * Envrionment Item Component
- *
- * Renders a table row for each environment.
- */
- const timeagoInstance = new Timeago();
-
- export default {
- components: {
- UserAvatarLink,
- CommitComponent,
- ActionsComponent,
- ExternalUrlComponent,
- StopComponent,
- RollbackComponent,
- TerminalButtonComponent,
- MonitoringButtonComponent,
- },
-
- directives: {
- tooltip,
- },
-
- props: {
- model: {
- type: Object,
- required: true,
- default: () => ({}),
- },
-
- canCreateDeployment: {
- type: Boolean,
- required: false,
- default: false,
- },
-
- canReadEnvironment: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-
- computed: {
- /**
- * Verifies if `last_deployment` key exists in the current Envrionment.
- * This key is required to render most of the html - this method works has
- * an helper.
- *
- * @returns {Boolean}
- */
- hasLastDeploymentKey() {
- if (this.model &&
- this.model.last_deployment &&
- !_.isEmpty(this.model.last_deployment)) {
- return true;
- }
- return false;
- },
-
- /**
- * Verifies is the given environment has manual actions.
- * Used to verify if we should render them or nor.
- *
- * @returns {Boolean|Undefined}
- */
- hasManualActions() {
- return this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.manual_actions &&
- this.model.last_deployment.manual_actions.length > 0;
- },
-
- /**
- * Returns the value of the `stop_action?` key provided in the response.
- *
- * @returns {Boolean}
- */
- hasStopAction() {
- return this.model && this.model['stop_action?'];
- },
-
- /**
- * Verifies if the `deployable` key is present in `last_deployment` key.
- * Used to verify whether we should or not render the rollback partial.
- *
- * @returns {Boolean|Undefined}
- */
- canRetry() {
- return this.model &&
- this.hasLastDeploymentKey &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable;
- },
-
- /**
- * Verifies if the date to be shown is present.
- *
- * @returns {Boolean|Undefined}
- */
- canShowDate() {
- return this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable &&
- this.model.last_deployment.deployable !== undefined;
- },
-
- /**
- * Human readable date.
- *
- * @returns {String}
- */
- createdDate() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable &&
- this.model.last_deployment.deployable.created_at) {
- return timeagoInstance.format(this.model.last_deployment.deployable.created_at);
- }
- return '';
- },
-
- /**
- * Returns the manual actions with the name parsed.
- *
- * @returns {Array.<Object>|Undefined}
- */
- manualActions() {
- if (this.hasManualActions) {
- return this.model.last_deployment.manual_actions.map((action) => {
- const parsedAction = {
- name: humanize(action.name),
- play_path: action.play_path,
- playable: action.playable,
- };
- return parsedAction;
- });
- }
- return [];
- },
-
- /**
- * Builds the string used in the user image alt attribute.
- *
- * @returns {String}
- */
- userImageAltDescription() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.user &&
- this.model.last_deployment.user.username) {
- return `${this.model.last_deployment.user.username}'s avatar'`;
- }
- return '';
- },
-
- /**
- * If provided, returns the commit tag.
- *
- * @returns {String|Undefined}
- */
- commitTag() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.tag) {
- return this.model.last_deployment.tag;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit ref.
- *
- * @returns {Object|Undefined}
- */
- commitRef() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.ref) {
- return this.model.last_deployment.ref;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit url.
- *
- * @returns {String|Undefined}
- */
- commitUrl() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.commit &&
- this.model.last_deployment.commit.commit_path) {
- return this.model.last_deployment.commit.commit_path;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit short sha.
- *
- * @returns {String|Undefined}
- */
- commitShortSha() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.commit &&
- this.model.last_deployment.commit.short_id) {
- return this.model.last_deployment.commit.short_id;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit title.
- *
- * @returns {String|Undefined}
- */
- commitTitle() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.commit &&
- this.model.last_deployment.commit.title) {
- return this.model.last_deployment.commit.title;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit tag.
- *
- * @returns {Object|Undefined}
- */
- commitAuthor() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.commit &&
- this.model.last_deployment.commit.author) {
- return this.model.last_deployment.commit.author;
- }
-
- return undefined;
- },
-
- /**
- * Verifies if the `retry_path` key is present and returns its value.
- *
- * @returns {String|Undefined}
- */
- retryUrl() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable &&
- this.model.last_deployment.deployable.retry_path) {
- return this.model.last_deployment.deployable.retry_path;
- }
- return undefined;
- },
-
- /**
- * Verifies if the `last?` key is present and returns its value.
- *
- * @returns {Boolean|Undefined}
- */
- isLastDeployment() {
- return this.model && this.model.last_deployment &&
- this.model.last_deployment['last?'];
- },
-
- /**
- * Builds the name of the builds needed to display both the name and the id.
- *
- * @returns {String}
- */
- buildName() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable) {
- const { deployable } = this.model.last_deployment;
- return `${deployable.name} #${deployable.id}`;
- }
- return '';
- },
-
- /**
- * Builds the needed string to show the internal id.
- *
- * @returns {String}
- */
- deploymentInternalId() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.iid) {
- return `#${this.model.last_deployment.iid}`;
- }
- return '';
- },
-
- /**
- * Verifies if the user object is present under last_deployment object.
- *
- * @returns {Boolean}
- */
- deploymentHasUser() {
- return this.model &&
- !_.isEmpty(this.model.last_deployment) &&
- !_.isEmpty(this.model.last_deployment.user);
- },
-
- /**
- * Returns the user object nested with the last_deployment object.
- * Used to render the template.
- *
- * @returns {Object}
- */
- deploymentUser() {
- if (this.model &&
- !_.isEmpty(this.model.last_deployment) &&
- !_.isEmpty(this.model.last_deployment.user)) {
- return this.model.last_deployment.user;
- }
- return {};
- },
-
- /**
- * Verifies if the build name column should be rendered by verifing
- * if all the information needed is present
- * and if the environment is not a folder.
- *
- * @returns {Boolean}
- */
- shouldRenderBuildName() {
- return !this.model.isFolder &&
- !_.isEmpty(this.model.last_deployment) &&
- !_.isEmpty(this.model.last_deployment.deployable);
- },
-
- /**
- * Verifies the presence of all the keys needed to render the buil_path.
- *
- * @return {String}
- */
- buildPath() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable &&
- this.model.last_deployment.deployable.build_path) {
- return this.model.last_deployment.deployable.build_path;
- }
-
- return '';
- },
-
- /**
- * Verifies the presence of all the keys needed to render the external_url.
- *
- * @return {String}
- */
- externalURL() {
- if (this.model && this.model.external_url) {
- return this.model.external_url;
- }
-
- return '';
- },
-
- /**
- * Verifies if deplyment internal ID should be rendered by verifing
- * if all the information needed is present
- * and if the environment is not a folder.
- *
- * @returns {Boolean}
- */
- shouldRenderDeploymentID() {
- return !this.model.isFolder &&
- !_.isEmpty(this.model.last_deployment) &&
- this.model.last_deployment.iid !== undefined;
- },
-
- environmentPath() {
- if (this.model && this.model.environment_path) {
- return this.model.environment_path;
- }
-
- return '';
- },
-
- monitoringUrl() {
- if (this.model && this.model.metrics_path) {
- return this.model.metrics_path;
- }
-
- return '';
- },
-
- displayEnvironmentActions() {
- return this.hasManualActions ||
- this.externalURL ||
- this.monitoringUrl ||
- this.hasStopAction ||
- this.canRetry;
- },
- },
-
- methods: {
- onClickFolder() {
- eventHub.$emit('toggleFolder', this.model);
- },
- },
- };
+import Timeago from 'timeago.js';
+import _ from 'underscore';
+import tooltip from '~/vue_shared/directives/tooltip';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import { humanize } from '~/lib/utils/text_utility';
+import ActionsComponent from './environment_actions.vue';
+import ExternalUrlComponent from './environment_external_url.vue';
+import StopComponent from './environment_stop.vue';
+import RollbackComponent from './environment_rollback.vue';
+import TerminalButtonComponent from './environment_terminal_button.vue';
+import MonitoringButtonComponent from './environment_monitoring.vue';
+import CommitComponent from '../../vue_shared/components/commit.vue';
+import eventHub from '../event_hub';
+
+/**
+ * Envrionment Item Component
+ *
+ * Renders a table row for each environment.
+ */
+const timeagoInstance = new Timeago();
+
+export default {
+ components: {
+ UserAvatarLink,
+ CommitComponent,
+ ActionsComponent,
+ ExternalUrlComponent,
+ StopComponent,
+ RollbackComponent,
+ TerminalButtonComponent,
+ MonitoringButtonComponent,
+ },
+
+ directives: {
+ tooltip,
+ },
+
+ props: {
+ model: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+
+ canCreateDeployment: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+
+ canReadEnvironment: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+
+ computed: {
+ /**
+ * Verifies if `last_deployment` key exists in the current Envrionment.
+ * This key is required to render most of the html - this method works has
+ * an helper.
+ *
+ * @returns {Boolean}
+ */
+ hasLastDeploymentKey() {
+ if (this.model && this.model.last_deployment && !_.isEmpty(this.model.last_deployment)) {
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Verifies is the given environment has manual actions.
+ * Used to verify if we should render them or nor.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ hasManualActions() {
+ return (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.manual_actions &&
+ this.model.last_deployment.manual_actions.length > 0
+ );
+ },
+
+ /**
+ * Checkes whether the environment is protected.
+ * (`is_protected` currently only set in EE)
+ *
+ * @returns {Boolean}
+ */
+ isProtected() {
+ return this.model && this.model.is_protected;
+ },
+
+ /**
+ * Returns whether the environment can be stopped.
+ *
+ * @returns {Boolean}
+ */
+ canStopEnvironment() {
+ return this.model && this.model.can_stop;
+ },
+
+ /**
+ * Verifies if the `deployable` key is present in `last_deployment` key.
+ * Used to verify whether we should or not render the rollback partial.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ canRetry() {
+ return (
+ this.model &&
+ this.hasLastDeploymentKey &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable.retry_path
+ );
+ },
+
+ /**
+ * Verifies if the date to be shown is present.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ canShowDate() {
+ return (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable !== undefined
+ );
+ },
+
+ /**
+ * Human readable date.
+ *
+ * @returns {String}
+ */
+ createdDate() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable.created_at
+ ) {
+ return timeagoInstance.format(this.model.last_deployment.deployable.created_at);
+ }
+ return '';
+ },
+
+ /**
+ * Returns the manual actions with the name parsed.
+ *
+ * @returns {Array.<Object>|Undefined}
+ */
+ manualActions() {
+ if (this.hasManualActions) {
+ return this.model.last_deployment.manual_actions.map(action => {
+ const parsedAction = {
+ name: humanize(action.name),
+ play_path: action.play_path,
+ playable: action.playable,
+ };
+ return parsedAction;
+ });
+ }
+ return [];
+ },
+
+ /**
+ * Builds the string used in the user image alt attribute.
+ *
+ * @returns {String}
+ */
+ userImageAltDescription() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.user &&
+ this.model.last_deployment.user.username
+ ) {
+ return `${this.model.last_deployment.user.username}'s avatar'`;
+ }
+ return '';
+ },
+
+ /**
+ * If provided, returns the commit tag.
+ *
+ * @returns {String|Undefined}
+ */
+ commitTag() {
+ if (this.model && this.model.last_deployment && this.model.last_deployment.tag) {
+ return this.model.last_deployment.tag;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit ref.
+ *
+ * @returns {Object|Undefined}
+ */
+ commitRef() {
+ if (this.model && this.model.last_deployment && this.model.last_deployment.ref) {
+ return this.model.last_deployment.ref;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit url.
+ *
+ * @returns {String|Undefined}
+ */
+ commitUrl() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.commit_path
+ ) {
+ return this.model.last_deployment.commit.commit_path;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit short sha.
+ *
+ * @returns {String|Undefined}
+ */
+ commitShortSha() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.short_id
+ ) {
+ return this.model.last_deployment.commit.short_id;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit title.
+ *
+ * @returns {String|Undefined}
+ */
+ commitTitle() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.title
+ ) {
+ return this.model.last_deployment.commit.title;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit tag.
+ *
+ * @returns {Object|Undefined}
+ */
+ commitAuthor() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.author
+ ) {
+ return this.model.last_deployment.commit.author;
+ }
+
+ return undefined;
+ },
+
+ /**
+ * Verifies if the `retry_path` key is present and returns its value.
+ *
+ * @returns {String|Undefined}
+ */
+ retryUrl() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable.retry_path
+ ) {
+ return this.model.last_deployment.deployable.retry_path;
+ }
+ return undefined;
+ },
+
+ /**
+ * Verifies if the `last?` key is present and returns its value.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ isLastDeployment() {
+ return this.model && this.model.last_deployment && this.model.last_deployment['last?'];
+ },
+
+ /**
+ * Builds the name of the builds needed to display both the name and the id.
+ *
+ * @returns {String}
+ */
+ buildName() {
+ if (this.model && this.model.last_deployment && this.model.last_deployment.deployable) {
+ const { deployable } = this.model.last_deployment;
+ return `${deployable.name} #${deployable.id}`;
+ }
+ return '';
+ },
+
+ /**
+ * Builds the needed string to show the internal id.
+ *
+ * @returns {String}
+ */
+ deploymentInternalId() {
+ if (this.model && this.model.last_deployment && this.model.last_deployment.iid) {
+ return `#${this.model.last_deployment.iid}`;
+ }
+ return '';
+ },
+
+ /**
+ * Verifies if the user object is present under last_deployment object.
+ *
+ * @returns {Boolean}
+ */
+ deploymentHasUser() {
+ return (
+ this.model &&
+ !_.isEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment.user)
+ );
+ },
+
+ /**
+ * Returns the user object nested with the last_deployment object.
+ * Used to render the template.
+ *
+ * @returns {Object}
+ */
+ deploymentUser() {
+ if (
+ this.model &&
+ !_.isEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment.user)
+ ) {
+ return this.model.last_deployment.user;
+ }
+ return {};
+ },
+
+ /**
+ * Verifies if the build name column should be rendered by verifing
+ * if all the information needed is present
+ * and if the environment is not a folder.
+ *
+ * @returns {Boolean}
+ */
+ shouldRenderBuildName() {
+ return (
+ !this.model.isFolder &&
+ !_.isEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment.deployable)
+ );
+ },
+
+ /**
+ * Verifies the presence of all the keys needed to render the buil_path.
+ *
+ * @return {String}
+ */
+ buildPath() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable.build_path
+ ) {
+ return this.model.last_deployment.deployable.build_path;
+ }
+
+ return '';
+ },
+
+ /**
+ * Verifies the presence of all the keys needed to render the external_url.
+ *
+ * @return {String}
+ */
+ externalURL() {
+ if (this.model && this.model.external_url) {
+ return this.model.external_url;
+ }
+
+ return '';
+ },
+
+ /**
+ * Verifies if deplyment internal ID should be rendered by verifing
+ * if all the information needed is present
+ * and if the environment is not a folder.
+ *
+ * @returns {Boolean}
+ */
+ shouldRenderDeploymentID() {
+ return (
+ !this.model.isFolder &&
+ !_.isEmpty(this.model.last_deployment) &&
+ this.model.last_deployment.iid !== undefined
+ );
+ },
+
+ environmentPath() {
+ if (this.model && this.model.environment_path) {
+ return this.model.environment_path;
+ }
+
+ return '';
+ },
+
+ monitoringUrl() {
+ if (this.model && this.model.metrics_path) {
+ return this.model.metrics_path;
+ }
+
+ return '';
+ },
+
+ displayEnvironmentActions() {
+ return (
+ this.hasManualActions ||
+ this.externalURL ||
+ this.monitoringUrl ||
+ this.canStopEnvironment ||
+ this.canRetry
+ );
+ },
+ },
+
+ methods: {
+ onClickFolder() {
+ eventHub.$emit('toggleFolder', this.model);
+ },
+ },
+};
</script>
<template>
<div
@@ -434,7 +466,7 @@
class="gl-responsive-table-row"
role="row">
<div
- class="table-section section-10"
+ class="table-section section-wrap section-15"
role="gridcell"
>
<div
@@ -444,16 +476,17 @@
>
{{ s__("Environments|Environment") }}
</div>
- <a
+ <span
v-if="!model.isFolder"
- :href="environmentPath"
- class="environment-name flex-truncate-parent table-mobile-content">
- <span
+ class="environment-name table-mobile-content">
+ <a
v-tooltip
+ :href="environmentPath"
:title="model.name"
- class="flex-truncate-child"
- >{{ model.name }}</span>
- </a>
+ >
+ {{ model.name }}
+ </a>
+ </span>
<span
v-else
class="folder-name"
@@ -527,7 +560,7 @@
<div
v-if="!model.isFolder"
- class="table-section section-25"
+ class="table-section section-20"
role="gridcell"
>
<div
@@ -580,11 +613,6 @@
class="btn-group table-action-buttons"
role="group">
- <actions-component
- v-if="hasManualActions && canCreateDeployment"
- :actions="manualActions"
- />
-
<external-url-component
v-if="externalURL && canReadEnvironment"
:external-url="externalURL"
@@ -595,21 +623,26 @@
:monitoring-url="monitoringUrl"
/>
+ <actions-component
+ v-if="hasManualActions && canCreateDeployment"
+ :actions="manualActions"
+ />
+
<terminal-button-component
v-if="model && model.terminal_path"
:terminal-path="model.terminal_path"
/>
- <stop-component
- v-if="hasStopAction && canCreateDeployment"
- :stop-url="model.stop_path"
- />
-
<rollback-component
v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment"
:retry-url="retryUrl"
/>
+
+ <stop-component
+ v-if="canStopEnvironment"
+ :environment="model"
+ />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/environments/components/environment_monitoring.vue b/app/assets/javascripts/environments/components/environment_monitoring.vue
index 947e8c901e9..ccc8419ca6d 100644
--- a/app/assets/javascripts/environments/components/environment_monitoring.vue
+++ b/app/assets/javascripts/environments/components/environment_monitoring.vue
@@ -1,29 +1,29 @@
<script>
- /**
- * Renders the Monitoring (Metrics) link in environments table.
- */
- import Icon from '~/vue_shared/components/icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
+/**
+ * Renders the Monitoring (Metrics) link in environments table.
+ */
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- components: {
- Icon,
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ monitoringUrl: {
+ type: String,
+ required: true,
},
- directives: {
- tooltip,
+ },
+ computed: {
+ title() {
+ return 'Monitoring';
},
- props: {
- monitoringUrl: {
- type: String,
- required: true,
- },
- },
- computed: {
- title() {
- return 'Monitoring';
- },
- },
- };
+ },
+};
</script>
<template>
<a
@@ -35,9 +35,6 @@
data-container="body"
rel="noopener noreferrer nofollow"
>
- <icon
- :size="12"
- name="chart"
- />
+ <icon name="chart" />
</a>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue
index 310835c5ea9..4deeef4beb9 100644
--- a/app/assets/javascripts/environments/components/environment_rollback.vue
+++ b/app/assets/javascripts/environments/components/environment_rollback.vue
@@ -1,56 +1,74 @@
<script>
- /**
- * Renders Rollback or Re deploy button in environments table depending
- * of the provided property `isLastDeployment`.
- *
- * Makes a post request when the button is clicked.
- */
- import eventHub from '../event_hub';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-
- export default {
- components: {
- loadingIcon,
+/**
+ * Renders Rollback or Re deploy button in environments table depending
+ * of the provided property `isLastDeployment`.
+ *
+ * Makes a post request when the button is clicked.
+ */
+import { s__ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import eventHub from '../event_hub';
+import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+export default {
+ components: {
+ Icon,
+ LoadingIcon,
+ },
+
+ directives: {
+ tooltip,
+ },
+
+ props: {
+ retryUrl: {
+ type: String,
+ default: '',
},
- props: {
- retryUrl: {
- type: String,
- default: '',
- },
-
- isLastDeployment: {
- type: Boolean,
- default: true,
- },
+
+ isLastDeployment: {
+ type: Boolean,
+ default: true,
},
- data() {
- return {
- isLoading: false,
- };
+ },
+ data() {
+ return {
+ isLoading: false,
+ };
+ },
+
+ computed: {
+ title() {
+ return this.isLastDeployment ? s__('Environments|Re-deploy to environment') : s__('Environments|Rollback environment');
},
- methods: {
- onClick() {
- this.isLoading = true;
+ },
+
+ methods: {
+ onClick() {
+ this.isLoading = true;
- eventHub.$emit('postAction', this.retryUrl);
- },
+ eventHub.$emit('postAction', { endpoint: this.retryUrl });
},
- };
+ },
+};
</script>
<template>
<button
+ v-tooltip
:disabled="isLoading"
+ :title="title"
type="button"
class="btn d-none d-sm-none d-md-block"
@click="onClick"
>
- <span v-if="isLastDeployment">
- {{ s__("Environments|Re-deploy") }}
- </span>
- <span v-else>
- {{ s__("Environments|Rollback") }}
- </span>
+ <icon
+ v-if="isLastDeployment"
+ name="repeat" />
+ <icon
+ v-else
+ name="redo"/>
<loading-icon v-if="isLoading" />
</button>
diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue
index eba58bedd6d..a814b3405f5 100644
--- a/app/assets/javascripts/environments/components/environment_stop.vue
+++ b/app/assets/javascripts/environments/components/environment_stop.vue
@@ -1,72 +1,78 @@
<script>
- /**
- * Renders the stop "button" that allows stop an environment.
- * Used in environments table.
- */
+/**
+ * Renders the stop "button" that allows stop an environment.
+ * Used in environments table.
+ */
- import $ from 'jquery';
- import eventHub from '../event_hub';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
+import $ from 'jquery';
+import Icon from '~/vue_shared/components/icon.vue';
+import { s__ } from '~/locale';
+import eventHub from '../event_hub';
+import LoadingButton from '../../vue_shared/components/loading_button.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- components: {
- loadingIcon,
- },
+export default {
+ components: {
+ Icon,
+ LoadingButton,
+ },
- directives: {
- tooltip,
- },
+ directives: {
+ tooltip,
+ },
- props: {
- stopUrl: {
- type: String,
- default: '',
- },
+ props: {
+ environment: {
+ type: Object,
+ required: true,
},
+ },
- data() {
- return {
- isLoading: false,
- };
- },
+ data() {
+ return {
+ isLoading: false,
+ };
+ },
- computed: {
- title() {
- return 'Stop';
- },
+ computed: {
+ title() {
+ return s__('Environments|Stop environment');
},
+ },
- methods: {
- onClick() {
- // eslint-disable-next-line no-alert
- if (window.confirm('Are you sure you want to stop this environment?')) {
- this.isLoading = true;
+ mounted() {
+ eventHub.$on('stopEnvironment', this.onStopEnvironment);
+ },
- $(this.$el).tooltip('dispose');
+ beforeDestroy() {
+ eventHub.$off('stopEnvironment', this.onStopEnvironment);
+ },
- eventHub.$emit('postAction', this.stopUrl);
- }
- },
+ methods: {
+ onClick() {
+ $(this.$el).tooltip('dispose');
+ eventHub.$emit('requestStopEnvironment', this.environment);
+ },
+ onStopEnvironment(environment) {
+ if (this.environment.id === environment.id) {
+ this.isLoading = true;
+ }
},
- };
+ },
+};
</script>
<template>
- <button
+ <loading-button
v-tooltip
- :disabled="isLoading"
+ :loading="isLoading"
:title="title"
:aria-label="title"
- type="button"
- class="btn stop-env-link d-none d-sm-none d-md-block"
+ container-class="btn btn-danger d-none d-sm-none d-md-block"
data-container="body"
+ data-toggle="modal"
+ data-target="#stop-environment-modal"
@click="onClick"
>
- <i
- class="fa fa-stop stop-env-icon"
- aria-hidden="true"
- >
- </i>
- <loading-icon v-if="isLoading" />
- </button>
+ <icon name="stop"/>
+ </loading-button>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.vue b/app/assets/javascripts/environments/components/environment_terminal_button.vue
index f8e3165f8cd..350417e5ad0 100644
--- a/app/assets/javascripts/environments/components/environment_terminal_button.vue
+++ b/app/assets/javascripts/environments/components/environment_terminal_button.vue
@@ -1,31 +1,31 @@
<script>
- /**
- * Renders a terminal button to open a web terminal.
- * Used in environments table.
- */
- import Icon from '~/vue_shared/components/icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
+/**
+ * Renders a terminal button to open a web terminal.
+ * Used in environments table.
+ */
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- components: {
- Icon,
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ terminalPath: {
+ type: String,
+ required: false,
+ default: '',
},
- directives: {
- tooltip,
+ },
+ computed: {
+ title() {
+ return 'Terminal';
},
- props: {
- terminalPath: {
- type: String,
- required: false,
- default: '',
- },
- },
- computed: {
- title() {
- return 'Terminal';
- },
- },
- };
+ },
+};
</script>
<template>
<a
@@ -36,9 +36,6 @@
class="btn terminal-button d-none d-sm-none d-md-block"
data-container="body"
>
- <icon
- :size="12"
- name="terminal"
- />
+ <icon name="terminal" />
</a>
</template>
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index b18f02343d6..8efdfb8abe0 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -5,10 +5,12 @@
import eventHub from '../event_hub';
import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
+ import StopEnvironmentModal from './stop_environment_modal.vue';
export default {
components: {
emptyState,
+ StopEnvironmentModal,
},
mixins: [
@@ -90,6 +92,8 @@
</script>
<template>
<div :class="cssContainerClass">
+ <stop-environment-modal :environment="environmentInStopModal" />
+
<div class="top-area">
<tabs
:tabs="tabs"
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index 22863e926d4..016e9f7c7b3 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -15,7 +15,7 @@ export default {
environments: {
type: Array,
required: true,
- default: () => ([]),
+ default: () => [],
},
canReadEnvironment: {
@@ -35,10 +35,7 @@ export default {
return `${window.location.pathname}/folders/${model.folderName}`;
},
shouldRenderFolderContent(env) {
- return env.isFolder &&
- env.isOpen &&
- env.children &&
- env.children.length > 0;
+ return env.isFolder && env.isOpen && env.children && env.children.length > 0;
},
},
};
@@ -53,7 +50,7 @@ export default {
role="row"
>
<div
- class="table-section section-10 environments-name"
+ class="table-section section-15 environments-name"
role="columnheader"
>
{{ s__("Environments|Environment") }}
@@ -71,7 +68,7 @@ export default {
{{ s__("Environments|Job") }}
</div>
<div
- class="table-section section-25 environments-commit"
+ class="table-section section-20 environments-commit"
role="columnheader"
>
{{ s__("Environments|Commit") }}
@@ -91,7 +88,7 @@ export default {
:model="model"
:can-create-deployment="canCreateDeployment"
:can-read-environment="canReadEnvironment"
- :key="i"
+ :key="`environment-item-${i}`"
/>
<template
diff --git a/app/assets/javascripts/environments/components/stop_environment_modal.vue b/app/assets/javascripts/environments/components/stop_environment_modal.vue
new file mode 100644
index 00000000000..657cc8cd1aa
--- /dev/null
+++ b/app/assets/javascripts/environments/components/stop_environment_modal.vue
@@ -0,0 +1,92 @@
+<script>
+import GlModal from '~/vue_shared/components/gl_modal.vue';
+import { s__, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+import eventHub from '../event_hub';
+
+export default {
+ id: 'stop-environment-modal',
+ name: 'StopEnvironmentModal',
+
+ components: {
+ GlModal,
+ LoadingButton,
+ },
+
+ directives: {
+ tooltip,
+ },
+
+ props: {
+ environment: {
+ type: Object,
+ required: true,
+ },
+ },
+
+ computed: {
+ noStopActionMessage() {
+ return sprintf(
+ s__(
+ `Environments|Note that this action will stop the environment,
+ but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment
+ due to no “stop environment action†being defined
+ in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file.`,
+ ),
+ {
+ emphasisStart: '<strong>',
+ emphasisEnd: '</strong>',
+ ciConfigLinkStart:
+ '<a href="https://docs.gitlab.com/ee/ci/yaml/" target="_blank" rel="noopener noreferrer">',
+ ciConfigLinkEnd: '</a>',
+ },
+ false,
+ );
+ },
+ },
+
+ methods: {
+ onSubmit() {
+ eventHub.$emit('stopEnvironment', this.environment);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-modal
+ :id="$options.id"
+ :footer-primary-button-text="s__('Environments|Stop environment')"
+ footer-primary-button-variant="danger"
+ @submit="onSubmit"
+ >
+ <template slot="header">
+ <h4
+ class="modal-title d-flex mw-100"
+ >
+ Stopping
+ <span
+ v-tooltip
+ :title="environment.name"
+ class="text-truncate ml-1 mr-1 flex-fill"
+ >{{ environment.name }}</span>
+ ?
+ </h4>
+ </template>
+
+ <p>{{ s__('Environments|Are you sure you want to stop this environment?') }}</p>
+
+ <div
+ v-if="!environment.has_stop_action"
+ class="warning_message"
+ >
+ <p v-html="noStopActionMessage"></p>
+ <a
+ href="https://docs.gitlab.com/ee/ci/environments.html#stopping-an-environment"
+ target="_blank"
+ rel="noopener noreferrer"
+ >{{ s__('Environments|Learn more about stopping environments') }}</a>
+ </div>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue
index 5f72a39c5cb..e69bfa0b2cc 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.vue
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue
@@ -1,12 +1,18 @@
<script>
import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
+ import StopEnvironmentModal from '../components/stop_environment_modal.vue';
export default {
+ components: {
+ StopEnvironmentModal,
+ },
+
mixins: [
environmentsMixin,
CIPaginationMixin,
],
+
props: {
endpoint: {
type: String,
@@ -38,6 +44,8 @@
</script>
<template>
<div :class="cssContainerClass">
+ <stop-environment-modal :environment="environmentInStopModal" />
+
<div
v-if="!isLoading"
class="top-area"
diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js
index a7a79dbca70..d88624f7f8d 100644
--- a/app/assets/javascripts/environments/mixins/environments_mixin.js
+++ b/app/assets/javascripts/environments/mixins/environments_mixin.js
@@ -40,6 +40,7 @@ export default {
scope: getParameterByName('scope') || 'available',
page: getParameterByName('page') || '1',
requestData: {},
+ environmentInStopModal: {},
};
},
@@ -85,7 +86,7 @@ export default {
Flash(s__('Environments|An error occurred while fetching the environments.'));
},
- postAction(endpoint) {
+ postAction({ endpoint, errorMessage }) {
if (!this.isMakingRequest) {
this.isLoading = true;
@@ -93,7 +94,7 @@ export default {
.then(() => this.fetchEnvironments())
.catch(() => {
this.isLoading = false;
- Flash(s__('Environments|An error occurred while making the request.'));
+ Flash(errorMessage || s__('Environments|An error occurred while making the request.'));
});
}
},
@@ -106,6 +107,15 @@ export default {
.catch(this.errorCallback);
},
+ updateStopModal(environment) {
+ this.environmentInStopModal = environment;
+ },
+
+ stopEnvironment(environment) {
+ const endpoint = environment.stop_path;
+ const errorMessage = s__('Environments|An error occurred while stopping the environment, please try again');
+ this.postAction({ endpoint, errorMessage });
+ },
},
computed: {
@@ -162,9 +172,13 @@ export default {
});
eventHub.$on('postAction', this.postAction);
+ eventHub.$on('requestStopEnvironment', this.updateStopModal);
+ eventHub.$on('stopEnvironment', this.stopEnvironment);
},
- beforeDestroyed() {
- eventHub.$off('postAction');
+ beforeDestroy() {
+ eventHub.$off('postAction', this.postAction);
+ eventHub.$off('requestStopEnvironment', this.updateStopModal);
+ eventHub.$off('stopEnvironment', this.stopEnvironment);
},
};
diff --git a/app/assets/javascripts/environments/services/environments_service.js b/app/assets/javascripts/environments/services/environments_service.js
index 3b121551aca..4e07ccba91a 100644
--- a/app/assets/javascripts/environments/services/environments_service.js
+++ b/app/assets/javascripts/environments/services/environments_service.js
@@ -13,7 +13,7 @@ export default class EnvironmentsService {
// eslint-disable-next-line class-methods-use-this
postAction(endpoint) {
- return axios.post(endpoint, {}, { emulateJSON: true });
+ return axios.post(endpoint, {});
}
getFolderContent(folderUrl) {
diff --git a/app/assets/javascripts/frequent_items/components/app.vue b/app/assets/javascripts/frequent_items/components/app.vue
new file mode 100644
index 00000000000..2f030de8967
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/app.vue
@@ -0,0 +1,122 @@
+<script>
+import { mapState, mapActions, mapGetters } from 'vuex';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+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 } from '../utils';
+import FrequentItemsSearchInput from './frequent_items_search_input.vue';
+import FrequentItemsList from './frequent_items_list.vue';
+import frequentItemsMixin from './frequent_items_mixin';
+
+export default {
+ store,
+ components: {
+ LoadingIcon,
+ FrequentItemsSearchInput,
+ FrequentItemsList,
+ },
+ mixins: [frequentItemsMixin],
+ props: {
+ currentUserName: {
+ type: String,
+ required: true,
+ },
+ currentItem: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['searchQuery', 'isLoadingItems', 'isFetchFailed', 'items']),
+ ...mapGetters(['hasSearchQuery']),
+ translations() {
+ return this.getTranslations(['loadingMessage', 'header']);
+ },
+ },
+ created() {
+ const { namespace, currentUserName, currentItem } = this;
+ const storageKey = `${currentUserName}/${STORAGE_KEY[namespace]}`;
+
+ this.setNamespace(namespace);
+ this.setStorageKey(storageKey);
+
+ if (currentItem.id) {
+ this.logItemAccess(storageKey, currentItem);
+ }
+
+ eventHub.$on(`${this.namespace}-dropdownOpen`, this.dropdownOpenHandler);
+ },
+ beforeDestroy() {
+ eventHub.$off(`${this.namespace}-dropdownOpen`, this.dropdownOpenHandler);
+ },
+ methods: {
+ ...mapActions(['setNamespace', 'setStorageKey', 'fetchFrequentItems']),
+ dropdownOpenHandler() {
+ if (this.searchQuery === '' || isMobile()) {
+ this.fetchFrequentItems();
+ }
+ },
+ logItemAccess(storageKey, item) {
+ if (!AccessorUtilities.isLocalStorageAccessSafe()) {
+ return false;
+ }
+
+ // Check if there's any frequent items list set
+ const storedRawItems = localStorage.getItem(storageKey);
+ const storedFrequentItems = storedRawItems
+ ? JSON.parse(storedRawItems)
+ : [{ ...item, frequency: 1 }]; // No frequent items list set, set one up.
+
+ // Check if item already exists in list
+ const itemMatchIndex = storedFrequentItems.findIndex(
+ frequentItem => frequentItem.id === item.id,
+ );
+
+ if (itemMatchIndex > -1) {
+ storedFrequentItems[itemMatchIndex] = updateExistingFrequentItem(
+ storedFrequentItems[itemMatchIndex],
+ item,
+ );
+ } else {
+ if (storedFrequentItems.length === FREQUENT_ITEMS.MAX_COUNT) {
+ storedFrequentItems.shift();
+ }
+
+ storedFrequentItems.push({ ...item, frequency: 1 });
+ }
+
+ return localStorage.setItem(storageKey, JSON.stringify(storedFrequentItems));
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <frequent-items-search-input
+ :namespace="namespace"
+ />
+ <loading-icon
+ v-if="isLoadingItems"
+ :label="translations.loadingMessage"
+ class="loading-animation prepend-top-20"
+ size="2"
+ />
+ <div
+ v-if="!isLoadingItems && !hasSearchQuery"
+ class="section-header"
+ >
+ {{ translations.header }}
+ </div>
+ <frequent-items-list
+ v-if="!isLoadingItems"
+ :items="items"
+ :namespace="namespace"
+ :has-search-query="hasSearchQuery"
+ :is-fetch-failed="isFetchFailed"
+ :matcher="searchQuery"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list.vue
new file mode 100644
index 00000000000..8e511aa2a36
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list.vue
@@ -0,0 +1,78 @@
+<script>
+import FrequentItemsListItem from './frequent_items_list_item.vue';
+import frequentItemsMixin from './frequent_items_mixin';
+
+export default {
+ components: {
+ FrequentItemsListItem,
+ },
+ mixins: [frequentItemsMixin],
+ props: {
+ items: {
+ type: Array,
+ required: true,
+ },
+ hasSearchQuery: {
+ type: Boolean,
+ required: true,
+ },
+ isFetchFailed: {
+ type: Boolean,
+ required: true,
+ },
+ matcher: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ translations() {
+ return this.getTranslations([
+ 'itemListEmptyMessage',
+ 'itemListErrorMessage',
+ 'searchListEmptyMessage',
+ 'searchListErrorMessage',
+ ]);
+ },
+ isListEmpty() {
+ return this.items.length === 0;
+ },
+ listEmptyMessage() {
+ if (this.hasSearchQuery) {
+ return this.isFetchFailed
+ ? this.translations.searchListErrorMessage
+ : this.translations.searchListEmptyMessage;
+ }
+
+ return this.isFetchFailed
+ ? this.translations.itemListErrorMessage
+ : this.translations.itemListEmptyMessage;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="frequent-items-list-container">
+ <ul class="list-unstyled">
+ <li
+ v-if="isListEmpty"
+ :class="{ 'section-failure': isFetchFailed }"
+ class="section-empty"
+ >
+ {{ listEmptyMessage }}
+ </li>
+ <frequent-items-list-item
+ v-for="item in items"
+ v-else
+ :key="item.id"
+ :item-id="item.id"
+ :item-name="item.name"
+ :namespace="item.namespace"
+ :web-url="item.webUrl"
+ :avatar-url="item.avatarUrl"
+ :matcher="matcher"
+ />
+ </ul>
+ </div>
+</template>
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
new file mode 100644
index 00000000000..1f1665ff7fe
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -0,0 +1,117 @@
+<script>
+/* eslint-disable vue/require-default-prop, vue/require-prop-types */
+import Identicon from '../../vue_shared/components/identicon.vue';
+
+export default {
+ components: {
+ Identicon,
+ },
+ props: {
+ matcher: {
+ type: String,
+ required: false,
+ },
+ itemId: {
+ type: Number,
+ required: true,
+ },
+ itemName: {
+ type: String,
+ required: true,
+ },
+ namespace: {
+ type: String,
+ required: false,
+ },
+ webUrl: {
+ type: String,
+ required: true,
+ },
+ avatarUrl: {
+ required: true,
+ validator(value) {
+ return value === null || typeof value === 'string';
+ },
+ },
+ },
+ computed: {
+ hasAvatar() {
+ return this.avatarUrl !== null;
+ },
+ highlightedItemName() {
+ if (this.matcher) {
+ const matcherRegEx = new RegExp(this.matcher, 'gi');
+ const matches = this.itemName.match(matcherRegEx);
+
+ if (matches && matches.length > 0) {
+ return this.itemName.replace(matches[0], `<b>${matches[0]}</b>`);
+ }
+ }
+ return this.itemName;
+ },
+ /**
+ * Smartly truncates item namespace by doing two things;
+ * 1. Only include Group names in path by removing item name
+ * 2. Only include first and last group names in the path
+ * when namespace has more than 2 groups present
+ *
+ * First part (removal of item name from namespace) can be
+ * done from backend but doing so involves migration of
+ * existing item namespaces which is not wise thing to do.
+ */
+ truncatedNamespace() {
+ if (!this.namespace) {
+ return null;
+ }
+ const namespaceArr = this.namespace.split(' / ');
+
+ namespaceArr.splice(-1, 1);
+ let namespace = namespaceArr.join(' / ');
+
+ if (namespaceArr.length > 2) {
+ namespace = `${namespaceArr[0]} / ... / ${namespaceArr.pop()}`;
+ }
+
+ return namespace;
+ },
+ },
+};
+</script>
+
+<template>
+ <li class="frequent-items-list-item-container">
+ <a
+ :href="webUrl"
+ class="clearfix"
+ >
+ <div class="frequent-items-item-avatar-container">
+ <img
+ v-if="hasAvatar"
+ :src="avatarUrl"
+ class="avatar s32"
+ />
+ <identicon
+ v-else
+ :entity-id="itemId"
+ :entity-name="itemName"
+ size-class="s32"
+ />
+ </div>
+ <div class="frequent-items-item-metadata-container">
+ <div
+ :title="itemName"
+ class="frequent-items-item-title"
+ v-html="highlightedItemName"
+ >
+ </div>
+ <div
+ v-if="truncatedNamespace"
+ :title="namespace"
+ class="frequent-items-item-namespace"
+ >
+ {{ truncatedNamespace }}
+ </div>
+ </div>
+ </a>
+ </li>
+</template>
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_mixin.js b/app/assets/javascripts/frequent_items/components/frequent_items_mixin.js
new file mode 100644
index 00000000000..704dc83ca8e
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_mixin.js
@@ -0,0 +1,23 @@
+import { TRANSLATION_KEYS } from '../constants';
+
+export default {
+ props: {
+ namespace: {
+ type: String,
+ required: true,
+ },
+ },
+ methods: {
+ getTranslations(keys) {
+ const translationStrings = keys.reduce(
+ (acc, key) => ({
+ ...acc,
+ [key]: TRANSLATION_KEYS[this.namespace][key],
+ }),
+ {},
+ );
+
+ return translationStrings;
+ },
+ },
+};
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
new file mode 100644
index 00000000000..a6a265eb3fd
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
@@ -0,0 +1,55 @@
+<script>
+import _ from 'underscore';
+import { mapActions } from 'vuex';
+import eventHub from '../event_hub';
+import frequentItemsMixin from './frequent_items_mixin';
+
+export default {
+ mixins: [frequentItemsMixin],
+ data() {
+ return {
+ searchQuery: '',
+ };
+ },
+ computed: {
+ translations() {
+ return this.getTranslations(['searchInputPlaceholder']);
+ },
+ },
+ watch: {
+ searchQuery: _.debounce(function debounceSearchQuery() {
+ this.setSearchQuery(this.searchQuery);
+ }, 500),
+ },
+ mounted() {
+ eventHub.$on(`${this.namespace}-dropdownOpen`, this.setFocus);
+ },
+ beforeDestroy() {
+ eventHub.$off(`${this.namespace}-dropdownOpen`, this.setFocus);
+ },
+ methods: {
+ ...mapActions(['setSearchQuery']),
+ setFocus() {
+ this.$refs.search.focus();
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="search-input-container d-none d-sm-block">
+ <input
+ ref="search"
+ v-model="searchQuery"
+ :placeholder="translations.searchInputPlaceholder"
+ type="search"
+ class="form-control"
+ />
+ <i
+ v-if="!searchQuery"
+ class="search-icon fa fa-fw fa-search"
+ aria-hidden="true"
+ >
+ </i>
+ </div>
+</template>
diff --git a/app/assets/javascripts/frequent_items/constants.js b/app/assets/javascripts/frequent_items/constants.js
new file mode 100644
index 00000000000..9bc17f5ef4f
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/constants.js
@@ -0,0 +1,38 @@
+import { s__ } from '~/locale';
+
+export const FREQUENT_ITEMS = {
+ MAX_COUNT: 20,
+ LIST_COUNT_DESKTOP: 5,
+ LIST_COUNT_MOBILE: 3,
+ ELIGIBLE_FREQUENCY: 3,
+};
+
+export const HOUR_IN_MS = 3600000;
+
+export const STORAGE_KEY = {
+ projects: 'frequent-projects',
+ groups: 'frequent-groups',
+};
+
+export const TRANSLATION_KEYS = {
+ projects: {
+ loadingMessage: s__('ProjectsDropdown|Loading projects'),
+ header: s__('ProjectsDropdown|Frequently visited'),
+ itemListErrorMessage: s__(
+ 'ProjectsDropdown|This feature requires browser localStorage support',
+ ),
+ itemListEmptyMessage: s__('ProjectsDropdown|Projects you visit often will appear here'),
+ searchListErrorMessage: s__('ProjectsDropdown|Something went wrong on our end.'),
+ searchListEmptyMessage: s__('ProjectsDropdown|Sorry, no projects matched your search'),
+ searchInputPlaceholder: s__('ProjectsDropdown|Search your projects'),
+ },
+ groups: {
+ loadingMessage: s__('GroupsDropdown|Loading groups'),
+ header: s__('GroupsDropdown|Frequently visited'),
+ itemListErrorMessage: s__('GroupsDropdown|This feature requires browser localStorage support'),
+ itemListEmptyMessage: s__('GroupsDropdown|Groups you visit often will appear here'),
+ searchListErrorMessage: s__('GroupsDropdown|Something went wrong on our end.'),
+ searchListEmptyMessage: s__('GroupsDropdown|Sorry, no groups matched your search'),
+ searchInputPlaceholder: s__('GroupsDropdown|Search your groups'),
+ },
+};
diff --git a/app/assets/javascripts/projects_dropdown/event_hub.js b/app/assets/javascripts/frequent_items/event_hub.js
index 0948c2e5352..0948c2e5352 100644
--- a/app/assets/javascripts/projects_dropdown/event_hub.js
+++ b/app/assets/javascripts/frequent_items/event_hub.js
diff --git a/app/assets/javascripts/frequent_items/index.js b/app/assets/javascripts/frequent_items/index.js
new file mode 100644
index 00000000000..5157ff211dc
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/index.js
@@ -0,0 +1,69 @@
+import $ from 'jquery';
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
+import eventHub from '~/frequent_items/event_hub';
+import frequentItems from './components/app.vue';
+
+Vue.use(Translate);
+
+const frequentItemDropdowns = [
+ {
+ namespace: 'projects',
+ key: 'project',
+ },
+ {
+ namespace: 'groups',
+ key: 'group',
+ },
+];
+
+document.addEventListener('DOMContentLoaded', () => {
+ frequentItemDropdowns.forEach(dropdown => {
+ const { namespace, key } = dropdown;
+ const el = document.getElementById(`js-${namespace}-dropdown`);
+ const navEl = document.getElementById(`nav-${namespace}-dropdown`);
+
+ // Don't do anything if element doesn't exist (No groups dropdown)
+ // This is for when the user accesses GitLab without logging in
+ if (!el || !navEl) {
+ return;
+ }
+
+ $(navEl).on('shown.bs.dropdown', () => {
+ eventHub.$emit(`${namespace}-dropdownOpen`);
+ });
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ components: {
+ frequentItems,
+ },
+ data() {
+ const { dataset } = this.$options.el;
+ const item = {
+ id: Number(dataset[`${key}Id`]),
+ name: dataset[`${key}Name`],
+ namespace: dataset[`${key}Namespace`],
+ webUrl: dataset[`${key}WebUrl`],
+ avatarUrl: dataset[`${key}AvatarUrl`] || null,
+ lastAccessedOn: Date.now(),
+ };
+
+ return {
+ currentUserName: dataset.userName,
+ currentItem: item,
+ };
+ },
+ render(createElement) {
+ return createElement('frequent-items', {
+ props: {
+ namespace,
+ currentUserName: this.currentUserName,
+ currentItem: this.currentItem,
+ },
+ });
+ },
+ });
+ });
+});
diff --git a/app/assets/javascripts/frequent_items/store/actions.js b/app/assets/javascripts/frequent_items/store/actions.js
new file mode 100644
index 00000000000..3dd89a82a42
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/actions.js
@@ -0,0 +1,81 @@
+import Api from '~/api';
+import AccessorUtilities from '~/lib/utils/accessor';
+import * as types from './mutation_types';
+import { getTopFrequentItems } from '../utils';
+
+export const setNamespace = ({ commit }, namespace) => {
+ commit(types.SET_NAMESPACE, namespace);
+};
+
+export const setStorageKey = ({ commit }, key) => {
+ commit(types.SET_STORAGE_KEY, key);
+};
+
+export const requestFrequentItems = ({ commit }) => {
+ commit(types.REQUEST_FREQUENT_ITEMS);
+};
+export const receiveFrequentItemsSuccess = ({ commit }, data) => {
+ commit(types.RECEIVE_FREQUENT_ITEMS_SUCCESS, data);
+};
+export const receiveFrequentItemsError = ({ commit }) => {
+ commit(types.RECEIVE_FREQUENT_ITEMS_ERROR);
+};
+
+export const fetchFrequentItems = ({ state, dispatch }) => {
+ dispatch('requestFrequentItems');
+
+ if (AccessorUtilities.isLocalStorageAccessSafe()) {
+ const storedFrequentItems = JSON.parse(localStorage.getItem(state.storageKey));
+
+ dispatch(
+ 'receiveFrequentItemsSuccess',
+ !storedFrequentItems ? [] : getTopFrequentItems(storedFrequentItems),
+ );
+ } else {
+ dispatch('receiveFrequentItemsError');
+ }
+};
+
+export const requestSearchedItems = ({ commit }) => {
+ commit(types.REQUEST_SEARCHED_ITEMS);
+};
+export const receiveSearchedItemsSuccess = ({ commit }, data) => {
+ commit(types.RECEIVE_SEARCHED_ITEMS_SUCCESS, data);
+};
+export const receiveSearchedItemsError = ({ commit }) => {
+ commit(types.RECEIVE_SEARCHED_ITEMS_ERROR);
+};
+export const fetchSearchedItems = ({ state, dispatch }, searchQuery) => {
+ dispatch('requestSearchedItems');
+
+ const params = {
+ simple: true,
+ per_page: 20,
+ membership: !!gon.current_user_id,
+ };
+
+ if (state.namespace === 'projects') {
+ params.order_by = 'last_activity_at';
+ }
+
+ return Api[state.namespace](searchQuery, params)
+ .then(results => {
+ dispatch('receiveSearchedItemsSuccess', results);
+ })
+ .catch(() => {
+ dispatch('receiveSearchedItemsError');
+ });
+};
+
+export const setSearchQuery = ({ commit, dispatch }, query) => {
+ commit(types.SET_SEARCH_QUERY, query);
+
+ if (query) {
+ dispatch('fetchSearchedItems', query);
+ } else {
+ dispatch('fetchFrequentItems');
+ }
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/frequent_items/store/getters.js b/app/assets/javascripts/frequent_items/store/getters.js
new file mode 100644
index 00000000000..00165db6684
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/getters.js
@@ -0,0 +1,4 @@
+export const hasSearchQuery = state => state.searchQuery !== '';
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/frequent_items/store/index.js b/app/assets/javascripts/frequent_items/store/index.js
new file mode 100644
index 00000000000..ece9e6419dd
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/index.js
@@ -0,0 +1,16 @@
+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 default () =>
+ new Vuex.Store({
+ actions,
+ getters,
+ mutations,
+ state: state(),
+ });
diff --git a/app/assets/javascripts/frequent_items/store/mutation_types.js b/app/assets/javascripts/frequent_items/store/mutation_types.js
new file mode 100644
index 00000000000..cbe2c9401ad
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/mutation_types.js
@@ -0,0 +1,9 @@
+export const SET_NAMESPACE = 'SET_NAMESPACE';
+export const SET_STORAGE_KEY = 'SET_STORAGE_KEY';
+export const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY';
+export const REQUEST_FREQUENT_ITEMS = 'REQUEST_FREQUENT_ITEMS';
+export const RECEIVE_FREQUENT_ITEMS_SUCCESS = 'RECEIVE_FREQUENT_ITEMS_SUCCESS';
+export const RECEIVE_FREQUENT_ITEMS_ERROR = 'RECEIVE_FREQUENT_ITEMS_ERROR';
+export const REQUEST_SEARCHED_ITEMS = 'REQUEST_SEARCHED_ITEMS';
+export const RECEIVE_SEARCHED_ITEMS_SUCCESS = 'RECEIVE_SEARCHED_ITEMS_SUCCESS';
+export const RECEIVE_SEARCHED_ITEMS_ERROR = 'RECEIVE_SEARCHED_ITEMS_ERROR';
diff --git a/app/assets/javascripts/frequent_items/store/mutations.js b/app/assets/javascripts/frequent_items/store/mutations.js
new file mode 100644
index 00000000000..41b660a243f
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/mutations.js
@@ -0,0 +1,71 @@
+import * as types from './mutation_types';
+
+export default {
+ [types.SET_NAMESPACE](state, namespace) {
+ Object.assign(state, {
+ namespace,
+ });
+ },
+ [types.SET_STORAGE_KEY](state, storageKey) {
+ Object.assign(state, {
+ storageKey,
+ });
+ },
+ [types.SET_SEARCH_QUERY](state, searchQuery) {
+ const hasSearchQuery = searchQuery !== '';
+
+ Object.assign(state, {
+ searchQuery,
+ isLoadingItems: true,
+ hasSearchQuery,
+ });
+ },
+ [types.REQUEST_FREQUENT_ITEMS](state) {
+ Object.assign(state, {
+ isLoadingItems: true,
+ hasSearchQuery: false,
+ });
+ },
+ [types.RECEIVE_FREQUENT_ITEMS_SUCCESS](state, rawItems) {
+ Object.assign(state, {
+ items: rawItems,
+ isLoadingItems: false,
+ hasSearchQuery: false,
+ isFetchFailed: false,
+ });
+ },
+ [types.RECEIVE_FREQUENT_ITEMS_ERROR](state) {
+ Object.assign(state, {
+ isLoadingItems: false,
+ hasSearchQuery: false,
+ isFetchFailed: true,
+ });
+ },
+ [types.REQUEST_SEARCHED_ITEMS](state) {
+ Object.assign(state, {
+ isLoadingItems: true,
+ hasSearchQuery: true,
+ });
+ },
+ [types.RECEIVE_SEARCHED_ITEMS_SUCCESS](state, rawItems) {
+ Object.assign(state, {
+ items: rawItems.map(rawItem => ({
+ id: rawItem.id,
+ name: rawItem.name,
+ namespace: rawItem.name_with_namespace || rawItem.full_name,
+ webUrl: rawItem.web_url,
+ avatarUrl: rawItem.avatar_url,
+ })),
+ isLoadingItems: false,
+ hasSearchQuery: true,
+ isFetchFailed: false,
+ });
+ },
+ [types.RECEIVE_SEARCHED_ITEMS_ERROR](state) {
+ Object.assign(state, {
+ isLoadingItems: false,
+ hasSearchQuery: true,
+ isFetchFailed: true,
+ });
+ },
+};
diff --git a/app/assets/javascripts/frequent_items/store/state.js b/app/assets/javascripts/frequent_items/store/state.js
new file mode 100644
index 00000000000..75b04febee4
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/state.js
@@ -0,0 +1,8 @@
+export default () => ({
+ namespace: '',
+ storageKey: '',
+ searchQuery: '',
+ isLoadingItems: false,
+ isFetchFailed: false,
+ items: [],
+});
diff --git a/app/assets/javascripts/frequent_items/utils.js b/app/assets/javascripts/frequent_items/utils.js
new file mode 100644
index 00000000000..aba692e4b99
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/utils.js
@@ -0,0 +1,49 @@
+import _ from 'underscore';
+import bp from '~/breakpoints';
+import { FREQUENT_ITEMS, HOUR_IN_MS } from './constants';
+
+export const isMobile = () => {
+ const screenSize = bp.getBreakpointSize();
+
+ return screenSize === 'sm' || screenSize === 'xs';
+};
+
+export const getTopFrequentItems = items => {
+ if (!items) {
+ return [];
+ }
+ const frequentItemsCount = isMobile()
+ ? FREQUENT_ITEMS.LIST_COUNT_MOBILE
+ : FREQUENT_ITEMS.LIST_COUNT_DESKTOP;
+
+ const frequentItems = items.filter(item => item.frequency >= FREQUENT_ITEMS.ELIGIBLE_FREQUENCY);
+
+ if (!frequentItems || frequentItems.length === 0) {
+ return [];
+ }
+
+ frequentItems.sort((itemA, itemB) => {
+ // Sort all frequent items in decending order of frequency
+ // and then by lastAccessedOn with recent most first
+ if (itemA.frequency !== itemB.frequency) {
+ return itemB.frequency - itemA.frequency;
+ } else if (itemA.lastAccessedOn !== itemB.lastAccessedOn) {
+ return itemB.lastAccessedOn - itemA.lastAccessedOn;
+ }
+
+ return 0;
+ });
+
+ return _.first(frequentItems, frequentItemsCount);
+};
+
+export const updateExistingFrequentItem = (frequentItem, item) => {
+ const accessedOverHourAgo =
+ Math.abs(item.lastAccessedOn - frequentItem.lastAccessedOn) / HOUR_IN_MS > 1;
+
+ return {
+ ...item,
+ frequency: accessedOverHourAgo ? frequentItem.frequency + 1 : frequentItem.frequency,
+ lastAccessedOn: accessedOverHourAgo ? Date.now() : frequentItem.lastAccessedOn,
+ };
+};
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 09186a865e4..73b2cd0b2c7 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -12,7 +12,7 @@ export const defaultAutocompleteConfig = {
members: true,
issues: true,
mergeRequests: true,
- epics: false,
+ epics: true,
milestones: true,
labels: true,
};
@@ -493,6 +493,7 @@ GfmAutoComplete.atTypeMap = {
'@': 'members',
'#': 'issues',
'!': 'mergeRequests',
+ '&': 'epics',
'~': 'labels',
'%': 'milestones',
'/': 'commands',
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 8d231e6c405..c3959ef3e9e 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, one-var-declaration-per-line, max-len, vars-on-top, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func */
+/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, one-var-declaration-per-line, max-len, vars-on-top, wrap-iife, no-unused-vars, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func */
/* global fuzzaldrinPlus */
import $ from 'jquery';
@@ -19,32 +19,42 @@ GitLabDropdownInput = (function() {
this.fieldName = this.options.fieldName || 'field-name';
$inputContainer = this.input.parent();
$clearButton = $inputContainer.find('.js-dropdown-input-clear');
- $clearButton.on('click', (function(_this) {
- // Clear click
- return function(e) {
- e.preventDefault();
- e.stopPropagation();
- return _this.input.val('').trigger('input').focus();
- };
- })(this));
+ $clearButton.on(
+ 'click',
+ (function(_this) {
+ // Clear click
+ return function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ return _this.input
+ .val('')
+ .trigger('input')
+ .focus();
+ };
+ })(this),
+ );
this.input
- .on('keydown', function (e) {
- var keyCode = e.which;
- if (keyCode === 13 && !options.elIsInput) {
- e.preventDefault();
- }
- })
- .on('input', function(e) {
- var val = e.currentTarget.value || _this.options.inputFieldName;
- val = val.split(' ').join('-') // replaces space with dash
- .replace(/[^a-zA-Z0-9 -]/g, '').toLowerCase() // replace non alphanumeric
- .replace(/(-)\1+/g, '-'); // replace repeated dashes
- _this.cb(_this.options.fieldName, val, {}, true);
- _this.input.closest('.dropdown')
- .find('.dropdown-toggle-text')
- .text(val);
- });
+ .on('keydown', function(e) {
+ var keyCode = e.which;
+ if (keyCode === 13 && !options.elIsInput) {
+ e.preventDefault();
+ }
+ })
+ .on('input', function(e) {
+ var val = e.currentTarget.value || _this.options.inputFieldName;
+ val = val
+ .split(' ')
+ .join('-') // replaces space with dash
+ .replace(/[^a-zA-Z0-9 -]/g, '')
+ .toLowerCase() // replace non alphanumeric
+ .replace(/(-)\1+/g, '-'); // replace repeated dashes
+ _this.cb(_this.options.fieldName, val, {}, true);
+ _this.input
+ .closest('.dropdown')
+ .find('.dropdown-toggle-text')
+ .text(val);
+ });
}
GitLabDropdownInput.prototype.onInput = function(cb) {
@@ -61,7 +71,7 @@ GitLabDropdownFilter = (function() {
ARROW_KEY_CODES = [38, 40];
- HAS_VALUE_CLASS = "has-value";
+ HAS_VALUE_CLASS = 'has-value';
function GitLabDropdownFilter(input, options) {
var $clearButton, $inputContainer, ref, timeout;
@@ -70,44 +80,59 @@ GitLabDropdownFilter = (function() {
this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true;
$inputContainer = this.input.parent();
$clearButton = $inputContainer.find('.js-dropdown-input-clear');
- $clearButton.on('click', (function(_this) {
- // Clear click
- return function(e) {
- e.preventDefault();
- e.stopPropagation();
- return _this.input.val('').trigger('input').focus();
- };
- })(this));
+ $clearButton.on(
+ 'click',
+ (function(_this) {
+ // Clear click
+ return function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ return _this.input
+ .val('')
+ .trigger('input')
+ .focus();
+ };
+ })(this),
+ );
// Key events
- timeout = "";
+ timeout = '';
this.input
- .on('keydown', function (e) {
+ .on('keydown', function(e) {
var keyCode = e.which;
if (keyCode === 13 && !options.elIsInput) {
e.preventDefault();
}
})
- .on('input', function() {
- if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
- $inputContainer.addClass(HAS_VALUE_CLASS);
- } else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
- $inputContainer.removeClass(HAS_VALUE_CLASS);
- }
- // Only filter asynchronously only if option remote is set
- if (this.options.remote) {
- clearTimeout(timeout);
- return timeout = setTimeout(function() {
- $inputContainer.parent().addClass('is-loading');
-
- return this.options.query(this.input.val(), function(data) {
- $inputContainer.parent().removeClass('is-loading');
- return this.options.callback(data);
- }.bind(this));
- }.bind(this), 250);
- } else {
- return this.filter(this.input.val());
- }
- }.bind(this));
+ .on(
+ 'input',
+ function() {
+ if (this.input.val() !== '' && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
+ $inputContainer.addClass(HAS_VALUE_CLASS);
+ } else if (this.input.val() === '' && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
+ $inputContainer.removeClass(HAS_VALUE_CLASS);
+ }
+ // Only filter asynchronously only if option remote is set
+ if (this.options.remote) {
+ clearTimeout(timeout);
+ return (timeout = setTimeout(
+ function() {
+ $inputContainer.parent().addClass('is-loading');
+
+ return this.options.query(
+ this.input.val(),
+ function(data) {
+ $inputContainer.parent().removeClass('is-loading');
+ return this.options.callback(data);
+ }.bind(this),
+ );
+ }.bind(this),
+ 250,
+ ));
+ } else {
+ return this.filter(this.input.val());
+ }
+ }.bind(this),
+ );
}
GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) {
@@ -120,7 +145,7 @@ GitLabDropdownFilter = (function() {
this.options.onFilter(search_text);
}
data = this.options.data();
- if ((data != null) && !this.options.filterByText) {
+ if (data != null && !this.options.filterByText) {
results = data;
if (search_text !== '') {
// When data is an array of objects therefore [object Array] e.g.
@@ -130,7 +155,7 @@ GitLabDropdownFilter = (function() {
// ]
if (_.isArray(data)) {
results = fuzzaldrinPlus.filter(data, search_text, {
- key: this.options.keys
+ key: this.options.keys,
});
} else {
// If data is grouped therefore an [object Object]. e.g.
@@ -149,7 +174,7 @@ GitLabDropdownFilter = (function() {
for (key in data) {
group = data[key];
tmp = fuzzaldrinPlus.filter(group, search_text, {
- key: this.options.keys
+ key: this.options.keys,
});
if (tmp.length) {
results[key] = tmp.map(function(item) {
@@ -180,7 +205,10 @@ GitLabDropdownFilter = (function() {
elements.show().removeClass('option-hidden');
}
- elements.parent().find('.dropdown-menu-empty-item').toggleClass('hidden', elements.is(':visible'));
+ elements
+ .parent()
+ .find('.dropdown-menu-empty-item')
+ .toggleClass('hidden', elements.is(':visible'));
}
};
@@ -194,23 +222,26 @@ GitLabDropdownRemote = (function() {
}
GitLabDropdownRemote.prototype.execute = function() {
- if (typeof this.dataEndpoint === "string") {
+ if (typeof this.dataEndpoint === 'string') {
return this.fetchData();
- } else if (typeof this.dataEndpoint === "function") {
+ } else if (typeof this.dataEndpoint === 'function') {
if (this.options.beforeSend) {
this.options.beforeSend();
}
- return this.dataEndpoint("", (function(_this) {
- // Fetch the data by calling the data funcfion
- return function(data) {
- if (_this.options.success) {
- _this.options.success(data);
- }
- if (_this.options.beforeSend) {
- return _this.options.beforeSend();
- }
- };
- })(this));
+ return this.dataEndpoint(
+ '',
+ (function(_this) {
+ // Fetch the data by calling the data funcfion
+ return function(data) {
+ if (_this.options.success) {
+ _this.options.success(data);
+ }
+ if (_this.options.beforeSend) {
+ return _this.options.beforeSend();
+ }
+ };
+ })(this),
+ );
}
};
@@ -220,33 +251,41 @@ GitLabDropdownRemote = (function() {
}
// Fetch the data through ajax if the data is a string
- return axios.get(this.dataEndpoint)
- .then(({ data }) => {
- if (this.options.success) {
- return this.options.success(data);
- }
- });
+ return axios.get(this.dataEndpoint).then(({ data }) => {
+ if (this.options.success) {
+ return this.options.success(data);
+ }
+ });
};
return GitLabDropdownRemote;
})();
GitLabDropdown = (function() {
- var ACTIVE_CLASS, FILTER_INPUT, NO_FILTER_INPUT, INDETERMINATE_CLASS, LOADING_CLASS, PAGE_TWO_CLASS, NON_SELECTABLE_CLASSES, SELECTABLE_CLASSES, CURSOR_SELECT_SCROLL_PADDING, currentIndex;
+ var ACTIVE_CLASS,
+ FILTER_INPUT,
+ NO_FILTER_INPUT,
+ INDETERMINATE_CLASS,
+ LOADING_CLASS,
+ PAGE_TWO_CLASS,
+ NON_SELECTABLE_CLASSES,
+ SELECTABLE_CLASSES,
+ CURSOR_SELECT_SCROLL_PADDING,
+ currentIndex;
- LOADING_CLASS = "is-loading";
+ LOADING_CLASS = 'is-loading';
- PAGE_TWO_CLASS = "is-page-two";
+ PAGE_TWO_CLASS = 'is-page-two';
- ACTIVE_CLASS = "is-active";
+ ACTIVE_CLASS = 'is-active';
- INDETERMINATE_CLASS = "is-indeterminate";
+ INDETERMINATE_CLASS = 'is-indeterminate';
currentIndex = -1;
NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item';
- SELECTABLE_CLASSES = ".dropdown-content li:not(" + NON_SELECTABLE_CLASSES + ", .option-hidden)";
+ SELECTABLE_CLASSES = '.dropdown-content li:not(' + NON_SELECTABLE_CLASSES + ', .option-hidden)';
CURSOR_SELECT_SCROLL_PADDING = 5;
@@ -263,15 +302,15 @@ GitLabDropdown = (function() {
this.opened = this.opened.bind(this);
this.shouldPropagate = this.shouldPropagate.bind(this);
self = this;
- selector = $(this.el).data("target");
+ selector = $(this.el).data('target');
this.dropdown = selector != null ? $(selector) : $(this.el).parent();
// Set Defaults
this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT);
this.noFilterInput = this.options.noFilterInput || this.getElement(NO_FILTER_INPUT);
this.highlight = !!this.options.highlight;
- this.filterInputBlur = this.options.filterInputBlur != null
- ? this.options.filterInputBlur
- : true;
+ this.icon = !!this.options.icon;
+ this.filterInputBlur =
+ this.options.filterInputBlur != null ? this.options.filterInputBlur : true;
// If no input is passed create a default one
self = this;
// If selector was passed
@@ -296,11 +335,17 @@ GitLabDropdown = (function() {
_this.fullData = data;
_this.parseData(_this.fullData);
_this.focusTextInput();
- if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val() && _this.filter.input.val().trim() !== '') {
+ if (
+ _this.options.filterable &&
+ _this.filter &&
+ _this.filter.input &&
+ _this.filter.input.val() &&
+ _this.filter.input.val().trim() !== ''
+ ) {
return _this.filter.input.trigger('input');
}
};
- // Remote data
+ // Remote data
})(this),
instance: this,
});
@@ -325,7 +370,7 @@ GitLabDropdown = (function() {
return function() {
selector = '.dropdown-content li:not(' + NON_SELECTABLE_CLASSES + ')';
if (_this.dropdown.find('.dropdown-toggle-page').length) {
- selector = ".dropdown-page-one " + selector;
+ selector = '.dropdown-page-one ' + selector;
}
return $(selector, this.instance.dropdown);
};
@@ -341,80 +386,97 @@ GitLabDropdown = (function() {
if (_this.filterInput.val() !== '') {
selector = SELECTABLE_CLASSES;
if (_this.dropdown.find('.dropdown-toggle-page').length) {
- selector = ".dropdown-page-one " + selector;
+ selector = '.dropdown-page-one ' + selector;
}
if ($(_this.el).is('input')) {
currentIndex = -1;
} else {
- $(selector, _this.dropdown).first().find('a').addClass('is-focused');
+ $(selector, _this.dropdown)
+ .first()
+ .find('a')
+ .addClass('is-focused');
currentIndex = 0;
}
}
};
- })(this)
+ })(this),
});
}
// Event listeners
- this.dropdown.on("shown.bs.dropdown", this.opened);
- this.dropdown.on("hidden.bs.dropdown", this.hidden);
- $(this.el).on("update.label", this.updateLabel);
- this.dropdown.on("click", ".dropdown-menu, .dropdown-menu-close", this.shouldPropagate);
- this.dropdown.on('keyup', (function(_this) {
- return function(e) {
- // Escape key
- if (e.which === 27) {
- return $('.dropdown-menu-close', _this.dropdown).trigger('click');
- }
- };
- })(this));
- this.dropdown.on('blur', 'a', (function(_this) {
- return function(e) {
- var $dropdownMenu, $relatedTarget;
- if (e.relatedTarget != null) {
- $relatedTarget = $(e.relatedTarget);
- $dropdownMenu = $relatedTarget.closest('.dropdown-menu');
- if ($dropdownMenu.length === 0) {
- return _this.dropdown.removeClass('show');
+ this.dropdown.on('shown.bs.dropdown', this.opened);
+ this.dropdown.on('hidden.bs.dropdown', this.hidden);
+ $(this.el).on('update.label', this.updateLabel);
+ this.dropdown.on('click', '.dropdown-menu, .dropdown-menu-close', this.shouldPropagate);
+ this.dropdown.on(
+ 'keyup',
+ (function(_this) {
+ return function(e) {
+ // Escape key
+ if (e.which === 27) {
+ return $('.dropdown-menu-close', _this.dropdown).trigger('click');
}
- }
- };
- })(this));
- if (this.dropdown.find(".dropdown-toggle-page").length) {
- this.dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on("click", (function(_this) {
+ };
+ })(this),
+ );
+ this.dropdown.on(
+ 'blur',
+ 'a',
+ (function(_this) {
return function(e) {
- e.preventDefault();
- e.stopPropagation();
- return _this.togglePage();
+ var $dropdownMenu, $relatedTarget;
+ if (e.relatedTarget != null) {
+ $relatedTarget = $(e.relatedTarget);
+ $dropdownMenu = $relatedTarget.closest('.dropdown-menu');
+ if ($dropdownMenu.length === 0) {
+ return _this.dropdown.removeClass('show');
+ }
+ }
};
- })(this));
+ })(this),
+ );
+ if (this.dropdown.find('.dropdown-toggle-page').length) {
+ this.dropdown.find('.dropdown-toggle-page, .dropdown-menu-back').on(
+ 'click',
+ (function(_this) {
+ return function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ return _this.togglePage();
+ };
+ })(this),
+ );
}
if (this.options.selectable) {
- selector = ".dropdown-content a";
- if (this.dropdown.find(".dropdown-toggle-page").length) {
- selector = ".dropdown-page-one .dropdown-content a";
+ selector = '.dropdown-content a';
+ if (this.dropdown.find('.dropdown-toggle-page').length) {
+ selector = '.dropdown-page-one .dropdown-content a';
}
- this.dropdown.on("click", selector, function(e) {
- var $el, selected, selectedObj, isMarking;
- $el = $(e.currentTarget);
- selected = self.rowClicked($el);
- selectedObj = selected ? selected[0] : null;
- isMarking = selected ? selected[1] : null;
- if (this.options.clicked) {
- this.options.clicked.call(this, {
- selectedObj,
- $el,
- e,
- isMarking,
- });
- }
+ this.dropdown.on(
+ 'click',
+ selector,
+ function(e) {
+ var $el, selected, selectedObj, isMarking;
+ $el = $(e.currentTarget);
+ selected = self.rowClicked($el);
+ selectedObj = selected ? selected[0] : null;
+ isMarking = selected ? selected[1] : null;
+ if (this.options.clicked) {
+ this.options.clicked.call(this, {
+ selectedObj,
+ $el,
+ e,
+ isMarking,
+ });
+ }
- // Update label right after all modifications in dropdown has been done
- if (this.options.toggleLabel) {
- this.updateLabel(selectedObj, $el, this);
- }
+ // Update label right after all modifications in dropdown has been done
+ if (this.options.toggleLabel) {
+ this.updateLabel(selectedObj, $el, this);
+ }
- $el.trigger('blur');
- }.bind(this));
+ $el.trigger('blur');
+ }.bind(this),
+ );
}
}
@@ -452,10 +514,15 @@ GitLabDropdown = (function() {
html = [];
for (name in data) {
groupData = data[name];
- html.push(this.renderItem({
- header: name
- // Add header for each group
- }, name));
+ html.push(
+ this.renderItem(
+ {
+ header: name,
+ // Add header for each group
+ },
+ name,
+ ),
+ );
this.renderData(groupData, name).map(function(item) {
return html.push(item);
});
@@ -474,20 +541,25 @@ GitLabDropdown = (function() {
if (group == null) {
group = false;
}
- return data.map((function(_this) {
- return function(obj, index) {
- return _this.renderItem(obj, group, index);
- };
- })(this));
+ return data.map(
+ (function(_this) {
+ return function(obj, index) {
+ return _this.renderItem(obj, group, index);
+ };
+ })(this),
+ );
};
GitLabDropdown.prototype.shouldPropagate = function(e) {
var $target;
if (this.options.multiSelect || this.options.shouldPropagate === false) {
$target = $(e.target);
- if ($target && !$target.hasClass('dropdown-menu-close') &&
- !$target.hasClass('dropdown-menu-close-icon') &&
- !$target.data('isLink')) {
+ if (
+ $target &&
+ !$target.hasClass('dropdown-menu-close') &&
+ !$target.hasClass('dropdown-menu-close-icon') &&
+ !$target.data('isLink')
+ ) {
e.stopPropagation();
return false;
} else {
@@ -497,9 +569,11 @@ GitLabDropdown = (function() {
};
GitLabDropdown.prototype.filteredFullData = function() {
- return this.fullData.filter(r => typeof r === 'object'
- && !Object.prototype.hasOwnProperty.call(r, 'beforeDivider')
- && !Object.prototype.hasOwnProperty.call(r, 'header')
+ return this.fullData.filter(
+ r =>
+ typeof r === 'object' &&
+ !Object.prototype.hasOwnProperty.call(r, 'beforeDivider') &&
+ !Object.prototype.hasOwnProperty.call(r, 'header'),
);
};
@@ -522,11 +596,16 @@ GitLabDropdown = (function() {
// matches the correct layout
const inputValue = this.filterInput.val();
if (this.fullData && hasMultiSelect && this.options.processData && inputValue.length === 0) {
- this.options.processData.call(this.options, inputValue, this.filteredFullData(), this.parseData.bind(this));
+ this.options.processData.call(
+ this.options,
+ inputValue,
+ this.filteredFullData(),
+ this.parseData.bind(this),
+ );
}
contentHtml = $('.dropdown-content', this.dropdown).html();
- if (this.remote && contentHtml === "") {
+ if (this.remote && contentHtml === '') {
this.remote.execute();
} else {
this.focusTextInput();
@@ -537,7 +616,11 @@ GitLabDropdown = (function() {
}
if (this.options.opened) {
- this.options.opened.call(this, e);
+ if (this.options.preserveContext) {
+ this.options.opened(e);
+ } else {
+ this.options.opened.call(this, e);
+ }
}
return this.dropdown.trigger('shown.gl.dropdown');
@@ -555,11 +638,11 @@ GitLabDropdown = (function() {
var $input;
this.resetRows();
this.removeArrayKeyEvent();
- $input = this.dropdown.find(".dropdown-input-field");
+ $input = this.dropdown.find('.dropdown-input-field');
if (this.options.filterable) {
$input.blur();
}
- if (this.dropdown.find(".dropdown-toggle-page").length) {
+ if (this.dropdown.find('.dropdown-toggle-page').length) {
$('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS);
}
if (this.options.hidden) {
@@ -601,7 +684,7 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.clearMenu = function() {
var selector;
selector = '.dropdown-content';
- if (this.dropdown.find(".dropdown-toggle-page").length) {
+ if (this.dropdown.find('.dropdown-toggle-page').length) {
if (this.options.containerSelector) {
selector = this.options.containerSelector;
} else {
@@ -619,7 +702,7 @@ GitLabDropdown = (function() {
value = this.options.id ? this.options.id(data) : data.id;
if (value) {
- value = value.toString().replace(/'/g, '\\\'');
+ value = value.toString().replace(/'/g, "\\'");
}
}
@@ -676,21 +759,27 @@ GitLabDropdown = (function() {
text = data.text != null ? data.text : '';
}
if (this.highlight) {
- text = this.highlightTextMatches(text, this.filterInput.val());
+ text = data.template
+ ? this.highlightTemplate(text, data.template)
+ : this.highlightTextMatches(text, this.filterInput.val());
}
// Create the list item & the link
var link = document.createElement('a');
link.href = url;
- if (this.highlight) {
+ if (this.icon) {
+ text = `<span>${text}</span>`;
+ link.classList.add('d-flex', 'align-items-center');
+ link.innerHTML = data.icon ? data.icon + text : text;
+ } else if (this.highlight) {
link.innerHTML = text;
} else {
link.textContent = text;
}
if (selected) {
- link.className = 'is-active';
+ link.classList.add('is-active');
}
if (group) {
@@ -703,17 +792,24 @@ GitLabDropdown = (function() {
return html;
};
+ GitLabDropdown.prototype.highlightTemplate = function(text, template) {
+ return `"<b>${_.escape(text)}</b>" ${template}`;
+ };
+
GitLabDropdown.prototype.highlightTextMatches = function(text, term) {
const occurrences = fuzzaldrinPlus.match(text, term);
const { indexOf } = [];
- return text.split('').map(function(character, i) {
- if (indexOf.call(occurrences, i) !== -1) {
- return "<b>" + character + "</b>";
- } else {
- return character;
- }
- }).join('');
+ return text
+ .split('')
+ .map(function(character, i) {
+ if (indexOf.call(occurrences, i) !== -1) {
+ return '<b>' + character + '</b>';
+ } else {
+ return character;
+ }
+ })
+ .join('');
};
GitLabDropdown.prototype.noResults = function() {
@@ -748,13 +844,15 @@ GitLabDropdown = (function() {
}
field = [];
- value = this.options.id
- ? this.options.id(selectedObject, el)
- : selectedObject.id;
+ value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id;
if (isInput) {
field = $(this.el);
} else if (value != null) {
- field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']");
+ field = this.dropdown
+ .parent()
+ .find(
+ "input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, "\\'") + "']",
+ );
}
if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) {
@@ -780,9 +878,12 @@ GitLabDropdown = (function() {
} else {
isMarking = true;
if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) {
- this.dropdown.find("." + ACTIVE_CLASS).removeClass(ACTIVE_CLASS);
+ this.dropdown.find('.' + ACTIVE_CLASS).removeClass(ACTIVE_CLASS);
if (!isInput) {
- this.dropdown.parent().find("input[name='" + fieldName + "']").remove();
+ this.dropdown
+ .parent()
+ .find("input[name='" + fieldName + "']")
+ .remove();
}
}
if (field && field.length && value == null) {
@@ -823,13 +924,16 @@ GitLabDropdown = (function() {
$('input[name="' + fieldName + '"]').remove();
}
- $input = $('<input>').attr('type', 'hidden').attr('name', fieldName).val(value);
+ $input = $('<input>')
+ .attr('type', 'hidden')
+ .attr('name', fieldName)
+ .val(value);
if (this.options.inputId != null) {
$input.attr('id', this.options.inputId);
}
if (this.options.multiSelect) {
- Object.keys(selectedObject).forEach((attribute) => {
+ Object.keys(selectedObject).forEach(attribute => {
$input.attr(`data-${attribute}`, selectedObject[attribute]);
});
}
@@ -844,13 +948,13 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.selectRowAtIndex = function(index) {
var $el, selector;
// If we pass an option index
- if (typeof index !== "undefined") {
- selector = SELECTABLE_CLASSES + ":eq(" + index + ") a";
+ if (typeof index !== 'undefined') {
+ selector = SELECTABLE_CLASSES + ':eq(' + index + ') a';
} else {
- selector = ".dropdown-content .is-focused";
+ selector = '.dropdown-content .is-focused';
}
- if (this.dropdown.find(".dropdown-toggle-page").length) {
- selector = ".dropdown-page-one " + selector;
+ if (this.dropdown.find('.dropdown-toggle-page').length) {
+ selector = '.dropdown-page-one ' + selector;
}
// simulate a click on the first link
$el = $(selector, this.dropdown);
@@ -867,44 +971,47 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.addArrowKeyEvent = function() {
var $input, ARROW_KEY_CODES, selector;
ARROW_KEY_CODES = [38, 40];
- $input = this.dropdown.find(".dropdown-input-field");
+ $input = this.dropdown.find('.dropdown-input-field');
selector = SELECTABLE_CLASSES;
- if (this.dropdown.find(".dropdown-toggle-page").length) {
- selector = ".dropdown-page-one " + selector;
- }
- return $('body').on('keydown', (function(_this) {
- return function(e) {
- var $listItems, PREV_INDEX, currentKeyCode;
- currentKeyCode = e.which;
- if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) {
- e.preventDefault();
- e.stopImmediatePropagation();
- PREV_INDEX = currentIndex;
- $listItems = $(selector, _this.dropdown);
- // if @options.filterable
- // $input.blur()
- if (currentKeyCode === 40) {
- // Move down
- if (currentIndex < ($listItems.length - 1)) {
- currentIndex += 1;
+ if (this.dropdown.find('.dropdown-toggle-page').length) {
+ selector = '.dropdown-page-one ' + selector;
+ }
+ return $('body').on(
+ 'keydown',
+ (function(_this) {
+ return function(e) {
+ var $listItems, PREV_INDEX, currentKeyCode;
+ currentKeyCode = e.which;
+ if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ PREV_INDEX = currentIndex;
+ $listItems = $(selector, _this.dropdown);
+ // if @options.filterable
+ // $input.blur()
+ if (currentKeyCode === 40) {
+ // Move down
+ if (currentIndex < $listItems.length - 1) {
+ currentIndex += 1;
+ }
+ } else if (currentKeyCode === 38) {
+ // Move up
+ if (currentIndex > 0) {
+ currentIndex -= 1;
+ }
}
- } else if (currentKeyCode === 38) {
- // Move up
- if (currentIndex > 0) {
- currentIndex -= 1;
+ if (currentIndex !== PREV_INDEX) {
+ _this.highlightRowAtIndex($listItems, currentIndex);
}
+ return false;
}
- if (currentIndex !== PREV_INDEX) {
- _this.highlightRowAtIndex($listItems, currentIndex);
+ if (currentKeyCode === 13 && currentIndex !== -1) {
+ e.preventDefault();
+ _this.selectRowAtIndex();
}
- return false;
- }
- if (currentKeyCode === 13 && currentIndex !== -1) {
- e.preventDefault();
- _this.selectRowAtIndex();
- }
- };
- })(this));
+ };
+ })(this),
+ );
};
GitLabDropdown.prototype.removeArrayKeyEvent = function() {
@@ -917,12 +1024,25 @@ GitLabDropdown = (function() {
};
GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) {
- var $dropdownContent, $listItem, dropdownContentBottom, dropdownContentHeight, dropdownContentTop, dropdownScrollTop, listItemBottom, listItemHeight, listItemTop;
+ var $dropdownContent,
+ $listItem,
+ dropdownContentBottom,
+ dropdownContentHeight,
+ dropdownContentTop,
+ dropdownScrollTop,
+ listItemBottom,
+ listItemHeight,
+ listItemTop;
+
+ if (!$listItems) {
+ $listItems = $(SELECTABLE_CLASSES, this.dropdown);
+ }
+
// Remove the class for the previously focused row
$('.is-focused', this.dropdown).removeClass('is-focused');
// Update the class for the row at the specific index
$listItem = $listItems.eq(index);
- $listItem.find('a:first-child').addClass("is-focused");
+ $listItem.find('a:first-child').addClass('is-focused');
// Dropdown content scroll area
$dropdownContent = $listItem.closest('.dropdown-content');
dropdownScrollTop = $dropdownContent.scrollTop();
@@ -936,15 +1056,19 @@ GitLabDropdown = (function() {
if (!index) {
// Scroll the dropdown content to the top
$dropdownContent.scrollTop(0);
- } else if (index === ($listItems.length - 1)) {
+ } else if (index === $listItems.length - 1) {
// Scroll the dropdown content to the bottom
$dropdownContent.scrollTop($dropdownContent.prop('scrollHeight'));
- } else if (listItemBottom > (dropdownContentBottom + dropdownScrollTop)) {
+ } else if (listItemBottom > dropdownContentBottom + dropdownScrollTop) {
// Scroll the dropdown content down
- $dropdownContent.scrollTop(listItemBottom - dropdownContentBottom + CURSOR_SELECT_SCROLL_PADDING);
- } else if (listItemTop < (dropdownContentTop + dropdownScrollTop)) {
+ $dropdownContent.scrollTop(
+ listItemBottom - dropdownContentBottom + CURSOR_SELECT_SCROLL_PADDING,
+ );
+ } else if (listItemTop < dropdownContentTop + dropdownScrollTop) {
// Scroll the dropdown content up
- return $dropdownContent.scrollTop(listItemTop - dropdownContentTop - CURSOR_SELECT_SCROLL_PADDING);
+ return $dropdownContent.scrollTop(
+ listItemTop - dropdownContentTop - CURSOR_SELECT_SCROLL_PADDING,
+ );
}
};
@@ -965,7 +1089,9 @@ GitLabDropdown = (function() {
toggleText = this.options.updateLabel;
}
- return $(this.el).find(".dropdown-toggle-text").text(toggleText);
+ return $(this.el)
+ .find('.dropdown-toggle-text')
+ .text(toggleText);
};
GitLabDropdown.prototype.clearField = function(field, isInput) {
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index f802971a3ca..c74de7ac34d 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -9,6 +9,13 @@ export default class GLForm {
this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input');
this.enableGFM = Object.assign({}, GFMConfig.defaultAutocompleteConfig, enableGFM);
+ // Disable autocomplete for keywords which do not have dataSources available
+ const dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {};
+ Object.keys(this.enableGFM).forEach(item => {
+ if (item !== 'emojis') {
+ this.enableGFM[item] = !!dataSources[item];
+ }
+ });
// Before we start, we should clean up any previous data for this form
this.destroy();
// Setup the form
diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js
index 029fd6a67d4..efba6fc1aff 100644
--- a/app/assets/javascripts/gpg_badges.js
+++ b/app/assets/javascripts/gpg_badges.js
@@ -1,23 +1,36 @@
import $ from 'jquery';
import { parseQueryStringIntoObject } from '~/lib/utils/common_utils';
import axios from '~/lib/utils/axios_utils';
-import flash from '~/flash';
+import createFlash from '~/flash';
import { __ } from '~/locale';
export default class GpgBadges {
static fetch() {
- const badges = $('.js-loading-gpg-badge');
const tag = $('.js-signature-container');
+ if (tag.length === 0) {
+ return Promise.resolve();
+ }
+
+ const badges = $('.js-loading-gpg-badge');
badges.html('<i class="fa fa-spinner fa-spin"></i>');
+ const displayError = () => createFlash(__('An error occurred while loading commit signatures'));
+
+ const endpoint = tag.data('signaturesPath');
+ if (!endpoint) {
+ displayError();
+ return Promise.reject(new Error('Missing commit signatures endpoint!'));
+ }
+
const params = parseQueryStringIntoObject(tag.serialize());
- return axios.get(tag.data('signaturesPath'), { params })
- .then(({ data }) => {
- data.signatures.forEach((signature) => {
- badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
- });
- })
- .catch(() => flash(__('An error occurred while loading commits')));
+ return axios
+ .get(endpoint, { params })
+ .then(({ data }) => {
+ data.signatures.forEach(signature => {
+ badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
+ });
+ })
+ .catch(displayError);
}
}
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index efbf2e3a295..2b9e2a929fc 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -78,17 +78,10 @@ export default {
>
<div
:class="{ 'project-row-contents': !isGroup }"
- class="group-row-contents">
- <item-actions
- v-if="isGroup"
- :group="group"
- :parent-group="parentGroup"
- />
- <item-stats
- :item="group"
- />
+ class="group-row-contents d-flex justify-content-end align-items-center"
+ >
<div
- class="folder-toggle-wrap"
+ class="folder-toggle-wrap append-right-4 d-flex align-items-center"
>
<item-caret
:is-group-open="group.isOpen"
@@ -100,7 +93,7 @@ export default {
</div>
<div
:class="{ 'content-loading': group.isChildrenLoading }"
- class="avatar-container prepend-top-8 prepend-left-5 s24 d-none d-sm-block"
+ class="avatar-container s24 d-none d-sm-block"
>
<a
:href="group.relativePath"
@@ -120,32 +113,46 @@ export default {
</a>
</div>
<div
- class="title namespace-title"
+ class="group-text flex-grow"
>
- <a
- v-tooltip
- :href="group.relativePath"
- :title="group.fullName"
- class="no-expand"
- data-placement="bottom"
- >{{
- // ending bracket must be by closing tag to prevent
- // link hover text-decoration from over-extending
- group.name
- }}</a>
- <span
- v-if="group.permission"
- class="user-access-role"
+ <div
+ class="title namespace-title append-right-8"
>
- {{ group.permission }}
- </span>
- </div>
- <div
- v-if="group.description"
- class="description">
- <span v-html="group.description">
- </span>
+ <a
+ v-tooltip
+ :href="group.relativePath"
+ :title="group.fullName"
+ class="no-expand"
+ data-placement="bottom"
+ >{{
+ // ending bracket must be by closing tag to prevent
+ // link hover text-decoration from over-extending
+ group.name
+ }}</a>
+ <span
+ v-if="group.permission"
+ class="user-access-role"
+ >
+ {{ group.permission }}
+ </span>
+ </div>
+ <div
+ v-if="group.description"
+ class="description"
+ >
+ <span v-html="group.description">
+ </span>
+ </div>
</div>
+ <item-stats
+ :item="group"
+ class="group-stats prepend-top-2"
+ />
+ <item-actions
+ v-if="isGroup"
+ :group="group"
+ :parent-group="parentGroup"
+ />
</div>
<group-folder
v-if="group.isOpen && hasChildren"
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index 310f6fe06cf..e37fc5c4be6 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -12,6 +12,7 @@ export default function groupsSelect() {
const skipGroups = $select.data('skipGroups') || [];
$select.select2({
placeholder: 'Search for a group',
+ allowClear: $select.hasClass('allowClear'),
multiple: $select.hasClass('multiselect'),
minimumInputLength: 0,
ajax: {
diff --git a/app/assets/javascripts/helpers/avatar_helper.js b/app/assets/javascripts/helpers/avatar_helper.js
new file mode 100644
index 00000000000..d3b1d0f11fd
--- /dev/null
+++ b/app/assets/javascripts/helpers/avatar_helper.js
@@ -0,0 +1,33 @@
+import _ from 'underscore';
+import { getFirstCharacterCapitalized } from '~/lib/utils/text_utility';
+
+export const DEFAULT_SIZE_CLASS = 's40';
+export const IDENTICON_BG_COUNT = 7;
+
+export function getIdenticonBackgroundClass(entityId) {
+ const type = (entityId % IDENTICON_BG_COUNT) + 1;
+ return `bg${type}`;
+}
+
+export function getIdenticonTitle(entityName) {
+ return getFirstCharacterCapitalized(entityName) || ' ';
+}
+
+export function renderIdenticon(entity, options = {}) {
+ const { sizeClass = DEFAULT_SIZE_CLASS } = options;
+
+ const bgClass = getIdenticonBackgroundClass(entity.id);
+ const title = getIdenticonTitle(entity.name);
+
+ return `<div class="avatar identicon ${_.escape(sizeClass)} ${_.escape(bgClass)}">${_.escape(title)}</div>`;
+}
+
+export function renderAvatar(entity, options = {}) {
+ if (!entity.avatar_url) {
+ return renderIdenticon(entity, options);
+ }
+
+ const { sizeClass = DEFAULT_SIZE_CLASS } = options;
+
+ return `<img src="${_.escape(entity.avatar_url)}" class="avatar ${_.escape(sizeClass)}" />`;
+}
diff --git a/app/assets/javascripts/ide/components/activity_bar.vue b/app/assets/javascripts/ide/components/activity_bar.vue
index 62697e0ecc3..2cebacc1c4c 100644
--- a/app/assets/javascripts/ide/components/activity_bar.vue
+++ b/app/assets/javascripts/ide/components/activity_bar.vue
@@ -13,11 +13,8 @@ export default {
tooltip,
},
computed: {
- ...mapGetters(['currentProject', 'hasChanges']),
+ ...mapGetters(['hasChanges']),
...mapState(['currentActivityView']),
- goBackUrl() {
- return document.referrer || this.currentProject.web_url;
- },
},
methods: {
...mapActions(['updateActivityBarView']),
@@ -36,22 +33,6 @@ export default {
<template>
<nav class="ide-activity-bar">
<ul class="list-unstyled">
- <li v-once>
- <a
- v-tooltip
- :href="goBackUrl"
- :title="s__('IDE|Go back')"
- :aria-label="s__('IDE|Go back')"
- data-container="body"
- data-placement="right"
- class="ide-sidebar-link"
- >
- <icon
- :size="16"
- name="go-back"
- />
- </a>
- </li>
<li>
<button
v-tooltip
diff --git a/app/assets/javascripts/ide/components/branches/item.vue b/app/assets/javascripts/ide/components/branches/item.vue
new file mode 100644
index 00000000000..cc3e84e3f77
--- /dev/null
+++ b/app/assets/javascripts/ide/components/branches/item.vue
@@ -0,0 +1,60 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import Timeago from '~/vue_shared/components/time_ago_tooltip.vue';
+import router from '../../ide_router';
+
+export default {
+ components: {
+ Icon,
+ Timeago,
+ },
+ props: {
+ item: {
+ type: Object,
+ required: true,
+ },
+ projectId: {
+ type: String,
+ required: true,
+ },
+ isActive: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ branchHref() {
+ return router.resolve(`/project/${this.projectId}/edit/${this.item.name}`).href;
+ },
+ },
+};
+</script>
+
+<template>
+ <a
+ :href="branchHref"
+ class="btn-link d-flex align-items-center"
+ >
+ <span class="d-flex append-right-default ide-search-list-current-icon">
+ <icon
+ v-if="isActive"
+ :size="18"
+ name="mobile-issue-close"
+ />
+ </span>
+ <span>
+ <strong>
+ {{ item.name }}
+ </strong>
+ <span
+ class="ide-merge-request-project-path d-block mt-1"
+ >
+ Updated
+ <timeago
+ :time="item.committedDate || ''"
+ />
+ </span>
+ </span>
+ </a>
+</template>
diff --git a/app/assets/javascripts/ide/components/branches/search_list.vue b/app/assets/javascripts/ide/components/branches/search_list.vue
new file mode 100644
index 00000000000..6db7b9d6b0e
--- /dev/null
+++ b/app/assets/javascripts/ide/components/branches/search_list.vue
@@ -0,0 +1,111 @@
+<script>
+import { mapActions, mapState } from 'vuex';
+import _ from 'underscore';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import Item from './item.vue';
+
+export default {
+ components: {
+ LoadingIcon,
+ Item,
+ Icon,
+ },
+ data() {
+ return {
+ search: '',
+ };
+ },
+ computed: {
+ ...mapState('branches', ['branches', 'isLoading']),
+ ...mapState(['currentBranchId', 'currentProjectId']),
+ hasBranches() {
+ return this.branches.length !== 0;
+ },
+ hasNoSearchResults() {
+ return this.search !== '' && !this.hasBranches;
+ },
+ },
+ watch: {
+ isLoading: {
+ handler: 'focusSearch',
+ },
+ },
+ mounted() {
+ this.loadBranches();
+ },
+ methods: {
+ ...mapActions('branches', ['fetchBranches']),
+ loadBranches() {
+ this.fetchBranches({ search: this.search });
+ },
+ searchBranches: _.debounce(function debounceSearch() {
+ this.loadBranches();
+ }, 250),
+ focusSearch() {
+ if (!this.isLoading) {
+ this.$nextTick(() => {
+ this.$refs.searchInput.focus();
+ });
+ }
+ },
+ isActiveBranch(item) {
+ return item.name === this.currentBranchId;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div class="dropdown-input mt-3 pb-3 mb-0 border-bottom">
+ <div class="position-relative">
+ <input
+ ref="searchInput"
+ :placeholder="__('Search branches')"
+ v-model="search"
+ type="search"
+ class="form-control dropdown-input-field"
+ @input="searchBranches"
+ />
+ <icon
+ :size="18"
+ name="search"
+ class="input-icon"
+ />
+ </div>
+ </div>
+ <div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
+ <loading-icon
+ v-if="isLoading"
+ class="mt-3 mb-3 align-self-center ml-auto mr-auto"
+ size="2"
+ />
+ <ul
+ v-else
+ class="mb-3 w-100"
+ >
+ <template v-if="hasBranches">
+ <li
+ v-for="item in branches"
+ :key="item.name"
+ >
+ <item
+ :item="item"
+ :project-id="currentProjectId"
+ :is-active="isActiveBranch(item)"
+ />
+ </li>
+ </template>
+ <li
+ v-else
+ class="ide-search-list-empty d-flex align-items-center justify-content-center"
+ >
+ <template v-if="hasNoSearchResults">
+ {{ __('No branches found') }}
+ </template>
+ </li>
+ </ul>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/changed_file_icon.vue b/app/assets/javascripts/ide/components/changed_file_icon.vue
index a4e06bbbe3c..720ae11aaa6 100644
--- a/app/assets/javascripts/ide/components/changed_file_icon.vue
+++ b/app/assets/javascripts/ide/components/changed_file_icon.vue
@@ -3,6 +3,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import { pluralize } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
+import { getCommitIconMap } from '../utils';
export default {
components: {
@@ -34,16 +35,14 @@ export default {
},
computed: {
changedIcon() {
- const suffix = this.file.staged && !this.showStagedIcon ? '-solid' : '';
- return this.file.tempFile && !this.forceModifiedIcon
- ? `file-addition${suffix}`
- : `file-modified${suffix}`;
- },
- stagedIcon() {
- return `${this.changedIcon}-solid`;
+ const suffix = !this.file.changed && this.file.staged && !this.showStagedIcon ? '-solid' : '';
+
+ if (this.forceModifiedIcon) return `file-modified${suffix}`;
+
+ return `${getCommitIconMap(this.file).icon}${suffix}`;
},
changedIconClass() {
- return `multi-${this.changedIcon} float-left`;
+ return `ide-${this.changedIcon} float-left`;
},
tooltipTitle() {
if (!this.showTooltip) return undefined;
@@ -66,6 +65,9 @@ export default {
return undefined;
},
+ showIcon() {
+ return this.file.changed || this.file.tempFile || this.file.staged || this.file.deleted;
+ },
},
};
</script>
@@ -79,7 +81,7 @@ export default {
class="ide-file-changed-icon"
>
<icon
- v-if="file.changed || file.tempFile || file.staged"
+ v-if="showIcon"
:name="changedIcon"
:size="12"
:css-classes="changedIconClass"
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
index eb7cb9745ec..a8b5c7a16d0 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import { mapActions, mapState, mapGetters } from 'vuex';
import { sprintf, __ } from '~/locale';
import * as consts from '../../stores/modules/commit/constants';
@@ -14,7 +15,7 @@ export default {
commitToCurrentBranchText() {
return sprintf(
__('Commit to %{branchName} branch'),
- { branchName: `<strong class="monospace">${this.currentBranchId}</strong>` },
+ { branchName: `<strong class="monospace">${_.escape(this.currentBranchId)}</strong>` },
false,
);
},
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index ee21eeda3cd..391004dcd3c 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -5,6 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import StageButton from './stage_button.vue';
import UnstageButton from './unstage_button.vue';
import { viewerTypes } from '../../constants';
+import { getCommitIconMap } from '../../utils';
export default {
components: {
@@ -42,11 +43,12 @@ export default {
},
computed: {
iconName() {
- const prefix = this.stagedList ? '-solid' : '';
- return this.file.tempFile ? `file-addition${prefix}` : `file-modified${prefix}`;
+ const suffix = this.stagedList ? '-solid' : '';
+
+ return `${getCommitIconMap(this.file).icon}${suffix}`;
},
iconClass() {
- return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
+ return `${getCommitIconMap(this.file).class} append-right-8`;
},
fullKey() {
return `${this.keyPrefix}-${this.file.key}`;
@@ -67,6 +69,8 @@ export default {
'stageChange',
]),
openFileInEditor() {
+ if (this.file.type === 'tree') return null;
+
return this.openPendingTab({
file: this.file,
keyPrefix: this.keyPrefix,
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
index 7014b9f605e..e6044401c9f 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
@@ -56,7 +56,7 @@ export default {
>
<icon
:size="12"
- name="more"
+ name="ellipsis_h"
/>
</button>
<div class="dropdown-menu dropdown-menu-right">
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index 9f016e0338f..2c8305aa0cc 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,6 +1,8 @@
<script>
import Mousetrap from 'mousetrap';
import { mapActions, mapState, mapGetters } from 'vuex';
+import { __ } from '~/locale';
+import NewModal from './new_dropdown/modal.vue';
import IdeSidebar from './ide_side_bar.vue';
import RepoTabs from './repo_tabs.vue';
import IdeStatusBar from './ide_status_bar.vue';
@@ -13,6 +15,7 @@ const originalStopCallback = Mousetrap.stopCallback;
export default {
components: {
+ NewModal,
IdeSidebar,
RepoTabs,
IdeStatusBar,
@@ -23,7 +26,6 @@ export default {
},
computed: {
...mapState([
- 'changedFiles',
'openFiles',
'viewer',
'currentMergeRequestId',
@@ -32,18 +34,10 @@ export default {
'currentProjectId',
'errorMessage',
]),
- ...mapGetters(['activeFile', 'hasChanges']),
+ ...mapGetters(['activeFile', 'hasChanges', 'someUncommitedChanges']),
},
mounted() {
- const returnValue = 'Are you sure you want to lose unsaved changes?';
- window.onbeforeunload = e => {
- if (!this.changedFiles.length) return undefined;
-
- Object.assign(e, {
- returnValue,
- });
- return returnValue;
- };
+ window.onbeforeunload = e => this.onBeforeUnload(e);
Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => {
if (e.preventDefault) {
@@ -57,6 +51,16 @@ export default {
},
methods: {
...mapActions(['toggleFileFinder']),
+ onBeforeUnload(e = {}) {
+ const returnValue = __('Are you sure you want to lose unsaved changes?');
+
+ if (!this.someUncommitedChanges) return undefined;
+
+ Object.assign(e, {
+ returnValue,
+ });
+ return returnValue;
+ },
mousetrapStopCallback(e, el, combo) {
if (
(combo === 't' && el.classList.contains('dropdown-input-field')) ||
@@ -137,5 +141,6 @@ export default {
/>
</div>
<ide-status-bar :file="activeFile"/>
+ <new-modal />
</article>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_project_header.vue b/app/assets/javascripts/ide/components/ide_project_header.vue
new file mode 100644
index 00000000000..6cf190288e8
--- /dev/null
+++ b/app/assets/javascripts/ide/components/ide_project_header.vue
@@ -0,0 +1,37 @@
+<script>
+import ProjectAvatarDefault from '~/vue_shared/components/project_avatar/default.vue';
+
+export default {
+ components: {
+ ProjectAvatarDefault,
+ },
+ props: {
+ project: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="context-header ide-context-header">
+ <a
+ :href="project.web_url"
+ :title="s__('IDE|Go to project')"
+ >
+ <project-avatar-default
+ :project="project"
+ :size="48"
+ />
+ <span class="ide-sidebar-project-title">
+ <span class="sidebar-context-title">
+ {{ project.name }}
+ </span>
+ <span class="sidebar-context-title text-secondary">
+ {{ project.path_with_namespace }}
+ </span>
+ </span>
+ </a>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/ide_review.vue b/app/assets/javascripts/ide/components/ide_review.vue
index f9978762c45..d09c99050fe 100644
--- a/app/assets/javascripts/ide/components/ide_review.vue
+++ b/app/assets/javascripts/ide/components/ide_review.vue
@@ -10,7 +10,7 @@ export default {
EditorModeDropdown,
},
computed: {
- ...mapGetters(['currentMergeRequest']),
+ ...mapGetters(['currentMergeRequest', 'activeFile']),
...mapState(['viewer', 'currentMergeRequestId']),
showLatestChangesText() {
return !this.currentMergeRequestId || this.viewer === viewerTypes.diff;
@@ -23,12 +23,20 @@ export default {
},
},
mounted() {
+ if (this.activeFile && this.activeFile.pending && !this.activeFile.deleted) {
+ this.$router.push(`/project${this.activeFile.url}`, () => {
+ this.updateViewer('editor');
+ });
+ } else if (this.activeFile && this.activeFile.deleted) {
+ this.resetOpenFiles();
+ }
+
this.$nextTick(() => {
this.updateViewer(this.currentMergeRequestId ? viewerTypes.mr : viewerTypes.diff);
});
},
methods: {
- ...mapActions(['updateViewer']),
+ ...mapActions(['updateViewer', 'resetOpenFiles']),
},
};
</script>
@@ -36,7 +44,6 @@ export default {
<template>
<ide-tree-list
:viewer-type="viewer"
- :disable-action-dropdown="true"
header-class="ide-review-header"
>
<template
diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
index 21906674c4b..4771c58a11d 100644
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_side_bar.vue
@@ -1,12 +1,6 @@
<script>
-import $ from 'jquery';
import { mapState, mapGetters } from 'vuex';
-import ProjectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
-import Icon from '~/vue_shared/components/icon.vue';
-import tooltip from '~/vue_shared/directives/tooltip';
-import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import Identicon from '../../vue_shared/components/identicon.vue';
import IdeTree from './ide_tree.vue';
import ResizablePanel from './resizable_panel.vue';
import ActivityBar from './activity_bar.vue';
@@ -14,43 +8,28 @@ import CommitSection from './repo_commit_section.vue';
import CommitForm from './commit_sidebar/form.vue';
import IdeReview from './ide_review.vue';
import SuccessMessage from './commit_sidebar/success_message.vue';
-import MergeRequestDropdown from './merge_requests/dropdown.vue';
+import IdeProjectHeader from './ide_project_header.vue';
import { activityBarViews } from '../constants';
export default {
- directives: {
- tooltip,
- },
components: {
- Icon,
- PanelResizer,
SkeletonLoadingContainer,
ResizablePanel,
ActivityBar,
- ProjectAvatarImage,
- Identicon,
CommitSection,
IdeTree,
CommitForm,
IdeReview,
SuccessMessage,
- MergeRequestDropdown,
- },
- data() {
- return {
- showTooltip: false,
- showMergeRequestsDropdown: false,
- };
+ IdeProjectHeader,
},
computed: {
...mapState([
'loading',
- 'currentBranchId',
'currentActivityView',
'changedFiles',
'stagedFiles',
'lastCommitMsg',
- 'currentMergeRequestId',
]),
...mapGetters(['currentProject', 'someUncommitedChanges']),
showSuccessMessage() {
@@ -59,46 +38,6 @@ export default {
(this.lastCommitMsg && !this.someUncommitedChanges)
);
},
- branchTooltipTitle() {
- return this.showTooltip ? this.currentBranchId : undefined;
- },
- },
- watch: {
- currentBranchId() {
- this.$nextTick(() => {
- if (!this.$refs.branchId) return;
-
- this.showTooltip = this.$refs.branchId.scrollWidth > this.$refs.branchId.offsetWidth;
- });
- },
- loading() {
- this.$nextTick(() => {
- this.addDropdownListeners();
- });
- },
- },
- mounted() {
- this.addDropdownListeners();
- },
- beforeDestroy() {
- $(this.$refs.mergeRequestDropdown)
- .off('show.bs.dropdown')
- .off('hide.bs.dropdown');
- },
- methods: {
- addDropdownListeners() {
- if (!this.$refs.mergeRequestDropdown) return;
-
- $(this.$refs.mergeRequestDropdown)
- .on('show.bs.dropdown', () => {
- this.toggleMergeRequestDropdown();
- }).on('hide.bs.dropdown', () => {
- this.toggleMergeRequestDropdown();
- });
- },
- toggleMergeRequestDropdown() {
- this.showMergeRequestsDropdown = !this.showMergeRequestsDropdown;
- },
},
};
</script>
@@ -108,12 +47,10 @@ export default {
:collapsible="false"
:initial-width="340"
side="left"
+ class="flex-column"
>
- <activity-bar
- v-if="!loading"
- />
- <div class="multi-file-commit-panel-inner">
- <template v-if="loading">
+ <template v-if="loading">
+ <div class="multi-file-commit-panel-inner">
<div
v-for="n in 3"
:key="n"
@@ -121,81 +58,23 @@ export default {
>
<skeleton-loading-container />
</div>
- </template>
- <template v-else>
- <div
- ref="mergeRequestDropdown"
- class="context-header ide-context-header dropdown"
- >
- <button
- type="button"
- data-toggle="dropdown"
- >
- <div
- v-if="currentProject.avatar_url"
- class="avatar-container s40 project-avatar"
- >
- <project-avatar-image
- :link-href="currentProject.path"
- :img-src="currentProject.avatar_url"
- :img-alt="currentProject.name"
- :img-size="40"
- class="avatar-container project-avatar"
- />
- </div>
- <identicon
- v-else
- :entity-id="currentProject.id"
- :entity-name="currentProject.name"
- size-class="s40"
+ </div>
+ </template>
+ <template v-else>
+ <ide-project-header
+ :project="currentProject"
+ />
+ <div class="ide-context-body d-flex flex-fill">
+ <activity-bar />
+ <div class="multi-file-commit-panel-inner">
+ <div class="multi-file-commit-panel-inner-content">
+ <component
+ :is="currentActivityView"
/>
- <div class="ide-sidebar-project-title">
- <div class="sidebar-context-title">
- {{ currentProject.name }}
- </div>
- <div class="d-flex">
- <div
- v-tooltip
- v-if="currentBranchId"
- ref="branchId"
- :title="branchTooltipTitle"
- class="sidebar-context-title ide-sidebar-branch-title"
- >
- <icon
- name="branch"
- css-classes="append-right-5"
- />{{ currentBranchId }}
- </div>
- <div
- v-if="currentMergeRequestId"
- :class="{
- 'prepend-left-8': currentBranchId
- }"
- class="sidebar-context-title ide-sidebar-branch-title"
- >
- <icon
- name="git-merge"
- css-classes="append-right-5"
- />!{{ currentMergeRequestId }}
- </div>
- </div>
- </div>
- <icon
- class="ml-auto"
- name="chevron-down"
- />
- </button>
- <merge-request-dropdown
- :show="showMergeRequestsDropdown"
- />
- </div>
- <div class="multi-file-commit-panel-inner-scroll">
- <component
- :is="currentActivityView"
- />
+ </div>
+ <commit-form />
</div>
- <commit-form />
- </template>
- </div>
+ </div>
+ </template>
</resizable-panel>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index 0582ad32e92..715dc1bfb42 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -5,6 +5,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import timeAgoMixin from '~/vue_shared/mixins/timeago';
import CiIcon from '../../vue_shared/components/ci_icon.vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
+import { rightSidebarViews } from '../constants';
export default {
components: {
@@ -49,6 +50,7 @@ export default {
this.stopPipelinePolling();
},
methods: {
+ ...mapActions(['setRightPane']),
...mapActions('pipelines', ['fetchLatestPipeline', 'stopPipelinePolling']),
startTimer() {
this.intervalId = setInterval(() => {
@@ -69,24 +71,31 @@ export default {
return `${this.currentProject.web_url}/commit/${shortSha}`;
},
},
+ rightSidebarViews,
};
</script>
<template>
<footer class="ide-status-bar">
<div
- v-if="lastCommit && lastCommitFormatedAge"
+ v-if="lastCommit"
class="ide-status-branch"
>
<span
v-if="latestPipeline && latestPipeline.details"
class="ide-status-pipeline"
>
- <ci-icon
- v-tooltip
- :status="latestPipeline.details.status"
- :title="latestPipeline.details.status.text"
- />
+ <button
+ type="button"
+ class="p-0 border-0 h-50"
+ @click="setRightPane($options.rightSidebarViews.pipelines)"
+ >
+ <ci-icon
+ v-tooltip
+ :status="latestPipeline.details.status"
+ :title="latestPipeline.details.status.text"
+ />
+ </button>
Pipeline
<a
:href="latestPipeline.details.status.details_path"
diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue
index 8fc4ebe6ca6..39d46a91731 100644
--- a/app/assets/javascripts/ide/components/ide_tree.vue
+++ b/app/assets/javascripts/ide/components/ide_tree.vue
@@ -1,26 +1,34 @@
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
-import NewDropdown from './new_dropdown/index.vue';
+import Icon from '~/vue_shared/components/icon.vue';
import IdeTreeList from './ide_tree_list.vue';
+import Upload from './new_dropdown/upload.vue';
+import NewEntryButton from './new_dropdown/button.vue';
export default {
components: {
- NewDropdown,
+ Icon,
+ Upload,
IdeTreeList,
+ NewEntryButton,
},
computed: {
...mapState(['currentBranchId']),
...mapGetters(['currentProject', 'currentTree', 'activeFile']),
},
mounted() {
- if (this.activeFile && this.activeFile.pending) {
+ if (!this.activeFile) return;
+
+ if (this.activeFile.pending && !this.activeFile.deleted) {
this.$router.push(`/project${this.activeFile.url}`, () => {
this.updateViewer('editor');
});
+ } else if (this.activeFile.deleted) {
+ this.resetOpenFiles();
}
},
methods: {
- ...mapActions(['updateViewer']),
+ ...mapActions(['updateViewer', 'openNewEntryModal', 'createTempEntry', 'resetOpenFiles']),
},
};
</script>
@@ -33,10 +41,28 @@ export default {
slot="header"
>
{{ __('Edit') }}
- <new-dropdown
- :project-id="currentProject.name_with_namespace"
- :branch="currentBranchId"
- />
+ <div class="ide-tree-actions ml-auto d-flex">
+ <new-entry-button
+ :label="__('New file')"
+ :show-label="false"
+ class="d-flex border-0 p-0 mr-3"
+ icon="doc-new"
+ @click="openNewEntryModal({ type: 'blob' })"
+ />
+ <upload
+ :show-label="false"
+ class="d-flex mr-3"
+ button-css-classes="border-0 p-0"
+ @create="createTempEntry"
+ />
+ <new-entry-button
+ :label="__('New directory')"
+ :show-label="false"
+ class="d-flex border-0 p-0"
+ icon="folder-new"
+ @click="openNewEntryModal({ type: 'tree' })"
+ />
+ </div>
</template>
</ide-tree-list>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index 0df99798d21..5611b37be7c 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -3,14 +3,14 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import RepoFile from './repo_file.vue';
-import NewDropdown from './new_dropdown/index.vue';
+import NavDropdown from './nav_dropdown.vue';
export default {
components: {
Icon,
RepoFile,
SkeletonLoadingContainer,
- NewDropdown,
+ NavDropdown,
},
props: {
viewerType: {
@@ -22,11 +22,6 @@ export default {
required: false,
default: null,
},
- disableActionDropdown: {
- type: Boolean,
- required: false,
- default: false,
- },
},
computed: {
...mapState(['currentBranchId']),
@@ -62,15 +57,19 @@ export default {
:class="headerClass"
class="ide-tree-header"
>
+ <nav-dropdown />
<slot name="header"></slot>
</header>
- <repo-file
- v-for="file in currentTree.tree"
- :key="file.key"
- :file="file"
- :level="0"
- :disable-action-dropdown="disableActionDropdown"
- />
+ <div
+ class="ide-tree-body"
+ >
+ <repo-file
+ v-for="file in currentTree.tree"
+ :key="file.key"
+ :file="file"
+ :level="0"
+ />
+ </div>
</template>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue
index f39ce545656..f884c26ed6a 100644
--- a/app/assets/javascripts/ide/components/jobs/detail.vue
+++ b/app/assets/javascripts/ide/components/jobs/detail.vue
@@ -131,6 +131,9 @@ export default {
v-show="detailJob.isLoading"
class="build-loader-animation"
>
+ <div class="dot"></div>
+ <div class="dot"></div>
+ <div class="dot"></div>
</div>
</pre>
</div>
diff --git a/app/assets/javascripts/ide/components/merge_requests/dropdown.vue b/app/assets/javascripts/ide/components/merge_requests/dropdown.vue
deleted file mode 100644
index 4b9824bf04b..00000000000
--- a/app/assets/javascripts/ide/components/merge_requests/dropdown.vue
+++ /dev/null
@@ -1,63 +0,0 @@
-<script>
-import { mapGetters } from 'vuex';
-import Tabs from '../../../vue_shared/components/tabs/tabs';
-import Tab from '../../../vue_shared/components/tabs/tab.vue';
-import List from './list.vue';
-
-export default {
- components: {
- Tabs,
- Tab,
- List,
- },
- props: {
- show: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- ...mapGetters('mergeRequests', ['assignedData', 'createdData']),
- createdMergeRequestLength() {
- return this.createdData.mergeRequests.length;
- },
- assignedMergeRequestLength() {
- return this.assignedData.mergeRequests.length;
- },
- },
-};
-</script>
-
-<template>
- <div class="dropdown-menu ide-merge-requests-dropdown p-0">
- <tabs
- v-if="show"
- stop-propagation
- >
- <tab active>
- <template slot="title">
- {{ __('Created by me') }}
- <span class="badge badge-pill">
- {{ createdMergeRequestLength }}
- </span>
- </template>
- <list
- :empty-text="__('You have not created any merge requests')"
- type="created"
- />
- </tab>
- <tab>
- <template slot="title">
- {{ __('Assigned to me') }}
- <span class="badge badge-pill">
- {{ assignedMergeRequestLength }}
- </span>
- </template>
- <list
- :empty-text="__('You do not have any assigned merge requests')"
- type="assigned"
- />
- </tab>
- </tabs>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/merge_requests/info.vue b/app/assets/javascripts/ide/components/merge_requests/info.vue
new file mode 100644
index 00000000000..199d2e74971
--- /dev/null
+++ b/app/assets/javascripts/ide/components/merge_requests/info.vue
@@ -0,0 +1,43 @@
+<script>
+import { mapGetters } from 'vuex';
+import Icon from '../../../vue_shared/components/icon.vue';
+import TitleComponent from '../../../issue_show/components/title.vue';
+import DescriptionComponent from '../../../issue_show/components/description.vue';
+
+export default {
+ components: {
+ Icon,
+ TitleComponent,
+ DescriptionComponent,
+ },
+ computed: {
+ ...mapGetters(['currentMergeRequest']),
+ },
+};
+</script>
+
+<template>
+ <div class="ide-merge-request-info h-100 d-flex flex-column">
+ <div class="detail-page-header">
+ <icon
+ name="git-merge"
+ class="align-self-center append-right-8"
+ />
+ <strong>
+ !{{ currentMergeRequest.iid }}
+ </strong>
+ </div>
+ <div class="issuable-details">
+ <title-component
+ :issuable-ref="currentMergeRequest.iid"
+ :title-html="currentMergeRequest.title_html"
+ :title-text="currentMergeRequest.title"
+ />
+ <description-component
+ :description-html="currentMergeRequest.description_html"
+ :description-text="currentMergeRequest.description"
+ :can-update="false"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/merge_requests/item.vue b/app/assets/javascripts/ide/components/merge_requests/item.vue
index 4e18376bd48..0c4ea80ba08 100644
--- a/app/assets/javascripts/ide/components/merge_requests/item.vue
+++ b/app/assets/javascripts/ide/components/merge_requests/item.vue
@@ -1,5 +1,6 @@
<script>
import Icon from '../../../vue_shared/components/icon.vue';
+import router from '../../ide_router';
export default {
components: {
@@ -29,22 +30,21 @@ export default {
pathWithID() {
return `${this.item.projectPathWithNamespace}!${this.item.iid}`;
},
- },
- methods: {
- clickItem() {
- this.$emit('click', this.item);
+ mergeRequestHref() {
+ const path = `/project/${this.item.projectPathWithNamespace}/merge_requests/${this.item.iid}`;
+
+ return router.resolve(path).href;
},
},
};
</script>
<template>
- <button
- type="button"
+ <a
+ :href="mergeRequestHref"
class="btn-link d-flex align-items-center"
- @click="clickItem"
>
- <span class="d-flex append-right-default ide-merge-request-current-icon">
+ <span class="d-flex append-right-default ide-search-list-current-icon">
<icon
v-if="isActive"
:size="18"
@@ -59,5 +59,5 @@ export default {
{{ pathWithID }}
</span>
</span>
- </button>
+ </a>
</template>
diff --git a/app/assets/javascripts/ide/components/merge_requests/list.vue b/app/assets/javascripts/ide/components/merge_requests/list.vue
index 19d3e48ee10..fc612956688 100644
--- a/app/assets/javascripts/ide/components/merge_requests/list.vue
+++ b/app/assets/javascripts/ide/components/merge_requests/list.vue
@@ -1,96 +1,101 @@
<script>
-import { mapActions, mapGetters, mapState } from 'vuex';
+import { mapActions, mapState } from 'vuex';
import _ from 'underscore';
-import LoadingIcon from '../../../vue_shared/components/loading_icon.vue';
+import { __ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Item from './item.vue';
+import TokenedInput from '../shared/tokened_input.vue';
+
+const SEARCH_TYPES = [
+ { type: 'created', label: __('Created by me') },
+ { type: 'assigned', label: __('Assigned to me') },
+];
export default {
components: {
LoadingIcon,
+ TokenedInput,
Item,
- },
- props: {
- type: {
- type: String,
- required: true,
- },
- emptyText: {
- type: String,
- required: true,
- },
+ Icon,
},
data() {
return {
search: '',
+ currentSearchType: null,
+ hasSearchFocus: false,
};
},
computed: {
- ...mapGetters('mergeRequests', ['getData']),
+ ...mapState('mergeRequests', ['mergeRequests', 'isLoading']),
...mapState(['currentMergeRequestId', 'currentProjectId']),
- data() {
- return this.getData(this.type);
- },
- isLoading() {
- return this.data.isLoading;
- },
- mergeRequests() {
- return this.data.mergeRequests;
- },
hasMergeRequests() {
return this.mergeRequests.length !== 0;
},
hasNoSearchResults() {
return this.search !== '' && !this.hasMergeRequests;
},
+ showSearchTypes() {
+ return this.hasSearchFocus && !this.search && !this.currentSearchType;
+ },
+ type() {
+ return this.currentSearchType
+ ? this.currentSearchType.type
+ : '';
+ },
+ searchTokens() {
+ return this.currentSearchType
+ ? [this.currentSearchType]
+ : [];
+ },
},
watch: {
- isLoading: {
- handler: 'focusSearch',
+ search() {
+ // When the search is updated, let's turn off this flag to hide the search types
+ this.hasSearchFocus = false;
},
},
mounted() {
this.loadMergeRequests();
},
methods: {
- ...mapActions('mergeRequests', ['fetchMergeRequests', 'openMergeRequest']),
+ ...mapActions('mergeRequests', ['fetchMergeRequests']),
loadMergeRequests() {
this.fetchMergeRequests({ type: this.type, search: this.search });
},
- viewMergeRequest(item) {
- this.openMergeRequest({
- projectPath: item.projectPathWithNamespace,
- id: item.iid,
- });
- },
searchMergeRequests: _.debounce(function debounceSearch() {
this.loadMergeRequests();
}, 250),
- focusSearch() {
- if (!this.isLoading) {
- this.$nextTick(() => {
- this.$refs.searchInput.focus();
- });
- }
+ onSearchFocus() {
+ this.hasSearchFocus = true;
+ },
+ setSearchType(searchType) {
+ this.currentSearchType = searchType;
+ this.loadMergeRequests();
},
},
+ searchTypes: SEARCH_TYPES,
};
</script>
<template>
<div>
<div class="dropdown-input mt-3 pb-3 mb-0 border-bottom">
- <input
- ref="searchInput"
- :placeholder="__('Search merge requests')"
- v-model="search"
- type="search"
- class="dropdown-input-field"
- @input="searchMergeRequests"
- />
- <i
- aria-hidden="true"
- class="fa fa-search dropdown-input-search"
- ></i>
+ <div class="position-relative">
+ <tokened-input
+ v-model="search"
+ :tokens="searchTokens"
+ :placeholder="__('Search merge requests')"
+ @focus="onSearchFocus"
+ @input="searchMergeRequests"
+ @removeToken="setSearchType(null)"
+ />
+ <icon
+ :size="18"
+ name="search"
+ class="input-icon"
+ />
+ </div>
</div>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
<loading-icon
@@ -98,35 +103,52 @@ export default {
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
size="2"
/>
- <ul
- v-else
- class="mb-3 w-100"
- >
- <template v-if="hasMergeRequests">
- <li
- v-for="item in mergeRequests"
- :key="item.id"
- >
- <item
- :item="item"
- :current-id="currentMergeRequestId"
- :current-project-id="currentProjectId"
- @click="viewMergeRequest"
- />
- </li>
- </template>
- <li
- v-else
- class="ide-merge-requests-empty d-flex align-items-center justify-content-center"
+ <template v-else>
+ <ul
+ class="mb-3 w-100"
>
- <template v-if="hasNoSearchResults">
- {{ __('No merge requests found') }}
+ <template v-if="showSearchTypes">
+ <li
+ v-for="searchType in $options.searchTypes"
+ :key="searchType.type"
+ >
+ <button
+ type="button"
+ class="btn-link d-flex align-items-center"
+ @click.stop="setSearchType(searchType)"
+ >
+ <span class="d-flex append-right-default ide-search-list-current-icon">
+ <icon
+ :size="18"
+ name="search"
+ />
+ </span>
+ <span>
+ {{ searchType.label }}
+ </span>
+ </button>
+ </li>
</template>
- <template v-else>
- {{ emptyText }}
+ <template v-else-if="hasMergeRequests">
+ <li
+ v-for="item in mergeRequests"
+ :key="item.id"
+ >
+ <item
+ :item="item"
+ :current-id="currentMergeRequestId"
+ :current-project-id="currentProjectId"
+ />
+ </li>
</template>
- </li>
- </ul>
+ <li
+ v-else
+ class="ide-search-list-empty d-flex align-items-center justify-content-center"
+ >
+ {{ __('No merge requests found') }}
+ </li>
+ </ul>
+ </template>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/nav_dropdown.vue b/app/assets/javascripts/ide/components/nav_dropdown.vue
new file mode 100644
index 00000000000..db36779c395
--- /dev/null
+++ b/app/assets/javascripts/ide/components/nav_dropdown.vue
@@ -0,0 +1,59 @@
+<script>
+import $ from 'jquery';
+import Icon from '~/vue_shared/components/icon.vue';
+import NavForm from './nav_form.vue';
+import NavDropdownButton from './nav_dropdown_button.vue';
+
+export default {
+ components: {
+ Icon,
+ NavDropdownButton,
+ NavForm,
+ },
+ data() {
+ return {
+ isVisibleDropdown: false,
+ };
+ },
+ mounted() {
+ this.addDropdownListeners();
+ },
+ beforeDestroy() {
+ this.removeDropdownListeners();
+ },
+ methods: {
+ addDropdownListeners() {
+ $(this.$refs.dropdown)
+ .on('show.bs.dropdown', () => this.showDropdown())
+ .on('hide.bs.dropdown', () => this.hideDropdown());
+ },
+ removeDropdownListeners() {
+ $(this.$refs.dropdown)
+ .off('show.bs.dropdown')
+ .off('hide.bs.dropdown');
+ },
+ showDropdown() {
+ this.isVisibleDropdown = true;
+ },
+ hideDropdown() {
+ this.isVisibleDropdown = false;
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ ref="dropdown"
+ class="btn-group ide-nav-dropdown dropdown"
+ >
+ <nav-dropdown-button />
+ <div
+ class="dropdown-menu dropdown-menu-left p-0"
+ >
+ <nav-form
+ v-if="isVisibleDropdown"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/nav_dropdown_button.vue b/app/assets/javascripts/ide/components/nav_dropdown_button.vue
new file mode 100644
index 00000000000..7f98769d484
--- /dev/null
+++ b/app/assets/javascripts/ide/components/nav_dropdown_button.vue
@@ -0,0 +1,54 @@
+<script>
+import { mapState } from 'vuex';
+import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+
+const EMPTY_LABEL = '-';
+
+export default {
+ components: {
+ Icon,
+ DropdownButton,
+ },
+ computed: {
+ ...mapState(['currentBranchId', 'currentMergeRequestId']),
+ mergeRequestLabel() {
+ return this.currentMergeRequestId
+ ? `!${this.currentMergeRequestId}`
+ : EMPTY_LABEL;
+ },
+ branchLabel() {
+ return this.currentBranchId || EMPTY_LABEL;
+ },
+ },
+};
+</script>
+
+<template>
+ <dropdown-button>
+ <span
+ class="row"
+ >
+ <span
+ class="col-7 text-truncate"
+ >
+ <icon
+ :size="16"
+ :aria-label="__('Current Branch')"
+ name="branch"
+ />
+ {{ branchLabel }}
+ </span>
+ <span
+ class="col-5 pl-0 text-truncate"
+ >
+ <icon
+ :size="16"
+ :aria-label="__('Merge Request')"
+ name="merge-request"
+ />
+ {{ mergeRequestLabel }}
+ </span>
+ </span>
+ </dropdown-button>
+</template>
diff --git a/app/assets/javascripts/ide/components/nav_form.vue b/app/assets/javascripts/ide/components/nav_form.vue
new file mode 100644
index 00000000000..718b836e11c
--- /dev/null
+++ b/app/assets/javascripts/ide/components/nav_form.vue
@@ -0,0 +1,40 @@
+<script>
+import Tabs from '~/vue_shared/components/tabs/tabs';
+import Tab from '~/vue_shared/components/tabs/tab.vue';
+import BranchesSearchList from './branches/search_list.vue';
+import MergeRequestSearchList from './merge_requests/list.vue';
+
+export default {
+ components: {
+ Tabs,
+ Tab,
+ BranchesSearchList,
+ MergeRequestSearchList,
+ },
+};
+</script>
+
+<template>
+ <div
+ class="ide-nav-form p-0"
+ >
+ <tabs
+ stop-propagation
+ >
+ <tab
+ active
+ >
+ <template slot="title">
+ {{ __('Merge Requests') }}
+ </template>
+ <merge-request-search-list />
+ </tab>
+ <tab>
+ <template slot="title">
+ {{ __('Branches') }}
+ </template>
+ <branches-search-list />
+ </tab>
+ </tabs>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/button.vue b/app/assets/javascripts/ide/components/new_dropdown/button.vue
new file mode 100644
index 00000000000..ff114e47741
--- /dev/null
+++ b/app/assets/javascripts/ide/components/new_dropdown/button.vue
@@ -0,0 +1,52 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ components: {
+ Icon,
+ },
+ props: {
+ label: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ icon: {
+ type: String,
+ required: true,
+ },
+ iconClasses: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ showLabel: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ },
+ methods: {
+ clicked() {
+ this.$emit('click');
+ },
+ },
+};
+</script>
+
+<template>
+ <button
+ :aria-label="label"
+ type="button"
+ class="btn-blank"
+ @click.stop.prevent="clicked"
+ >
+ <icon
+ :name="icon"
+ :css-classes="iconClasses"
+ />
+ <template v-if="showLabel">
+ {{ label }}
+ </template>
+ </button>
+</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index 1e398d7e1aa..f02fd6cf7ea 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -3,15 +3,18 @@ import { mapActions } from 'vuex';
import icon from '~/vue_shared/components/icon.vue';
import newModal from './modal.vue';
import upload from './upload.vue';
+import ItemButton from './button.vue';
+import { modalTypes } from '../../constants';
export default {
components: {
icon,
newModal,
upload,
+ ItemButton,
},
props: {
- branch: {
+ type: {
type: String,
required: true,
},
@@ -20,35 +23,41 @@ export default {
required: false,
default: '',
},
+ mouseOver: {
+ type: Boolean,
+ required: true,
+ },
},
data() {
return {
- openModal: false,
- modalType: '',
dropdownOpen: false,
};
},
watch: {
dropdownOpen() {
this.$nextTick(() => {
- this.$refs.dropdownMenu.scrollIntoView();
+ this.$refs.dropdownMenu.scrollIntoView({
+ block: 'nearest',
+ });
});
},
+ mouseOver() {
+ if (!this.mouseOver) {
+ this.dropdownOpen = false;
+ }
+ },
},
methods: {
- ...mapActions(['createTempEntry']),
+ ...mapActions(['createTempEntry', 'openNewEntryModal', 'deleteEntry']),
createNewItem(type) {
- this.modalType = type;
- this.openModal = true;
+ this.openNewEntryModal({ type, path: this.path });
this.dropdownOpen = false;
},
- hideModal() {
- this.openModal = false;
- },
openDropdown() {
this.dropdownOpen = !this.dropdownOpen;
},
},
+ modalTypes,
};
</script>
@@ -58,63 +67,71 @@ export default {
:class="{
show: dropdownOpen,
}"
- class="dropdown"
+ class="dropdown d-flex"
>
<button
+ :aria-label="__('Create new file or directory')"
type="button"
- class="btn btn-sm btn-default dropdown-toggle add-to-tree"
- aria-label="Create new file or directory"
+ class="rounded border-0 d-flex ide-entry-dropdown-toggle"
@click.stop="openDropdown()"
>
<icon
- :size="12"
- name="plus"
- css-classes="float-left"
+ name="ellipsis_v"
/>
<icon
- :size="12"
name="arrow-down"
- css-classes="float-left"
/>
</button>
<ul
ref="dropdownMenu"
class="dropdown-menu dropdown-menu-right"
>
+ <template v-if="type === 'tree'">
+ <li>
+ <item-button
+ :label="__('New file')"
+ class="d-flex"
+ icon="doc-new"
+ icon-classes="mr-2"
+ @click="createNewItem('blob')"
+ />
+ </li>
+ <li>
+ <upload
+ :path="path"
+ @create="createTempEntry"
+ />
+ </li>
+ <li>
+ <item-button
+ :label="__('New directory')"
+ class="d-flex"
+ icon="folder-new"
+ icon-classes="mr-2"
+ @click="createNewItem($options.modalTypes.tree)"
+ />
+ </li>
+ <li class="divider"></li>
+ </template>
<li>
- <a
- href="#"
- role="button"
- @click.stop.prevent="createNewItem('blob')"
- >
- {{ __('New file') }}
- </a>
- </li>
- <li>
- <upload
- :branch-id="branch"
- :path="path"
- @create="createTempEntry"
+ <item-button
+ :label="__('Rename')"
+ class="d-flex"
+ icon="pencil"
+ icon-classes="mr-2"
+ @click="createNewItem($options.modalTypes.rename)"
/>
</li>
<li>
- <a
- href="#"
- role="button"
- @click.stop.prevent="createNewItem('tree')"
- >
- {{ __('New directory') }}
- </a>
+ <item-button
+ :label="__('Delete')"
+ class="d-flex"
+ icon="remove"
+ icon-classes="mr-2"
+ @click="deleteEntry(path)"
+ />
</li>
</ul>
</div>
- <new-modal
- v-if="openModal"
- :type="modalType"
- :branch-id="branch"
- :path="path"
- @hide="hideModal"
- @create="createTempEntry"
- />
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index 1e9668d5154..e500ef0e1b5 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -1,80 +1,90 @@
<script>
import { __ } from '~/locale';
-import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
+import { mapActions, mapState } from 'vuex';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
+import { modalTypes } from '../../constants';
export default {
components: {
- DeprecatedModal,
- },
- props: {
- branchId: {
- type: String,
- required: true,
- },
- type: {
- type: String,
- required: true,
- },
- path: {
- type: String,
- required: true,
- },
+ GlModal,
},
data() {
return {
- entryName: this.path !== '' ? `${this.path}/` : '',
+ name: '',
};
},
computed: {
+ ...mapState(['entryModal']),
+ entryName: {
+ get() {
+ if (this.entryModal.type === modalTypes.rename) {
+ return this.name || this.entryModal.entry.name;
+ }
+
+ return this.name || (this.entryModal.path !== '' ? `${this.entryModal.path}/` : '');
+ },
+ set(val) {
+ this.name = val;
+ },
+ },
modalTitle() {
- if (this.type === 'tree') {
+ if (this.entryModal.type === modalTypes.tree) {
return __('Create new directory');
+ } else if (this.entryModal.type === modalTypes.rename) {
+ return this.entryModal.entry.type === modalTypes.tree ? __('Rename folder') : __('Rename file');
}
return __('Create new file');
},
buttonLabel() {
- if (this.type === 'tree') {
+ if (this.entryModal.type === modalTypes.tree) {
return __('Create directory');
+ } else if (this.entryModal.type === modalTypes.rename) {
+ return this.entryModal.entry.type === modalTypes.tree ? __('Rename folder') : __('Rename file');
}
return __('Create file');
},
},
- mounted() {
- this.$refs.fieldName.focus();
- },
methods: {
- createEntryInStore() {
- this.$emit('create', {
- branchId: this.branchId,
- name: this.entryName,
- type: this.type,
- });
-
- this.hideModal();
+ ...mapActions(['createTempEntry', 'renameEntry']),
+ submitForm() {
+ if (this.entryModal.type === modalTypes.rename) {
+ this.renameEntry({
+ path: this.entryModal.entry.path,
+ name: this.entryName,
+ });
+ } else {
+ this.createTempEntry({
+ name: this.name,
+ type: this.entryModal.type,
+ });
+ }
+ },
+ focusInput() {
+ this.$refs.fieldName.focus();
},
- hideModal() {
- this.$emit('hide');
+ closedModal() {
+ this.name = '';
},
},
};
</script>
<template>
- <deprecated-modal
- :title="modalTitle"
- :primary-button-label="buttonLabel"
- kind="success"
- @cancel="hideModal"
- @submit="createEntryInStore"
+ <gl-modal
+ id="ide-new-entry"
+ :header-title-text="modalTitle"
+ :footer-primary-button-text="buttonLabel"
+ footer-primary-button-variant="success"
+ @submit="submitForm"
+ @open="focusInput"
+ @closed="closedModal"
>
- <form
- slot="body"
+ <div
class="form-group row"
- @submit.prevent="createEntryInStore"
>
- <label class="label-light col-form-label col-sm-3">
+ <label class="label-bold col-form-label col-sm-3">
{{ __('Name') }}
</label>
<div class="col-sm-9">
@@ -85,6 +95,6 @@ export default {
class="form-control"
/>
</div>
- </form>
- </deprecated-modal>
+ </div>
+ </gl-modal>
</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
index 677b282bd61..5b1743bb30e 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
@@ -1,71 +1,85 @@
<script>
- export default {
- props: {
- branchId: {
- type: String,
- required: true,
- },
- path: {
- type: String,
- required: false,
- default: '',
- },
+import Icon from '~/vue_shared/components/icon.vue';
+import ItemButton from './button.vue';
+
+export default {
+ components: {
+ Icon,
+ ItemButton,
+ },
+ props: {
+ path: {
+ type: String,
+ required: false,
+ default: '',
},
- mounted() {
- this.$refs.fileUpload.addEventListener('change', this.openFile);
+ showLabel: {
+ type: Boolean,
+ required: false,
+ default: true,
},
- beforeDestroy() {
- this.$refs.fileUpload.removeEventListener('change', this.openFile);
+ buttonCssClasses: {
+ type: String,
+ required: false,
+ default: null,
},
- methods: {
- createFile(target, file, isText) {
- const { name } = file;
- let { result } = target;
+ },
+ mounted() {
+ this.$refs.fileUpload.addEventListener('change', this.openFile);
+ },
+ beforeDestroy() {
+ this.$refs.fileUpload.removeEventListener('change', this.openFile);
+ },
+ methods: {
+ createFile(target, file, isText) {
+ const { name } = file;
+ let { result } = target;
- if (!isText) {
- // eslint-disable-next-line prefer-destructuring
- result = result.split('base64,')[1];
- }
+ if (!isText) {
+ // eslint-disable-next-line prefer-destructuring
+ result = result.split('base64,')[1];
+ }
- this.$emit('create', {
- name: `${(this.path ? `${this.path}/` : '')}${name}`,
- branchId: this.branchId,
- type: 'blob',
- content: result,
- base64: !isText,
- });
- },
- readFile(file) {
- const reader = new FileReader();
- const isText = file.type.match(/text.*/) !== null;
+ this.$emit('create', {
+ name: `${this.path ? `${this.path}/` : ''}${name}`,
+ type: 'blob',
+ content: result,
+ base64: !isText,
+ });
+ },
+ readFile(file) {
+ const reader = new FileReader();
+ const isText = file.type.match(/text.*/) !== null;
- reader.addEventListener('load', e => this.createFile(e.target, file, isText), { once: true });
+ reader.addEventListener('load', e => this.createFile(e.target, file, isText), { once: true });
- if (isText) {
- reader.readAsText(file);
- } else {
- reader.readAsDataURL(file);
- }
- },
- openFile() {
- Array.from(this.$refs.fileUpload.files).forEach(file => this.readFile(file));
- },
- startFileUpload() {
- this.$refs.fileUpload.click();
- },
+ if (isText) {
+ reader.readAsText(file);
+ } else {
+ reader.readAsDataURL(file);
+ }
+ },
+ openFile() {
+ Array.from(this.$refs.fileUpload.files).forEach(file => this.readFile(file));
},
- };
+ startFileUpload() {
+ this.$refs.fileUpload.click();
+ },
+ },
+};
</script>
<template>
<div>
- <a
- href="#"
- role="button"
- @click.stop.prevent="startFileUpload"
- >
- {{ __('Upload file') }}
- </a>
+ <item-button
+ :class="buttonCssClasses"
+ :show-label="showLabel"
+ :icon-classes="showLabel ? 'mr-2' : ''"
+ :label="__('Upload file')"
+ class="d-flex"
+ icon="upload"
+ @click="startFileUpload"
+ />
<input
id="file-upload"
ref="fileUpload"
diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue
index 5cd2c9ce188..79df225c432 100644
--- a/app/assets/javascripts/ide/components/panes/right.vue
+++ b/app/assets/javascripts/ide/components/panes/right.vue
@@ -1,11 +1,13 @@
<script>
-import { mapActions, mapState } from 'vuex';
+import { mapActions, mapState, mapGetters } from 'vuex';
import tooltip from '../../../vue_shared/directives/tooltip';
import Icon from '../../../vue_shared/components/icon.vue';
import { rightSidebarViews } from '../../constants';
import PipelinesList from '../pipelines/list.vue';
import JobsDetail from '../jobs/detail.vue';
+import MergeRequestInfo from '../merge_requests/info.vue';
import ResizablePanel from '../resizable_panel.vue';
+import Clientside from '../preview/clientside.vue';
export default {
directives: {
@@ -16,15 +18,21 @@ export default {
PipelinesList,
JobsDetail,
ResizablePanel,
+ MergeRequestInfo,
+ Clientside,
},
computed: {
- ...mapState(['rightPane']),
+ ...mapState(['rightPane', 'currentMergeRequestId', 'clientsidePreviewEnabled']),
+ ...mapGetters(['packageJson']),
pipelinesActive() {
return (
this.rightPane === rightSidebarViews.pipelines ||
this.rightPane === rightSidebarViews.jobsDetail
);
},
+ showLivePreview() {
+ return this.packageJson && this.clientsidePreviewEnabled;
+ },
},
methods: {
...mapActions(['setRightPane']),
@@ -47,17 +55,41 @@ export default {
:collapsible="false"
:initial-width="350"
:min-size="350"
- class="multi-file-commit-panel-inner"
+ :class="`ide-right-sidebar-${rightPane}`"
side="right"
+ class="multi-file-commit-panel-inner"
>
<component :is="rightPane" />
</resizable-panel>
<nav class="ide-activity-bar">
<ul class="list-unstyled">
+ <li
+ v-if="currentMergeRequestId"
+ >
+ <button
+ v-tooltip
+ :title="__('Merge Request')"
+ :aria-label="__('Merge Request')"
+ :class="{
+ active: rightPane === $options.rightSidebarViews.mergeRequestInfo
+ }"
+ data-container="body"
+ data-placement="left"
+ class="ide-sidebar-link is-right"
+ type="button"
+ @click="clickTab($event, $options.rightSidebarViews.mergeRequestInfo)"
+ >
+ <icon
+ :size="16"
+ name="text-description"
+ />
+ </button>
+ </li>
<li>
<button
v-tooltip
:title="__('Pipelines')"
+ :aria-label="__('Pipelines')"
:class="{
active: pipelinesActive
}"
@@ -73,6 +105,26 @@ export default {
/>
</button>
</li>
+ <li v-if="showLivePreview">
+ <button
+ v-tooltip
+ :title="__('Live preview')"
+ :aria-label="__('Live preview')"
+ :class="{
+ active: rightPane === $options.rightSidebarViews.clientSidePreview
+ }"
+ data-container="body"
+ data-placement="left"
+ class="ide-sidebar-link is-right"
+ type="button"
+ @click="clickTab($event, $options.rightSidebarViews.clientSidePreview)"
+ >
+ <icon
+ :size="16"
+ name="live-preview"
+ />
+ </button>
+ </li>
</ul>
</nav>
</div>
diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue
new file mode 100644
index 00000000000..fef36eae7b1
--- /dev/null
+++ b/app/assets/javascripts/ide/components/preview/clientside.vue
@@ -0,0 +1,171 @@
+<script>
+import { mapActions, mapGetters, mapState } from 'vuex';
+import _ from 'underscore';
+import { Manager } from 'smooshpack';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import Navigator from './navigator.vue';
+import { packageJsonPath } from '../../constants';
+import { createPathWithExt } from '../../utils';
+
+export default {
+ components: {
+ LoadingIcon,
+ Navigator,
+ },
+ data() {
+ return {
+ manager: {},
+ loading: false,
+ };
+ },
+ computed: {
+ ...mapState(['entries', 'promotionSvgPath', 'links']),
+ ...mapGetters(['packageJson', 'currentProject']),
+ normalizedEntries() {
+ return Object.keys(this.entries).reduce((acc, path) => {
+ const file = this.entries[path];
+
+ if (file.type === 'tree' || !(file.raw || file.content)) return acc;
+
+ return {
+ ...acc,
+ [`/${path}`]: {
+ code: file.content || file.raw,
+ },
+ };
+ }, {});
+ },
+ mainEntry() {
+ if (!this.packageJson.raw) return false;
+
+ const parsedPackage = JSON.parse(this.packageJson.raw);
+
+ return parsedPackage.main;
+ },
+ showPreview() {
+ return this.mainEntry && !this.loading;
+ },
+ showEmptyState() {
+ return !this.mainEntry && !this.loading;
+ },
+ showOpenInCodeSandbox() {
+ return this.currentProject && this.currentProject.visibility === 'public';
+ },
+ sandboxOpts() {
+ return {
+ files: { ...this.normalizedEntries },
+ entry: `/${this.mainEntry}`,
+ showOpenInCodeSandbox: this.showOpenInCodeSandbox,
+ };
+ },
+ },
+ watch: {
+ entries: {
+ deep: true,
+ handler: 'update',
+ },
+ },
+ mounted() {
+ this.loading = true;
+
+ return this.loadFileContent(packageJsonPath)
+ .then(() => {
+ this.loading = false;
+ })
+ .then(() => this.$nextTick())
+ .then(() => this.initPreview());
+ },
+ beforeDestroy() {
+ if (!_.isEmpty(this.manager)) {
+ this.manager.listener();
+ }
+ this.manager = {};
+
+ clearTimeout(this.timeout);
+ this.timeout = null;
+ },
+ methods: {
+ ...mapActions(['getFileData', 'getRawFileData']),
+ loadFileContent(path) {
+ return this.getFileData({ path, makeFileActive: false }).then(() =>
+ this.getRawFileData({ path }),
+ );
+ },
+ initPreview() {
+ if (!this.mainEntry) return null;
+
+ return this.loadFileContent(this.mainEntry)
+ .then(() => this.$nextTick())
+ .then(() =>
+ this.initManager('#ide-preview', this.sandboxOpts, {
+ fileResolver: {
+ isFile: p => Promise.resolve(!!this.entries[createPathWithExt(p)]),
+ readFile: p => this.loadFileContent(createPathWithExt(p)).then(content => content),
+ },
+ }),
+ );
+ },
+ update() {
+ if (this.timeout) return;
+
+ this.timeout = setTimeout(() => {
+ if (_.isEmpty(this.manager)) {
+ this.initPreview();
+
+ return;
+ }
+
+ this.manager.updatePreview(this.sandboxOpts);
+
+ clearTimeout(this.timeout);
+ this.timeout = null;
+ }, 500);
+ },
+ initManager(el, opts, resolver) {
+ this.manager = new Manager(el, opts, resolver);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="preview h-100 w-100 d-flex flex-column">
+ <template v-if="showPreview">
+ <navigator
+ :manager="manager"
+ />
+ <div id="ide-preview"></div>
+ </template>
+ <div
+ v-else-if="showEmptyState"
+ v-once
+ class="d-flex h-100 flex-column align-items-center justify-content-center svg-content"
+ >
+ <img
+ :src="promotionSvgPath"
+ :alt="s__('IDE|Live Preview')"
+ width="130"
+ height="100"
+ />
+ <h3>
+ {{ s__('IDE|Live Preview') }}
+ </h3>
+ <p class="text-center">
+ {{ s__('IDE|Preview your web application using Web IDE client-side evaluation.') }}
+ </p>
+ <a
+ :href="links.webIDEHelpPagePath"
+ class="btn btn-primary"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {{ s__('IDE|Get started with Live Preview') }}
+ </a>
+ </div>
+ <loading-icon
+ v-else
+ size="2"
+ class="align-self-center mt-auto mb-auto"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/preview/navigator.vue b/app/assets/javascripts/ide/components/preview/navigator.vue
new file mode 100644
index 00000000000..4bf346946b6
--- /dev/null
+++ b/app/assets/javascripts/ide/components/preview/navigator.vue
@@ -0,0 +1,147 @@
+<script>
+import { listen } from 'codesandbox-api';
+import Icon from '~/vue_shared/components/icon.vue';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+
+export default {
+ components: {
+ Icon,
+ LoadingIcon,
+ },
+ props: {
+ manager: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ currentBrowsingIndex: null,
+ navigationStack: [],
+ forwardNavigationStack: [],
+ path: '',
+ loading: true,
+ };
+ },
+ computed: {
+ backButtonDisabled() {
+ return this.navigationStack.length <= 1;
+ },
+ forwardButtonDisabled() {
+ return !this.forwardNavigationStack.length;
+ },
+ },
+ mounted() {
+ this.listener = listen(e => {
+ switch (e.type) {
+ case 'urlchange':
+ this.onUrlChange(e);
+ break;
+ case 'done':
+ this.loading = false;
+ break;
+ default:
+ break;
+ }
+ });
+ },
+ beforeDestroy() {
+ this.listener();
+ },
+ methods: {
+ onUrlChange(e) {
+ const lastPath = this.path;
+
+ this.path = e.url.replace(this.manager.bundlerURL, '') || '/';
+
+ if (lastPath !== this.path) {
+ this.currentBrowsingIndex =
+ this.currentBrowsingIndex === null ? 0 : this.currentBrowsingIndex + 1;
+ this.navigationStack.push(this.path);
+ }
+ },
+ back() {
+ const lastPath = this.path;
+
+ this.visitPath(this.navigationStack[this.currentBrowsingIndex - 1]);
+
+ this.forwardNavigationStack.push(lastPath);
+
+ if (this.currentBrowsingIndex === 1) {
+ this.currentBrowsingIndex = null;
+ this.navigationStack = [];
+ }
+ },
+ forward() {
+ this.visitPath(this.forwardNavigationStack.splice(0, 1)[0]);
+ },
+ refresh() {
+ this.visitPath(this.path);
+ },
+ visitPath(path) {
+ this.manager.iframe.src = `${this.manager.bundlerURL}${path}`;
+ },
+ },
+};
+</script>
+
+<template>
+ <header class="ide-preview-header d-flex align-items-center">
+ <button
+ :aria-label="s__('IDE|Back')"
+ :disabled="backButtonDisabled"
+ :class="{
+ 'disabled-content': backButtonDisabled
+ }"
+ type="button"
+ class="ide-navigator-btn d-flex align-items-center d-transparent border-0 bg-transparent"
+ @click="back"
+ >
+ <icon
+ :size="24"
+ name="chevron-left"
+ class="m-auto"
+ />
+ </button>
+ <button
+ :aria-label="s__('IDE|Back')"
+ :disabled="forwardButtonDisabled"
+ :class="{
+ 'disabled-content': forwardButtonDisabled
+ }"
+ type="button"
+ class="ide-navigator-btn d-flex align-items-center d-transparent border-0 bg-transparent"
+ @click="forward"
+ >
+ <icon
+ :size="24"
+ name="chevron-right"
+ class="m-auto"
+ />
+ </button>
+ <button
+ :aria-label="s__('IDE|Refresh preview')"
+ type="button"
+ class="ide-navigator-btn d-flex align-items-center d-transparent border-0 bg-transparent"
+ @click="refresh"
+ >
+ <icon
+ :size="18"
+ name="retry"
+ class="m-auto"
+ />
+ </button>
+ <div class="position-relative w-100 prepend-left-4">
+ <input
+ :value="path || '/'"
+ type="text"
+ class="ide-navigator-location form-control bg-white"
+ readonly
+ />
+ <loading-icon
+ v-if="loading"
+ class="position-absolute ide-preview-loading-icon"
+ />
+ </div>
+ </header>
+</template>
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 50ab242ba2a..6f1a941fbc4 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -44,7 +44,7 @@ export default {
},
},
mounted() {
- if (this.lastOpenedFile) {
+ if (this.lastOpenedFile && this.lastOpenedFile.type !== 'tree') {
this.openPendingTab({
file: this.lastOpenedFile,
keyPrefix: this.lastOpenedFile.changed ? stageKeys.unstaged : stageKeys.staged,
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 08ee12fd98f..f9badb01535 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -87,7 +87,9 @@ export default {
this.editor.updateDimensions();
},
viewer() {
- this.createEditorInstance();
+ if (!this.file.pending) {
+ this.createEditorInstance();
+ }
},
panelResizing() {
if (!this.panelResizing) {
@@ -109,6 +111,7 @@ export default {
},
methods: {
...mapActions([
+ 'getFileData',
'getRawFileData',
'changeFileContent',
'setFileLanguage',
@@ -123,10 +126,16 @@ export default {
this.editor.clearEditor();
- this.getRawFileData({
+ this.getFileData({
path: this.file.path,
- baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '',
+ makeFileActive: false,
})
+ .then(() =>
+ this.getRawFileData({
+ path: this.file.path,
+ baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '',
+ }),
+ )
.then(() => {
this.createEditorInstance();
})
@@ -246,6 +255,8 @@ export default {
ref="editor"
:class="{
'is-readonly': isCommitModeActive,
+ 'is-deleted': file.deleted,
+ 'is-added': file.tempFile
}"
class="multi-file-editor-holder"
>
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
index f490a3a2a39..dbdf0be2809 100644
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ b/app/assets/javascripts/ide/components/repo_file.vue
@@ -34,11 +34,11 @@ export default {
type: Number,
required: true,
},
- disableActionDropdown: {
- type: Boolean,
- required: false,
- default: false,
- },
+ },
+ data() {
+ return {
+ mouseOver: false,
+ };
},
computed: {
...mapGetters([
@@ -134,14 +134,16 @@ export default {
.replace(/[/]$/g, '');
// - strip ending "/"
- const filePath = this.file.path
- .replace(/[/]$/g, '');
+ const filePath = this.file.path.replace(/[/]$/g, '');
return filePath === routePath;
},
hasUrlAtCurrentRoute() {
return this.$router.currentRoute.path === `/project${this.file.url}`;
},
+ toggleHover(over) {
+ this.mouseOver = over;
+ },
},
};
</script>
@@ -153,6 +155,8 @@ export default {
class="file"
role="button"
@click="clickFile"
+ @mouseover="toggleHover(true)"
+ @mouseout="toggleHover(false)"
>
<div
class="file-name"
@@ -189,7 +193,7 @@ export default {
data-container="body"
data-placement="right"
name="file-modified"
- css-classes="prepend-left-5 multi-file-modified"
+ css-classes="prepend-left-5 ide-file-modified"
/>
</span>
<changed-file-icon
@@ -202,10 +206,9 @@ export default {
/>
</span>
<new-dropdown
- v-if="isTree && !disableActionDropdown"
- :project-id="file.projectId"
- :branch="file.branchId"
+ :type="file.type"
:path="file.path"
+ :mouse-over="mouseOver"
class="float-right prepend-left-8"
/>
</div>
diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue
index 03772ae4a4c..db47b75ec5c 100644
--- a/app/assets/javascripts/ide/components/repo_tab.vue
+++ b/app/assets/javascripts/ide/components/repo_tab.vue
@@ -37,7 +37,7 @@ export default {
return this.fileHasChanged ? !this.tabMouseOver : false;
},
fileHasChanged() {
- return this.tab.changed || this.tab.tempFile || this.tab.staged;
+ return this.tab.changed || this.tab.tempFile || this.tab.staged || this.tab.deleted;
},
},
@@ -71,7 +71,8 @@ export default {
<template>
<li
:class="{
- active: tab.active
+ active: tab.active,
+ disabled: tab.pending
}"
@click="clickFile(tab)"
@mouseover="mouseOverTab"
@@ -105,7 +106,6 @@ export default {
<changed-file-icon
v-else
:file="tab"
- :force-modified-icon="true"
/>
</button>
</li>
diff --git a/app/assets/javascripts/ide/components/shared/tokened_input.vue b/app/assets/javascripts/ide/components/shared/tokened_input.vue
new file mode 100644
index 00000000000..a7a12f6785d
--- /dev/null
+++ b/app/assets/javascripts/ide/components/shared/tokened_input.vue
@@ -0,0 +1,121 @@
+<script>
+import { __ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ components: {
+ Icon,
+ },
+ props: {
+ placeholder: {
+ type: String,
+ required: false,
+ default: __('Search'),
+ },
+ tokens: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ value: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ backspaceCount: 0,
+ };
+ },
+ computed: {
+ placeholderText() {
+ return this.tokens.length
+ ? ''
+ : this.placeholder;
+ },
+ },
+ watch: {
+ tokens() {
+ this.$refs.input.focus();
+ },
+ },
+ methods: {
+ onFocus() {
+ this.$emit('focus');
+ },
+ onBlur() {
+ this.$emit('blur');
+ },
+ onInput(evt) {
+ this.$emit('input', evt.target.value);
+ },
+ onBackspace() {
+ if (!this.value && this.tokens.length) {
+ this.backspaceCount += 1;
+ } else {
+ this.backspaceCount = 0;
+ return;
+ }
+
+ if (this.backspaceCount > 1) {
+ this.removeToken(this.tokens[this.tokens.length - 1]);
+ this.backspaceCount = 0;
+ }
+ },
+ removeToken(token) {
+ this.$emit('removeToken', token);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="filtered-search-wrapper">
+ <div class="filtered-search-box">
+ <div class="tokens-container list-unstyled">
+ <div
+ v-for="token in tokens"
+ :key="token.label"
+ class="filtered-search-token"
+ >
+ <button
+ class="selectable btn-blank"
+ type="button"
+ @click.stop="removeToken(token)"
+ @keyup.delete="removeToken(token)"
+ >
+ <div
+ class="value-container rounded"
+ >
+ <div
+ class="value"
+ >{{ token.label }}</div>
+ <div
+ class="remove-token inverted"
+ >
+ <icon
+ :size="10"
+ name="close"
+ />
+ </div>
+ </div>
+ </button>
+ </div>
+ <div class="input-token">
+ <input
+ ref="input"
+ :placeholder="placeholderText"
+ :value="value"
+ type="search"
+ class="form-control filtered-search"
+ @input="onInput"
+ @focus="onFocus"
+ @blur="onBlur"
+ @keyup.delete="onBackspace"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index 12e0c3aeef0..8caa5b86a9b 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -31,9 +31,33 @@ export const diffModes = {
export const rightSidebarViews = {
pipelines: 'pipelines-list',
jobsDetail: 'jobs-detail',
+ mergeRequestInfo: 'merge-request-info',
+ clientSidePreview: 'clientside',
};
export const stageKeys = {
unstaged: 'unstaged',
staged: 'staged',
};
+
+export const commitItemIconMap = {
+ addition: {
+ icon: 'file-addition',
+ class: 'ide-file-addition',
+ },
+ modified: {
+ icon: 'file-modified',
+ class: 'ide-file-modified',
+ },
+ deleted: {
+ icon: 'file-deletion',
+ class: 'ide-file-deletion',
+ },
+};
+
+export const modalTypes = {
+ rename: 'rename',
+ tree: 'tree',
+};
+
+export const packageJsonPath = 'package.json';
diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js
index cc8dbb942d8..82f6f981e7a 100644
--- a/app/assets/javascripts/ide/ide_router.js
+++ b/app/assets/javascripts/ide/ide_router.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
+import { join as joinPath } from 'path';
import flash from '~/flash';
import store from './stores';
import { activityBarViews } from './constants';
@@ -37,17 +38,29 @@ const router = new VueRouter({
base: `${gon.relative_url_root}/-/ide/`,
routes: [
{
- path: '/project/:namespace/:project+',
+ path: '/project/:namespace+/:project',
component: EmptyRouterComponent,
children: [
{
- path: ':targetmode(edit|tree|blob)/*',
+ path: ':targetmode(edit|tree|blob)/:branchid+/-/*',
component: EmptyRouterComponent,
},
{
+ path: ':targetmode(edit|tree|blob)/:branchid+/',
+ redirect: to => joinPath(to.path, '/-/'),
+ },
+ {
+ path: ':targetmode(edit|tree|blob)',
+ redirect: to => joinPath(to.path, '/master/-/'),
+ },
+ {
path: 'merge_requests/:mrid',
component: EmptyRouterComponent,
},
+ {
+ path: '',
+ redirect: to => joinPath(to.path, '/edit/master/-/'),
+ },
],
},
],
@@ -63,11 +76,10 @@ router.beforeEach((to, from, next) => {
.then(() => {
const fullProjectId = `${to.params.namespace}/${to.params.project}`;
- const baseSplit = (to.params[0] && to.params[0].split('/-/')) || [''];
- const branchId = baseSplit[0].slice(-1) === '/' ? baseSplit[0].slice(0, -1) : baseSplit[0];
+ const branchId = to.params.branchid;
if (branchId) {
- const basePath = baseSplit.length > 1 ? baseSplit[1] : '';
+ const basePath = to.params[0] || '';
store.dispatch('setCurrentBranchId', branchId);
@@ -101,10 +113,11 @@ router.beforeEach((to, from, next) => {
store
.dispatch('getMergeRequestData', {
projectId: fullProjectId,
+ targetProjectId: to.query.target_project,
mergeRequestId: to.params.mrid,
})
.then(mr => {
- store.dispatch('updateActivityBarView', activityBarViews.review);
+ store.dispatch('setCurrentBranchId', mr.source_branch);
store.dispatch('getBranchData', {
projectId: fullProjectId,
@@ -119,16 +132,22 @@ router.beforeEach((to, from, next) => {
.then(() =>
store.dispatch('getMergeRequestVersions', {
projectId: fullProjectId,
+ targetProjectId: to.query.target_project,
mergeRequestId: to.params.mrid,
}),
)
.then(() =>
store.dispatch('getMergeRequestChanges', {
projectId: fullProjectId,
+ targetProjectId: to.query.target_project,
mergeRequestId: to.params.mrid,
}),
)
.then(mrChanges => {
+ if (mrChanges.changes.length) {
+ store.dispatch('updateActivityBarView', activityBarViews.review);
+ }
+
mrChanges.changes.forEach((change, ind) => {
const changeTreeEntry = store.state.entries[change.new_path];
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index 2d74192e6b3..79e38ae911e 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -4,6 +4,7 @@ import Translate from '~/vue_shared/translate';
import ide from './components/ide.vue';
import store from './stores';
import router from './ide_router';
+import { convertPermissionToBoolean } from '../lib/utils/common_utils';
Vue.use(Translate);
@@ -23,13 +24,18 @@ export function initIde(el) {
noChangesStateSvgPath: el.dataset.noChangesStateSvgPath,
committedStateSvgPath: el.dataset.committedStateSvgPath,
pipelinesEmptyStateSvgPath: el.dataset.pipelinesEmptyStateSvgPath,
+ promotionSvgPath: el.dataset.promotionSvgPath,
});
this.setLinks({
ciHelpPagePath: el.dataset.ciHelpPagePath,
+ webIDEHelpPagePath: el.dataset.webIdeHelpPagePath,
+ });
+ this.setInitialData({
+ clientsidePreviewEnabled: convertPermissionToBoolean(el.dataset.clientsidePreviewEnabled),
});
},
methods: {
- ...mapActions(['setEmptyStateSvgs', 'setLinks']),
+ ...mapActions(['setEmptyStateSvgs', 'setLinks', 'setInitialData']),
},
render(createElement) {
return createElement('ide');
diff --git a/app/assets/javascripts/ide/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js
index 78e6f632728..60bddb34977 100644
--- a/app/assets/javascripts/ide/lib/common/model.js
+++ b/app/assets/javascripts/ide/lib/common/model.js
@@ -7,7 +7,7 @@ export default class Model {
this.disposable = new Disposable();
this.file = file;
this.head = head;
- this.content = file.content !== '' ? file.content : file.raw;
+ this.content = file.content !== '' || file.deleted ? file.content : file.raw;
this.disposable.add(
(this.originalModel = monacoEditor.createModel(
diff --git a/app/assets/javascripts/ide/lib/themes/gl_theme.js b/app/assets/javascripts/ide/lib/themes/gl_theme.js
index 2fc96250c7d..439ae50448a 100644
--- a/app/assets/javascripts/ide/lib/themes/gl_theme.js
+++ b/app/assets/javascripts/ide/lib/themes/gl_theme.js
@@ -9,6 +9,7 @@ export default {
'diffEditor.insertedTextBackground': '#ddfbe6',
'diffEditor.removedTextBackground': '#f9d7dc',
'editor.selectionBackground': '#aad6f8',
+ 'editorIndentGuide.activeBackground': '#cccccc',
},
},
};
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index 3e939f0c1a3..f0193d8e8ea 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -8,7 +8,7 @@ export default {
});
},
getRawFileData(file) {
- if (file.tempFile) {
+ if (file.tempFile && !file.prevPath) {
return Promise.resolve(file.content);
}
@@ -18,7 +18,7 @@ export default {
return axios
.get(file.rawPath, {
- params: { format: 'json' },
+ transformResponse: [f => f],
})
.then(({ data }) => data);
},
@@ -33,15 +33,15 @@ export default {
return axios
.get(file.rawPath.replace(`/raw/${file.branchId}/${file.path}`, `/raw/${sha}/${file.path}`), {
- params: { format: 'json' },
+ transformResponse: [f => f],
})
.then(({ data }) => data);
},
getProjectData(namespace, project) {
return Api.project(`${namespace}/${project}`);
},
- getProjectMergeRequestData(projectId, mergeRequestId) {
- return Api.mergeRequest(projectId, mergeRequestId);
+ getProjectMergeRequestData(projectId, mergeRequestId, params = {}) {
+ return Api.mergeRequest(projectId, mergeRequestId, params);
},
getProjectMergeRequestChanges(projectId, mergeRequestId) {
return Api.mergeRequestChanges(projectId, mergeRequestId);
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 5e91fa915ff..aa02dfbddc4 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -52,7 +52,7 @@ export const setResizingStatus = ({ commit }, resizing) => {
export const createTempEntry = (
{ state, commit, dispatch },
- { branchId, name, type, content = '', base64 = false },
+ { name, type, content = '', base64 = false },
) =>
new Promise(resolve => {
const worker = new FilesDecoratorWorker();
@@ -81,7 +81,7 @@ export const createTempEntry = (
commit(types.CREATE_TMP_ENTRY, {
data,
projectId: state.currentProjectId,
- branchId,
+ branchId: state.currentBranchId,
});
if (type === 'blob') {
@@ -100,7 +100,7 @@ export const createTempEntry = (
worker.postMessage({
data: [fullName],
projectId: state.currentProjectId,
- branchId,
+ branchId: state.currentBranchId,
type,
tempFile: true,
base64,
@@ -178,6 +178,47 @@ export const setLinks = ({ commit }, links) => commit(types.SET_LINKS, links);
export const setErrorMessage = ({ commit }, errorMessage) =>
commit(types.SET_ERROR_MESSAGE, errorMessage);
+export const openNewEntryModal = ({ commit }, { type, path = '' }) => {
+ commit(types.OPEN_NEW_ENTRY_MODAL, { type, path });
+
+ // open the modal manually so we don't mess around with dropdown/rows
+ $('#ide-new-entry').modal('show');
+};
+
+export const deleteEntry = ({ commit, dispatch, state }, path) => {
+ const entry = state.entries[path];
+
+ if (state.unusedSeal) dispatch('burstUnusedSeal');
+ if (entry.opened) dispatch('closeFile', entry);
+
+ if (entry.type === 'tree') {
+ entry.tree.forEach(f => dispatch('deleteEntry', f.path));
+ }
+
+ commit(types.DELETE_ENTRY, path);
+
+ if (entry.parentPath && state.entries[entry.parentPath].tree.length === 0) {
+ dispatch('deleteEntry', entry.parentPath);
+ }
+};
+
+export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
+
+export const renameEntry = ({ dispatch, commit, state }, { path, name, entryPath = null }) => {
+ const entry = state.entries[entryPath || path];
+ commit(types.RENAME_ENTRY, { path, name, entryPath });
+
+ if (entry.type === 'tree') {
+ state.entries[entryPath || path].tree.forEach(f =>
+ dispatch('renameEntry', { path, name, entryPath: f.path }),
+ );
+ }
+
+ if (!entryPath) {
+ dispatch('deleteEntry', path);
+ }
+};
+
export * from './actions/tree';
export * from './actions/file';
export * from './actions/project';
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index 6c0887e11ee..c9795750d65 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -54,24 +54,25 @@ export const setFileActive = ({ commit, state, getters, dispatch }, path) => {
commit(types.SET_FILE_ACTIVE, { path, active: true });
dispatch('scrollToTab');
-
- commit(types.SET_CURRENT_PROJECT, file.projectId);
- commit(types.SET_CURRENT_BRANCH, file.branchId);
};
export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive = true }) => {
const file = state.entries[path];
+
+ if (file.raw || (file.tempFile && !file.prevPath)) return Promise.resolve();
+
commit(types.TOGGLE_LOADING, { entry: file });
+
+ const url = file.prevPath ? file.url.replace(file.path, file.prevPath) : file.url;
+
return service
- .getFileData(
- `${gon.relative_url_root ? gon.relative_url_root : ''}${file.url.replace('/-/', '/')}`,
- )
+ .getFileData(`${gon.relative_url_root ? gon.relative_url_root : ''}${url.replace('/-/', '/')}`)
.then(({ data, headers }) => {
const normalizedHeaders = normalizeHeaders(headers);
setPageTitle(decodeURI(normalizedHeaders['PAGE-TITLE']));
commit(types.SET_FILE_DATA, { data, file });
- commit(types.TOGGLE_FILE_OPEN, path);
+ if (makeFileActive) commit(types.TOGGLE_FILE_OPEN, path);
if (makeFileActive) dispatch('setFileActive', path);
commit(types.TOGGLE_LOADING, { entry: file });
})
@@ -97,7 +98,7 @@ export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) =
service
.getRawFileData(file)
.then(raw => {
- commit(types.SET_FILE_RAW_DATA, { file, raw });
+ if (!(file.tempFile && !file.prevPath)) commit(types.SET_FILE_RAW_DATA, { file, raw });
if (file.mrChange && file.mrChange.new_file === false) {
service
.getBaseRawFileData(file, baseSha)
@@ -172,9 +173,22 @@ export const setFileViewMode = ({ commit }, { file, viewMode }) => {
export const discardFileChanges = ({ dispatch, state, commit, getters }, path) => {
const file = state.entries[path];
+ if (file.deleted && file.parentPath) {
+ dispatch('restoreTree', file.parentPath);
+ }
+
+ if (file.movedPath) {
+ commit(types.DISCARD_FILE_CHANGES, file.movedPath);
+ commit(types.REMOVE_FILE_FROM_CHANGED, file.movedPath);
+ }
+
commit(types.DISCARD_FILE_CHANGES, path);
commit(types.REMOVE_FILE_FROM_CHANGED, path);
+ if (file.prevPath) {
+ dispatch('discardFileChanges', file.prevPath);
+ }
+
if (file.tempFile && file.opened) {
commit(types.TOGGLE_FILE_OPEN, path);
} else if (getters.activeFile && file.path === getters.activeFile.path) {
diff --git a/app/assets/javascripts/ide/stores/actions/merge_request.js b/app/assets/javascripts/ide/stores/actions/merge_request.js
index 4aa151abcb7..1887b77b00b 100644
--- a/app/assets/javascripts/ide/stores/actions/merge_request.js
+++ b/app/assets/javascripts/ide/stores/actions/merge_request.js
@@ -4,12 +4,14 @@ import * as types from '../mutation_types';
export const getMergeRequestData = (
{ commit, dispatch, state },
- { projectId, mergeRequestId, force = false } = {},
+ { projectId, mergeRequestId, targetProjectId = null, force = false } = {},
) =>
new Promise((resolve, reject) => {
if (!state.projects[projectId].mergeRequests[mergeRequestId] || force) {
service
- .getProjectMergeRequestData(projectId, mergeRequestId)
+ .getProjectMergeRequestData(targetProjectId || projectId, mergeRequestId, {
+ render_html: true,
+ })
.then(({ data }) => {
commit(types.SET_MERGE_REQUEST, {
projectPath: projectId,
@@ -38,12 +40,12 @@ export const getMergeRequestData = (
export const getMergeRequestChanges = (
{ commit, dispatch, state },
- { projectId, mergeRequestId, force = false } = {},
+ { projectId, mergeRequestId, targetProjectId = null, force = false } = {},
) =>
new Promise((resolve, reject) => {
if (!state.projects[projectId].mergeRequests[mergeRequestId].changes.length || force) {
service
- .getProjectMergeRequestChanges(projectId, mergeRequestId)
+ .getProjectMergeRequestChanges(targetProjectId || projectId, mergeRequestId)
.then(({ data }) => {
commit(types.SET_MERGE_REQUEST_CHANGES, {
projectPath: projectId,
@@ -71,12 +73,12 @@ export const getMergeRequestChanges = (
export const getMergeRequestVersions = (
{ commit, dispatch, state },
- { projectId, mergeRequestId, force = false } = {},
+ { projectId, mergeRequestId, targetProjectId = null, force = false } = {},
) =>
new Promise((resolve, reject) => {
if (!state.projects[projectId].mergeRequests[mergeRequestId].versions.length || force) {
service
- .getProjectMergeRequestVersions(projectId, mergeRequestId)
+ .getProjectMergeRequestVersions(targetProjectId || projectId, mergeRequestId)
.then(res => res.data)
.then(data => {
commit(types.SET_MERGE_REQUEST_VERSIONS, {
diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js
index ffaaaabff17..9288bbe32f5 100644
--- a/app/assets/javascripts/ide/stores/actions/tree.js
+++ b/app/assets/javascripts/ide/stores/actions/tree.js
@@ -21,14 +21,12 @@ export const showTreeEntry = ({ commit, dispatch, state }, path) => {
export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
if (row.type === 'tree') {
dispatch('toggleTreeOpen', row.path);
- } else if (row.type === 'blob' && (row.opened || row.changed)) {
- if (row.changed && !row.opened) {
+ } else if (row.type === 'blob') {
+ if (!row.opened) {
commit(types.TOGGLE_FILE_OPEN, row.path);
}
dispatch('setFileActive', row.path);
- } else {
- dispatch('getFileData', { path: row.path });
}
dispatch('showTreeEntry', row.path);
@@ -91,3 +89,13 @@ export const getFiles = ({ state, commit, dispatch }, { projectId, branchId } =
resolve();
}
});
+
+export const restoreTree = ({ dispatch, commit, state }, path) => {
+ const entry = state.entries[path];
+
+ commit(types.RESTORE_TREE, path);
+
+ if (entry.parentPath) {
+ dispatch('restoreTree', entry.parentPath);
+ }
+};
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
index 5ce268b0d05..709748fb530 100644
--- a/app/assets/javascripts/ide/stores/getters.js
+++ b/app/assets/javascripts/ide/stores/getters.js
@@ -1,5 +1,5 @@
import { getChangesCountForFiles, filePathMatches } from './utils';
-import { activityBarViews } from '../constants';
+import { activityBarViews, packageJsonPath } from '../constants';
export const activeFile = state => state.openFiles.find(file => file.active) || null;
@@ -67,9 +67,9 @@ export const someUncommitedChanges = state =>
!!(state.changedFiles.length || state.stagedFiles.length);
export const getChangesInFolder = state => path => {
- const changedFilesCount = state.changedFiles.filter(f => filePathMatches(f, path)).length;
+ const changedFilesCount = state.changedFiles.filter(f => filePathMatches(f.path, path)).length;
const stagedFilesCount = state.stagedFiles.filter(
- f => filePathMatches(f, path) && !getChangedFile(state)(f.path),
+ f => filePathMatches(f.path, path) && !getChangedFile(state)(f.path),
).length;
return changedFilesCount + stagedFilesCount;
@@ -90,5 +90,7 @@ export const lastCommit = (state, getters) => {
export const currentBranch = (state, getters) =>
getters.currentProject && getters.currentProject.branches[state.currentBranchId];
+export const packageJson = state => state.entries[packageJsonPath];
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/ide/stores/index.js b/app/assets/javascripts/ide/stores/index.js
index f8ce8a67ec0..a601dc8f5a0 100644
--- a/app/assets/javascripts/ide/stores/index.js
+++ b/app/assets/javascripts/ide/stores/index.js
@@ -7,6 +7,7 @@ import mutations from './mutations';
import commitModule from './modules/commit';
import pipelines from './modules/pipelines';
import mergeRequests from './modules/merge_requests';
+import branches from './modules/branches';
Vue.use(Vuex);
@@ -20,6 +21,7 @@ export const createStore = () =>
commit: commitModule,
pipelines,
mergeRequests,
+ branches,
},
});
diff --git a/app/assets/javascripts/ide/stores/modules/branches/actions.js b/app/assets/javascripts/ide/stores/modules/branches/actions.js
new file mode 100644
index 00000000000..74aa98ef9f9
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/branches/actions.js
@@ -0,0 +1,39 @@
+import { __ } from '~/locale';
+import Api from '~/api';
+import * as types from './mutation_types';
+
+export const requestBranches = ({ commit }) => commit(types.REQUEST_BRANCHES);
+export const receiveBranchesError = ({ commit, dispatch }, { search }) => {
+ dispatch(
+ 'setErrorMessage',
+ {
+ text: __('Error loading branches.'),
+ action: payload =>
+ dispatch('fetchBranches', payload).then(() =>
+ dispatch('setErrorMessage', null, { root: true }),
+ ),
+ actionText: __('Please try again'),
+ actionPayload: { search },
+ },
+ { root: true },
+ );
+ commit(types.RECEIVE_BRANCHES_ERROR);
+};
+export const receiveBranchesSuccess = ({ commit }, data) =>
+ commit(types.RECEIVE_BRANCHES_SUCCESS, data);
+
+export const fetchBranches = ({ dispatch, rootGetters }, { search = '' }) => {
+ dispatch('requestBranches');
+ dispatch('resetBranches');
+
+ return Api.branches(rootGetters.currentProject.id, search, { sort: 'updated_desc' })
+ .then(({ data }) => dispatch('receiveBranchesSuccess', data))
+ .catch(() => dispatch('receiveBranchesError', { search }));
+};
+
+export const resetBranches = ({ commit }) => commit(types.RESET_BRANCHES);
+
+export const openBranch = ({ rootState, dispatch }, id) =>
+ dispatch('goToRoute', `/project/${rootState.currentProjectId}/edit/${id}`, { root: true });
+
+export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/branches/index.js b/app/assets/javascripts/ide/stores/modules/branches/index.js
new file mode 100644
index 00000000000..04e7e0f08f1
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/branches/index.js
@@ -0,0 +1,10 @@
+import state from './state';
+import * as actions from './actions';
+import mutations from './mutations';
+
+export default {
+ namespaced: true,
+ state: state(),
+ actions,
+ mutations,
+};
diff --git a/app/assets/javascripts/ide/stores/modules/branches/mutation_types.js b/app/assets/javascripts/ide/stores/modules/branches/mutation_types.js
new file mode 100644
index 00000000000..2272f7b9531
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/branches/mutation_types.js
@@ -0,0 +1,5 @@
+export const REQUEST_BRANCHES = 'REQUEST_BRANCHES';
+export const RECEIVE_BRANCHES_ERROR = 'RECEIVE_BRANCHES_ERROR';
+export const RECEIVE_BRANCHES_SUCCESS = 'RECEIVE_BRANCHES_SUCCESS';
+
+export const RESET_BRANCHES = 'RESET_BRANCHES';
diff --git a/app/assets/javascripts/ide/stores/modules/branches/mutations.js b/app/assets/javascripts/ide/stores/modules/branches/mutations.js
new file mode 100644
index 00000000000..081ec2d4c28
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/branches/mutations.js
@@ -0,0 +1,21 @@
+/* eslint-disable no-param-reassign */
+import * as types from './mutation_types';
+
+export default {
+ [types.REQUEST_BRANCHES](state) {
+ state.isLoading = true;
+ },
+ [types.RECEIVE_BRANCHES_ERROR](state) {
+ state.isLoading = false;
+ },
+ [types.RECEIVE_BRANCHES_SUCCESS](state, data) {
+ state.isLoading = false;
+ state.branches = data.map(branch => ({
+ name: branch.name,
+ committedDate: branch.commit.committed_date,
+ }));
+ },
+ [types.RESET_BRANCHES](state) {
+ state.branches = [];
+ },
+};
diff --git a/app/assets/javascripts/ide/stores/modules/branches/state.js b/app/assets/javascripts/ide/stores/modules/branches/state.js
new file mode 100644
index 00000000000..89bf220c45f
--- /dev/null
+++ b/app/assets/javascripts/ide/stores/modules/branches/state.js
@@ -0,0 +1,4 @@
+export default () => ({
+ isLoading: false,
+ branches: [],
+});
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index 69b6fe2985b..462ca45db9b 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -1,7 +1,6 @@
import $ from 'jquery';
import { sprintf, __ } from '~/locale';
import flash from '~/flash';
-import { stripHtml } from '~/lib/utils/text_utility';
import * as rootTypes from '../../mutation_types';
import { createCommitPayload, createNewMergeRequestUrl } from '../../utils';
import router from '../../../ide_router';
@@ -175,11 +174,13 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
dispatch('updateActivityBarView', activityBarViews.edit, { root: true });
dispatch('updateViewer', 'editor', { root: true });
- router.push(
- `/project/${rootState.currentProjectId}/blob/${getters.branchName}/-/${
- rootGetters.activeFile.path
- }`,
- );
+ if (rootGetters.activeFile) {
+ router.push(
+ `/project/${rootState.currentProjectId}/blob/${getters.branchName}/-/${
+ rootGetters.activeFile.path
+ }`,
+ );
+ }
}
})
.then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH))
@@ -198,11 +199,18 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
if (err.response.status === 400) {
$('#ide-create-branch-modal').modal('show');
} else {
- let errMsg = __('Error committing changes. Please try again.');
- if (err.response.data && err.response.data.message) {
- errMsg += ` (${stripHtml(err.response.data.message)})`;
- }
- flash(errMsg, 'alert', document, null, false, true);
+ dispatch(
+ 'setErrorMessage',
+ {
+ text: __('An error accured whilst committing your changes.'),
+ action: () =>
+ dispatch('commitChanges').then(() =>
+ dispatch('setErrorMessage', null, { root: true }),
+ ),
+ actionText: __('Please try again'),
+ },
+ { root: true },
+ );
window.dispatchEvent(new Event('resize'));
}
diff --git a/app/assets/javascripts/ide/stores/modules/commit/getters.js b/app/assets/javascripts/ide/stores/modules/commit/getters.js
index 3db4b2f903e..03777e6c10b 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/getters.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/getters.js
@@ -1,7 +1,15 @@
-import { sprintf, n__ } from '../../../../locale';
+import { sprintf, n__, __ } from '../../../../locale';
import * as consts from './constants';
const BRANCH_SUFFIX_COUNT = 5;
+const createTranslatedTextForFiles = (files, text) => {
+ if (!files.length) return null;
+
+ return sprintf(n__('%{text} %{files}', '%{text} %{files} files', files.length), {
+ files: files.reduce((acc, val) => acc.concat(val.path), []).join(', '),
+ text,
+ });
+};
export const discardDraftButtonDisabled = state =>
state.commitMessage === '' || state.submitCommitLoading;
@@ -29,14 +37,16 @@ export const branchName = (state, getters, rootState) => {
export const preBuiltCommitMessage = (state, _, rootState) => {
if (state.commitMessage) return state.commitMessage;
- const files = (rootState.stagedFiles.length
- ? rootState.stagedFiles
- : rootState.changedFiles
- ).reduce((acc, val) => acc.concat(val.path), []);
+ const files = rootState.stagedFiles.length ? rootState.stagedFiles : rootState.changedFiles;
+ const modifiedFiles = files.filter(f => !f.deleted);
+ const deletedFiles = files.filter(f => f.deleted);
- return sprintf(n__('Update %{files}', 'Update %{files} files', files.length), {
- files: files.join(', '),
- });
+ return [
+ createTranslatedTextForFiles(modifiedFiles, __('Update')),
+ createTranslatedTextForFiles(deletedFiles, __('Deleted')),
+ ]
+ .filter(t => t)
+ .join('\n');
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
index 551dd322c9b..baa2497ec5b 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
@@ -1,48 +1,40 @@
import { __ } from '../../../../locale';
import Api from '../../../../api';
-import flash from '../../../../flash';
-import router from '../../../ide_router';
import { scopes } from './constants';
import * as types from './mutation_types';
-import * as rootTypes from '../../mutation_types';
-export const requestMergeRequests = ({ commit }, type) =>
- commit(types.REQUEST_MERGE_REQUESTS, type);
-export const receiveMergeRequestsError = ({ commit }, type) => {
- flash(__('Error loading merge requests.'));
- commit(types.RECEIVE_MERGE_REQUESTS_ERROR, type);
+export const requestMergeRequests = ({ commit }) =>
+ commit(types.REQUEST_MERGE_REQUESTS);
+export const receiveMergeRequestsError = ({ commit, dispatch }, { type, search }) => {
+ dispatch(
+ 'setErrorMessage',
+ {
+ text: __('Error loading merge requests.'),
+ action: payload =>
+ dispatch('fetchMergeRequests', payload).then(() =>
+ dispatch('setErrorMessage', null, { root: true }),
+ ),
+ actionText: __('Please try again'),
+ actionPayload: { type, search },
+ },
+ { root: true },
+ );
+ commit(types.RECEIVE_MERGE_REQUESTS_ERROR);
};
-export const receiveMergeRequestsSuccess = ({ commit }, { type, data }) =>
- commit(types.RECEIVE_MERGE_REQUESTS_SUCCESS, { type, data });
+export const receiveMergeRequestsSuccess = ({ commit }, data) =>
+ commit(types.RECEIVE_MERGE_REQUESTS_SUCCESS, data);
export const fetchMergeRequests = ({ dispatch, state: { state } }, { type, search = '' }) => {
- const scope = scopes[type];
- dispatch('requestMergeRequests', type);
- dispatch('resetMergeRequests', type);
+ dispatch('requestMergeRequests');
+ dispatch('resetMergeRequests');
- Api.mergeRequests({ scope, state, search })
- .then(({ data }) => dispatch('receiveMergeRequestsSuccess', { type, data }))
- .catch(() => dispatch('receiveMergeRequestsError', type));
-};
-
-export const resetMergeRequests = ({ commit }, type) => commit(types.RESET_MERGE_REQUESTS, type);
+ const scope = type ? scopes[type] : 'all';
-export const openMergeRequest = ({ commit, dispatch }, { projectPath, id }) => {
- commit(rootTypes.CLEAR_PROJECTS, null, { root: true });
- commit(rootTypes.SET_CURRENT_MERGE_REQUEST, `${id}`, { root: true });
- commit(rootTypes.RESET_OPEN_FILES, null, { root: true });
- dispatch('setCurrentBranchId', '', { root: true });
- dispatch('pipelines/stopPipelinePolling', null, { root: true })
- .then(() => {
- dispatch('pipelines/resetLatestPipeline', null, { root: true });
- dispatch('pipelines/clearEtagPoll', null, { root: true });
- })
- .catch(e => {
- throw e;
- });
- dispatch('setRightPane', null, { root: true });
-
- router.push(`/project/${projectPath}/merge_requests/${id}`);
+ return Api.mergeRequests({ scope, state, search })
+ .then(({ data }) => dispatch('receiveMergeRequestsSuccess', data))
+ .catch(() => dispatch('receiveMergeRequestsError', { type, search }));
};
+export const resetMergeRequests = ({ commit }) => commit(types.RESET_MERGE_REQUESTS);
+
export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/getters.js b/app/assets/javascripts/ide/stores/modules/merge_requests/getters.js
deleted file mode 100644
index 8e2b234be8d..00000000000
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/getters.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export const getData = state => type => state[type];
-
-export const assignedData = state => state.assigned;
-export const createdData = state => state.created;
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/index.js b/app/assets/javascripts/ide/stores/modules/merge_requests/index.js
index 2e6dfb420f4..04e7e0f08f1 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/index.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/index.js
@@ -1,6 +1,5 @@
import state from './state';
import * as actions from './actions';
-import * as getters from './getters';
import mutations from './mutations';
export default {
@@ -8,5 +7,4 @@ export default {
state: state(),
actions,
mutations,
- getters,
};
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js b/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js
index 971da0806bd..98102a68e08 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js
@@ -2,15 +2,15 @@
import * as types from './mutation_types';
export default {
- [types.REQUEST_MERGE_REQUESTS](state, type) {
- state[type].isLoading = true;
+ [types.REQUEST_MERGE_REQUESTS](state) {
+ state.isLoading = true;
},
- [types.RECEIVE_MERGE_REQUESTS_ERROR](state, type) {
- state[type].isLoading = false;
+ [types.RECEIVE_MERGE_REQUESTS_ERROR](state) {
+ state.isLoading = false;
},
- [types.RECEIVE_MERGE_REQUESTS_SUCCESS](state, { type, data }) {
- state[type].isLoading = false;
- state[type].mergeRequests = data.map(mergeRequest => ({
+ [types.RECEIVE_MERGE_REQUESTS_SUCCESS](state, data) {
+ state.isLoading = false;
+ state.mergeRequests = data.map(mergeRequest => ({
id: mergeRequest.id,
iid: mergeRequest.iid,
title: mergeRequest.title,
@@ -20,7 +20,7 @@ export default {
.replace(`/merge_requests/${mergeRequest.iid}`, ''),
}));
},
- [types.RESET_MERGE_REQUESTS](state, type) {
- state[type].mergeRequests = [];
+ [types.RESET_MERGE_REQUESTS](state) {
+ state.mergeRequests = [];
},
};
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/state.js b/app/assets/javascripts/ide/stores/modules/merge_requests/state.js
index 57eb6b04283..4748ccfa2e6 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/state.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/state.js
@@ -1,13 +1,7 @@
import { states } from './constants';
export default () => ({
- created: {
- isLoading: false,
- mergeRequests: [],
- },
- assigned: {
- isLoading: false,
- mergeRequests: [],
- },
+ isLoading: false,
+ mergeRequests: [],
state: states.opened,
});
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
index fe1dc9ac8f8..3e67b222e66 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
@@ -1,7 +1,7 @@
import Visibility from 'visibilityjs';
import axios from 'axios';
+import httpStatus from '../../../../lib/utils/http_status';
import { __ } from '../../../../locale';
-import flash from '../../../../flash';
import Poll from '../../../../lib/utils/poll';
import service from '../../../services';
import { rightSidebarViews } from '../../../constants';
@@ -18,10 +18,27 @@ export const stopPipelinePolling = () => {
export const restartPipelinePolling = () => {
if (eTagPoll) eTagPoll.restart();
};
+export const forcePipelineRequest = () => {
+ if (eTagPoll) eTagPoll.makeRequest();
+};
export const requestLatestPipeline = ({ commit }) => commit(types.REQUEST_LATEST_PIPELINE);
-export const receiveLatestPipelineError = ({ commit, dispatch }) => {
- flash(__('There was an error loading latest pipeline'));
+export const receiveLatestPipelineError = ({ commit, dispatch }, err) => {
+ if (err.status !== httpStatus.NOT_FOUND) {
+ dispatch(
+ 'setErrorMessage',
+ {
+ text: __('An error occured whilst fetching the latest pipline.'),
+ action: () =>
+ dispatch('forcePipelineRequest').then(() =>
+ dispatch('setErrorMessage', null, { root: true }),
+ ),
+ actionText: __('Please try again'),
+ actionPayload: null,
+ },
+ { root: true },
+ );
+ }
commit(types.RECEIVE_LASTEST_PIPELINE_ERROR);
dispatch('stopPipelinePolling');
};
@@ -46,7 +63,7 @@ export const fetchLatestPipeline = ({ dispatch, rootGetters }) => {
method: 'lastCommitPipelines',
data: { getters: rootGetters },
successCallback: ({ data }) => dispatch('receiveLatestPipelineSuccess', data),
- errorCallback: () => dispatch('receiveLatestPipelineError'),
+ errorCallback: err => dispatch('receiveLatestPipelineError', err),
});
if (!Visibility.hidden()) {
@@ -63,9 +80,21 @@ export const fetchLatestPipeline = ({ dispatch, rootGetters }) => {
};
export const requestJobs = ({ commit }, id) => commit(types.REQUEST_JOBS, id);
-export const receiveJobsError = ({ commit }, id) => {
- flash(__('There was an error loading jobs'));
- commit(types.RECEIVE_JOBS_ERROR, id);
+export const receiveJobsError = ({ commit, dispatch }, stage) => {
+ dispatch(
+ 'setErrorMessage',
+ {
+ text: __('An error occured whilst loading the pipelines jobs.'),
+ action: payload =>
+ dispatch('fetchJobs', payload).then(() =>
+ dispatch('setErrorMessage', null, { root: true }),
+ ),
+ actionText: __('Please try again'),
+ actionPayload: stage,
+ },
+ { root: true },
+ );
+ commit(types.RECEIVE_JOBS_ERROR, stage.id);
};
export const receiveJobsSuccess = ({ commit }, { id, data }) =>
commit(types.RECEIVE_JOBS_SUCCESS, { id, data });
@@ -73,10 +102,10 @@ export const receiveJobsSuccess = ({ commit }, { id, data }) =>
export const fetchJobs = ({ dispatch }, stage) => {
dispatch('requestJobs', stage.id);
- axios
+ return axios
.get(stage.dropdownPath)
.then(({ data }) => dispatch('receiveJobsSuccess', { id: stage.id, data }))
- .catch(() => dispatch('receiveJobsError', stage.id));
+ .catch(() => dispatch('receiveJobsError', stage));
};
export const toggleStageCollapsed = ({ commit }, stageId) =>
@@ -90,8 +119,18 @@ export const setDetailJob = ({ commit, dispatch }, job) => {
};
export const requestJobTrace = ({ commit }) => commit(types.REQUEST_JOB_TRACE);
-export const receiveJobTraceError = ({ commit }) => {
- flash(__('Error fetching job trace'));
+export const receiveJobTraceError = ({ commit, dispatch }) => {
+ dispatch(
+ 'setErrorMessage',
+ {
+ text: __('An error occured whilst fetching the job trace.'),
+ action: () =>
+ dispatch('fetchJobTrace').then(() => dispatch('setErrorMessage', null, { root: true })),
+ actionText: __('Please try again'),
+ actionPayload: null,
+ },
+ { root: true },
+ );
commit(types.RECEIVE_JOB_TRACE_ERROR);
};
export const receiveJobTraceSuccess = ({ commit }, data) =>
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index 555802e1811..5a7991d2fa7 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -74,3 +74,9 @@ export const CLEAR_PROJECTS = 'CLEAR_PROJECTS';
export const RESET_OPEN_FILES = 'RESET_OPEN_FILES';
export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE';
+
+export const OPEN_NEW_ENTRY_MODAL = 'OPEN_NEW_ENTRY_MODAL';
+export const DELETE_ENTRY = 'DELETE_ENTRY';
+export const RENAME_ENTRY = 'RENAME_ENTRY';
+
+export const RESTORE_TREE = 'RESTORE_TREE';
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index 702be2140e2..1eda5768709 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-param-reassign */
import * as types from './mutation_types';
import projectMutations from './mutations/project';
import mergeRequestMutation from './mutations/merge_request';
@@ -114,13 +115,20 @@ export default {
},
[types.SET_EMPTY_STATE_SVGS](
state,
- { emptyStateSvgPath, noChangesStateSvgPath, committedStateSvgPath, pipelinesEmptyStateSvgPath },
+ {
+ emptyStateSvgPath,
+ noChangesStateSvgPath,
+ committedStateSvgPath,
+ pipelinesEmptyStateSvgPath,
+ promotionSvgPath,
+ },
) {
Object.assign(state, {
emptyStateSvgPath,
noChangesStateSvgPath,
committedStateSvgPath,
pipelinesEmptyStateSvgPath,
+ promotionSvgPath,
});
},
[types.TOGGLE_FILE_FINDER](state, fileFindVisible) {
@@ -130,11 +138,14 @@ export default {
},
[types.UPDATE_FILE_AFTER_COMMIT](state, { file, lastCommit }) {
const changedFile = state.changedFiles.find(f => f.path === file.path);
+ const { prevPath } = file;
Object.assign(state.entries[file.path], {
raw: file.content,
changed: !!changedFile,
staged: false,
+ prevPath: '',
+ moved: false,
lastCommit: Object.assign(state.entries[file.path].lastCommit, {
id: lastCommit.commit.id,
url: lastCommit.commit_path,
@@ -143,6 +154,18 @@ export default {
updatedAt: lastCommit.commit.authored_date,
}),
});
+
+ if (prevPath) {
+ // Update URLs after file has moved
+ const regex = new RegExp(`${prevPath}$`);
+
+ Object.assign(state.entries[file.path], {
+ rawPath: file.rawPath.replace(regex, file.path),
+ permalink: file.permalink.replace(regex, file.path),
+ commitsPath: file.commitsPath.replace(regex, file.path),
+ blamePath: file.blamePath.replace(regex, file.path),
+ });
+ }
},
[types.BURST_UNUSED_SEAL](state) {
Object.assign(state, {
@@ -166,6 +189,65 @@ export default {
[types.SET_ERROR_MESSAGE](state, errorMessage) {
Object.assign(state, { errorMessage });
},
+ [types.OPEN_NEW_ENTRY_MODAL](state, { type, path }) {
+ Object.assign(state, {
+ entryModal: {
+ type,
+ path,
+ entry: { ...state.entries[path] },
+ },
+ });
+ },
+ [types.DELETE_ENTRY](state, path) {
+ const entry = state.entries[path];
+ const parent = entry.parentPath
+ ? state.entries[entry.parentPath]
+ : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
+
+ entry.deleted = true;
+
+ parent.tree = parent.tree.filter(f => f.path !== entry.path);
+
+ if (entry.type === 'blob') {
+ state.changedFiles = state.changedFiles.concat(entry);
+ }
+ },
+ [types.RENAME_ENTRY](state, { path, name, entryPath = null }) {
+ const oldEntry = state.entries[entryPath || path];
+ const nameRegex =
+ !entryPath && oldEntry.type === 'blob'
+ ? new RegExp(`${oldEntry.name}$`)
+ : new RegExp(`^${path}`);
+ const newPath = oldEntry.path.replace(nameRegex, name);
+ const parentPath = oldEntry.parentPath ? oldEntry.parentPath.replace(nameRegex, name) : '';
+
+ state.entries[newPath] = {
+ ...oldEntry,
+ id: newPath,
+ key: `${name}-${oldEntry.type}-${oldEntry.id}`,
+ path: newPath,
+ name: entryPath ? oldEntry.name : name,
+ tempFile: true,
+ prevPath: oldEntry.path,
+ url: oldEntry.url.replace(new RegExp(`${oldEntry.path}/?$`), newPath),
+ tree: [],
+ parentPath,
+ raw: '',
+ };
+ oldEntry.moved = true;
+ oldEntry.movedPath = newPath;
+
+ const parent = parentPath
+ ? state.entries[parentPath]
+ : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
+ const newEntry = state.entries[newPath];
+
+ parent.tree = sortTree(parent.tree.concat(newEntry));
+
+ if (newEntry.type === 'blob') {
+ state.changedFiles = state.changedFiles.concat(newEntry);
+ }
+ },
...projectMutations,
...mergeRequestMutation,
...fileMutations,
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index 46547820425..a937fb157f8 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign */
import * as types from '../mutation_types';
+import { sortTree } from '../utils';
import { diffModes } from '../../constants';
export default {
@@ -43,7 +44,7 @@ export default {
rawPath: data.raw_path,
binary: data.binary,
renderError: data.render_error,
- raw: null,
+ raw: (state.entries[file.path] && state.entries[file.path].raw) || null,
baseRaw: null,
html: data.html,
size: data.size,
@@ -51,9 +52,27 @@ export default {
});
},
[types.SET_FILE_RAW_DATA](state, { file, raw }) {
- Object.assign(state.entries[file.path], {
- raw,
- });
+ const openPendingFile = state.openFiles.find(
+ f => f.path === file.path && f.pending && !(f.tempFile && !f.prevPath),
+ );
+
+ if (file.tempFile) {
+ Object.assign(state.entries[file.path], {
+ content: raw,
+ });
+ } else {
+ Object.assign(state.entries[file.path], {
+ raw,
+ });
+ }
+
+ if (!openPendingFile) return;
+
+ if (!openPendingFile.tempFile) {
+ openPendingFile.raw = raw;
+ } else if (openPendingFile.tempFile) {
+ openPendingFile.content = raw;
+ }
},
[types.SET_FILE_BASE_RAW_DATA](state, { file, baseRaw }) {
Object.assign(state.entries[file.path], {
@@ -109,11 +128,30 @@ export default {
},
[types.DISCARD_FILE_CHANGES](state, path) {
const stagedFile = state.stagedFiles.find(f => f.path === path);
+ const entry = state.entries[path];
+ const { deleted, prevPath } = entry;
Object.assign(state.entries[path], {
content: stagedFile ? stagedFile.content : state.entries[path].raw,
changed: false,
+ deleted: false,
+ moved: false,
+ movedPath: '',
});
+
+ if (deleted) {
+ const parent = entry.parentPath
+ ? state.entries[entry.parentPath]
+ : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
+
+ parent.tree = sortTree(parent.tree.concat(entry));
+ } else if (prevPath) {
+ const parent = entry.parentPath
+ ? state.entries[entry.parentPath]
+ : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
+
+ parent.tree = parent.tree.filter(f => f.path !== path);
+ }
},
[types.ADD_FILE_TO_CHANGED](state, path) {
Object.assign(state, {
diff --git a/app/assets/javascripts/ide/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js
index 2cf34af9274..eac7441ee54 100644
--- a/app/assets/javascripts/ide/stores/mutations/tree.js
+++ b/app/assets/javascripts/ide/stores/mutations/tree.js
@@ -1,4 +1,5 @@
import * as types from '../mutation_types';
+import { sortTree } from '../utils';
export default {
[types.TOGGLE_TREE_OPEN](state, path) {
@@ -36,4 +37,14 @@ export default {
changedFiles: [],
});
},
+ [types.RESTORE_TREE](state, path) {
+ const entry = state.entries[path];
+ const parent = entry.parentPath
+ ? state.entries[entry.parentPath]
+ : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
+
+ if (!parent.tree.find(f => f.path === path)) {
+ parent.tree = sortTree(parent.tree.concat(entry));
+ }
+ },
};
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index be229b2c723..46b52fa00fc 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -26,4 +26,10 @@ export default () => ({
rightPane: null,
links: {},
errorMessage: null,
+ entryModal: {
+ type: '',
+ path: '',
+ entry: {},
+ },
+ clientsidePreviewEnabled: false,
});
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index 9e6b86dd844..0ede76fd1e0 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -46,6 +46,10 @@ export const dataStructure = () => ({
parentPath: null,
lastOpenedAt: 0,
mrChange: null,
+ deleted: false,
+ prevPath: '',
+ movedPath: '',
+ moved: false,
});
export const decorateData = entity => {
@@ -105,15 +109,37 @@ export const setPageTitle = title => {
document.title = title;
};
+export const commitActionForFile = file => {
+ if (file.prevPath) {
+ return 'move';
+ } else if (file.deleted) {
+ return 'delete';
+ } else if (file.tempFile) {
+ return 'create';
+ }
+
+ return 'update';
+};
+
+export const getCommitFiles = stagedFiles =>
+ stagedFiles.reduce((acc, file) => {
+ if (file.moved) return acc;
+
+ return acc.concat({
+ ...file,
+ });
+ }, []);
+
export const createCommitPayload = ({ branch, getters, newBranch, state, rootState }) => ({
branch,
commit_message: state.commitMessage || getters.preBuiltCommitMessage,
- actions: rootState.stagedFiles.map(f => ({
- action: f.tempFile ? 'create' : 'update',
+ actions: getCommitFiles(rootState.stagedFiles).map(f => ({
+ action: commitActionForFile(f),
file_path: f.path,
- content: f.content,
+ previous_path: f.prevPath === '' ? undefined : f.prevPath,
+ content: f.content || undefined,
encoding: f.base64 ? 'base64' : 'text',
- last_commit_id: newBranch ? undefined : f.lastCommitSha,
+ last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
})),
start_branch: newBranch ? rootState.currentBranchId : undefined,
});
@@ -141,8 +167,7 @@ export const sortTree = sortedTree =>
)
.sort(sortTreesByTypeAndName);
-export const filePathMatches = (f, path) =>
- f.path.replace(new RegExp(`${f.name}$`), '').indexOf(`${path}/`) === 0;
+export const filePathMatches = (filePath, path) => filePath.indexOf(`${path}/`) === 0;
export const getChangesCountForFiles = (files, path) =>
- files.filter(f => filePathMatches(f, path)).length;
+ files.filter(f => filePathMatches(f.path, path)).length;
diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js
new file mode 100644
index 00000000000..d895eca7af0
--- /dev/null
+++ b/app/assets/javascripts/ide/utils.js
@@ -0,0 +1,17 @@
+import { commitItemIconMap } from './constants';
+
+export const getCommitIconMap = file => {
+ if (file.deleted) {
+ return commitItemIconMap.deleted;
+ } else if (file.tempFile) {
+ return commitItemIconMap.addition;
+ }
+
+ return commitItemIconMap.modified;
+};
+
+export const createPathWithExt = p => {
+ const ext = p.lastIndexOf('.') >= 0 ? p.substring(p.lastIndexOf('.') + 1) : '';
+
+ return `${p.substring(1, p.lastIndexOf('.') + 1 || p.length)}${ext || '.js'}`;
+};
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js
index f9ff0722c01..0035d809062 100644
--- a/app/assets/javascripts/importer_status.js
+++ b/app/assets/javascripts/importer_status.js
@@ -36,6 +36,8 @@ class ImporterStatus {
const $targetField = $tr.find('.import-target');
const $namespaceInput = $targetField.find('.js-select-namespace option:selected');
const id = $tr.attr('id').replace('repo_', '');
+ const repoData = $tr.data();
+
let targetNamespace;
let newName;
if ($namespaceInput.length > 0) {
@@ -45,12 +47,20 @@ class ImporterStatus {
}
$btn.disable().addClass('is-loading');
- return axios.post(this.importUrl, {
+ 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#repo_${id}`);
job.attr('id', `project_${data.id}`);
@@ -70,6 +80,9 @@ class ImporterStatus {
.catch((error) => {
let details = error;
+ const $statusField = $(`#repo_${this.id} .job-status`);
+ $statusField.text(__('Failed'));
+
if (error.response && error.response.data && error.response.data.errors) {
details = error.response.data.errors;
}
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index b6364318537..ad928484952 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -108,6 +108,11 @@
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
projectPath: {
type: String,
required: true,
@@ -282,6 +287,7 @@
:issuable-templates="issuableTemplates"
:markdown-docs-path="markdownDocsPath"
:markdown-preview-path="markdownPreviewPath"
+ :markdown-version="markdownVersion"
:project-path="projectPath"
:project-namespace="projectNamespace"
:show-delete-button="showDeleteButton"
diff --git a/app/assets/javascripts/issue_show/components/edited.vue b/app/assets/javascripts/issue_show/components/edited.vue
index 5ff5b1630b1..05cd976f196 100644
--- a/app/assets/javascripts/issue_show/components/edited.vue
+++ b/app/assets/javascripts/issue_show/components/edited.vue
@@ -46,7 +46,7 @@
by
<a
:href="updatedByPath"
- class="author_link"
+ class="author-link"
>
<span>{{ updatedByName }}</span>
</a>
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index 5f58f671c73..97acc5ba385 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -20,6 +20,11 @@
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
canAttachFile: {
type: Boolean,
required: false,
@@ -47,6 +52,7 @@
<markdown-field
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
+ :markdown-version="markdownVersion"
:can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete"
>
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
index 5bfc072e3da..e509bb52f7d 100644
--- a/app/assets/javascripts/issue_show/components/form.vue
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -35,6 +35,11 @@
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
projectPath: {
type: String,
required: true,
@@ -97,6 +102,7 @@
:form-state="formState"
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
+ :markdown-version="markdownVersion"
:can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete"
/>
diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue
index 12101c0daa5..b5e8e0ea44b 100644
--- a/app/assets/javascripts/issue_show/components/title.vue
+++ b/app/assets/javascripts/issue_show/components/title.vue
@@ -1,67 +1,67 @@
<script>
- import animateMixin from '../mixins/animate';
- import eventHub from '../event_hub';
- import tooltip from '../../vue_shared/directives/tooltip';
- import { spriteIcon } from '../../lib/utils/common_utils';
+import animateMixin from '../mixins/animate';
+import eventHub from '../event_hub';
+import tooltip from '../../vue_shared/directives/tooltip';
+import { spriteIcon } from '../../lib/utils/common_utils';
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ mixins: [animateMixin],
+ props: {
+ issuableRef: {
+ type: [String, Number],
+ required: true,
},
- mixins: [animateMixin],
- props: {
- issuableRef: {
- type: String,
- required: true,
- },
- canUpdate: {
- required: false,
- type: Boolean,
- default: false,
- },
- titleHtml: {
- type: String,
- required: true,
- },
- titleText: {
- type: String,
- required: true,
- },
- showInlineEditButton: {
- type: Boolean,
- required: false,
- default: false,
- },
+ canUpdate: {
+ required: false,
+ type: Boolean,
+ default: false,
},
- data() {
- return {
- preAnimation: false,
- pulseAnimation: false,
- titleEl: document.querySelector('title'),
- };
+ titleHtml: {
+ type: String,
+ required: true,
},
- computed: {
- pencilIcon() {
- return spriteIcon('pencil', 'link-highlight');
- },
+ titleText: {
+ type: String,
+ required: true,
},
- watch: {
- titleHtml() {
- this.setPageTitle();
- this.animateChange();
- },
+ showInlineEditButton: {
+ type: Boolean,
+ required: false,
+ default: false,
},
- methods: {
- setPageTitle() {
- const currentPageTitleScope = this.titleEl.innerText.split('·');
- currentPageTitleScope[0] = `${this.titleText} (${this.issuableRef}) `;
- this.titleEl.textContent = currentPageTitleScope.join('·');
- },
- edit() {
- eventHub.$emit('open.form');
- },
+ },
+ data() {
+ return {
+ preAnimation: false,
+ pulseAnimation: false,
+ titleEl: document.querySelector('title'),
+ };
+ },
+ computed: {
+ pencilIcon() {
+ return spriteIcon('pencil', 'link-highlight');
},
- };
+ },
+ watch: {
+ titleHtml() {
+ this.setPageTitle();
+ this.animateChange();
+ },
+ },
+ methods: {
+ setPageTitle() {
+ const currentPageTitleScope = this.titleEl.innerText.split('·');
+ currentPageTitleScope[0] = `${this.titleText} (${this.issuableRef}) `;
+ this.titleEl.textContent = currentPageTitleScope.join('·');
+ },
+ edit() {
+ eventHub.$emit('open.form');
+ },
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 37a45d1d1a2..cb851ff6745 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -39,7 +39,7 @@ export default class LabelsSelect {
showNo = $dropdown.data('showNo');
showAny = $dropdown.data('showAny');
showMenuAbove = $dropdown.data('showMenuAbove');
- defaultLabel = $dropdown.data('defaultLabel');
+ defaultLabel = $dropdown.data('defaultLabel') || 'Label';
abilityName = $dropdown.data('abilityName');
$selectbox = $dropdown.closest('.selectbox');
$block = $selectbox.closest('.block');
@@ -244,21 +244,21 @@ export default class LabelsSelect {
var $dropdownInputField = $dropdownParent.find('.dropdown-input-field');
var isSelected = el !== null ? el.hasClass('is-active') : false;
- var { title } = selected;
+ var title = selected ? selected.title : null;
var selectedLabels = this.selected;
if ($dropdownInputField.length && $dropdownInputField.val().length) {
$dropdownParent.find('.dropdown-input-clear').trigger('click');
}
- if (selected.id === 0) {
+ if (selected && selected.id === 0) {
this.selected = [];
return 'No Label';
}
else if (isSelected) {
this.selected.push(title);
}
- else {
+ else if (!isSelected && title) {
var index = this.selected.indexOf(title);
this.selected.splice(index, 1);
}
@@ -409,6 +409,14 @@ export default class LabelsSelect {
}
}
},
+ opened: function(e) {
+ if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ const previousSelection = $dropdown.attr('data-selected');
+ this.selected = previousSelection ? previousSelection.split(',') : [];
+ $dropdown.data('glDropdown').updateLabel();
+ }
+ },
+ preserveContext: true,
});
// Set dropdown data
diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js
index 9482d131344..bd2212edec7 100644
--- a/app/assets/javascripts/lazy_loader.js
+++ b/app/assets/javascripts/lazy_loader.js
@@ -1,6 +1,7 @@
import _ from 'underscore';
-export const placeholderImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
+export const placeholderImage =
+ 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
const SCROLL_THRESHOLD = 300;
export default class LazyLoader {
@@ -18,11 +19,17 @@ export default class LazyLoader {
scrollContainer.addEventListener('load', () => this.loadCheck());
}
searchLazyImages() {
- this.lazyImages = [].slice.call(document.querySelectorAll('.lazy'));
+ const that = this;
+ requestIdleCallback(
+ () => {
+ that.lazyImages = [].slice.call(document.querySelectorAll('.lazy'));
- if (this.lazyImages.length) {
- this.checkElementsInView();
- }
+ if (that.lazyImages.length) {
+ that.checkElementsInView();
+ }
+ },
+ { timeout: 500 },
+ );
}
startContentObserver() {
const contentNode = document.querySelector(this.observerNode) || document.querySelector('body');
@@ -48,14 +55,16 @@ export default class LazyLoader {
const visHeight = scrollTop + window.innerHeight + SCROLL_THRESHOLD;
// Loading Images which are in the current viewport or close to them
- this.lazyImages = this.lazyImages.filter((selectedImage) => {
+ this.lazyImages = this.lazyImages.filter(selectedImage => {
if (selectedImage.getAttribute('data-src')) {
const imgBoundRect = selectedImage.getBoundingClientRect();
const imgTop = scrollTop + imgBoundRect.top;
const imgBound = imgTop + imgBoundRect.height;
if (scrollTop < imgBound && visHeight > imgTop) {
- LazyLoader.loadImage(selectedImage);
+ requestAnimationFrame(() => {
+ LazyLoader.loadImage(selectedImage);
+ });
return false;
}
@@ -66,7 +75,18 @@ export default class LazyLoader {
}
static loadImage(img) {
if (img.getAttribute('data-src')) {
- img.setAttribute('src', img.getAttribute('data-src'));
+ let imgUrl = img.getAttribute('data-src');
+ // Only adding width + height for avatars for now
+ if (imgUrl.indexOf('/avatar/') > -1 && imgUrl.indexOf('?') === -1) {
+ let targetWidth = null;
+ if (img.getAttribute('width')) {
+ targetWidth = img.getAttribute('width');
+ } else {
+ targetWidth = img.width;
+ }
+ if (targetWidth) imgUrl += `?width=${targetWidth}`;
+ }
+ img.setAttribute('src', imgUrl);
img.removeAttribute('data-src');
img.classList.remove('lazy');
img.classList.add('js-lazy-loaded');
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 6b7550efff8..2f3dd6f6cbc 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -541,6 +541,26 @@ export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
});
};
+/**
+ * Method to round of values with decimal places
+ * with provided precision.
+ *
+ * Taken from https://stackoverflow.com/a/7343013/414749
+ *
+ * Eg; roundOffFloat(3.141592, 3) = 3.142
+ *
+ * Refer to spec/javascripts/lib/utils/common_utils_spec.js for
+ * more supported examples.
+ *
+ * @param {Float} number
+ * @param {Number} precision
+ */
+export const roundOffFloat = (number, precision = 0) => {
+ // eslint-disable-next-line no-restricted-properties
+ const multiplier = Math.pow(10, precision);
+ return Math.round(number * multiplier) / multiplier;
+};
+
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 7cca32dc6fa..1f66fa811ea 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -1,11 +1,10 @@
import $ from 'jquery';
import timeago from 'timeago.js';
-import dateFormat from 'vendor/date.format';
+import dateFormat from 'dateformat';
import { pluralize } from './text_utility';
import { languageCode, s__ } from '../../locale';
window.timeago = timeago;
-window.dateFormat = dateFormat;
/**
* Returns i18n month names array.
@@ -143,7 +142,8 @@ export const localTimeAgo = ($timeagoEls, setTimeago = true) => {
if (setTimeago) {
// Recreate with custom template
$(el).tooltip({
- template: '<div class="tooltip local-timeago" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',
+ template:
+ '<div class="tooltip local-timeago" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',
});
}
@@ -275,10 +275,8 @@ export const totalDaysInMonth = date => {
*
* @param {Array} quarter
*/
-export const totalDaysInQuarter = quarter => quarter.reduce(
- (acc, month) => acc + totalDaysInMonth(month),
- 0,
-);
+export const totalDaysInQuarter = quarter =>
+ quarter.reduce((acc, month) => acc + totalDaysInMonth(month), 0);
/**
* Returns list of Dates referring to Sundays of the month
@@ -333,14 +331,8 @@ export const getTimeframeWindowFrom = (startDate, length) => {
// Iterate and set date for the size of length
// and push date reference to timeframe list
const timeframe = new Array(length)
- .fill()
- .map(
- (val, i) => new Date(
- startDate.getFullYear(),
- startDate.getMonth() + i,
- 1,
- ),
- );
+ .fill()
+ .map((val, i) => new Date(startDate.getFullYear(), startDate.getMonth() + i, 1));
// Change date of last timeframe item to last date of the month
timeframe[length - 1].setDate(totalDaysInMonth(timeframe[length - 1]));
@@ -362,14 +354,15 @@ export const getTimeframeWindowFrom = (startDate, length) => {
* @param {Date} date
* @param {Array} quarter
*/
-export const dayInQuarter = (date, quarter) => quarter.reduce((acc, month) => {
- if (date.getMonth() > month.getMonth()) {
- return acc + totalDaysInMonth(month);
- } else if (date.getMonth() === month.getMonth()) {
- return acc + date.getDate();
- }
- return acc + 0;
-}, 0);
+export const dayInQuarter = (date, quarter) =>
+ quarter.reduce((acc, month) => {
+ if (date.getMonth() > month.getMonth()) {
+ return acc + totalDaysInMonth(month);
+ } else if (date.getMonth() === month.getMonth()) {
+ return acc + date.getDate();
+ }
+ return acc + 0;
+ }, 0);
window.gl = window.gl || {};
window.gl.utils = {
diff --git a/app/assets/javascripts/lib/utils/http_status.js b/app/assets/javascripts/lib/utils/http_status.js
index 229d53b18b0..e4852c85378 100644
--- a/app/assets/javascripts/lib/utils/http_status.js
+++ b/app/assets/javascripts/lib/utils/http_status.js
@@ -2,11 +2,34 @@
* exports HTTP status codes
*/
-export default {
+const httpStatusCodes = {
ABORTED: 0,
- NO_CONTENT: 204,
OK: 200,
+ CREATED: 201,
+ ACCEPTED: 202,
+ NON_AUTHORITATIVE_INFORMATION: 203,
+ NO_CONTENT: 204,
+ RESET_CONTENT: 205,
+ PARTIAL_CONTENT: 206,
+ MULTI_STATUS: 207,
+ ALREADY_REPORTED: 208,
+ IM_USED: 226,
MULTIPLE_CHOICES: 300,
BAD_REQUEST: 400,
NOT_FOUND: 404,
};
+
+export const successCodes = [
+ httpStatusCodes.OK,
+ httpStatusCodes.CREATED,
+ httpStatusCodes.ACCEPTED,
+ httpStatusCodes.NON_AUTHORITATIVE_INFORMATION,
+ httpStatusCodes.NO_CONTENT,
+ httpStatusCodes.RESET_CONTENT,
+ httpStatusCodes.PARTIAL_CONTENT,
+ httpStatusCodes.MULTI_STATUS,
+ httpStatusCodes.ALREADY_REPORTED,
+ httpStatusCodes.IM_USED,
+];
+
+export default httpStatusCodes;
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
index 7fca80c2fdb..198711cf427 100644
--- a/app/assets/javascripts/lib/utils/poll.js
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -1,4 +1,4 @@
-import httpStatusCodes from './http_status';
+import httpStatusCodes, { successCodes } from './http_status';
import { normalizeHeaders } from './common_utils';
/**
@@ -38,7 +38,7 @@ import { normalizeHeaders } from './common_utils';
* } else {
* poll.stop();
* }
-* });
+ * });
*
* 1. Checks for response and headers before start polling
* 2. Interval is provided by `Poll-Interval` header.
@@ -51,8 +51,8 @@ export default class Poll {
constructor(options = {}) {
this.options = options;
this.options.data = options.data || {};
- this.options.notificationCallback = options.notificationCallback ||
- function notificationCallback() {};
+ this.options.notificationCallback =
+ options.notificationCallback || function notificationCallback() {};
this.intervalHeader = 'POLL-INTERVAL';
this.timeoutID = null;
@@ -62,7 +62,7 @@ export default class Poll {
checkConditions(response) {
const headers = normalizeHeaders(response.headers);
const pollInterval = parseInt(headers[this.intervalHeader], 10);
- if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
+ if (pollInterval > 0 && successCodes.indexOf(response.status) !== -1 && this.canPoll) {
this.timeoutID = setTimeout(() => {
this.makeRequest();
}, pollInterval);
@@ -77,11 +77,11 @@ export default class Poll {
notificationCallback(true);
return resource[method](data)
- .then((response) => {
+ .then(response => {
this.checkConditions(response);
notificationCallback(false);
})
- .catch((error) => {
+ .catch(error => {
notificationCallback(false);
if (error.status === httpStatusCodes.ABORTED) {
return;
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index 5f25c6ce1ae..2be3c97bd95 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -76,6 +76,20 @@ export function capitalizeFirstCharacter(text) {
}
/**
+ * Returns the first character capitalized
+ *
+ * If falsey, returns empty string.
+ *
+ * @param {String} text
+ * @return {String}
+ */
+export function getFirstCharacterCapitalized(text) {
+ return text
+ ? text.charAt(0).toUpperCase()
+ : '';
+}
+
+/**
* Replaces all html tags from a string with the given replacement.
*
* @param {String} string
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index c9ce838cd48..2718f73a830 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -26,7 +26,7 @@ import './feature_highlight/feature_highlight_options';
import LazyLoader from './lazy_loader';
import initLogoAnimation from './logo';
import './milestone_select';
-import './projects_dropdown';
+import './frequent_items';
import initBreadcrumbs from './breadcrumb';
import initDispatcher from './dispatcher';
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 7d6f1000ebe..53d7504de35 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -69,12 +69,23 @@ let { location } = window;
export default class MergeRequestTabs {
constructor({ action, setUrl, stubLocation } = {}) {
- const mergeRequestTabs = document.querySelector('.js-tabs-affix');
+ this.mergeRequestTabs = document.querySelector('.merge-request-tabs-container');
+ this.mergeRequestTabsAll =
+ this.mergeRequestTabs && this.mergeRequestTabs.querySelectorAll
+ ? this.mergeRequestTabs.querySelectorAll('.merge-request-tabs li')
+ : null;
+ this.mergeRequestTabPanes = document.querySelector('#diff-notes-app');
+ this.mergeRequestTabPanesAll =
+ this.mergeRequestTabPanes && this.mergeRequestTabPanes.querySelectorAll
+ ? this.mergeRequestTabPanes.querySelectorAll('.tab-pane')
+ : null;
const navbar = document.querySelector('.navbar-gitlab');
const peek = document.getElementById('js-peek');
const paddingTop = 16;
+
this.commitsTab = document.querySelector('.tab-content .commits.tab-pane');
+ this.currentTab = null;
this.diffsLoaded = false;
this.pipelinesLoaded = false;
this.commitsLoaded = false;
@@ -84,15 +95,15 @@ export default class MergeRequestTabs {
this.setUrl = setUrl !== undefined ? setUrl : true;
this.setCurrentAction = this.setCurrentAction.bind(this);
this.tabShown = this.tabShown.bind(this);
- this.showTab = this.showTab.bind(this);
+ this.clickTab = this.clickTab.bind(this);
this.stickyTop = navbar ? navbar.offsetHeight - paddingTop : 0;
if (peek) {
this.stickyTop += peek.offsetHeight;
}
- if (mergeRequestTabs) {
- this.stickyTop += mergeRequestTabs.offsetHeight;
+ if (this.mergeRequestTabs) {
+ this.stickyTop += this.mergeRequestTabs.offsetHeight;
}
if (stubLocation) {
@@ -100,25 +111,22 @@ export default class MergeRequestTabs {
}
this.bindEvents();
- this.activateTab(action);
+ if (
+ this.mergeRequestTabs &&
+ this.mergeRequestTabs.querySelector(`a[data-action='${action}']`) &&
+ this.mergeRequestTabs.querySelector(`a[data-action='${action}']`).click
+ )
+ this.mergeRequestTabs.querySelector(`a[data-action='${action}']`).click();
this.initAffix();
}
bindEvents() {
- $(document)
- .on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
- .on('click', '.js-show-tab', this.showTab);
-
- $('.merge-request-tabs a[data-toggle="tab"]').on('click', this.clickTab);
+ $('.merge-request-tabs a[data-toggle="tabvue"]').on('click', this.clickTab);
}
// Used in tests
unbindEvents() {
- $(document)
- .off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
- .off('click', '.js-show-tab', this.showTab);
-
- $('.merge-request-tabs a[data-toggle="tab"]').off('click', this.clickTab);
+ $('.merge-request-tabs a[data-toggle="tabvue"]').off('click', this.clickTab);
}
destroyPipelinesView() {
@@ -130,58 +138,87 @@ export default class MergeRequestTabs {
}
}
- showTab(e) {
- e.preventDefault();
- this.activateTab($(e.target).data('action'));
- }
-
clickTab(e) {
- if (e.currentTarget && isMetaClick(e)) {
- const targetLink = e.currentTarget.getAttribute('href');
+ if (e.currentTarget) {
e.stopImmediatePropagation();
e.preventDefault();
- window.open(targetLink, '_blank');
+
+ const { action } = e.currentTarget.dataset;
+
+ if (action) {
+ const href = e.currentTarget.getAttribute('href');
+ this.tabShown(action, href);
+ } else if (isMetaClick(e)) {
+ const targetLink = e.currentTarget.getAttribute('href');
+ window.open(targetLink, '_blank');
+ }
}
}
- tabShown(e) {
- const $target = $(e.target);
- const action = $target.data('action');
-
- if (action === 'commits') {
- this.loadCommits($target.attr('href'));
- this.expandView();
- this.resetViewContainer();
- this.destroyPipelinesView();
- } else if (this.isDiffAction(action)) {
- if (!isInVueNoteablePage()) {
- this.loadDiff($target.attr('href'));
- }
- if (bp.getBreakpointSize() !== 'lg') {
- this.shrinkView();
+ tabShown(action, href) {
+ if (action !== this.currentTab && this.mergeRequestTabs) {
+ this.currentTab = action;
+
+ if (this.mergeRequestTabPanesAll) {
+ this.mergeRequestTabPanesAll.forEach(el => {
+ const tabPane = el;
+ tabPane.style.display = 'none';
+ });
}
- if (this.diffViewType() === 'parallel') {
- this.expandViewContainer();
+
+ if (this.mergeRequestTabsAll) {
+ this.mergeRequestTabsAll.forEach(el => {
+ el.classList.remove('active');
+ });
}
- this.destroyPipelinesView();
- this.commitsTab.classList.remove('active');
- } else if (action === 'pipelines') {
- this.resetViewContainer();
- this.mountPipelinesView();
- } else {
- if (bp.getBreakpointSize() !== 'xs') {
+
+ const tabPane = this.mergeRequestTabPanes.querySelector(`#${action}`);
+ if (tabPane) tabPane.style.display = 'block';
+ const tab = this.mergeRequestTabs.querySelector(`.${action}-tab`);
+ if (tab) tab.classList.add('active');
+
+ if (action === 'commits') {
+ this.loadCommits(href);
+ this.expandView();
+ this.resetViewContainer();
+ this.destroyPipelinesView();
+ } else if (action === 'new') {
this.expandView();
+ this.resetViewContainer();
+ this.destroyPipelinesView();
+ } else if (this.isDiffAction(action)) {
+ if (!isInVueNoteablePage()) {
+ this.loadDiff(href);
+ }
+ if (bp.getBreakpointSize() !== 'lg') {
+ this.shrinkView();
+ }
+ if (this.diffViewType() === 'parallel') {
+ this.expandViewContainer();
+ }
+ this.destroyPipelinesView();
+ this.commitsTab.classList.remove('active');
+ } else if (action === 'pipelines') {
+ this.resetViewContainer();
+ this.mountPipelinesView();
+ } else {
+ this.mergeRequestTabPanes.querySelector('#notes').style.display = 'block';
+ this.mergeRequestTabs.querySelector('.notes-tab').classList.add('active');
+
+ if (bp.getBreakpointSize() !== 'xs') {
+ this.expandView();
+ }
+ this.resetViewContainer();
+ this.destroyPipelinesView();
+
+ initDiscussionTab();
+ }
+ if (this.setUrl) {
+ this.setCurrentAction(action);
}
- this.resetViewContainer();
- this.destroyPipelinesView();
- initDiscussionTab();
- }
- if (this.setUrl) {
- this.setCurrentAction(action);
+ this.eventHub.$emit('MergeRequestTabChange', this.getCurrentAction());
}
-
- this.eventHub.$emit('MergeRequestTabChange', this.getCurrentAction());
}
scrollToElement(container) {
@@ -194,12 +231,6 @@ export default class MergeRequestTabs {
}
}
- // Activate a tab based on the current action
- activateTab(action) {
- // important note: the .tab('show') method triggers 'shown.bs.tab' event itself
- $(`.merge-request-tabs a[data-action='${action}']`).tab('show');
- }
-
// Replaces the current Merge Request-specific action in the URL with a new one
//
// If the action is "notes", the URL is reset to the standard
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 77acba6e355..640a4c8260f 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -5,6 +5,7 @@
import $ from 'jquery';
import _ from 'underscore';
import { __ } from '~/locale';
+import '~/gl_dropdown';
import axios from './lib/utils/axios_utils';
import { timeFor } from './lib/utils/datetime_utility';
import ModalStore from './boards/stores/modal_store';
@@ -251,3 +252,5 @@ export default class MilestoneSelect {
});
}
}
+
+window.MilestoneSelect = MilestoneSelect;
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index e1c8b6a6d4a..6afaefc56f8 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -1,5 +1,7 @@
<script>
import _ from 'underscore';
+import { s__ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
import Flash from '../../flash';
import MonitoringService from '../services/monitoring_service';
import GraphGroup from './graph_group.vue';
@@ -13,6 +15,7 @@ export default {
Graph,
GraphGroup,
EmptyState,
+ Icon,
},
props: {
hasMetrics: {
@@ -80,6 +83,14 @@ export default {
type: String,
required: true,
},
+ environmentsEndpoint: {
+ type: String,
+ required: true,
+ },
+ currentEnvironmentName: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -96,6 +107,7 @@ export default {
this.service = new MonitoringService({
metricsEndpoint: this.metricsEndpoint,
deploymentEndpoint: this.deploymentEndpoint,
+ environmentsEndpoint: this.environmentsEndpoint,
});
eventHub.$on('toggleAspectRatio', this.toggleAspectRatio);
eventHub.$on('hoverChanged', this.hoverChanged);
@@ -122,7 +134,11 @@ export default {
this.service
.getDeploymentData()
.then(data => this.store.storeDeploymentData(data))
- .catch(() => new Flash('Error getting deployment information.')),
+ .catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))),
+ this.service
+ .getEnvironmentsData()
+ .then((data) => this.store.storeEnvironmentsData(data))
+ .catch(() => Flash(s__('Metrics|There was an error getting environments information.'))),
])
.then(() => {
if (this.store.groups.length < 1) {
@@ -131,6 +147,7 @@ export default {
}
this.showEmptyState = false;
})
+ .then(this.resize)
.catch(() => {
this.state = 'unableToConnect';
});
@@ -155,8 +172,41 @@ export default {
<template>
<div
v-if="!showEmptyState"
- class="prometheus-graphs"
+ class="prometheus-graphs prepend-top-10"
>
+ <div class="environments d-flex align-items-center">
+ {{ s__('Metrics|Environment') }}
+ <div class="dropdown prepend-left-10">
+ <button
+ class="dropdown-menu-toggle"
+ data-toggle="dropdown"
+ type="button"
+ >
+ <span>
+ {{ currentEnvironmentName }}
+ </span>
+ <icon
+ name="chevron-down"
+ />
+ </button>
+ <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
+ <ul>
+ <li
+ v-for="environment in store.environmentsData"
+ :key="environment.latest.id"
+ >
+ <a
+ :href="environment.latest.metrics_path"
+ :class="{ 'is-active': environment.latest.name == currentEnvironmentName }"
+ class="dropdown-item"
+ >
+ {{ environment.latest.name }}
+ </a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
<graph-group
v-for="(groupData, index) in store.groups"
:key="index"
diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue
index 92fe98508ad..1e6803abf3a 100644
--- a/app/assets/javascripts/monitoring/components/graph/flag.vue
+++ b/app/assets/javascripts/monitoring/components/graph/flag.vue
@@ -125,6 +125,7 @@ export default {
:class="flagOrientation"
class="prometheus-graph-flag popover"
>
+ <div class="arrow-shadow"></div>
<div class="arrow"></div>
<div class="popover-title">
<h5 v-if="deploymentFlagData">
diff --git a/app/assets/javascripts/monitoring/services/monitoring_service.js b/app/assets/javascripts/monitoring/services/monitoring_service.js
index 6fcca36d2fa..260d424378e 100644
--- a/app/assets/javascripts/monitoring/services/monitoring_service.js
+++ b/app/assets/javascripts/monitoring/services/monitoring_service.js
@@ -1,6 +1,7 @@
import axios from '../../lib/utils/axios_utils';
import statusCodes from '../../lib/utils/http_status';
import { backOff } from '../../lib/utils/common_utils';
+import { s__ } from '../../locale';
const MAX_REQUESTS = 3;
@@ -23,9 +24,10 @@ function backOffRequest(makeRequestCallback) {
}
export default class MonitoringService {
- constructor({ metricsEndpoint, deploymentEndpoint }) {
+ constructor({ metricsEndpoint, deploymentEndpoint, environmentsEndpoint }) {
this.metricsEndpoint = metricsEndpoint;
this.deploymentEndpoint = deploymentEndpoint;
+ this.environmentsEndpoint = environmentsEndpoint;
}
getGraphsData() {
@@ -33,7 +35,7 @@ export default class MonitoringService {
.then(resp => resp.data)
.then((response) => {
if (!response || !response.data) {
- throw new Error('Unexpected metrics data response from prometheus endpoint');
+ throw new Error(s__('Metrics|Unexpected metrics data response from prometheus endpoint'));
}
return response.data;
});
@@ -47,9 +49,20 @@ export default class MonitoringService {
.then(resp => resp.data)
.then((response) => {
if (!response || !response.deployments) {
- throw new Error('Unexpected deployment data response from prometheus endpoint');
+ throw new Error(s__('Metrics|Unexpected deployment data response from prometheus endpoint'));
}
return response.deployments;
});
}
+
+ getEnvironmentsData() {
+ return axios.get(this.environmentsEndpoint)
+ .then(resp => resp.data)
+ .then((response) => {
+ if (!response || !response.environments) {
+ throw new Error(s__('Metrics|There was an error fetching the environments data, please try again'));
+ }
+ return response.environments;
+ });
+ }
}
diff --git a/app/assets/javascripts/monitoring/stores/monitoring_store.js b/app/assets/javascripts/monitoring/stores/monitoring_store.js
index 535c415cd6d..176f7d9eef2 100644
--- a/app/assets/javascripts/monitoring/stores/monitoring_store.js
+++ b/app/assets/javascripts/monitoring/stores/monitoring_store.js
@@ -1,7 +1,10 @@
import _ from 'underscore';
function sortMetrics(metrics) {
- return _.chain(metrics).sortBy('title').sortBy('weight').value();
+ return _.chain(metrics)
+ .sortBy('title')
+ .sortBy('weight')
+ .value();
}
function normalizeMetrics(metrics) {
@@ -24,6 +27,7 @@ export default class MonitoringStore {
constructor() {
this.groups = [];
this.deploymentData = [];
+ this.environmentsData = [];
}
storeMetrics(groups = []) {
@@ -37,6 +41,12 @@ export default class MonitoringStore {
this.deploymentData = deploymentData;
}
+ storeEnvironmentsData(environmentsData = []) {
+ this.environmentsData = environmentsData.filter(
+ environment => !!environment.latest.last_deployment,
+ );
+ }
+
getMetricsCount() {
return this.groups.reduce((count, group) => count + group.metrics.length, 0);
}
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 48cda28a1ae..8124ae6201f 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1251,13 +1251,15 @@ export default class Notes {
var postUrl = $originalContentEl.data('postUrl');
var targetId = $originalContentEl.data('targetId');
var targetType = $originalContentEl.data('targetType');
+ var markdownVersion = $originalContentEl.data('markdownVersion');
this.glForm = new GLForm($editForm.find('form'), this.enableGFM);
$editForm
.find('form')
.attr('action', `${postUrl}?html=true`)
- .attr('data-remote', 'true');
+ .attr('data-remote', 'true')
+ .attr('data-markdown-version', markdownVersion);
$editForm.find('.js-form-target-id').val(targetId);
$editForm.find('.js-form-target-type').val(targetType);
$editForm
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index c6a524f68cb..6612bc44e0b 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -34,6 +34,11 @@ export default {
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
},
data() {
return {
@@ -344,6 +349,7 @@ Please check your network connection and try again.`;
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
+ :markdown-version="markdownVersion"
:add-spacing-classes="false">
<textarea
id="note-body"
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index d321f2ce15e..27ff7dea909 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -48,16 +48,17 @@ export default {
imageDiffHtml() {
return this.discussion.imageDiffHtml;
},
- currentUser() {
- return this.noteableData.current_user;
- },
userColorScheme() {
return window.gon.user_color_scheme;
},
normalizedDiffLines() {
- const lines = this.discussion.truncatedDiffLines || [];
+ if (this.discussion.truncatedDiffLines) {
+ return this.discussion.truncatedDiffLines.map(line =>
+ trimFirstCharOfLineContent(convertObjectPropsToCamelCase(line)),
+ );
+ }
- return lines.map(line => trimFirstCharOfLineContent(convertObjectPropsToCamelCase(line)));
+ return [];
},
},
mounted() {
@@ -94,7 +95,7 @@ export default {
>
<diff-file-header
:diff-file="diffFile"
- :current-user="currentUser"
+ :can-current-user-fork="false"
:discussions-expanded="isDiscussionsExpanded"
:expanded="!isCollapsed"
/>
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index 6385b75e557..ad6e7cf501d 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -5,19 +5,20 @@ import resolvedSvg from 'icons/_icon_status_success_solid.svg';
import mrIssueSvg from 'icons/_icon_mr_issue.svg';
import nextDiscussionSvg from 'icons/_next_discussion.svg';
import { pluralize } from '../../lib/utils/text_utility';
-import { scrollToElement } from '../../lib/utils/common_utils';
+import discussionNavigation from '../mixins/discussion_navigation';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
directives: {
tooltip,
},
+ mixins: [discussionNavigation],
computed: {
...mapGetters([
'getUserData',
'getNoteableData',
'discussionCount',
- 'unresolvedDiscussions',
+ 'firstUnresolvedDiscussionId',
'resolvedDiscussionCount',
]),
isLoggedIn() {
@@ -35,11 +36,6 @@ export default {
resolveAllDiscussionsIssuePath() {
return this.getNoteableData.create_issue_to_resolve_discussions_path;
},
- firstUnresolvedDiscussionId() {
- const item = this.unresolvedDiscussions[0] || {};
-
- return item.id;
- },
},
created() {
this.resolveSvg = resolveSvg;
@@ -50,22 +46,10 @@ export default {
methods: {
...mapActions(['expandDiscussion']),
jumpToFirstUnresolvedDiscussion() {
- const discussionId = this.firstUnresolvedDiscussionId;
- if (!discussionId) {
- return;
- }
-
- const el = document.querySelector(`[data-discussion-id="${discussionId}"]`);
- const activeTab = window.mrTabs.currentAction;
-
- if (activeTab === 'commits' || activeTab === 'pipelines') {
- window.mrTabs.activateTab('show');
- }
+ const diffTab = window.mrTabs.currentAction === 'diffs';
+ const discussionId = this.firstUnresolvedDiscussionId(diffTab);
- if (el) {
- this.expandDiscussion({ discussionId });
- scrollToElement(el);
- }
+ this.jumpToDiscussion(discussionId);
},
},
};
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index d2db68df98e..6f4a0709825 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -92,6 +92,7 @@ export default {
:is-editing="isEditing"
:note-body="noteBody"
:note-id="note.id"
+ :markdown-version="note.cached_markdown_version"
@handleFormUpdate="handleFormUpdate"
@cancelForm="formCancelHandler"
/>
diff --git a/app/assets/javascripts/notes/components/note_edited_text.vue b/app/assets/javascripts/notes/components/note_edited_text.vue
index 391bb2ae179..d848335022f 100644
--- a/app/assets/javascripts/notes/components/note_edited_text.vue
+++ b/app/assets/javascripts/notes/components/note_edited_text.vue
@@ -42,7 +42,7 @@ export default {
by
<a
:href="editedBy.path"
- class="js-vue-author author_link">
+ class="js-vue-author author-link">
{{ editedBy.name }}
</a>
</template>
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index a4e3faa5d75..abcd4422d7c 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -7,7 +7,7 @@ import issuableStateMixin from '../mixins/issuable_state';
import resolvable from '../mixins/resolvable';
export default {
- name: 'IssueNoteForm',
+ name: 'NoteForm',
components: {
issueWarning,
markdownField,
@@ -24,6 +24,11 @@ export default {
required: false,
default: 0,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
saveButtonTitle: {
type: String,
required: false,
@@ -156,6 +161,7 @@ export default {
<markdown-field
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
+ :markdown-version="markdownVersion"
:quick-actions-docs-path="quickActionsDocsPath"
:add-spacing-classes="false">
<textarea
@@ -194,7 +200,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
class="btn btn-cancel note-edit-cancel js-close-discussion-note-form"
type="button"
@click="cancelHandler()">
- {{ __('Discard draft') }}
+ Cancel
</button>
</div>
</form>
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index ee3580895df..a621418cf72 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -74,6 +74,9 @@ export default {
</div>
<a :href="author.path">
<span class="note-header-author-name">{{ author.name }}</span>
+ <span
+ v-if="author.status_tooltip_html"
+ v-html="author.status_tooltip_html"></span>
<span class="note-headline-light">
@{{ author.username }}
</span>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index bee635398b3..0fe1c16854a 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -1,11 +1,11 @@
<script>
-import _ from 'underscore';
import { mapActions, mapGetters } from 'vuex';
import resolveDiscussionsSvg from 'icons/_icon_mr_issue.svg';
import nextDiscussionsSvg from 'icons/_next_discussion.svg';
-import { convertObjectPropsToCamelCase, scrollToElement } from '~/lib/utils/common_utils';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { truncateSha } from '~/lib/utils/text_utility';
import systemNote from '~/vue_shared/components/notes/system_note.vue';
+import { s__ } from '~/locale';
import Flash from '../../flash';
import { SYSTEM_NOTE } from '../constants';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
@@ -20,6 +20,7 @@ import placeholderSystemNote from '../../vue_shared/components/notes/placeholder
import autosave from '../mixins/autosave';
import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
+import discussionNavigation from '../mixins/discussion_navigation';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
@@ -39,7 +40,7 @@ export default {
directives: {
tooltip,
},
- mixins: [autosave, noteable, resolvable],
+ mixins: [autosave, noteable, resolvable, discussionNavigation],
props: {
discussion: {
type: Object,
@@ -60,6 +61,11 @@ export default {
required: false,
default: false,
},
+ discussionsByDiffOrder: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -74,7 +80,12 @@ export default {
'discussionCount',
'resolvedDiscussionCount',
'allDiscussions',
+ 'unresolvedDiscussionsIdsByDiff',
+ 'unresolvedDiscussionsIdsByDate',
'unresolvedDiscussions',
+ 'unresolvedDiscussionsIdsOrdered',
+ 'nextUnresolvedDiscussionId',
+ 'isLastUnresolvedDiscussion',
]),
transformedDiscussion() {
return {
@@ -125,6 +136,10 @@ export default {
hasMultipleUnresolvedDiscussions() {
return this.unresolvedDiscussions.length > 1;
},
+ showJumpToNextDiscussion() {
+ return this.hasMultipleUnresolvedDiscussions &&
+ !this.isLastUnresolvedDiscussion(this.discussion.id, this.discussionsByDiffOrder);
+ },
shouldRenderDiffs() {
const { diffDiscussion, diffFile } = this.transformedDiscussion;
@@ -144,19 +159,17 @@ export default {
return this.isDiffDiscussion ? '' : 'card discussion-wrapper';
},
},
- mounted() {
- if (this.isReplying) {
- this.initAutoSave(this.transformedDiscussion);
- }
- },
- updated() {
- if (this.isReplying) {
- if (!this.autosave) {
- this.initAutoSave(this.transformedDiscussion);
+ watch: {
+ isReplying() {
+ if (this.isReplying) {
+ this.$nextTick(() => {
+ // Pass an extra key to separate reply and note edit forms
+ this.initAutoSave(this.transformedDiscussion, ['Reply']);
+ });
} else {
- this.setAutoSave();
+ this.disposeAutoSave();
}
- }
+ },
},
created() {
this.resolveDiscussionsSvg = resolveDiscussionsSvg;
@@ -194,16 +207,18 @@ export default {
showReplyForm() {
this.isReplying = true;
},
- cancelReplyForm(shouldConfirm) {
- if (shouldConfirm && this.$refs.noteForm.isDirty) {
+ cancelReplyForm(shouldConfirm, isDirty) {
+ if (shouldConfirm && isDirty) {
+ const msg = s__('Notes|Are you sure you want to cancel creating this comment?');
+
// eslint-disable-next-line no-alert
- if (!window.confirm('Are you sure you want to cancel creating this comment?')) {
+ if (!window.confirm(msg)) {
return;
}
}
- this.resetAutoSave();
this.isReplying = false;
+ this.resetAutoSave();
},
saveReply(noteText, form, callback) {
const postData = {
@@ -241,21 +256,10 @@ Please check your network connection and try again.`;
});
},
jumpToNextDiscussion() {
- const discussionIds = this.allDiscussions.map(d => d.id);
- const unresolvedIds = this.unresolvedDiscussions.map(d => d.id);
- const currentIndex = discussionIds.indexOf(this.discussion.id);
- const remainingAfterCurrent = discussionIds.slice(currentIndex + 1);
- const nextIndex = _.findIndex(remainingAfterCurrent, id => unresolvedIds.indexOf(id) > -1);
-
- if (nextIndex > -1) {
- const nextId = remainingAfterCurrent[nextIndex];
- const el = document.querySelector(`[data-discussion-id="${nextId}"]`);
+ const nextId =
+ this.nextUnresolvedDiscussionId(this.discussion.id, this.discussionsByDiffOrder);
- if (el) {
- this.expandDiscussion({ discussionId: nextId });
- scrollToElement(el);
- }
- }
+ this.jumpToDiscussion(nextId);
},
},
};
@@ -397,7 +401,7 @@ Please check your network connection and try again.`;
</a>
</div>
<div
- v-if="hasMultipleUnresolvedDiscussions"
+ v-if="showJumpToNextDiscussion"
class="btn-group"
role="group">
<button
@@ -420,7 +424,8 @@ Please check your network connection and try again.`;
:is-editing="false"
save-button-title="Comment"
@handleFormUpdate="saveReply"
- @cancelForm="cancelReplyForm" />
+ @cancelForm="cancelReplyForm"
+ />
<note-signed-out-widget v-if="!canReply" />
</div>
</div>
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 7853847fc37..9b8713b40fb 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -43,6 +43,11 @@ export default {
required: false,
default: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
},
data() {
return {
@@ -175,7 +180,7 @@ export default {
<template>
<div
- v-if="shouldShow"
+ v-show="shouldShow"
id="notes"
>
<ul
@@ -192,6 +197,7 @@ export default {
<comment-form
:noteable-type="noteableType"
+ :markdown-version="markdownVersion"
/>
</div>
</template>
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index eed3a82854d..3aef30c608c 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -15,6 +15,7 @@ document.addEventListener('DOMContentLoaded', () => {
const notesDataset = document.getElementById('js-vue-notes').dataset;
const parsedUserData = JSON.parse(notesDataset.currentUserData);
const noteableData = JSON.parse(notesDataset.noteableData);
+ const markdownVersion = parseInt(notesDataset.markdownVersion, 10);
let currentUserData = {};
noteableData.noteableType = notesDataset.noteableType;
@@ -33,6 +34,7 @@ document.addEventListener('DOMContentLoaded', () => {
return {
noteableData,
currentUserData,
+ markdownVersion,
notesData: JSON.parse(notesDataset.notesData),
};
},
@@ -42,6 +44,7 @@ document.addEventListener('DOMContentLoaded', () => {
noteableData: this.noteableData,
notesData: this.notesData,
userData: this.currentUserData,
+ markdownVersion: this.markdownVersion,
},
});
},
diff --git a/app/assets/javascripts/notes/mixins/autosave.js b/app/assets/javascripts/notes/mixins/autosave.js
index 36cc8d5d056..4f45f912479 100644
--- a/app/assets/javascripts/notes/mixins/autosave.js
+++ b/app/assets/javascripts/notes/mixins/autosave.js
@@ -4,12 +4,18 @@ import { capitalizeFirstCharacter } from '../../lib/utils/text_utility';
export default {
methods: {
- initAutoSave(noteable) {
- this.autosave = new Autosave($(this.$refs.noteForm.$refs.textarea), [
+ initAutoSave(noteable, extraKeys = []) {
+ let keys = [
'Note',
- capitalizeFirstCharacter(noteable.noteable_type),
+ capitalizeFirstCharacter(noteable.noteable_type || noteable.noteableType),
noteable.id,
- ]);
+ ];
+
+ if (extraKeys) {
+ keys = keys.concat(extraKeys);
+ }
+
+ this.autosave = new Autosave($(this.$refs.noteForm.$refs.textarea), keys);
},
resetAutoSave() {
this.autosave.reset();
@@ -17,5 +23,8 @@ export default {
setAutoSave() {
this.autosave.save();
},
+ disposeAutoSave() {
+ this.autosave.dispose();
+ },
},
};
diff --git a/app/assets/javascripts/notes/mixins/discussion_navigation.js b/app/assets/javascripts/notes/mixins/discussion_navigation.js
new file mode 100644
index 00000000000..f7c4deee1f8
--- /dev/null
+++ b/app/assets/javascripts/notes/mixins/discussion_navigation.js
@@ -0,0 +1,29 @@
+import { scrollToElement } from '~/lib/utils/common_utils';
+
+export default {
+ methods: {
+ jumpToDiscussion(id) {
+ if (id) {
+ const activeTab = window.mrTabs.currentAction;
+ const selector =
+ activeTab === 'diffs'
+ ? `ul.notes[data-discussion-id="${id}"]`
+ : `div.discussion[data-discussion-id="${id}"]`;
+ const el = document.querySelector(selector);
+
+ if (activeTab === 'commits' || activeTab === 'pipelines') {
+ window.mrTabs.activateTab('show');
+ }
+
+ if (el) {
+ this.expandDiscussion({ discussionId: id });
+
+ scrollToElement(el);
+ return true;
+ }
+ }
+
+ return false;
+ },
+ },
+};
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 671fa4d7d22..3eefbe11c37 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -15,6 +15,8 @@ let eTagPoll;
export const expandDiscussion = ({ commit }, data) => commit(types.EXPAND_DISCUSSION, data);
+export const collapseDiscussion = ({ commit }, data) => commit(types.COLLAPSE_DISCUSSION, data);
+
export const setNotesData = ({ commit }, data) => commit(types.SET_NOTES_DATA, data);
export const setNoteableData = ({ commit }, data) => commit(types.SET_NOTEABLE_DATA, data);
@@ -41,6 +43,15 @@ export const fetchDiscussions = ({ commit }, path) =>
commit(types.SET_INITIAL_DISCUSSIONS, discussions);
});
+export const refetchDiscussionById = ({ commit }, { path, discussionId }) =>
+ service
+ .fetchDiscussions(path)
+ .then(res => res.json())
+ .then(discussions => {
+ const selectedDiscussion = discussions.find(discussion => discussion.id === discussionId);
+ if (selectedDiscussion) commit(types.UPDATE_DISCUSSION, selectedDiscussion);
+ });
+
export const deleteNote = ({ commit }, note) =>
service.deleteNote(note.path).then(() => {
commit(types.DELETE_NOTE, note);
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index a5518383d44..5b3b9f8776f 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -82,12 +82,15 @@ export const allDiscussions = (state, getters) => {
return Object.values(resolved).concat(unresolved);
};
+export const allResolvableDiscussions = (state, getters) =>
+ getters.allDiscussions.filter(d => !d.individual_note && d.resolvable);
+
export const resolvedDiscussionsById = state => {
const map = {};
- state.discussions.forEach(n => {
+ state.discussions.filter(d => d.resolvable).forEach(n => {
if (n.notes) {
- const resolved = n.notes.every(note => note.resolved && !note.system);
+ const resolved = n.notes.filter(note => note.resolvable).every(note => note.resolved);
if (resolved) {
map[n.id] = n;
@@ -98,6 +101,51 @@ export const resolvedDiscussionsById = state => {
return map;
};
+// Gets Discussions IDs ordered by the date of their initial note
+export const unresolvedDiscussionsIdsByDate = (state, getters) =>
+ getters.allResolvableDiscussions
+ .filter(d => !d.resolved)
+ .sort((a, b) => {
+ const aDate = new Date(a.notes[0].created_at);
+ const bDate = new Date(b.notes[0].created_at);
+
+ if (aDate < bDate) {
+ return -1;
+ }
+
+ return aDate === bDate ? 0 : 1;
+ })
+ .map(d => d.id);
+
+// Gets Discussions IDs ordered by their position in the diff
+//
+// Sorts the array of resolvable yet unresolved discussions by
+// comparing file names first. If file names are the same, compares
+// line numbers.
+export const unresolvedDiscussionsIdsByDiff = (state, getters) =>
+ getters.allResolvableDiscussions
+ .filter(d => !d.resolved)
+ .sort((a, b) => {
+ if (!a.diff_file || !b.diff_file) {
+ return 0;
+ }
+
+ // Get file names comparison result
+ const filenameComparison = a.diff_file.file_path.localeCompare(b.diff_file.file_path);
+
+ // Get the line numbers, to compare within the same file
+ const aLines = [a.position.formatter.new_line, a.position.formatter.old_line];
+ const bLines = [b.position.formatter.new_line, b.position.formatter.old_line];
+
+ return filenameComparison < 0 ||
+ (filenameComparison === 0 &&
+ // .max() because one of them might be zero (if removed/added)
+ Math.max(aLines[0], aLines[1]) < Math.max(bLines[0], bLines[1]))
+ ? -1
+ : 1;
+ })
+ .map(d => d.id);
+
export const resolvedDiscussionCount = (state, getters) => {
const resolvedMap = getters.resolvedDiscussionsById;
@@ -114,5 +162,42 @@ export const discussionTabCounter = state => {
return all.length;
};
+// Returns the list of discussion IDs ordered according to given parameter
+// @param {Boolean} diffOrder - is ordered by diff?
+export const unresolvedDiscussionsIdsOrdered = (state, getters) => diffOrder => {
+ if (diffOrder) {
+ return getters.unresolvedDiscussionsIdsByDiff;
+ }
+ return getters.unresolvedDiscussionsIdsByDate;
+};
+
+// Checks if a given discussion is the last in the current order (diff or date)
+// @param {Boolean} discussionId - id of the discussion
+// @param {Boolean} diffOrder - is ordered by diff?
+export const isLastUnresolvedDiscussion = (state, getters) => (discussionId, diffOrder) => {
+ const idsOrdered = getters.unresolvedDiscussionsIdsOrdered(diffOrder);
+ const lastDiscussionId = idsOrdered[idsOrdered.length - 1];
+
+ return lastDiscussionId === discussionId;
+};
+
+// Gets the ID of the discussion following the one provided, respecting order (diff or date)
+// @param {Boolean} discussionId - id of the current discussion
+// @param {Boolean} diffOrder - is ordered by diff?
+export const nextUnresolvedDiscussionId = (state, getters) => (discussionId, diffOrder) => {
+ const idsOrdered = getters.unresolvedDiscussionsIdsOrdered(diffOrder);
+ const currentIndex = idsOrdered.indexOf(discussionId);
+
+ return idsOrdered.slice(currentIndex + 1, currentIndex + 2)[0];
+};
+
+// @param {Boolean} diffOrder - is ordered by diff?
+export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => {
+ if (diffOrder) {
+ return getters.unresolvedDiscussionsIdsByDiff[0];
+ }
+ return getters.unresolvedDiscussionsIdsByDate[0];
+};
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index a25098fbc06..6f374f78691 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -1,7 +1,6 @@
export const ADD_NEW_NOTE = 'ADD_NEW_NOTE';
export const ADD_NEW_REPLY_TO_DISCUSSION = 'ADD_NEW_REPLY_TO_DISCUSSION';
export const DELETE_NOTE = 'DELETE_NOTE';
-export const EXPAND_DISCUSSION = 'EXPAND_DISCUSSION';
export const REMOVE_PLACEHOLDER_NOTES = 'REMOVE_PLACEHOLDER_NOTES';
export const SET_NOTES_DATA = 'SET_NOTES_DATA';
export const SET_NOTEABLE_DATA = 'SET_NOTEABLE_DATA';
@@ -11,12 +10,16 @@ export const SET_LAST_FETCHED_AT = 'SET_LAST_FETCHED_AT';
export const SET_TARGET_NOTE_HASH = 'SET_TARGET_NOTE_HASH';
export const SHOW_PLACEHOLDER_NOTE = 'SHOW_PLACEHOLDER_NOTE';
export const TOGGLE_AWARD = 'TOGGLE_AWARD';
-export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION';
export const UPDATE_NOTE = 'UPDATE_NOTE';
export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION';
export const SET_DISCUSSION_DIFF_LINES = 'SET_DISCUSSION_DIFF_LINES';
export const SET_NOTES_FETCHED_STATE = 'SET_NOTES_FETCHED_STATE';
+// DISCUSSION
+export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
+export const EXPAND_DISCUSSION = 'EXPAND_DISCUSSION';
+export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION';
+
// Issue
export const CLOSE_ISSUE = 'CLOSE_ISSUE';
export const REOPEN_ISSUE = 'REOPEN_ISSUE';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index e5e40ce07fa..ab6a95e2601 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -58,6 +58,11 @@ export default {
discussion.expanded = true;
},
+ [types.COLLAPSE_DISCUSSION](state, { discussionId }) {
+ const discussion = utils.findNoteObjectById(state.discussions, discussionId);
+ discussion.expanded = false;
+ },
+
[types.REMOVE_PLACEHOLDER_NOTES](state) {
const { discussions } = state;
@@ -114,7 +119,6 @@ export default {
Object.assign(state, { discussions });
},
-
[types.SET_LAST_FETCHED_AT](state, fetchedAt) {
Object.assign(state, { lastFetchedAt: fetchedAt });
},
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index ff19b9a9c30..9aa83ce6269 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -39,6 +39,7 @@ export default class Todos {
}
initFilters() {
+ this.initFilterDropdown($('.js-group-search'), 'group_id', ['text']);
this.initFilterDropdown($('.js-project-search'), 'project_id', ['text']);
this.initFilterDropdown($('.js-type-search'), 'type');
this.initFilterDropdown($('.js-action-search'), 'action_id');
@@ -53,7 +54,16 @@ export default class Todos {
filterable: searchFields ? true : false,
search: { fields: searchFields },
data: $dropdown.data('data'),
- clicked: () => $dropdown.closest('form.filter-form').submit(),
+ clicked: () => {
+ const $formEl = $dropdown.closest('form.filter-form');
+ const mutexDropdowns = {
+ group_id: 'project_id',
+ project_id: 'group_id',
+ };
+
+ $formEl.find(`input[name="${mutexDropdowns[fieldName]}"]`).remove();
+ $formEl.submit();
+ },
});
}
diff --git a/app/assets/javascripts/pages/profiles/keys/index.js b/app/assets/javascripts/pages/profiles/keys/index.js
new file mode 100644
index 00000000000..1cd3ee1dfdb
--- /dev/null
+++ b/app/assets/javascripts/pages/profiles/keys/index.js
@@ -0,0 +1,16 @@
+import AddSshKeyValidation from '~/profile/add_ssh_key_validation';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const input = document.querySelector('.js-add-ssh-key-validation-input');
+ const warning = document.querySelector('.js-add-ssh-key-validation-warning');
+ const originalSubmit = input.form.querySelector('.js-add-ssh-key-validation-original-submit');
+ const confirmSubmit = warning.querySelector('.js-add-ssh-key-validation-confirm-submit');
+
+ const addSshKeyValidation = new AddSshKeyValidation(
+ input,
+ warning,
+ originalSubmit,
+ confirmSubmit,
+ );
+ addSshKeyValidation.register();
+});
diff --git a/app/assets/javascripts/pages/profiles/show/emoji_menu.js b/app/assets/javascripts/pages/profiles/show/emoji_menu.js
new file mode 100644
index 00000000000..094837b40e0
--- /dev/null
+++ b/app/assets/javascripts/pages/profiles/show/emoji_menu.js
@@ -0,0 +1,18 @@
+import { AwardsHandler } from '~/awards_handler';
+
+class EmojiMenu extends AwardsHandler {
+ constructor(emoji, toggleButtonSelector, menuClass, selectEmojiCallback) {
+ super(emoji);
+
+ this.selectEmojiCallback = selectEmojiCallback;
+ this.toggleButtonSelector = toggleButtonSelector;
+ this.menuClass = menuClass;
+ }
+
+ postEmoji($emojiButton, awardUrl, selectedEmoji, callback) {
+ this.selectEmojiCallback(selectedEmoji, this.emoji.glEmojiTag(selectedEmoji));
+ callback();
+ }
+}
+
+export default EmojiMenu;
diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js
new file mode 100644
index 00000000000..949219a0837
--- /dev/null
+++ b/app/assets/javascripts/pages/profiles/show/index.js
@@ -0,0 +1,49 @@
+import $ from 'jquery';
+import createFlash from '~/flash';
+import GfmAutoComplete from '~/gfm_auto_complete';
+import EmojiMenu from './emoji_menu';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu';
+ const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector);
+ const statusEmojiField = document.getElementById('js-status-emoji-field');
+ const statusMessageField = document.getElementById('js-status-message-field');
+ const findNoEmojiPlaceholder = () => document.getElementById('js-no-emoji-placeholder');
+
+ const removeStatusEmoji = () => {
+ const statusEmoji = toggleEmojiMenuButton.querySelector('gl-emoji');
+ if (statusEmoji) {
+ statusEmoji.remove();
+ }
+ };
+
+ const selectEmojiCallback = (emoji, emojiTag) => {
+ statusEmojiField.value = emoji;
+ findNoEmojiPlaceholder().classList.add('hidden');
+ removeStatusEmoji();
+ toggleEmojiMenuButton.innerHTML += emojiTag;
+ };
+
+ const clearEmojiButton = document.getElementById('js-clear-user-status-button');
+ clearEmojiButton.addEventListener('click', () => {
+ statusEmojiField.value = '';
+ statusMessageField.value = '';
+ removeStatusEmoji();
+ findNoEmojiPlaceholder().classList.remove('hidden');
+ });
+
+ const emojiAutocomplete = new GfmAutoComplete();
+ emojiAutocomplete.setup($(statusMessageField), { emojis: true });
+
+ import(/* webpackChunkName: 'emoji' */ '~/emoji')
+ .then(Emoji => {
+ const emojiMenu = new EmojiMenu(
+ Emoji,
+ toggleEmojiMenuButtonSelector,
+ 'js-status-emoji-menu',
+ selectEmojiCallback,
+ );
+ emojiMenu.bindEvents();
+ })
+ .catch(() => createFlash('Failed to load emoji list!'));
+});
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index 85c6862d629..84e5bb3c46e 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import BlobViewer from '~/blob/viewer/index';
import initBlob from '~/pages/projects/init_blob';
+import GpgBadges from '~/gpg_badges';
document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
@@ -26,4 +27,6 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
}
+
+ GpgBadges.fetch();
});
diff --git a/app/assets/javascripts/pages/projects/clusters/gcp/login/index.js b/app/assets/javascripts/pages/projects/clusters/gcp/login/index.js
deleted file mode 100644
index 0c2d7d7c96a..00000000000
--- a/app/assets/javascripts/pages/projects/clusters/gcp/login/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import gcpSignupOffer from '~/clusters/components/gcp_signup_offer';
-
-gcpSignupOffer();
diff --git a/app/assets/javascripts/pages/projects/clusters/new/index.js b/app/assets/javascripts/pages/projects/clusters/new/index.js
deleted file mode 100644
index 0c2d7d7c96a..00000000000
--- a/app/assets/javascripts/pages/projects/clusters/new/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import gcpSignupOffer from '~/clusters/components/gcp_signup_offer';
-
-gcpSignupOffer();
diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js
index de1e13de7e9..cc0e6553e83 100644
--- a/app/assets/javascripts/pages/projects/index.js
+++ b/app/assets/javascripts/pages/projects/index.js
@@ -1,7 +1,21 @@
+import gcpSignupOffer from '~/clusters/components/gcp_signup_offer';
+import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
import Project from './project';
import ShortcutsNavigation from '../../shortcuts_navigation';
document.addEventListener('DOMContentLoaded', () => {
+ const { page } = document.body.dataset;
+ const newClusterViews = [
+ 'projects:clusters:new',
+ 'projects:clusters:create_gcp',
+ 'projects:clusters:create_user',
+ ];
+
+ if (newClusterViews.indexOf(page) > -1) {
+ gcpSignupOffer();
+ initGkeDropdowns();
+ }
+
new Project(); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
});
diff --git a/app/assets/javascripts/pages/projects/jobs/terminal/index.js b/app/assets/javascripts/pages/projects/jobs/terminal/index.js
new file mode 100644
index 00000000000..7129e24cee1
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/jobs/terminal/index.js
@@ -0,0 +1,3 @@
+import initTerminal from '~/terminal/';
+
+document.addEventListener('DOMContentLoaded', initTerminal);
diff --git a/app/assets/javascripts/pages/projects/new/index.js b/app/assets/javascripts/pages/projects/new/index.js
index 7db644e2477..097403ba9e2 100644
--- a/app/assets/javascripts/pages/projects/new/index.js
+++ b/app/assets/javascripts/pages/projects/new/index.js
@@ -1,9 +1,7 @@
-import initProjectLoadingSpinner from '../shared/save_project_loader';
import initProjectVisibilitySelector from '../../../project_visibility';
import initProjectNew from '../../../projects/project_new';
document.addEventListener('DOMContentLoaded', () => {
- initProjectLoadingSpinner();
initProjectVisibilitySelector();
initProjectNew.bindEvents();
});
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
index d0613804067..0d05668b285 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
@@ -68,7 +68,7 @@
:name="inputNameAttribute"
:value="cronInterval"
:checked="isEditable"
- class="label-light"
+ class="label-bold"
type="radio"
@click="toggleCustomInput(true)"
/>
@@ -93,13 +93,13 @@
v-model="cronInterval"
:name="inputNameAttribute"
:value="cronIntervalPresets.everyDay"
- class="label-light"
+ class="label-bold"
type="radio"
@click="toggleCustomInput(false)"
/>
<label
- class="label-light"
+ class="label-bold"
for="every-day"
>
{{ __('Every day (at 4:00am)') }}
@@ -112,13 +112,13 @@
v-model="cronInterval"
:name="inputNameAttribute"
:value="cronIntervalPresets.everyWeek"
- class="label-light"
+ class="label-bold"
type="radio"
@click="toggleCustomInput(false)"
/>
<label
- class="label-light"
+ class="label-bold"
for="every-week"
>
{{ __('Every week (Sundays at 4:00am)') }}
@@ -131,13 +131,13 @@
v-model="cronInterval"
:name="inputNameAttribute"
:value="cronIntervalPresets.everyMonth"
- class="label-light"
+ class="label-bold"
type="radio"
@click="toggleCustomInput(false)"
/>
<label
- class="label-light"
+ class="label-bold"
for="every-month"
>
{{ __('Every month (on the 1st at 4:00am)') }}
diff --git a/app/assets/javascripts/pages/projects/pipelines/builds/index.js b/app/assets/javascripts/pages/projects/pipelines/builds/index.js
index 7a57e417b41..957c72008bd 100644
--- a/app/assets/javascripts/pages/projects/pipelines/builds/index.js
+++ b/app/assets/javascripts/pages/projects/pipelines/builds/index.js
@@ -1,7 +1,2 @@
-import initPipelineDetails from '~/pipelines/pipeline_details_bundle';
-import initPipelines from '../init_pipelines';
-
-document.addEventListener('DOMContentLoaded', () => {
- initPipelines();
- initPipelineDetails();
-});
+// /builds is an alias for show
+import '../show/index';
diff --git a/app/assets/javascripts/pages/projects/pipelines/failures/index.js b/app/assets/javascripts/pages/projects/pipelines/failures/index.js
index fbe9824c34b..8ba6fc3ae1d 100644
--- a/app/assets/javascripts/pages/projects/pipelines/failures/index.js
+++ b/app/assets/javascripts/pages/projects/pipelines/failures/index.js
@@ -1,3 +1,2 @@
-import initPipelines from '../init_pipelines';
-
-document.addEventListener('DOMContentLoaded', initPipelines);
+// /failures is an alias for show
+import '../show/index';
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 1faa59fb45b..8f5ac3d8082 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
@@ -23,17 +23,12 @@ document.addEventListener('DOMContentLoaded', () => {
saveEndpoint: variableListEl.dataset.saveEndpoint,
});
- // hide extra auto devops settings based on data-attributes
- const autoDevOpsSettings = document.querySelector('.js-auto-devops-settings');
+ // hide extra auto devops settings based checkbox state
const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings');
-
- autoDevOpsSettings.addEventListener('click', event => {
+ const instanceDefaultBadge = document.querySelector('.js-instance-default-badge');
+ document.querySelector('.js-toggle-extra-settings').addEventListener('click', event => {
const { target } = event;
- if (target.classList.contains('js-toggle-extra-settings')) {
- autoDevOpsExtraSettings.classList.toggle(
- 'hidden',
- !!(target.dataset && target.dataset.hideExtraSettings),
- );
- }
+ if (instanceDefaultBadge) instanceDefaultBadge.style.display = 'none';
+ autoDevOpsExtraSettings.classList.toggle('hidden', !target.checked);
});
});
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue
index 17b91479ea5..83437363af5 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue
@@ -24,7 +24,7 @@
<div class="project-feature-row">
<label
v-if="label"
- class="label-light"
+ class="label-bold"
>
{{ label }}
<a
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 3b0f0f960b8..b76f2f76449 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -7,6 +7,7 @@ import TreeView from '~/tree';
import BlobViewer from '~/blob/viewer/index';
import Activities from '~/activities';
import { ajaxGet } from '~/lib/utils/common_utils';
+import GpgBadges from '~/gpg_badges';
import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown';
@@ -16,7 +17,7 @@ document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
new NotificationsForm(); // eslint-disable-line no-new
new UserCallout({ // eslint-disable-line no-new
- setCalloutPerProject: true,
+ setCalloutPerProject: false,
className: 'js-autodevops-banner',
});
@@ -38,4 +39,6 @@ document.addEventListener('DOMContentLoaded', () => {
$(treeSlider).waitForImages(() => {
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
});
+
+ GpgBadges.fetch();
});
diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js
index 7ad082a5e61..33d69d891d8 100644
--- a/app/assets/javascripts/pages/projects/tree/show/index.js
+++ b/app/assets/javascripts/pages/projects/tree/show/index.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import Vue from 'vue';
import initBlob from '~/blob_edit/blob_bundle';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
+import GpgBadges from '~/gpg_badges';
import TreeView from '../../../../tree';
import ShortcutsNavigation from '../../../../shortcuts_navigation';
import BlobViewer from '../../../../blob/viewer';
@@ -14,7 +15,8 @@ document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new
$('#tree-slider').waitForImages(() =>
- ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath));
+ ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath),
+ );
initBlob();
const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status');
@@ -36,4 +38,6 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
}
+
+ GpgBadges.fetch();
});
diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js
index 2e1fe78b3fa..e3e0ab91993 100644
--- a/app/assets/javascripts/pages/search/show/search.js
+++ b/app/assets/javascripts/pages/search/show/search.js
@@ -105,7 +105,7 @@ export default class Search {
getProjectsData(term) {
return new Promise((resolve) => {
if (this.groupId) {
- Api.groupProjects(this.groupId, term, resolve);
+ Api.groupProjects(this.groupId, term, {}, resolve);
} else {
Api.projects(term, {
order_by: 'id',
diff --git a/app/assets/javascripts/pages/snippets/form.js b/app/assets/javascripts/pages/snippets/form.js
index 758bbafead3..f369c7ef9a6 100644
--- a/app/assets/javascripts/pages/snippets/form.js
+++ b/app/assets/javascripts/pages/snippets/form.js
@@ -8,6 +8,7 @@ export default () => {
members: false,
issues: false,
mergeRequests: false,
+ epics: false,
milestones: false,
labels: false,
});
diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js
index 50d042fef29..9892a039941 100644
--- a/app/assets/javascripts/pages/users/activity_calendar.js
+++ b/app/assets/javascripts/pages/users/activity_calendar.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import _ from 'underscore';
import { scaleLinear, scaleThreshold } from 'd3-scale';
import { select } from 'd3-selection';
+import dateFormat from 'dateformat';
import { getDayName, getDayDifference } from '~/lib/utils/datetime_utility';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
@@ -26,7 +27,7 @@ function getSystemDate(systemUtcOffsetSeconds) {
function formatTooltipText({ date, count }) {
const dateObject = new Date(date);
const dateDayName = getDayName(dateObject);
- const dateText = dateObject.format('mmm d, yyyy');
+ const dateText = dateFormat(dateObject, 'mmm d, yyyy');
let contribText = 'No contributions';
if (count > 0) {
@@ -84,7 +85,7 @@ export default class ActivityCalendar {
date.setDate(date.getDate() + i);
const day = date.getDay();
- const count = timestamps[date.format('yyyy-mm-dd')] || 0;
+ const count = timestamps[dateFormat(date, 'yyyy-mm-dd')] || 0;
// Create a new group array if this is the first day of the week
// or if is first object
diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
index b76965f280b..0fdb0a080cf 100644
--- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
+++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
@@ -1,13 +1,10 @@
<script>
import $ from 'jquery';
-import PerformanceBarService from '../services/performance_bar_service';
import detailedMetric from './detailed_metric.vue';
import requestSelector from './request_selector.vue';
import simpleMetric from './simple_metric.vue';
-import Flash from '../../flash';
-
export default {
components: {
detailedMetric,
@@ -69,37 +66,13 @@ export default {
},
},
mounted() {
- this.interceptor = PerformanceBarService.registerInterceptor(
- this.peekUrl,
- this.loadRequestDetails,
- );
-
- this.loadRequestDetails(this.requestId, window.location.href);
this.currentRequest = this.requestId;
if (this.lineProfileModal.length) {
this.lineProfileModal.modal('toggle');
}
},
- beforeDestroy() {
- PerformanceBarService.removeInterceptor(this.interceptor);
- },
methods: {
- loadRequestDetails(requestId, requestUrl) {
- if (!this.store.canTrackRequest(requestUrl)) {
- return;
- }
-
- this.store.addRequest(requestId, requestUrl);
-
- PerformanceBarService.fetchRequestDetails(this.peekUrl, requestId)
- .then(res => {
- this.store.addRequestDetails(requestId, res.data.data);
- })
- .catch(() =>
- Flash(`Error getting performance bar results for ${requestId}`),
- );
- },
changeCurrentRequest(newRequestId) {
this.currentRequest = newRequestId;
},
diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js
index 4a98aed7679..41880d27516 100644
--- a/app/assets/javascripts/performance_bar/index.js
+++ b/app/assets/javascripts/performance_bar/index.js
@@ -1,12 +1,13 @@
import Vue from 'vue';
-import performanceBarApp from './components/performance_bar_app.vue';
+import Flash from '../flash';
+import PerformanceBarService from './services/performance_bar_service';
import PerformanceBarStore from './stores/performance_bar_store';
export default ({ container }) =>
new Vue({
el: container,
components: {
- performanceBarApp,
+ performanceBarApp: () => import('./components/performance_bar_app.vue'),
},
data() {
const performanceBarData = document.querySelector(this.$options.el)
@@ -21,6 +22,34 @@ export default ({ container }) =>
profileUrl: performanceBarData.profileUrl,
};
},
+ mounted() {
+ this.interceptor = PerformanceBarService.registerInterceptor(
+ this.peekUrl,
+ this.loadRequestDetails,
+ );
+
+ this.loadRequestDetails(this.requestId, window.location.href);
+ },
+ beforeDestroy() {
+ PerformanceBarService.removeInterceptor(this.interceptor);
+ },
+ methods: {
+ loadRequestDetails(requestId, requestUrl) {
+ if (!this.store.canTrackRequest(requestUrl)) {
+ return;
+ }
+
+ this.store.addRequest(requestId, requestUrl);
+
+ PerformanceBarService.fetchRequestDetails(this.peekUrl, requestId)
+ .then(res => {
+ this.store.addRequestDetails(requestId, res.data.data);
+ })
+ .catch(() =>
+ Flash(`Error getting performance bar results for ${requestId}`),
+ );
+ },
+ },
render(createElement) {
return createElement('performance-bar-app', {
props: {
diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
index c32dc83da8e..8487c8036ee 100644
--- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
@@ -1,5 +1,6 @@
<script>
import $ from 'jquery';
+import _ from 'underscore';
import JobNameComponent from './job_name_component.vue';
import JobComponent from './job_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
@@ -13,7 +14,7 @@ import tooltip from '../../../vue_shared/directives/tooltip';
* "id": 4256,
* "name": "test",
* "status": {
- * "icon": "icon_status_success",
+ * "icon": "status_success",
* "text": "passed",
* "label": "passed",
* "group": "success",
@@ -46,7 +47,7 @@ export default {
computed: {
tooltipText() {
- return `${this.job.name} - ${this.job.status.label}`;
+ return _.escape(`${this.job.name} - ${this.job.status.label}`);
},
},
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index 4ec67f6c01b..1952dd453f4 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import StageColumnComponent from './stage_column_component.vue';
@@ -26,7 +27,8 @@ export default {
methods: {
capitalizeStageName(name) {
- return name.charAt(0).toUpperCase() + name.slice(1);
+ const escapedName = _.escape(name);
+ return escapedName.charAt(0).toUpperCase() + escapedName.slice(1);
},
isFirstColumn(index) {
diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue
index 8af984ef91a..66f95147193 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import ActionComponent from './action_component.vue';
import JobNameComponent from './job_name_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
@@ -12,7 +13,7 @@ import tooltip from '../../../vue_shared/directives/tooltip';
* "id": 4256,
* "name": "test",
* "status": {
- * "icon": "icon_status_success",
+ * "icon": "status_success",
* "text": "passed",
* "label": "passed",
* "group": "success",
@@ -61,7 +62,7 @@ export default {
const textBuilder = [];
if (this.job.name) {
- textBuilder.push(this.job.name);
+ textBuilder.push(_.escape(this.job.name));
}
if (this.job.name && this.status.tooltip) {
@@ -69,7 +70,7 @@ export default {
}
if (this.status.tooltip) {
- textBuilder.push(`${this.job.status.tooltip}`);
+ textBuilder.push(this.job.status.tooltip);
}
return textBuilder.join(' ');
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 2c728582b7c..e7b2de52f76 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import JobComponent from './job_component.vue';
import DropdownJobComponent from './dropdown_job_component.vue';
@@ -37,7 +38,7 @@ export default {
},
jobId(job) {
- return `ci-badge-${job.name}`;
+ return `ci-badge-${_.escape(job.name)}`;
},
buildConnnectorClass(index) {
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index 56fdb858088..c7df69c69ed 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -175,6 +175,7 @@ export default {
<span
:aria-label="stage.title"
aria-hidden="true"
+ class="no-pointer-events"
>
<icon :name="borderlessIcon" />
</span>
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 0e973cab4d2..0964baf8954 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -28,12 +28,16 @@ MarkdownPreview.prototype.ajaxCache = {};
MarkdownPreview.prototype.showPreview = function ($form) {
var mdText;
+ var markdownVersion;
+ var url;
var preview = $form.find('.js-md-preview');
- var url = preview.data('url');
if (preview.hasClass('md-preview-loading')) {
return;
}
+
mdText = $form.find('textarea.markdown-area').val();
+ markdownVersion = $form.attr('data-markdown-version');
+ url = this.versionedPreviewPath(preview.data('url'), markdownVersion);
if (mdText.trim().length === 0) {
preview.text(this.emptyMessage);
@@ -59,6 +63,14 @@ MarkdownPreview.prototype.showPreview = function ($form) {
}
};
+MarkdownPreview.prototype.versionedPreviewPath = function (markdownPreviewPath, markdownVersion) {
+ if (typeof markdownVersion === 'undefined') {
+ return markdownPreviewPath;
+ }
+
+ return `${markdownPreviewPath}${markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'}markdown_version=${markdownVersion}`;
+};
+
MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) {
if (!url) {
return;
diff --git a/app/assets/javascripts/profile/add_ssh_key_validation.js b/app/assets/javascripts/profile/add_ssh_key_validation.js
new file mode 100644
index 00000000000..ab6a6c1896c
--- /dev/null
+++ b/app/assets/javascripts/profile/add_ssh_key_validation.js
@@ -0,0 +1,43 @@
+export default class AddSshKeyValidation {
+ constructor(inputElement, warningElement, originalSubmitElement, confirmSubmitElement) {
+ this.inputElement = inputElement;
+ this.form = inputElement.form;
+
+ this.warningElement = warningElement;
+
+ this.originalSubmitElement = originalSubmitElement;
+ this.confirmSubmitElement = confirmSubmitElement;
+
+ this.isValid = false;
+ }
+
+ register() {
+ this.form.addEventListener('submit', event => this.submit(event));
+
+ this.confirmSubmitElement.addEventListener('click', () => {
+ this.isValid = true;
+ this.form.submit();
+ });
+
+ this.inputElement.addEventListener('input', () => this.toggleWarning(false));
+ }
+
+ submit(event) {
+ this.isValid = AddSshKeyValidation.isPublicKey(this.inputElement.value);
+
+ if (this.isValid) return true;
+
+ event.preventDefault();
+ this.toggleWarning(true);
+ return false;
+ }
+
+ toggleWarning(isVisible) {
+ this.warningElement.classList.toggle('hide', !isVisible);
+ this.originalSubmitElement.classList.toggle('hide', isVisible);
+ }
+
+ static isPublicKey(value) {
+ return /^(ssh|ecdsa-sha2)-/.test(value);
+ }
+}
diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js
index 8cf7f2f23d0..e49c67ffb5c 100644
--- a/app/assets/javascripts/profile/profile.js
+++ b/app/assets/javascripts/profile/profile.js
@@ -49,13 +49,15 @@ export default class Profile {
saveForm() {
const self = this;
- const formData = new FormData(this.form[0]);
+ const formData = new FormData(this.form.get(0));
const avatarBlob = this.avatarGlCrop.getBlob();
if (avatarBlob != null) {
formData.append('user[avatar]', avatarBlob, 'avatar.png');
}
+ formData.delete('user[avatar]-trigger');
+
axios({
method: this.form.attr('method'),
url: this.form.attr('action'),
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index 240dde56325..bce7556bd40 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -47,7 +47,10 @@ export default function projectSelect() {
projectsCallback = finalCallback;
}
if (_this.groupId) {
- return Api.groupProjects(_this.groupId, query.term, projectsCallback);
+ return Api.groupProjects(_this.groupId, query.term, {
+ with_issues_enabled: _this.withIssuesEnabled,
+ with_merge_requests_enabled: _this.withMergeRequestsEnabled,
+ }, projectsCallback);
} else {
return Api.projects(query.term, {
order_by: _this.orderBy,
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 002edb4663c..04badad0f34 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -37,9 +37,10 @@ const bindEvents = () => {
const $projectFieldsForm = $('.project-fields-form');
const $selectedTemplateText = $('.selected-template');
const $changeTemplateBtn = $('.change-template');
- const $selectedIcon = $('.selected-icon svg');
+ const $selectedIcon = $('.selected-icon');
const $templateProjectNameInput = $('#template-project-name #project_path');
const $pushNewProjectTipTrigger = $('.push-new-project-tip');
+ const $projectTemplateButtons = $('.project-templates-buttons');
if ($newProjectForm.length !== 1) {
return;
@@ -88,35 +89,35 @@ const bindEvents = () => {
}
function chooseTemplate() {
- $('.template-option').hide();
+ $projectTemplateButtons.addClass('hidden');
$projectFieldsForm.addClass('selected');
- $selectedIcon.removeClass('d-block');
+ $selectedIcon.empty();
const value = $(this).val();
const templates = {
rails: {
text: 'Ruby on Rails',
- icon: '.selected-icon .icon-rails',
+ icon: '.template-option svg.icon-rails',
},
express: {
text: 'NodeJS Express',
- icon: '.selected-icon .icon-node-express',
+ icon: '.template-option svg.icon-node-express',
},
spring: {
text: 'Spring',
- icon: '.selected-icon .icon-java-spring',
+ icon: '.template-option svg.icon-java-spring',
},
};
const selectedTemplate = templates[value];
$selectedTemplateText.text(selectedTemplate.text);
- $(selectedTemplate.icon).addClass('d-block');
+ $(selectedTemplate.icon).clone().addClass('d-block').appendTo($selectedIcon);
$templateProjectNameInput.focus();
}
$useTemplateBtn.on('change', chooseTemplate);
$changeTemplateBtn.on('click', () => {
- $('.template-option').show();
+ $projectTemplateButtons.removeClass('hidden');
$projectFieldsForm.removeClass('selected');
$useTemplateBtn.prop('checked', false);
});
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index a4c7c143e56..1c1e17563a1 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -1,27 +1,27 @@
<script>
- import Visibility from 'visibilityjs';
- import ciIcon from '~/vue_shared/components/ci_icon.vue';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
- import Poll from '~/lib/utils/poll';
- import Flash from '~/flash';
- import { s__, sprintf } from '~/locale';
- import tooltip from '~/vue_shared/directives/tooltip';
- import CommitPipelineService from '../services/commit_pipeline_service';
+import Visibility from 'visibilityjs';
+import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+import Poll from '~/lib/utils/poll';
+import Flash from '~/flash';
+import { s__, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import CommitPipelineService from '../services/commit_pipeline_service';
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ ciIcon,
+ loadingIcon,
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
},
- components: {
- ciIcon,
- loadingIcon,
- },
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- /* This prop can be used to replace some of the `render_commit_status`
+ /* This prop can be used to replace some of the `render_commit_status`
used across GitLab, this way we could use this vue component and add a
realtime status where it makes sense
realtime: {
@@ -29,76 +29,77 @@
required: false,
default: true,
}, */
+ },
+ data() {
+ return {
+ ciStatus: {},
+ isLoading: true,
+ };
+ },
+ computed: {
+ statusTitle() {
+ return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text });
},
- data() {
- return {
- ciStatus: {},
- isLoading: true,
- };
- },
- computed: {
- statusTitle() {
- return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text });
- },
+ },
+ mounted() {
+ this.service = new CommitPipelineService(this.endpoint);
+ this.initPolling();
+ },
+ methods: {
+ successCallback(res) {
+ const { pipelines } = res.data;
+ if (pipelines.length > 0) {
+ // The pipeline entity always keeps the latest pipeline info on the `details.status`
+ this.ciStatus = pipelines[0].details.status;
+ }
+ this.isLoading = false;
},
- mounted() {
- this.service = new CommitPipelineService(this.endpoint);
- this.initPolling();
+ errorCallback() {
+ this.ciStatus = {
+ text: 'not found',
+ icon: 'status_notfound',
+ group: 'notfound',
+ };
+ this.isLoading = false;
+ Flash(s__('Something went wrong on our end'));
},
- methods: {
- successCallback(res) {
- const { pipelines } = res.data;
- if (pipelines.length > 0) {
- // The pipeline entity always keeps the latest pipeline info on the `details.status`
- this.ciStatus = pipelines[0].details.status;
- }
- this.isLoading = false;
- },
- errorCallback() {
- this.ciStatus = {
- text: 'not found',
- icon: 'status_notfound',
- group: 'notfound',
- };
- this.isLoading = false;
- Flash(s__('Something went wrong on our end'));
- },
- initPolling() {
- this.poll = new Poll({
- resource: this.service,
- method: 'fetchData',
- successCallback: response => this.successCallback(response),
- errorCallback: this.errorCallback,
- });
+ initPolling() {
+ this.poll = new Poll({
+ resource: this.service,
+ method: 'fetchData',
+ successCallback: response => this.successCallback(response),
+ errorCallback: this.errorCallback,
+ });
+
+ if (!Visibility.hidden()) {
+ this.isLoading = true;
+ this.poll.makeRequest();
+ } else {
+ this.fetchPipelineCommitData();
+ }
+ Visibility.change(() => {
if (!Visibility.hidden()) {
- this.isLoading = true;
- this.poll.makeRequest();
+ this.poll.restart();
} else {
- this.fetchPipelineCommitData();
+ this.poll.stop();
}
-
- Visibility.change(() => {
- if (!Visibility.hidden()) {
- this.poll.restart();
- } else {
- this.poll.stop();
- }
- });
- },
- fetchPipelineCommitData() {
- this.service.fetchData()
- .then(this.successCallback)
- .catch(this.errorCallback);
- },
+ });
},
- destroy() {
- this.poll.stop();
+ fetchPipelineCommitData() {
+ this.service
+ .fetchData()
+ .then(this.successCallback)
+ .catch(this.errorCallback);
},
- };
+ },
+ destroy() {
+ this.poll.stop();
+ },
+};
</script>
<template>
- <div>
+ <div class="ci-status-link">
<loading-icon
v-if="isLoading"
label="Loading pipeline status"
@@ -113,6 +114,7 @@
:title="statusTitle"
:aria-label="statusTitle"
:status="ciStatus"
+ :size="24"
data-container="body"
/>
</a>
diff --git a/app/assets/javascripts/projects_dropdown/components/app.vue b/app/assets/javascripts/projects_dropdown/components/app.vue
deleted file mode 100644
index 73d49488299..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/app.vue
+++ /dev/null
@@ -1,158 +0,0 @@
-<script>
-import bs from '../../breakpoints';
-import eventHub from '../event_hub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-
-import projectsListFrequent from './projects_list_frequent.vue';
-import projectsListSearch from './projects_list_search.vue';
-
-import search from './search.vue';
-
-export default {
- components: {
- search,
- loadingIcon,
- projectsListFrequent,
- projectsListSearch,
- },
- props: {
- currentProject: {
- type: Object,
- required: true,
- },
- store: {
- type: Object,
- required: true,
- },
- service: {
- type: Object,
- required: true,
- },
- },
- data() {
- return {
- isLoadingProjects: false,
- isFrequentsListVisible: false,
- isSearchListVisible: false,
- isLocalStorageFailed: false,
- isSearchFailed: false,
- searchQuery: '',
- };
- },
- computed: {
- frequentProjects() {
- return this.store.getFrequentProjects();
- },
- searchProjects() {
- return this.store.getSearchedProjects();
- },
- },
- created() {
- if (this.currentProject.id) {
- this.logCurrentProjectAccess();
- }
-
- eventHub.$on('dropdownOpen', this.fetchFrequentProjects);
- eventHub.$on('searchProjects', this.fetchSearchedProjects);
- eventHub.$on('searchCleared', this.handleSearchClear);
- eventHub.$on('searchFailed', this.handleSearchFailure);
- },
- beforeDestroy() {
- eventHub.$off('dropdownOpen', this.fetchFrequentProjects);
- eventHub.$off('searchProjects', this.fetchSearchedProjects);
- eventHub.$off('searchCleared', this.handleSearchClear);
- eventHub.$off('searchFailed', this.handleSearchFailure);
- },
- methods: {
- toggleFrequentProjectsList(state) {
- this.isLoadingProjects = !state;
- this.isSearchListVisible = !state;
- this.isFrequentsListVisible = state;
- },
- toggleSearchProjectsList(state) {
- this.isLoadingProjects = !state;
- this.isFrequentsListVisible = !state;
- this.isSearchListVisible = state;
- },
- toggleLoader(state) {
- this.isFrequentsListVisible = !state;
- this.isSearchListVisible = !state;
- this.isLoadingProjects = state;
- },
- fetchFrequentProjects() {
- const screenSize = bs.getBreakpointSize();
- if (this.searchQuery && (screenSize !== 'sm' && screenSize !== 'xs')) {
- this.toggleSearchProjectsList(true);
- } else {
- this.toggleLoader(true);
- this.isLocalStorageFailed = false;
- const projects = this.service.getFrequentProjects();
- if (projects) {
- this.toggleFrequentProjectsList(true);
- this.store.setFrequentProjects(projects);
- } else {
- this.isLocalStorageFailed = true;
- this.toggleFrequentProjectsList(true);
- this.store.setFrequentProjects([]);
- }
- }
- },
- fetchSearchedProjects(searchQuery) {
- this.searchQuery = searchQuery;
- this.toggleLoader(true);
- this.service
- .getSearchedProjects(this.searchQuery)
- .then(res => res.json())
- .then(results => {
- this.toggleSearchProjectsList(true);
- this.store.setSearchedProjects(results);
- })
- .catch(() => {
- this.isSearchFailed = true;
- this.toggleSearchProjectsList(true);
- });
- },
- logCurrentProjectAccess() {
- this.service.logProjectAccess(this.currentProject);
- },
- handleSearchClear() {
- this.searchQuery = '';
- this.toggleFrequentProjectsList(true);
- this.store.clearSearchedProjects();
- },
- handleSearchFailure() {
- this.isSearchFailed = true;
- this.toggleSearchProjectsList(true);
- },
- },
-};
-</script>
-
-<template>
- <div>
- <search/>
- <loading-icon
- v-if="isLoadingProjects"
- :label="s__('ProjectsDropdown|Loading projects')"
- class="loading-animation prepend-top-20"
- size="2"
- />
- <div
- v-if="isFrequentsListVisible"
- class="section-header"
- >
- {{ s__('ProjectsDropdown|Frequently visited') }}
- </div>
- <projects-list-frequent
- v-if="isFrequentsListVisible"
- :local-storage-failed="isLocalStorageFailed"
- :projects="frequentProjects"
- />
- <projects-list-search
- v-if="isSearchListVisible"
- :search-failed="isSearchFailed"
- :matcher="searchQuery"
- :projects="searchProjects"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue
deleted file mode 100644
index 625e0aa548c..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<script>
- import { s__ } from '../../locale';
- import projectsListItem from './projects_list_item.vue';
-
- export default {
- components: {
- projectsListItem,
- },
- props: {
- projects: {
- type: Array,
- required: true,
- },
- localStorageFailed: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- isListEmpty() {
- return this.projects.length === 0;
- },
- listEmptyMessage() {
- return this.localStorageFailed ?
- s__('ProjectsDropdown|This feature requires browser localStorage support') :
- s__('ProjectsDropdown|Projects you visit often will appear here');
- },
- },
- };
-</script>
-
-<template>
- <div
- class="projects-list-frequent-container"
- >
- <ul
- class="list-unstyled"
- >
- <li
- v-if="isListEmpty"
- class="section-empty"
- >
- {{ listEmptyMessage }}
- </li>
- <projects-list-item
- v-for="(project, index) in projects"
- v-else
- :key="index"
- :project-id="project.id"
- :project-name="project.name"
- :namespace="project.namespace"
- :web-url="project.webUrl"
- :avatar-url="project.avatarUrl"
- />
- </ul>
- </div>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue
deleted file mode 100644
index eafbf6c99e2..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue
+++ /dev/null
@@ -1,116 +0,0 @@
-<script>
- /* eslint-disable vue/require-default-prop, vue/require-prop-types */
- import identicon from '../../vue_shared/components/identicon.vue';
-
- export default {
- components: {
- identicon,
- },
- props: {
- matcher: {
- type: String,
- required: false,
- },
- projectId: {
- type: Number,
- required: true,
- },
- projectName: {
- type: String,
- required: true,
- },
- namespace: {
- type: String,
- required: true,
- },
- webUrl: {
- type: String,
- required: true,
- },
- avatarUrl: {
- required: true,
- validator(value) {
- return value === null || typeof value === 'string';
- },
- },
- },
- computed: {
- hasAvatar() {
- return this.avatarUrl !== null;
- },
- highlightedProjectName() {
- if (this.matcher) {
- const matcherRegEx = new RegExp(this.matcher, 'gi');
- const matches = this.projectName.match(matcherRegEx);
-
- if (matches && matches.length > 0) {
- return this.projectName.replace(matches[0], `<b>${matches[0]}</b>`);
- }
- }
- return this.projectName;
- },
- /**
- * Smartly truncates project namespace by doing two things;
- * 1. Only include Group names in path by removing project name
- * 2. Only include first and last group names in the path
- * when namespace has more than 2 groups present
- *
- * First part (removal of project name from namespace) can be
- * done from backend but doing so involves migration of
- * existing project namespaces which is not wise thing to do.
- */
- truncatedNamespace() {
- const namespaceArr = this.namespace.split(' / ');
- namespaceArr.splice(-1, 1);
- let namespace = namespaceArr.join(' / ');
-
- if (namespaceArr.length > 2) {
- namespace = `${namespaceArr[0]} / ... / ${namespaceArr.pop()}`;
- }
-
- return namespace;
- },
- },
- };
-</script>
-
-<template>
- <li
- class="projects-list-item-container"
- >
- <a
- :href="webUrl"
- class="clearfix"
- >
- <div
- class="project-item-avatar-container"
- >
- <img
- v-if="hasAvatar"
- :src="avatarUrl"
- class="avatar s32"
- />
- <identicon
- v-else
- :entity-id="projectId"
- :entity-name="projectName"
- size-class="s32"
- />
- </div>
- <div
- class="project-item-metadata-container"
- >
- <div
- :title="projectName"
- class="project-title"
- v-html="highlightedProjectName"
- >
- </div>
- <div
- :title="namespace"
- class="project-namespace"
- >{{ truncatedNamespace }}</div>
- </div>
- </a>
- </li>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue
deleted file mode 100644
index 76e9cb9e53f..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue
+++ /dev/null
@@ -1,63 +0,0 @@
-<script>
-import { s__ } from '../../locale';
-import projectsListItem from './projects_list_item.vue';
-
-export default {
- components: {
- projectsListItem,
- },
- props: {
- matcher: {
- type: String,
- required: true,
- },
- projects: {
- type: Array,
- required: true,
- },
- searchFailed: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- isListEmpty() {
- return this.projects.length === 0;
- },
- listEmptyMessage() {
- return this.searchFailed ?
- s__('ProjectsDropdown|Something went wrong on our end.') :
- s__('ProjectsDropdown|Sorry, no projects matched your search');
- },
- },
-};
-</script>
-
-<template>
- <div
- class="projects-list-search-container"
- >
- <ul
- class="list-unstyled"
- >
- <li
- v-if="isListEmpty"
- :class="{ 'section-failure': searchFailed }"
- class="section-empty"
- >
- {{ listEmptyMessage }}
- </li>
- <projects-list-item
- v-for="(project, index) in projects"
- v-else
- :key="index"
- :project-id="project.id"
- :project-name="project.name"
- :namespace="project.namespace"
- :web-url="project.webUrl"
- :avatar-url="project.avatarUrl"
- :matcher="matcher"
- />
- </ul>
- </div>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/components/search.vue b/app/assets/javascripts/projects_dropdown/components/search.vue
deleted file mode 100644
index 28f2a18f2a6..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/search.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script>
- import _ from 'underscore';
- import eventHub from '../event_hub';
-
- export default {
- data() {
- return {
- searchQuery: '',
- };
- },
- watch: {
- searchQuery() {
- this.handleInput();
- },
- },
- mounted() {
- eventHub.$on('dropdownOpen', this.setFocus);
- },
- beforeDestroy() {
- eventHub.$off('dropdownOpen', this.setFocus);
- },
- methods: {
- setFocus() {
- this.$refs.search.focus();
- },
- emitSearchEvents() {
- if (this.searchQuery) {
- eventHub.$emit('searchProjects', this.searchQuery);
- } else {
- eventHub.$emit('searchCleared');
- }
- },
- /**
- * Callback function within _.debounce is intentionally
- * kept as ES5 `function() {}` instead of ES6 `() => {}`
- * as it otherwise messes up function context
- * and component reference is no longer accessible via `this`
- */
- // eslint-disable-next-line func-names
- handleInput: _.debounce(function () {
- this.emitSearchEvents();
- }, 500),
- },
- };
-</script>
-
-<template>
- <div
- class="search-input-container d-none d-sm-block"
- >
- <input
- ref="search"
- v-model="searchQuery"
- :placeholder="s__('ProjectsDropdown|Search your projects')"
- type="search"
- class="form-control"
- />
- <i
- v-if="!searchQuery"
- class="search-icon fa fa-fw fa-search"
- aria-hidden="true"
- >
- </i>
- </div>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/constants.js b/app/assets/javascripts/projects_dropdown/constants.js
deleted file mode 100644
index 8937097184c..00000000000
--- a/app/assets/javascripts/projects_dropdown/constants.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export const FREQUENT_PROJECTS = {
- MAX_COUNT: 20,
- LIST_COUNT_DESKTOP: 5,
- LIST_COUNT_MOBILE: 3,
- ELIGIBLE_FREQUENCY: 3,
-};
-
-export const HOUR_IN_MS = 3600000;
-
-export const STORAGE_KEY = 'frequent-projects';
diff --git a/app/assets/javascripts/projects_dropdown/index.js b/app/assets/javascripts/projects_dropdown/index.js
deleted file mode 100644
index 6056f12aa4f..00000000000
--- a/app/assets/javascripts/projects_dropdown/index.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import $ from 'jquery';
-import Vue from 'vue';
-
-import Translate from '../vue_shared/translate';
-import eventHub from './event_hub';
-import ProjectsService from './service/projects_service';
-import ProjectsStore from './store/projects_store';
-
-import projectsDropdownApp from './components/app.vue';
-
-Vue.use(Translate);
-
-document.addEventListener('DOMContentLoaded', () => {
- const el = document.getElementById('js-projects-dropdown');
- const navEl = document.getElementById('nav-projects-dropdown');
-
- // Don't do anything if element doesn't exist (No projects dropdown)
- // This is for when the user accesses GitLab without logging in
- if (!el || !navEl) {
- return;
- }
-
- $(navEl).on('shown.bs.dropdown', () => {
- eventHub.$emit('dropdownOpen');
- });
-
- // eslint-disable-next-line no-new
- new Vue({
- el,
- components: {
- projectsDropdownApp,
- },
- data() {
- const { dataset } = this.$options.el;
- const store = new ProjectsStore();
- const service = new ProjectsService(dataset.userName);
-
- const project = {
- id: Number(dataset.projectId),
- name: dataset.projectName,
- namespace: dataset.projectNamespace,
- webUrl: dataset.projectWebUrl,
- avatarUrl: dataset.projectAvatarUrl || null,
- lastAccessedOn: Date.now(),
- };
-
- return {
- store,
- service,
- state: store.state,
- currentUserName: dataset.userName,
- currentProject: project,
- };
- },
- render(createElement) {
- return createElement('projects-dropdown-app', {
- props: {
- currentUserName: this.currentUserName,
- currentProject: this.currentProject,
- store: this.store,
- service: this.service,
- },
- });
- },
- });
-});
diff --git a/app/assets/javascripts/projects_dropdown/service/projects_service.js b/app/assets/javascripts/projects_dropdown/service/projects_service.js
deleted file mode 100644
index ed1c3deead2..00000000000
--- a/app/assets/javascripts/projects_dropdown/service/projects_service.js
+++ /dev/null
@@ -1,137 +0,0 @@
-import _ from 'underscore';
-import Vue from 'vue';
-import VueResource from 'vue-resource';
-
-import bp from '../../breakpoints';
-import Api from '../../api';
-import AccessorUtilities from '../../lib/utils/accessor';
-
-import { FREQUENT_PROJECTS, HOUR_IN_MS, STORAGE_KEY } from '../constants';
-
-Vue.use(VueResource);
-
-export default class ProjectsService {
- constructor(currentUserName) {
- this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
- this.currentUserName = currentUserName;
- this.storageKey = `${this.currentUserName}/${STORAGE_KEY}`;
- this.projectsPath = Vue.resource(Api.buildUrl(Api.projectsPath));
- }
-
- getSearchedProjects(searchQuery) {
- return this.projectsPath.get({
- simple: true,
- per_page: 20,
- membership: !!gon.current_user_id,
- order_by: 'last_activity_at',
- search: searchQuery,
- });
- }
-
- getFrequentProjects() {
- if (this.isLocalStorageAvailable) {
- return this.getTopFrequentProjects();
- }
- return null;
- }
-
- logProjectAccess(project) {
- let matchFound = false;
- let storedFrequentProjects;
-
- if (this.isLocalStorageAvailable) {
- const storedRawProjects = localStorage.getItem(this.storageKey);
-
- // Check if there's any frequent projects list set
- if (!storedRawProjects) {
- // No frequent projects list set, set one up.
- storedFrequentProjects = [];
- storedFrequentProjects.push({ ...project, frequency: 1 });
- } else {
- // Check if project is already present in frequents list
- // When found, update metadata of it.
- storedFrequentProjects = JSON.parse(storedRawProjects).map(projectItem => {
- if (projectItem.id === project.id) {
- matchFound = true;
- const diff = Math.abs(project.lastAccessedOn - projectItem.lastAccessedOn) / HOUR_IN_MS;
- const updatedProject = {
- ...project,
- frequency: projectItem.frequency,
- lastAccessedOn: projectItem.lastAccessedOn,
- };
-
- // Check if duration since last access of this project
- // is over an hour
- if (diff > 1) {
- return {
- ...updatedProject,
- frequency: updatedProject.frequency + 1,
- lastAccessedOn: Date.now(),
- };
- }
-
- return {
- ...updatedProject,
- };
- }
-
- return projectItem;
- });
-
- // Check whether currently logged project is present in frequents list
- if (!matchFound) {
- // We always keep size of frequents collection to 20 projects
- // out of which only 5 projects with
- // highest value of `frequency` and most recent `lastAccessedOn`
- // are shown in projects dropdown
- if (storedFrequentProjects.length === FREQUENT_PROJECTS.MAX_COUNT) {
- storedFrequentProjects.shift(); // Remove an item from head of array
- }
-
- storedFrequentProjects.push({ ...project, frequency: 1 });
- }
- }
-
- localStorage.setItem(this.storageKey, JSON.stringify(storedFrequentProjects));
- }
- }
-
- getTopFrequentProjects() {
- const storedFrequentProjects = JSON.parse(localStorage.getItem(this.storageKey));
- let frequentProjectsCount = FREQUENT_PROJECTS.LIST_COUNT_DESKTOP;
-
- if (!storedFrequentProjects) {
- return [];
- }
-
- if (bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'xs') {
- frequentProjectsCount = FREQUENT_PROJECTS.LIST_COUNT_MOBILE;
- }
-
- const frequentProjects = storedFrequentProjects.filter(
- project => project.frequency >= FREQUENT_PROJECTS.ELIGIBLE_FREQUENCY,
- );
-
- if (!frequentProjects || frequentProjects.length === 0) {
- return [];
- }
-
- // Sort all frequent projects in decending order of frequency
- // and then by lastAccessedOn with recent most first
- frequentProjects.sort((projectA, projectB) => {
- if (projectA.frequency < projectB.frequency) {
- return 1;
- } else if (projectA.frequency > projectB.frequency) {
- return -1;
- } else if (projectA.lastAccessedOn < projectB.lastAccessedOn) {
- return 1;
- } else if (projectA.lastAccessedOn > projectB.lastAccessedOn) {
- return -1;
- }
-
- return 0;
- });
-
- return _.first(frequentProjects, frequentProjectsCount);
- }
-}
diff --git a/app/assets/javascripts/projects_dropdown/store/projects_store.js b/app/assets/javascripts/projects_dropdown/store/projects_store.js
deleted file mode 100644
index ffefbe693f4..00000000000
--- a/app/assets/javascripts/projects_dropdown/store/projects_store.js
+++ /dev/null
@@ -1,33 +0,0 @@
-export default class ProjectsStore {
- constructor() {
- this.state = {};
- this.state.frequentProjects = [];
- this.state.searchedProjects = [];
- }
-
- setFrequentProjects(rawProjects) {
- this.state.frequentProjects = rawProjects;
- }
-
- getFrequentProjects() {
- return this.state.frequentProjects;
- }
-
- setSearchedProjects(rawProjects) {
- this.state.searchedProjects = rawProjects.map(rawProject => ({
- id: rawProject.id,
- name: rawProject.name,
- namespace: rawProject.name_with_namespace,
- webUrl: rawProject.web_url,
- avatarUrl: rawProject.avatar_url,
- }));
- }
-
- getSearchedProjects() {
- return this.state.searchedProjects;
- }
-
- clearSearchedProjects() {
- this.state.searchedProjects = [];
- }
-}
diff --git a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
new file mode 100644
index 00000000000..7b37f4e9a97
--- /dev/null
+++ b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
@@ -0,0 +1,116 @@
+<script>
+ import { mapActions, mapGetters, mapState } from 'vuex';
+ import { s__ } from '~/locale';
+ import { componentNames } from './issue_body';
+ import ReportSection from './report_section.vue';
+ import SummaryRow from './summary_row.vue';
+ import IssuesList from './issues_list.vue';
+ import Modal from './modal.vue';
+ import createStore from '../store';
+ import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils';
+
+ export default {
+ name: 'GroupedTestReportsApp',
+ store: createStore(),
+ components: {
+ ReportSection,
+ SummaryRow,
+ IssuesList,
+ Modal,
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
+ },
+ },
+ componentNames,
+ computed: {
+ ...mapState([
+ 'reports',
+ 'isLoading',
+ 'hasError',
+ 'summary',
+ ]),
+ ...mapState({
+ modalTitle: state => state.modal.title || '',
+ modalData: state => state.modal.data || {},
+ }),
+ ...mapGetters([
+ 'summaryStatus',
+ ]),
+ groupedSummaryText() {
+ if (this.isLoading) {
+ return s__('Reports|Test summary results are being parsed');
+ }
+
+ if (this.hasError) {
+ return s__('Reports|Test summary failed loading results');
+ }
+
+ return summaryTextBuilder(s__('Reports|Test summary'), this.summary);
+ },
+ },
+ created() {
+ this.setEndpoint(this.endpoint);
+
+ this.fetchReports();
+ },
+ methods: {
+ ...mapActions(['setEndpoint', 'fetchReports']),
+ reportText(report) {
+ const summary = report.summary || {};
+ return reportTextBuilder(report.name, summary);
+ },
+ getReportIcon(report) {
+ return statusIcon(report.status);
+ },
+ shouldRenderIssuesList(report) {
+ return (
+ report.existing_failures.length > 0 ||
+ report.new_failures.length > 0 ||
+ report.resolved_failures.length > 0
+ );
+ },
+ },
+ };
+</script>
+<template>
+ <report-section
+ :status="summaryStatus"
+ :success-text="groupedSummaryText"
+ :loading-text="groupedSummaryText"
+ :error-text="groupedSummaryText"
+ :has-issues="reports.length > 0"
+ class="mr-widget-border-top grouped-security-reports mr-report"
+ >
+ <div
+ slot="body"
+ class="mr-widget-grouped-section report-block"
+ >
+ <template
+ v-for="(report, i) in reports"
+ >
+ <summary-row
+ :summary="reportText(report)"
+ :status-icon="getReportIcon(report)"
+ :key="`summary-row-${i}`"
+ />
+ <issues-list
+ v-if="shouldRenderIssuesList(report)"
+ :unresolved-issues="report.existing_failures"
+ :new-issues="report.new_failures"
+ :resolved-issues="report.resolved_failures"
+ :key="`issues-list-${i}`"
+ :component="$options.componentNames.TestIssueBody"
+ class="report-block-group-list"
+ />
+ </template>
+
+ <modal
+ :title="modalTitle"
+ :modal-data="modalData"
+ />
+ </div>
+ </report-section>
+</template>
diff --git a/app/assets/javascripts/reports/components/issue_body.js b/app/assets/javascripts/reports/components/issue_body.js
new file mode 100644
index 00000000000..8b5af263d50
--- /dev/null
+++ b/app/assets/javascripts/reports/components/issue_body.js
@@ -0,0 +1,9 @@
+import TestIssueBody from './test_issue_body.vue';
+
+export const components = {
+ TestIssueBody,
+};
+
+export const componentNames = {
+ TestIssueBody: TestIssueBody.name,
+};
diff --git a/app/assets/javascripts/reports/components/issue_status_icon.vue b/app/assets/javascripts/reports/components/issue_status_icon.vue
new file mode 100644
index 00000000000..85811698a37
--- /dev/null
+++ b/app/assets/javascripts/reports/components/issue_status_icon.vue
@@ -0,0 +1,57 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import {
+ STATUS_FAILED,
+ STATUS_NEUTRAL,
+ STATUS_SUCCESS,
+} from '../constants';
+
+export default {
+ name: 'IssueStatusIcon',
+ components: {
+ Icon,
+ },
+ props: {
+ // failed || success
+ status: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ iconName() {
+ if (this.isStatusFailed) {
+ return 'status_failed_borderless';
+ } else if (this.isStatusSuccess) {
+ return 'status_success_borderless';
+ }
+
+ return 'status_created_borderless';
+ },
+ isStatusFailed() {
+ return this.status === STATUS_FAILED;
+ },
+ isStatusSuccess() {
+ return this.status === STATUS_SUCCESS;
+ },
+ isStatusNeutral() {
+ return this.status === STATUS_NEUTRAL;
+ },
+ },
+};
+</script>
+<template>
+ <div
+ :class="{
+ failed: isStatusFailed,
+ success: isStatusSuccess,
+ neutral: isStatusNeutral,
+ }"
+ class="report-block-list-icon"
+ >
+ <icon
+ :name="iconName"
+ :size="32"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/reports/components/issues_list.vue b/app/assets/javascripts/reports/components/issues_list.vue
new file mode 100644
index 00000000000..dbb8848d1fa
--- /dev/null
+++ b/app/assets/javascripts/reports/components/issues_list.vue
@@ -0,0 +1,85 @@
+<script>
+import IssuesBlock from './report_issues.vue';
+import {
+ STATUS_SUCCESS,
+ STATUS_FAILED,
+ STATUS_NEUTRAL,
+} from '../constants';
+
+/**
+ * Renders block of issues
+ */
+
+export default {
+ components: {
+ IssuesBlock,
+ },
+ success: STATUS_SUCCESS,
+ failed: STATUS_FAILED,
+ neutral: STATUS_NEUTRAL,
+ props: {
+ newIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ unresolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ resolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ neutralIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ component: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+};
+</script>
+<template>
+ <div class="report-block-container">
+
+ <issues-block
+ v-if="newIssues.length"
+ :component="component"
+ :issues="newIssues"
+ class="js-mr-code-new-issues"
+ status="failed"
+ is-new
+ />
+
+ <issues-block
+ v-if="unresolvedIssues.length"
+ :component="component"
+ :issues="unresolvedIssues"
+ :status="$options.failed"
+ class="js-mr-code-new-issues"
+ />
+
+ <issues-block
+ v-if="neutralIssues.length"
+ :component="component"
+ :issues="neutralIssues"
+ :status="$options.neutral"
+ class="js-mr-code-non-issues"
+ />
+
+ <issues-block
+ v-if="resolvedIssues.length"
+ :component="component"
+ :issues="resolvedIssues"
+ :status="$options.success"
+ class="js-mr-code-resolved-issues"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/reports/components/modal.vue b/app/assets/javascripts/reports/components/modal.vue
new file mode 100644
index 00000000000..acc5c6d85e2
--- /dev/null
+++ b/app/assets/javascripts/reports/components/modal.vue
@@ -0,0 +1,73 @@
+<script>
+ import Modal from '~/vue_shared/components/gl_modal.vue';
+ import LoadingButton from '~/vue_shared/components/loading_button.vue';
+ import CodeBlock from '~/vue_shared/components/code_block.vue';
+ import { fieldTypes } from '../constants';
+
+ export default {
+ components: {
+ Modal,
+ LoadingButton,
+ CodeBlock,
+ },
+ props: {
+ title: {
+ type: String,
+ required: true,
+ },
+ modalData: {
+ type: Object,
+ required: true,
+ },
+ },
+ fieldTypes,
+ };
+</script>
+<template>
+ <modal
+ id="modal-mrwidget-reports"
+ :header-title-text="title"
+ class="modal-security-report-dast modal-hide-footer"
+ >
+ <slot>
+ <div
+ v-for="(field, key, index) in modalData"
+ v-if="field.value"
+ :key="index"
+ class="row prepend-top-10 append-bottom-10"
+ >
+ <strong class="col-sm-3 text-right">
+ {{ field.text }}:
+ </strong>
+
+ <div class="col-sm-9 text-secondary">
+ <code-block
+ v-if="field.type === $options.fieldTypes.codeBock"
+ :code="field.value"
+ />
+
+ <template v-else-if="field.type === $options.fieldTypes.link">
+ <a
+ :href="field.value"
+ target="_blank"
+ rel="noopener noreferrer"
+ class="js-modal-link"
+ >
+ {{ field.value }}
+ </a>
+ </template>
+
+ <template v-else-if="field.type === $options.fieldTypes.miliseconds">
+ {{ field.value }} ms
+ </template>
+
+ <template v-else-if="field.type === $options.fieldTypes.text">
+ {{ field.value }}
+ </template>
+ </div>
+ </div>
+ </slot>
+ <div slot="footer">
+ </div>
+ </modal>
+</template>
diff --git a/app/assets/javascripts/reports/components/modal_open_name.vue b/app/assets/javascripts/reports/components/modal_open_name.vue
new file mode 100644
index 00000000000..4f81cee2a38
--- /dev/null
+++ b/app/assets/javascripts/reports/components/modal_open_name.vue
@@ -0,0 +1,33 @@
+<script>
+import { mapActions } from 'vuex';
+
+export default {
+ props: {
+ issue: {
+ type: Object,
+ required: true,
+ },
+ // failed || success
+ status: {
+ type: String,
+ required: true,
+ },
+ },
+ methods: {
+ ...mapActions(['openModal']),
+ handleIssueClick() {
+ const { issue, status, openModal } = this;
+ openModal({ issue, status });
+ },
+ },
+};
+</script>
+<template>
+ <button
+ type="button"
+ class="btn-link btn-blank text-left break-link vulnerability-name-button"
+ @click="handleIssueClick()"
+ >
+ {{ issue.title }}
+ </button>
+</template>
diff --git a/app/assets/javascripts/reports/components/report_issues.vue b/app/assets/javascripts/reports/components/report_issues.vue
new file mode 100644
index 00000000000..884f55c8dec
--- /dev/null
+++ b/app/assets/javascripts/reports/components/report_issues.vue
@@ -0,0 +1,59 @@
+<script>
+import IssueStatusIcon from './issue_status_icon.vue';
+import { components, componentNames } from './issue_body';
+
+export default {
+ name: 'ReportIssues',
+ components: {
+ IssueStatusIcon,
+ ...components,
+ },
+ props: {
+ issues: {
+ type: Array,
+ required: true,
+ },
+ component: {
+ type: String,
+ required: false,
+ default: '',
+ validator: value => value === '' || Object.values(componentNames).includes(value),
+ },
+ // failed || success
+ status: {
+ type: String,
+ required: true,
+ },
+ isNew: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <ul class="report-block-list">
+ <li
+ v-for="(issue, index) in issues"
+ :class="{ 'is-dismissed': issue.isDismissed }"
+ :key="index"
+ class="report-block-list-issue"
+ >
+ <issue-status-icon
+ :status="issue.status || status"
+ class="append-right-5"
+ />
+
+ <component
+ v-if="component"
+ :is="component"
+ :issue="issue"
+ :status="issue.status || status"
+ :is-new="isNew"
+ />
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/reports/components/report_link.vue b/app/assets/javascripts/reports/components/report_link.vue
new file mode 100644
index 00000000000..74d68f9f439
--- /dev/null
+++ b/app/assets/javascripts/reports/components/report_link.vue
@@ -0,0 +1,29 @@
+<script>
+export default {
+ name: 'ReportIssueLink',
+ props: {
+ issue: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <div class="report-block-list-issue-description-link">
+ in
+
+ <a
+ v-if="issue.urlPath"
+ :href="issue.urlPath"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ class="break-link"
+ >
+ {{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
+ </a>
+ <template v-else>
+ {{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue
new file mode 100644
index 00000000000..dc609d6f90e
--- /dev/null
+++ b/app/assets/javascripts/reports/components/report_section.vue
@@ -0,0 +1,181 @@
+<script>
+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';
+
+const LOADING = 'LOADING';
+const ERROR = 'ERROR';
+const SUCCESS = 'SUCCESS';
+
+export default {
+ name: 'ReportSection',
+ components: {
+ IssuesList,
+ StatusIcon,
+ Popover,
+ },
+ props: {
+ alwaysOpen: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ component: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ status: {
+ type: String,
+ required: true,
+ },
+ loadingText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ errorText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ successText: {
+ type: String,
+ required: true,
+ },
+ unresolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ resolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ neutralIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ infoText: {
+ type: [String, Boolean],
+ required: false,
+ default: false,
+ },
+ hasIssues: {
+ type: Boolean,
+ required: true,
+ },
+ popoverOptions: {
+ type: Object,
+ default: () => ({}),
+ required: false,
+ },
+ },
+
+ data() {
+ return {
+ isCollapsed: true,
+ };
+ },
+
+ computed: {
+ collapseText() {
+ return this.isCollapsed ? __('Expand') : __('Collapse');
+ },
+ isLoading() {
+ return this.status === LOADING;
+ },
+ loadingFailed() {
+ return this.status === ERROR;
+ },
+ isSuccess() {
+ return this.status === SUCCESS;
+ },
+ isCollapsible() {
+ return !this.alwaysOpen && this.hasIssues;
+ },
+ isExpanded() {
+ return this.alwaysOpen || !this.isCollapsed;
+ },
+ statusIconName() {
+ if (this.isLoading) {
+ return 'loading';
+ }
+ if (this.loadingFailed || this.unresolvedIssues.length || this.neutralIssues.length) {
+ return 'warning';
+ }
+ return 'success';
+ },
+ headerText() {
+ if (this.isLoading) {
+ return this.loadingText;
+ }
+
+ if (this.isSuccess) {
+ return this.successText;
+ }
+
+ if (this.loadingFailed) {
+ return this.errorText;
+ }
+
+ return '';
+ },
+ hasPopover() {
+ return Object.keys(this.popoverOptions).length > 0;
+ },
+ },
+ methods: {
+ toggleCollapsed() {
+ this.isCollapsed = !this.isCollapsed;
+ },
+ },
+};
+</script>
+<template>
+ <section class="media-section">
+ <div class="media">
+ <status-icon :status="statusIconName" />
+ <div class="media-body space-children d-flex flex-align-self-center">
+ <span class="js-code-text code-text">
+ {{ headerText }}
+
+ <popover
+ v-if="hasPopover"
+ :options="popoverOptions"
+ class="prepend-left-5"
+ />
+ </span>
+
+ <slot name="actionButtons"></slot>
+
+ <button
+ v-if="isCollapsible"
+ type="button"
+ class="js-collapse-btn btn float-right btn-sm"
+ @click="toggleCollapsed"
+ >
+ {{ collapseText }}
+ </button>
+ </div>
+ </div>
+
+ <div
+ v-if="hasIssues"
+ v-show="isExpanded"
+ class="js-report-section-container"
+ >
+ <slot name="body">
+ <issues-list
+ :unresolved-issues="unresolvedIssues"
+ :resolved-issues="resolvedIssues"
+ :neutral-issues="neutralIssues"
+ :component="component"
+ />
+ </slot>
+ </div>
+ </section>
+</template>
diff --git a/app/assets/javascripts/reports/components/summary_row.vue b/app/assets/javascripts/reports/components/summary_row.vue
new file mode 100644
index 00000000000..4456d84c968
--- /dev/null
+++ b/app/assets/javascripts/reports/components/summary_row.vue
@@ -0,0 +1,71 @@
+<script>
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import Popover from '~/vue_shared/components/help_popover.vue';
+
+/**
+ * Renders the summary row for each report
+ *
+ * Used both in MR widget and Pipeline's view for:
+ * - Unit tests reports
+ * - Security reports
+ */
+
+export default {
+ name: 'ReportSummaryRow',
+ components: {
+ CiIcon,
+ LoadingIcon,
+ Popover,
+ },
+ props: {
+ summary: {
+ type: String,
+ required: true,
+ },
+ statusIcon: {
+ type: String,
+ required: true,
+ },
+ popoverOptions: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ iconStatus() {
+ return {
+ group: this.statusIcon,
+ icon: `status_${this.statusIcon}`,
+ };
+ },
+ },
+};
+</script>
+<template>
+ <div class="report-block-list-issue report-block-list-issue-parent">
+ <div class="report-block-list-icon append-right-10 prepend-left-5">
+ <loading-icon
+ v-if="statusIcon === 'loading'"
+ css-class="report-block-list-loading-icon"
+ />
+ <ci-icon
+ v-else
+ :status="iconStatus"
+ />
+ </div>
+
+ <div class="report-block-list-issue-description">
+ <div class="report-block-list-issue-description-text">
+ {{ summary }}
+ </div>
+
+ <popover
+ v-if="popoverOptions"
+ :options="popoverOptions"
+ />
+
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/reports/components/test_issue_body.vue b/app/assets/javascripts/reports/components/test_issue_body.vue
new file mode 100644
index 00000000000..cd443a49b52
--- /dev/null
+++ b/app/assets/javascripts/reports/components/test_issue_body.vue
@@ -0,0 +1,44 @@
+<script>
+ import { mapActions } from 'vuex';
+
+ export default {
+ name: 'TestIssueBody',
+ props: {
+ issue: {
+ type: Object,
+ required: true,
+ },
+ // failed || success
+ status: {
+ type: String,
+ required: true,
+ },
+ isNew: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ methods: {
+ ...mapActions(['openModal']),
+ },
+ };
+</script>
+<template>
+ <div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
+ <div class="report-block-list-issue-description-text">
+ <button
+ type="button"
+ class="btn-link btn-blank text-left break-link vulnerability-name-button"
+ @click="openModal({ issue })"
+ >
+ <div
+ v-if="isNew"
+ class="badge badge-danger append-right-5"
+ >
+ {{ s__('New') }}
+ </div>{{ issue.name }}
+ </button>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/reports/constants.js b/app/assets/javascripts/reports/constants.js
new file mode 100644
index 00000000000..c323dc543f3
--- /dev/null
+++ b/app/assets/javascripts/reports/constants.js
@@ -0,0 +1,18 @@
+export const fieldTypes = {
+ codeBock: 'codeBlock',
+ link: 'link',
+ miliseconds: 'miliseconds',
+ text: 'text',
+};
+
+export const LOADING = 'LOADING';
+export const ERROR = 'ERROR';
+export const SUCCESS = 'SUCCESS';
+
+export const STATUS_FAILED = 'failed';
+export const STATUS_SUCCESS = 'success';
+export const STATUS_NEUTRAL = 'neutral';
+
+export const ICON_WARNING = 'warning';
+export const ICON_SUCCESS = 'success';
+export const ICON_NOTFOUND = 'notfound';
diff --git a/app/assets/javascripts/reports/store/actions.js b/app/assets/javascripts/reports/store/actions.js
new file mode 100644
index 00000000000..acabcc1d193
--- /dev/null
+++ b/app/assets/javascripts/reports/store/actions.js
@@ -0,0 +1,88 @@
+import Visibility from 'visibilityjs';
+import $ from 'jquery';
+import axios from '../../lib/utils/axios_utils';
+import Poll from '../../lib/utils/poll';
+import * as types from './mutation_types';
+import httpStatusCodes from '../../lib/utils/http_status';
+
+export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
+
+export const requestReports = ({ commit }) => commit(types.REQUEST_REPORTS);
+
+let eTagPoll;
+
+export const clearEtagPoll = () => {
+ eTagPoll = null;
+};
+
+export const stopPolling = () => {
+ if (eTagPoll) eTagPoll.stop();
+};
+
+export const restartPolling = () => {
+ if (eTagPoll) eTagPoll.restart();
+};
+
+/**
+ * We need to poll the reports endpoint while they are being parsed in the Backend.
+ * This can take up to one minute.
+ *
+ * Poll.js will handle etag response.
+ * While http status code is 204, it means it's parsing, and we'll keep polling
+ * When http status code is 200, it means parsing is done, we can show the results & stop polling
+ * When http status code is 500, it means parsing went wrong and we stop polling
+ */
+export const fetchReports = ({ state, dispatch }) => {
+ dispatch('requestReports');
+
+ eTagPoll = new Poll({
+ resource: {
+ getReports(endpoint) {
+ return axios.get(endpoint);
+ },
+ },
+ data: state.endpoint,
+ method: 'getReports',
+ successCallback: ({ data, status }) => dispatch('receiveReportsSuccess', {
+ data, status,
+ }),
+ errorCallback: () => dispatch('receiveReportsError'),
+ });
+
+ if (!Visibility.hidden()) {
+ eTagPoll.makeRequest();
+ } else {
+ axios
+ .get(state.endpoint)
+ .then(({ data, status }) => dispatch('receiveReportsSuccess', { data, status }))
+ .catch(() => dispatch('receiveReportsError'));
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ dispatch('restartPolling');
+ } else {
+ dispatch('stopPolling');
+ }
+ });
+};
+
+export const receiveReportsSuccess = ({ commit }, response) => {
+ // With 204 we keep polling and don't update the state
+ if (response.status === httpStatusCodes.OK) {
+ commit(types.RECEIVE_REPORTS_SUCCESS, response.data);
+ }
+};
+
+export const receiveReportsError = ({ commit }) => commit(types.RECEIVE_REPORTS_ERROR);
+
+export const openModal = ({ dispatch }, payload) => {
+ dispatch('setModalData', payload);
+
+ $('#modal-mrwidget-reports').modal('show');
+};
+
+export const setModalData = ({ commit }, payload) => commit(types.SET_ISSUE_MODAL_DATA, payload);
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/reports/store/getters.js b/app/assets/javascripts/reports/store/getters.js
new file mode 100644
index 00000000000..95266194acb
--- /dev/null
+++ b/app/assets/javascripts/reports/store/getters.js
@@ -0,0 +1,16 @@
+import { LOADING, ERROR, SUCCESS, STATUS_FAILED } from '../constants';
+
+export const summaryStatus = state => {
+ if (state.isLoading) {
+ return LOADING;
+ }
+
+ if (state.hasError || state.status === STATUS_FAILED) {
+ return ERROR;
+ }
+
+ return SUCCESS;
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/reports/store/index.js b/app/assets/javascripts/reports/store/index.js
new file mode 100644
index 00000000000..9d8f7dc3b74
--- /dev/null
+++ b/app/assets/javascripts/reports/store/index.js
@@ -0,0 +1,15 @@
+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 default () => new Vuex.Store({
+ actions,
+ mutations,
+ getters,
+ state: state(),
+});
diff --git a/app/assets/javascripts/reports/store/mutation_types.js b/app/assets/javascripts/reports/store/mutation_types.js
new file mode 100644
index 00000000000..82bda31df5d
--- /dev/null
+++ b/app/assets/javascripts/reports/store/mutation_types.js
@@ -0,0 +1,7 @@
+export const SET_ENDPOINT = 'SET_ENDPOINT';
+
+export const REQUEST_REPORTS = 'REQUEST_REPORTS';
+export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS';
+export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR';
+export const SET_ISSUE_MODAL_DATA = 'SET_ISSUE_MODAL_DATA';
+
diff --git a/app/assets/javascripts/reports/store/mutations.js b/app/assets/javascripts/reports/store/mutations.js
new file mode 100644
index 00000000000..1983a8c9e56
--- /dev/null
+++ b/app/assets/javascripts/reports/store/mutations.js
@@ -0,0 +1,49 @@
+/* eslint-disable no-param-reassign */
+import * as types from './mutation_types';
+
+export default {
+ [types.SET_ENDPOINT](state, endpoint) {
+ state.endpoint = endpoint;
+ },
+ [types.REQUEST_REPORTS](state) {
+ state.isLoading = true;
+ },
+ [types.RECEIVE_REPORTS_SUCCESS](state, response) {
+ // Make sure to clean previous state in case it was an error
+ state.hasError = false;
+
+ state.isLoading = false;
+
+ state.summary.total = response.summary.total;
+ state.summary.resolved = response.summary.resolved;
+ state.summary.failed = response.summary.failed;
+
+ state.status = response.status;
+ state.reports = response.suites;
+
+ },
+ [types.RECEIVE_REPORTS_ERROR](state) {
+ state.isLoading = false;
+ state.hasError = true;
+
+ state.reports = [];
+ state.summary = {
+ total: 0,
+ resolved: 0,
+ failed: 0,
+ };
+ state.status = null;
+ },
+ [types.SET_ISSUE_MODAL_DATA](state, payload) {
+ state.modal.title = payload.issue.name;
+
+ Object.keys(payload.issue).forEach((key) => {
+ if (Object.prototype.hasOwnProperty.call(state.modal.data, key)) {
+ state.modal.data[key] = {
+ ...state.modal.data[key],
+ value: payload.issue[key],
+ };
+ }
+ });
+ },
+};
diff --git a/app/assets/javascripts/reports/store/state.js b/app/assets/javascripts/reports/store/state.js
new file mode 100644
index 00000000000..4cab2e27a16
--- /dev/null
+++ b/app/assets/javascripts/reports/store/state.js
@@ -0,0 +1,61 @@
+import { s__ } from '~/locale';
+import { fieldTypes } from '../constants';
+
+export default () => ({
+ endpoint: null,
+
+ isLoading: false,
+ hasError: false,
+
+ status: null,
+
+ summary: {
+ total: 0,
+ resolved: 0,
+ failed: 0,
+ },
+
+ /**
+ * Each report will have the following format:
+ * {
+ * name: {String},
+ * summary: {
+ * total: {Number},
+ * resolved: {Number},
+ * failed: {Number},
+ * },
+ * new_failures: {Array.<Object>},
+ * resolved_failures: {Array.<Object>},
+ * existing_failures: {Array.<Object>},
+ * }
+ */
+ reports: [],
+
+ modal: {
+ title: null,
+
+ data: {
+ class: {
+ value: null,
+ text: s__('Reports|Class'),
+ type: fieldTypes.link,
+ },
+ execution_time: {
+ value: null,
+ text: s__('Reports|Execution time'),
+ type: fieldTypes.miliseconds,
+ },
+ failure: {
+ value: null,
+ text: s__('Reports|Failure'),
+ type: fieldTypes.codeBock,
+ },
+ system_output: {
+ value: null,
+ text: s__('Reports|System output'),
+ type: fieldTypes.codeBock,
+ },
+ },
+ },
+
+});
diff --git a/app/assets/javascripts/reports/store/utils.js b/app/assets/javascripts/reports/store/utils.js
new file mode 100644
index 00000000000..35632218269
--- /dev/null
+++ b/app/assets/javascripts/reports/store/utils.js
@@ -0,0 +1,59 @@
+import { sprintf, n__, s__ } from '~/locale';
+import {
+ STATUS_FAILED,
+ STATUS_SUCCESS,
+ ICON_WARNING,
+ ICON_SUCCESS,
+ ICON_NOTFOUND,
+} from '../constants';
+
+const textBuilder = results => {
+ const { failed, resolved, total } = results;
+
+ const failedString = failed
+ ? n__('%d failed test result', '%d failed test results', failed)
+ : null;
+ const resolvedString = resolved
+ ? n__('%d fixed test result', '%d fixed test results', resolved)
+ : null;
+ const totalString = total ? n__('out of %d total test', 'out of %d total tests', total) : null;
+
+ let resultsString = s__('Reports|no changed test results');
+
+ if (failed) {
+ if (resolved) {
+ resultsString = sprintf(s__('Reports|%{failedString} and %{resolvedString}'), {
+ failedString,
+ resolvedString,
+ });
+ } else {
+ resultsString = failedString;
+ }
+ } else if (resolved) {
+ resultsString = resolvedString;
+ }
+
+ return `${resultsString} ${totalString}`;
+};
+
+export const summaryTextBuilder = (name = '', results = {}) => {
+ const resultsString = textBuilder(results);
+ return `${name} contained ${resultsString}`;
+};
+
+export const reportTextBuilder = (name = '', results = {}) => {
+ const resultsString = textBuilder(results);
+ return `${name} found ${resultsString}`;
+};
+
+export const statusIcon = status => {
+ if (status === STATUS_FAILED) {
+ return ICON_WARNING;
+ }
+
+ if (status === STATUS_SUCCESS) {
+ return ICON_SUCCESS;
+ }
+
+ return ICON_NOTFOUND;
+};
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index 5b2e0468784..aec09b8bc0a 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -1,9 +1,18 @@
-/* eslint-disable no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, consistent-return, object-shorthand, prefer-template, quotes, class-methods-use-this, no-lonely-if, no-else-return, vars-on-top, max-len */
+/* eslint-disable no-return-assign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, object-shorthand, prefer-template, class-methods-use-this, no-lonely-if, vars-on-top, max-len */
import $ from 'jquery';
+import { escape, throttle } from 'underscore';
+import { s__, sprintf } from '~/locale';
+import { getIdenticonBackgroundClass, getIdenticonTitle } from '~/helpers/avatar_helper';
import axios from './lib/utils/axios_utils';
import DropdownUtils from './filtered_search/dropdown_utils';
-import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from './lib/utils/common_utils';
+import {
+ isInGroupsPage,
+ isInProjectPage,
+ getGroupSlug,
+ getProjectSlug,
+ spriteIcon,
+} from './lib/utils/common_utils';
/**
* Search input in top navigation bar.
@@ -52,6 +61,7 @@ function setSearchOptions() {
if ($dashboardOptionsDataEl.length) {
gl.dashboardOptions = {
+ name: s__('SearchAutocomplete|All GitLab'),
issuesPath: $dashboardOptionsDataEl.data('issuesPath'),
mrPath: $dashboardOptionsDataEl.data('mrPath'),
};
@@ -69,8 +79,8 @@ export default class SearchAutocomplete {
this.projectRef = projectRef || (this.optsEl.data('autocompleteProjectRef') || '');
this.dropdown = this.wrap.find('.dropdown');
this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle');
+ this.dropdownMenu = this.dropdown.find('.dropdown-menu');
this.dropdownContent = this.dropdown.find('.dropdown-content');
- this.locationBadgeEl = this.getElement('.location-badge');
this.scopeInputEl = this.getElement('#scope');
this.searchInput = this.getElement('.search-input');
this.projectInputEl = this.getElement('#search_project_id');
@@ -78,6 +88,7 @@ export default class SearchAutocomplete {
this.searchCodeInputEl = this.getElement('#search_code');
this.repositoryInputEl = this.getElement('#repository_ref');
this.clearInput = this.getElement('.js-clear-input');
+ this.scrollFadeInitialized = false;
this.saveOriginalState();
// Only when user is logged in
@@ -98,17 +109,18 @@ export default class SearchAutocomplete {
this.onSearchInputFocus = this.onSearchInputFocus.bind(this);
this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this);
this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this);
+ this.setScrollFade = this.setScrollFade.bind(this);
}
getElement(selector) {
return this.wrap.find(selector);
}
saveOriginalState() {
- return this.originalState = this.serializeState();
+ return (this.originalState = this.serializeState());
}
saveTextLength() {
- return this.lastTextLength = this.searchInput.val().length;
+ return (this.lastTextLength = this.searchInput.val().length);
}
createAutocomplete() {
@@ -117,6 +129,7 @@ export default class SearchAutocomplete {
filterable: true,
filterRemote: true,
highlight: true,
+ icon: true,
enterCallback: false,
filterInput: 'input#search',
search: {
@@ -137,7 +150,11 @@ export default class SearchAutocomplete {
if (!term) {
const contents = this.getCategoryContents();
if (contents) {
- this.searchInput.data('glDropdown').filter.options.callback(contents);
+ const glDropdownInstance = this.searchInput.data('glDropdown');
+
+ if (glDropdownInstance) {
+ glDropdownInstance.filter.options.callback(contents);
+ }
this.enableAutocomplete();
}
return;
@@ -150,60 +167,87 @@ export default class SearchAutocomplete {
this.loadingSuggestions = true;
- return axios.get(this.autocompletePath, {
- params: {
- project_id: this.projectId,
- project_ref: this.projectRef,
- term: term,
- },
- }).then((response) => {
- // Hide dropdown menu if no suggestions returns
- if (!response.data.length) {
- this.disableAutocomplete();
- return;
- }
+ return axios
+ .get(this.autocompletePath, {
+ params: {
+ project_id: this.projectId,
+ project_ref: this.projectRef,
+ term: term,
+ },
+ })
+ .then(response => {
+ // Hide dropdown menu if no suggestions returns
+ if (!response.data.length) {
+ this.disableAutocomplete();
+ return;
+ }
- const data = [];
- // List results
- let firstCategory = true;
- let lastCategory;
- for (let i = 0, len = response.data.length; i < len; i += 1) {
- const suggestion = response.data[i];
- // Add group header before list each group
- if (lastCategory !== suggestion.category) {
- if (!firstCategory) {
- data.push('separator');
- }
- if (firstCategory) {
- firstCategory = false;
+ const data = [];
+ // List results
+ let firstCategory = true;
+ let lastCategory;
+ for (let i = 0, len = response.data.length; i < len; i += 1) {
+ const suggestion = response.data[i];
+ // Add group header before list each group
+ if (lastCategory !== suggestion.category) {
+ if (!firstCategory) {
+ data.push('separator');
+ }
+ if (firstCategory) {
+ firstCategory = false;
+ }
+ data.push({
+ header: suggestion.category,
+ });
+ lastCategory = suggestion.category;
}
data.push({
- header: suggestion.category,
+ id: `${suggestion.category.toLowerCase()}-${suggestion.id}`,
+ icon: this.getAvatar(suggestion),
+ category: suggestion.category,
+ text: suggestion.label,
+ url: suggestion.url,
});
- lastCategory = suggestion.category;
}
- data.push({
- id: `${suggestion.category.toLowerCase()}-${suggestion.id}`,
- category: suggestion.category,
- text: suggestion.label,
- url: suggestion.url,
- });
- }
- // Add option to proceed with the search
- if (data.length) {
- data.push('separator');
- data.push({
- text: `Result name contains "${term}"`,
- url: `/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`,
- });
- }
+ // Add option to proceed with the search
+ if (data.length) {
+ const icon = spriteIcon('search', 's16 inline-search-icon');
+ let template;
- callback(data);
+ if (this.projectInputEl.val()) {
+ template = s__('SearchAutocomplete|in this project');
+ }
+ if (this.groupInputEl.val()) {
+ template = s__('SearchAutocomplete|in this group');
+ }
- this.loadingSuggestions = false;
- }).catch(() => {
- this.loadingSuggestions = false;
- });
+ data.unshift('separator');
+ data.unshift({
+ icon,
+ text: term,
+ template: s__('SearchAutocomplete|in all GitLab'),
+ url: `/search?search=${term}`,
+ });
+
+ if (template) {
+ data.unshift({
+ icon,
+ text: term,
+ template,
+ url: `/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`,
+ });
+ }
+ }
+
+ callback(data);
+
+ this.loadingSuggestions = false;
+ this.highlightFirstRow();
+ this.setScrollFade();
+ })
+ .catch(() => {
+ this.loadingSuggestions = false;
+ });
}
getCategoryContents() {
@@ -232,21 +276,21 @@ export default class SearchAutocomplete {
const issueItems = [
{
- text: 'Issues assigned to me',
+ text: s__('SearchAutocomplete|Issues assigned to me'),
url: `${issuesPath}/?assignee_id=${userId}`,
},
{
- text: "Issues I've created",
+ text: s__("SearchAutocomplete|Issues I've created"),
url: `${issuesPath}/?author_id=${userId}`,
},
];
const mergeRequestItems = [
{
- text: 'Merge requests assigned to me',
+ text: s__('SearchAutocomplete|Merge requests assigned to me'),
url: `${mrPath}/?assignee_id=${userId}`,
},
{
- text: "Merge requests I've created",
+ text: s__("SearchAutocomplete|Merge requests I've created"),
url: `${mrPath}/?author_id=${userId}`,
},
];
@@ -255,7 +299,7 @@ export default class SearchAutocomplete {
if (issuesDisabled) {
items = baseItems.concat(mergeRequestItems);
} else {
- items = baseItems.concat(...issueItems, 'separator', ...mergeRequestItems);
+ items = baseItems.concat(...issueItems, ...mergeRequestItems);
}
return items;
}
@@ -268,8 +312,6 @@ export default class SearchAutocomplete {
search_code: this.searchCodeInputEl.val(),
repository_ref: this.repositoryInputEl.val(),
scope: this.scopeInputEl.val(),
- // Location badge
- _location: this.locationBadgeEl.text(),
};
}
@@ -279,10 +321,12 @@ export default class SearchAutocomplete {
this.searchInput.on('focus', this.onSearchInputFocus);
this.searchInput.on('blur', this.onSearchInputBlur);
this.clearInput.on('click', this.onClearInputClick);
- this.locationBadgeEl.on('click', () => this.searchInput.focus());
+ this.dropdownContent.on('scroll', throttle(this.setScrollFade, 250));
}
enableAutocomplete() {
+ this.setScrollFade();
+
// No need to enable anything if user is not logged in
if (!gon.current_user_id) {
return;
@@ -304,10 +348,6 @@ export default class SearchAutocomplete {
onSearchInputKeyUp(e) {
switch (e.keyCode) {
case KEYCODE.BACKSPACE:
- // when trying to remove the location badge
- if (this.lastTextLength === 0 && this.badgePresent()) {
- this.removeLocationBadge();
- }
// When removing the last character and no badge is present
if (this.lastTextLength === 1) {
this.disableAutocomplete();
@@ -368,37 +408,13 @@ export default class SearchAutocomplete {
}
}
- addLocationBadge(item) {
- var badgeText, category, value;
- category = item.category != null ? item.category + ": " : '';
- value = item.value != null ? item.value : '';
- badgeText = "" + category + value;
- this.locationBadgeEl.text(badgeText).show();
- return this.wrap.addClass('has-location-badge');
- }
-
- hasLocationBadge() {
- return this.wrap.is('.has-location-badge');
- }
-
restoreOriginalState() {
var i, input, inputs, len;
inputs = Object.keys(this.originalState);
for (i = 0, len = inputs.length; i < len; i += 1) {
input = inputs[i];
- this.getElement("#" + input).val(this.originalState[input]);
+ this.getElement('#' + input).val(this.originalState[input]);
}
- if (this.originalState._location === '') {
- return this.locationBadgeEl.hide();
- } else {
- return this.addLocationBadge({
- value: this.originalState._location,
- });
- }
- }
-
- badgePresent() {
- return this.locationBadgeEl.length;
}
resetSearchState() {
@@ -407,22 +423,11 @@ export default class SearchAutocomplete {
results = [];
for (i = 0, len = inputs.length; i < len; i += 1) {
input = inputs[i];
- // _location isnt a input
- if (input === '_location') {
- break;
- }
- results.push(this.getElement("#" + input).val(''));
+ results.push(this.getElement('#' + input).val(''));
}
return results;
}
- removeLocationBadge() {
- this.locationBadgeEl.hide();
- this.resetSearchState();
- this.wrap.removeClass('has-location-badge');
- return this.disableAutocomplete();
- }
-
disableAutocomplete() {
if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('show')) {
this.searchInput.addClass('disabled');
@@ -440,23 +445,57 @@ export default class SearchAutocomplete {
onClick(item, $el, e) {
if (window.location.pathname.indexOf(item.url) !== -1) {
if (!e.metaKey) e.preventDefault();
- if (!this.badgePresent) {
- if (item.category === 'Projects') {
- this.projectInputEl.val(item.id);
- this.addLocationBadge({
- value: 'This project',
- });
- }
- if (item.category === 'Groups') {
- this.groupInputEl.val(item.id);
- this.addLocationBadge({
- value: 'This group',
- });
- }
+ if (item.category === 'Projects') {
+ this.projectInputEl.val(item.id);
+ }
+ if (item.category === 'Groups') {
+ this.groupInputEl.val(item.id);
}
$el.removeClass('is-active');
this.disableAutocomplete();
return this.searchInput.val('').focus();
}
}
+
+ highlightFirstRow() {
+ this.searchInput.data('glDropdown').highlightRowAtIndex(null, 0);
+ }
+
+ getAvatar(item) {
+ if (!Object.hasOwnProperty.call(item, 'avatar_url')) {
+ return false;
+ }
+
+ const { label, id } = item;
+ const avatarUrl = item.avatar_url;
+ const avatar = avatarUrl
+ ? `<img class="search-item-avatar" src="${avatarUrl}" />`
+ : `<div class="s16 avatar identicon ${getIdenticonBackgroundClass(id)}">${getIdenticonTitle(
+ escape(label),
+ )}</div>`;
+
+ return avatar;
+ }
+
+ isScrolledUp() {
+ const el = this.dropdownContent[0];
+ const currentPosition = this.contentClientHeight + el.scrollTop;
+
+ return currentPosition < this.maxPosition;
+ }
+
+ initScrollFade() {
+ const el = this.dropdownContent[0];
+ this.scrollFadeInitialized = true;
+
+ this.contentClientHeight = el.clientHeight;
+ this.maxPosition = el.scrollHeight;
+ this.dropdownMenu.addClass('dropdown-content-faded-mask');
+ }
+
+ setScrollFade() {
+ this.initScrollFade();
+
+ this.dropdownMenu.toggleClass('fade-out', !this.isScrolledUp());
+ }
}
diff --git a/app/assets/javascripts/shared/milestones/form.js b/app/assets/javascripts/shared/milestones/form.js
index 060f374310c..8681a1776c6 100644
--- a/app/assets/javascripts/shared/milestones/form.js
+++ b/app/assets/javascripts/shared/milestones/form.js
@@ -8,10 +8,11 @@ export default (initGFM = true) => {
new DueDateSelectors(); // eslint-disable-line no-new
// eslint-disable-next-line no-new
new GLForm($('.milestone-form'), {
- emojis: initGFM,
+ emojis: true,
members: initGFM,
issues: initGFM,
mergeRequests: initGFM,
+ epics: initGFM,
milestones: initGFM,
labels: initGFM,
});
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js
index 78f7353eb0d..6b595764bc5 100644
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ b/app/assets/javascripts/shortcuts_navigation.js
@@ -20,6 +20,7 @@ export default class ShortcutsNavigation extends Shortcuts {
Mousetrap.bind('g s', () => findAndFollowLink('.shortcuts-snippets'));
Mousetrap.bind('g k', () => findAndFollowLink('.shortcuts-kubernetes'));
Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-environments'));
+ Mousetrap.bind('g l', () => findAndFollowLink('.shortcuts-metrics'));
Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue'));
this.enabledHelp.push('.hidden-shortcut.project');
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
index d22a1e1ac66..dd155c133ce 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
@@ -187,7 +187,7 @@ export default {
<template v-else-if="hasOneUser">
<a
:href="assigneeUrl(firstUser)"
- class="author_link bold"
+ class="author-link bold"
>
<img
:alt="assigneeAlt(firstUser)"
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index 33dd6c981b6..56d57f6aac8 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -120,7 +120,7 @@
>
<a
:href="participant.web_url"
- class="author_link"
+ class="author-link"
>
<user-avatar-image
:lazy="true"
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
index d335c3f55af..dc599e1b9fc 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
@@ -42,11 +42,14 @@ export default {
return this.timeEstimate - this.timeSpent;
},
timeRemainingPercent() {
- return `${Math.floor((this.timeSpent / this.timeEstimate) * 100)}%`;
+ return Math.floor((this.timeSpent / this.timeEstimate) * 100);
},
timeRemainingStatusClass() {
return this.timeEstimate >= this.timeSpent ? 'within_estimate' : 'over_estimate';
},
+ progressBarVariant() {
+ return this.timeRemainingPercent > 100 ? 'danger' : 'primary';
+ },
},
};
</script>
@@ -62,16 +65,10 @@ export default {
data-placement="top"
role="timeRemainingDisplay"
>
- <div
- :aria-valuenow="timeRemainingPercent"
- class="meter-container"
- >
- <div
- :style="{ width: timeRemainingPercent }"
- class="meter-fill"
- >
- </div>
- </div>
+ <gl-progress-bar
+ :value="timeRemainingPercent"
+ :variant="progressBarVariant"
+ />
<div class="compare-display-container">
<div class="compare-display float-left">
<span class="compare-label">
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
new file mode 100644
index 00000000000..ffaed9c7193
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
@@ -0,0 +1,98 @@
+<script>
+import { __ } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+
+import Icon from '~/vue_shared/components/icon.vue';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+
+const MARK_TEXT = __('Mark todo as done');
+const TODO_TEXT = __('Add todo');
+
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ Icon,
+ LoadingIcon,
+ },
+ props: {
+ issuableId: {
+ type: Number,
+ required: true,
+ },
+ issuableType: {
+ type: String,
+ required: true,
+ },
+ isTodo: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ isActionActive: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ collapsed: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ buttonClasses() {
+ return this.collapsed ?
+ 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state' :
+ 'btn btn-default btn-todo issuable-header-btn float-right';
+ },
+ buttonLabel() {
+ return this.isTodo ? MARK_TEXT : TODO_TEXT;
+ },
+ collapsedButtonIconClasses() {
+ return this.isTodo ? 'todo-undone' : '';
+ },
+ collapsedButtonIcon() {
+ return this.isTodo ? 'todo-done' : 'todo-add';
+ },
+ },
+ methods: {
+ handleButtonClick() {
+ this.$emit('toggleTodo');
+ },
+ },
+};
+</script>
+
+<template>
+ <button
+ v-tooltip
+ :class="buttonClasses"
+ :title="buttonLabel"
+ :aria-label="buttonLabel"
+ :data-issuable-id="issuableId"
+ :data-issuable-type="issuableType"
+ type="button"
+ data-container="body"
+ data-placement="left"
+ data-boundary="viewport"
+ @click="handleButtonClick"
+ >
+ <icon
+ v-show="collapsed"
+ :css-classes="collapsedButtonIconClasses"
+ :name="collapsedButtonIcon"
+ />
+ <span
+ v-show="!collapsed"
+ class="issuable-todo-inner"
+ >
+ {{ buttonLabel }}
+ </span>
+ <loading-icon
+ v-show="isActionActive"
+ :inline="true"
+ />
+ </button>
+</template>
diff --git a/app/assets/javascripts/terminal/index.js b/app/assets/javascripts/terminal/index.js
index 1a75e072c4e..49aeb377c74 100644
--- a/app/assets/javascripts/terminal/index.js
+++ b/app/assets/javascripts/terminal/index.js
@@ -1,9 +1,3 @@
-import 'vendor/xterm/encoding-indexes';
-import 'vendor/xterm/encoding';
-import Terminal from 'vendor/xterm/xterm';
-import 'vendor/xterm/fit';
-import './terminal';
+import Terminal from './terminal';
-window.Terminal = Terminal;
-
-export default () => new gl.Terminal({ selector: '#terminal' });
+export default () => new Terminal({ selector: '#terminal' });
diff --git a/app/assets/javascripts/terminal/terminal.js b/app/assets/javascripts/terminal/terminal.js
index caffcddf3b0..74c5bbe45a4 100644
--- a/app/assets/javascripts/terminal/terminal.js
+++ b/app/assets/javascripts/terminal/terminal.js
@@ -1,70 +1,71 @@
-/* global Terminal */
-
import $ from 'jquery';
+import { Terminal } from 'xterm';
+import * as fit from 'xterm/lib/addons/fit/fit';
-(() => {
- class GLTerminal {
-
- constructor(options) {
- this.options = options || {};
-
- if (!Object.prototype.hasOwnProperty.call(this.options, 'cursorBlink')) {
- this.options.cursorBlink = true;
- }
+export default class GLTerminal {
+ constructor(options = {}) {
+ this.options = Object.assign({}, {
+ cursorBlink: true,
+ screenKeys: true,
+ }, options);
- if (!Object.prototype.hasOwnProperty.call(this.options, 'screenKeys')) {
- this.options.screenKeys = true;
- }
+ this.container = document.querySelector(options.selector);
- this.container = document.querySelector(options.selector);
+ this.setSocketUrl();
+ this.createTerminal();
- this.setSocketUrl();
- this.createTerminal();
- $(window).off('resize.terminal').on('resize.terminal', () => {
+ $(window)
+ .off('resize.terminal')
+ .on('resize.terminal', () => {
this.terminal.fit();
});
- }
+ }
+
+ setSocketUrl() {
+ const { protocol, hostname, port } = window.location;
+ const wsProtocol = protocol === 'https:' ? 'wss://' : 'ws://';
+ const path = this.container.dataset.projectPath;
- setSocketUrl() {
- const { protocol, hostname, port } = window.location;
- const wsProtocol = protocol === 'https:' ? 'wss://' : 'ws://';
- const path = this.container.dataset.projectPath;
+ this.socketUrl = `${wsProtocol}${hostname}:${port}${path}`;
+ }
- this.socketUrl = `${wsProtocol}${hostname}:${port}${path}`;
- }
+ createTerminal() {
+ Terminal.applyAddon(fit);
- createTerminal() {
- this.terminal = new Terminal(this.options);
- this.socket = new WebSocket(this.socketUrl, ['terminal.gitlab.com']);
- this.socket.binaryType = 'arraybuffer';
+ this.terminal = new Terminal(this.options);
- this.terminal.open(this.container);
- this.socket.onopen = () => { this.runTerminal(); };
- this.socket.onerror = () => { this.handleSocketFailure(); };
- }
+ this.socket = new WebSocket(this.socketUrl, ['terminal.gitlab.com']);
+ this.socket.binaryType = 'arraybuffer';
- runTerminal() {
- const decoder = new TextDecoder('utf-8');
- const encoder = new TextEncoder('utf-8');
+ this.terminal.open(this.container);
+ this.terminal.fit();
+ this.terminal.focus();
- this.terminal.on('data', (data) => {
- this.socket.send(encoder.encode(data));
- });
+ this.socket.onopen = () => {
+ this.runTerminal();
+ };
+ this.socket.onerror = () => {
+ this.handleSocketFailure();
+ };
+ }
- this.socket.addEventListener('message', (ev) => {
- this.terminal.write(decoder.decode(ev.data));
- });
+ runTerminal() {
+ const decoder = new TextDecoder('utf-8');
+ const encoder = new TextEncoder('utf-8');
- this.isTerminalInitialized = true;
- this.terminal.fit();
- }
+ this.terminal.on('data', data => {
+ this.socket.send(encoder.encode(data));
+ });
- handleSocketFailure() {
- this.terminal.write('\r\nConnection failure');
- }
+ this.socket.addEventListener('message', ev => {
+ this.terminal.write(decoder.decode(ev.data));
+ });
+ this.isTerminalInitialized = true;
+ this.terminal.fit();
}
- window.gl = window.gl || {};
- gl.Terminal = GLTerminal;
-})();
+ handleSocketFailure() {
+ this.terminal.write('\r\nConnection failure');
+ }
+}
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index e3d7645040d..e19bbbacf4d 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -206,8 +206,8 @@ function UsersSelect(currentUser, els, options = {}) {
return $collapsedSidebar.html(collapsedAssigneeTemplate(user));
});
};
- collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <a class="author_link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>');
- assigneeTemplate = _.template('<% if (username) { %> <a class="author_link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>');
+ collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <a class="author-link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>');
+ assigneeTemplate = _.template('<% if (username) { %> <a class="author-link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>');
return $dropdown.glDropdown({
showMenuAbove: showMenuAbove,
data: function(term, callback) {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
index c44419d24e6..21f21232596 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
@@ -1,4 +1,5 @@
<script>
+import Icon from '~/vue_shared/components/icon.vue';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import tooltip from '../../vue_shared/directives/tooltip';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
@@ -14,6 +15,7 @@ export default {
LoadingButton,
MemoryUsage,
StatusIcon,
+ Icon,
},
directives: {
tooltip,
@@ -77,67 +79,62 @@ export default {
</script>
<template>
- <div class="mr-widget-heading deploy-heading">
+ <div class="mr-widget-heading deploy-heading append-bottom-default">
<div class="ci-widget media">
- <div class="ci-status-icon ci-status-icon-success">
- <span class="js-icon-link icon-link">
- <status-icon status="success" />
- </span>
- </div>
<div class="media-body">
<div class="deploy-body">
- <template v-if="hasDeploymentMeta">
- <span>
- Deployed to
- </span>
- <a
- :href="deployment.url"
- target="_blank"
- rel="noopener noreferrer nofollow"
- class="deploy-link js-deploy-meta"
+ <div class="deployment-info">
+ <template v-if="hasDeploymentMeta">
+ <span>
+ Deployed to
+ </span>
+ <a
+ :href="deployment.url"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ class="deploy-link js-deploy-meta"
+ >
+ {{ deployment.name }}
+ </a>
+ </template>
+ <span
+ v-tooltip
+ v-if="hasDeploymentTime"
+ :title="deployment.deployed_at_formatted"
+ class="js-deploy-time"
>
- {{ deployment.name }}
- </a>
- </template>
- <template v-if="hasExternalUrls">
- <span>
- on
+ {{ deployTimeago }}
</span>
+ <memory-usage
+ v-if="hasMetrics"
+ :metrics-url="deployment.metrics_url"
+ :metrics-monitoring-url="deployment.metrics_monitoring_url"
+ />
+ </div>
+ <div>
<a
+ v-if="hasExternalUrls"
:href="deployment.external_url"
target="_blank"
rel="noopener noreferrer nofollow"
- class="deploy-link js-deploy-url"
+ class="deploy-link js-deploy-url btn btn-default btn-sm inline"
>
- {{ deployment.external_url_formatted }}
- <i
- class="fa fa-external-link"
- aria-hidden="true"
- >
- </i>
+ <span>
+ View app
+ <icon name="external-link" />
+ </span>
</a>
- </template>
- <span
- v-tooltip
- v-if="hasDeploymentTime"
- :title="deployment.deployed_at_formatted"
- class="js-deploy-time"
- >
- {{ deployTimeago }}
- </span>
- <loading-button
- v-if="deployment.stop_url"
- :loading="isStopping"
- container-class="btn btn-default btn-sm prepend-left-default"
- label="Stop environment"
- @click="stopEnvironment"
- />
+ <loading-button
+ v-if="deployment.stop_url"
+ :loading="isStopping"
+ container-class="btn btn-default btn-sm inline prepend-left-4"
+ title="Stop environment"
+ @click="stopEnvironment"
+ >
+ <icon name="stop" />
+ </loading-button>
+ </div>
</div>
- <memory-usage
- v-if="hasMetrics"
- :metrics-url="deployment.metrics_url"
- :metrics-monitoring-url="deployment.metrics_monitoring_url"
- />
</div>
</div>
</div>
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 3ce9d8dc26a..a4c2289c590 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
@@ -1,8 +1,8 @@
<script>
import tooltip from '~/vue_shared/directives/tooltip';
import { n__ } from '~/locale';
-import { webIDEUrl } from '~/lib/utils/url_utility';
-import icon from '~/vue_shared/components/icon.vue';
+import { mergeUrlParams, webIDEUrl } from '~/lib/utils/url_utility';
+import Icon from '~/vue_shared/components/icon.vue';
import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
export default {
@@ -11,7 +11,7 @@ export default {
tooltip,
},
components: {
- icon,
+ Icon,
clipboardButton,
},
props: {
@@ -43,7 +43,10 @@ export default {
return this.isBranchTitleLong(this.mr.targetBranch);
},
webIdePath() {
- return webIDEUrl(this.mr.statusPath.replace('.json', ''));
+ return mergeUrlParams({
+ target_project: this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath ?
+ this.mr.targetProjectFullPath : '',
+ }, webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`));
},
},
methods: {
@@ -54,104 +57,114 @@ export default {
};
</script>
<template>
- <div class="mr-source-target">
- <div class="normal">
- <strong>
- {{ s__("mrWidget|Request to merge") }}
- <span
- :class="{ 'label-truncated': isSourceBranchLong }"
- :title="isSourceBranchLong ? mr.sourceBranch : ''"
- :v-tooltip="isSourceBranchLong"
- class="label-branch js-source-branch"
- data-placement="bottom"
- v-html="mr.sourceBranchLink"
- >
- </span>
+ <div class="mr-source-target append-bottom-default">
+ <div class="git-merge-icon-container append-right-default">
+ <icon name="git-merge" />
+ </div>
+ <div class="git-merge-container d-flex">
+ <div class="normal">
+ <strong>
+ {{ s__("mrWidget|Request to merge") }}
+ <span
+ :class="{ 'label-truncated': isSourceBranchLong }"
+ :title="isSourceBranchLong ? mr.sourceBranch : ''"
+ :v-tooltip="isSourceBranchLong"
+ class="label-branch js-source-branch"
+ data-placement="bottom"
+ v-html="mr.sourceBranchLink"
+ >
+ </span>
- <clipboard-button
- :text="branchNameClipboardData"
- :title="__('Copy branch name to clipboard')"
- css-class="btn-default btn-transparent btn-clipboard"
- />
+ <clipboard-button
+ :text="branchNameClipboardData"
+ :title="__('Copy branch name to clipboard')"
+ css-class="btn-default btn-transparent btn-clipboard"
+ />
- {{ s__("mrWidget|into") }}
+ {{ s__("mrWidget|into") }}
- <span
- :v-tooltip="isTargetBranchLong"
- :class="{ 'label-truncatedtooltip': isTargetBranchLong }"
- :title="isTargetBranchLong ? mr.targetBranch : ''"
- class="label-branch"
- data-placement="bottom"
- >
- <a
- :href="mr.targetBranchTreePath"
- class="js-target-branch"
+ <span
+ :v-tooltip="isTargetBranchLong"
+ :class="{ 'label-truncatedtooltip': isTargetBranchLong }"
+ :title="isTargetBranchLong ? mr.targetBranch : ''"
+ class="label-branch"
+ data-placement="bottom"
>
- {{ mr.targetBranch }}
- </a>
- </span>
- </strong>
- <span
- v-if="shouldShowCommitsBehindText"
- class="diverged-commits-count"
- >
- (<a :href="mr.targetBranchPath">{{ commitsText }}</a>)
- </span>
- </div>
+ <a
+ :href="mr.targetBranchTreePath"
+ class="js-target-branch"
+ >
+ {{ mr.targetBranch }}
+ </a>
+ </span>
+ </strong>
+ <div
+ v-if="shouldShowCommitsBehindText"
+ class="diverged-commits-count"
+ >
+ <span class="monospace">{{ mr.sourceBranch }}</span>
+ is {{ commitsText }}
+ <span class="monospace">{{ mr.targetBranch }}</span>
+ </div>
+ </div>
- <div v-if="mr.isOpen">
- <a
- v-if="!mr.sourceBranchRemoved"
- :href="webIdePath"
- class="btn btn-sm btn-default inline js-web-ide"
- >
- {{ s__("mrWidget|Web IDE") }}
- </a>
- <button
- :disabled="mr.sourceBranchRemoved"
- data-target="#modal_merge_info"
- data-toggle="modal"
- class="btn btn-sm btn-default inline js-check-out-branch"
- type="button"
+ <div
+ v-if="mr.isOpen"
+ class="branch-actions"
>
- {{ s__("mrWidget|Check out branch") }}
- </button>
- <span class="dropdown prepend-left-10">
+ <a
+ v-if="!mr.sourceBranchRemoved"
+ :href="webIdePath"
+ class="btn btn-default inline js-web-ide d-none d-md-inline-block"
+ >
+ {{ s__("mrWidget|Open in Web IDE") }}
+ </a>
<button
+ :disabled="mr.sourceBranchRemoved"
+ data-target="#modal_merge_info"
+ data-toggle="modal"
+ class="btn btn-default inline js-check-out-branch"
type="button"
- class="btn btn-sm inline dropdown-toggle"
- data-toggle="dropdown"
- aria-label="Download as"
- aria-haspopup="true"
- aria-expanded="false"
>
- <icon name="download" />
- <i
- class="fa fa-caret-down"
- aria-hidden="true">
- </i>
+ {{ s__("mrWidget|Check out branch") }}
</button>
- <ul class="dropdown-menu dropdown-menu-right">
- <li>
- <a
- :href="mr.emailPatchesPath"
- class="js-download-email-patches"
- download
- >
- {{ s__("mrWidget|Email patches") }}
- </a>
- </li>
- <li>
- <a
- :href="mr.plainDiffPath"
- class="js-download-plain-diff"
- download
- >
- {{ s__("mrWidget|Plain diff") }}
- </a>
- </li>
- </ul>
- </span>
+ <span class="dropdown prepend-left-10">
+ <button
+ type="button"
+ class="btn inline dropdown-toggle"
+ data-toggle="dropdown"
+ aria-label="Download as"
+ aria-haspopup="true"
+ aria-expanded="false"
+ >
+ <icon name="download" />
+ <i
+ class="fa fa-caret-down"
+ aria-hidden="true">
+ </i>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-right">
+ <li>
+ <a
+ :href="mr.emailPatchesPath"
+ class="js-download-email-patches"
+ download
+ >
+ {{ s__("mrWidget|Email patches") }}
+ </a>
+ </li>
+ <li>
+ <a
+ :href="mr.plainDiffPath"
+ class="js-download-plain-diff"
+ download
+ >
+ {{ s__("mrWidget|Plain diff") }}
+ </a>
+ </li>
+ </ul>
+ </span>
+ </div>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
index 2f0b5e12c12..4a3fd01fa39 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
@@ -26,6 +26,10 @@ export default {
type: String,
required: false,
},
+ sourceBranchLink: {
+ type: String,
+ required: false,
+ },
},
computed: {
hasPipeline() {
@@ -54,12 +58,18 @@ export default {
<template>
<div
v-if="hasPipeline || hasCIError"
- class="mr-widget-heading"
+ class="mr-widget-heading append-bottom-default"
>
<div class="ci-widget media">
<template v-if="hasCIError">
- <div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10">
- <icon name="status_failed" />
+ <div
+ class="add-border ci-status-icon ci-status-icon-failed ci-error
+ js-ci-error append-right-default"
+ >
+ <icon
+ :size="32"
+ name="status_failed_borderless"
+ />
</div>
<div class="media-body">
Could not connect to the CI server. Please check your settings and try again
@@ -68,50 +78,66 @@ export default {
<template v-else-if="hasPipeline">
<a
:href="status.details_path"
- class="append-right-10"
+ class="align-self-start append-right-default"
>
- <ci-icon :status="status" />
+ <ci-icon
+ :status="status"
+ :size="32"
+ :borderless="true"
+ class="add-border"
+ />
</a>
+ <div class="ci-widget-container d-flex">
+ <div class="ci-widget-content">
+ <div class="media-body">
+ <div class="font-weight-bold">
+ Pipeline
+ <a
+ :href="pipeline.path"
+ class="pipeline-id font-weight-normal pipeline-number"
+ >#{{ pipeline.id }}</a>
- <div class="media-body">
- Pipeline
- <a
- :href="pipeline.path"
- class="pipeline-id"
- >
- #{{ pipeline.id }}
- </a>
-
- {{ pipeline.details.status.label }}
+ {{ pipeline.details.status.label }}
- <template v-if="hasCommitInfo">
- for
-
- <a
- :href="pipeline.commit.commit_path"
- class="commit-sha js-commit-link"
- >
- {{ pipeline.commit.short_id }}</a>.
- </template>
-
- <span class="mr-widget-pipeline-graph">
- <span
- v-if="hasStages"
- class="stage-cell"
- >
+ <template v-if="hasCommitInfo">
+ for
+ <a
+ :href="pipeline.commit.commit_path"
+ class="commit-sha js-commit-link font-weight-normal"
+ >
+ {{ pipeline.commit.short_id }}</a>
+ on
+ <span
+ class="label-branch"
+ v-html="sourceBranchLink"
+ >
+ </span>
+ </template>
+ </div>
<div
- v-for="(stage, i) in pipeline.details.stages"
- :key="i"
- class="stage-container dropdown js-mini-pipeline-graph"
+ v-if="pipeline.coverage"
+ class="coverage"
>
- <pipeline-stage :stage="stage" />
+ Coverage {{ pipeline.coverage }}%
</div>
+ </div>
+ </div>
+ <div>
+ <span class="mr-widget-pipeline-graph">
+ <span
+ v-if="hasStages"
+ class="stage-cell"
+ >
+ <div
+ v-for="(stage, i) in pipeline.details.stages"
+ :key="i"
+ class="stage-container dropdown js-mini-pipeline-graph mr-widget-pipeline-stages"
+ >
+ <pipeline-stage :stage="stage" />
+ </div>
+ </span>
</span>
- </span>
-
- <template v-if="pipeline.coverage">
- Coverage {{ pipeline.coverage }}%
- </template>
+ </div>
</div>
</template>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
index 1fdc3218671..9aff95dcfec 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
@@ -32,7 +32,7 @@
};
</script>
<template>
- <div class="space-children flex-container-block append-right-10">
+ <div class="space-children d-flex append-right-10 widget-status-icon">
<div
v-if="isLoading"
class="mr-widget-icon"
@@ -43,6 +43,7 @@
<ci-icon
v-else
:status="statusObj"
+ :size="24"
/>
<button
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
index 0d9a560c88e..97f4196b94d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
@@ -82,7 +82,7 @@
<div class="mr-widget-body media">
<status-icon status="success" />
<div class="media-body">
- <h4 class="flex-container-block">
+ <h4 class="d-flex align-items-start">
<span class="append-right-10">
{{ s__("mrWidget|Set by") }}
<mr-widget-author :author="mr.setToMWPSBy" />
@@ -119,7 +119,7 @@
</p>
<p
v-else
- class="flex-container-block"
+ class="d-flex align-items-start"
>
<span class="append-right-10">
{{ s__("mrWidget|The source branch will not be removed") }}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index fe777a07189..a5ca7b719a1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -233,7 +233,7 @@ export default {
<status-icon :status="iconClass" />
<div class="media-body">
<div class="mr-widget-body-controls media space-children">
- <span class="btn-group append-bottom-5">
+ <span class="btn-group">
<button
:disabled="isMergeButtonDisabled"
:class="mergeButtonClass"
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 09477da40b5..80593d1f34a 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
@@ -36,6 +36,7 @@ import {
notify,
SourceBranchRemovalStatus,
} from './dependencies';
+import GroupedTestReportsApp from '../reports/components/grouped_test_reports_app.vue';
import { setFaviconOverlay } from '../lib/utils/common_utils';
export default {
@@ -68,6 +69,7 @@ export default {
'mr-widget-auto-merge-failed': AutoMergeFailed,
'mr-widget-rebase': RebaseState,
SourceBranchRemovalStatus,
+ GroupedTestReportsApp,
},
props: {
mrData: {
@@ -252,41 +254,48 @@ export default {
:pipeline="mr.pipeline"
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
+ :source-branch-link="mr.sourceBranchLink"
/>
<deployment
v-for="deployment in mr.deployments"
:key="deployment.id"
:deployment="deployment"
/>
- <div class="mr-widget-section">
- <component
- :is="componentName"
- :mr="mr"
- :service="service"
+ <div class="mr-section-container">
+ <grouped-test-reports-app
+ v-if="mr.testResultsPath"
+ :endpoint="mr.testResultsPath"
/>
+ <div class="mr-widget-section">
+ <component
+ :is="componentName"
+ :mr="mr"
+ :service="service"
+ />
- <section
- v-if="mr.allowCollaboration"
- class="mr-info-list mr-links"
- >
- {{ s__("mrWidget|Allows commits from members who can merge to the target branch") }}
- </section>
+ <section
+ v-if="mr.allowCollaboration"
+ class="mr-info-list mr-links"
+ >
+ {{ s__("mrWidget|Allows commits from members who can merge to the target branch") }}
+ </section>
- <mr-widget-related-links
- v-if="shouldRenderRelatedLinks"
- :state="mr.state"
- :related-links="mr.relatedLinks"
- />
+ <mr-widget-related-links
+ v-if="shouldRenderRelatedLinks"
+ :state="mr.state"
+ :related-links="mr.relatedLinks"
+ />
- <source-branch-removal-status
- v-if="shouldRenderSourceBranchRemovalStatus"
- />
- </div>
- <div
- v-if="shouldRenderMergeHelp"
- class="mr-widget-footer"
- >
- <mr-widget-merge-help />
+ <source-branch-removal-status
+ v-if="shouldRenderSourceBranchRemovalStatus"
+ />
+ </div>
+ <div
+ v-if="shouldRenderMergeHelp"
+ class="mr-widget-footer"
+ >
+ <mr-widget-merge-help />
+ </div>
</div>
</div>
</template>
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 c881cd496d1..672e5280b5e 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
@@ -16,10 +16,11 @@ export default class MergeRequestStore {
const pipelineStatus = data.pipeline ? data.pipeline.details.status : null;
this.squash = data.squash;
- this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath ||
- data.squash_before_merge_help_path;
+ this.squashBeforeMergeHelpPath =
+ this.squashBeforeMergeHelpPath || data.squash_before_merge_help_path;
this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true;
+ this.iid = data.iid;
this.title = data.title;
this.targetBranch = data.target_branch;
this.sourceBranch = data.source_branch;
@@ -85,6 +86,8 @@ export default class MergeRequestStore {
this.isMergeAllowed = data.mergeable || false;
this.mergeOngoing = data.merge_ongoing;
this.allowCollaboration = data.allow_collaboration;
+ this.targetProjectFullPath = data.target_project_full_path;
+ this.sourceProjectFullPath = data.source_project_full_path;
// Cherry-pick and Revert actions related
this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
@@ -97,13 +100,16 @@ export default class MergeRequestStore {
this.hasCI = data.has_ci;
this.ciStatus = data.ci_status;
this.isPipelineFailed = this.ciStatus === 'failed' || this.ciStatus === 'canceled';
- this.isPipelinePassing = this.ciStatus === 'success' || this.ciStatus === 'success_with_warnings';
+ this.isPipelinePassing =
+ this.ciStatus === 'success' || this.ciStatus === 'success_with_warnings';
this.isPipelineSkipped = this.ciStatus === 'skipped';
this.pipelineDetailedStatus = pipelineStatus;
this.isPipelineActive = data.pipeline ? data.pipeline.active : false;
this.isPipelineBlocked = pipelineStatus ? pipelineStatus.group === 'manual' : false;
this.ciStatusFaviconPath = pipelineStatus ? pipelineStatus.favicon : null;
+ this.testResultsPath = data.test_reports_path;
+
this.setState(data);
}
diff --git a/app/assets/javascripts/vue_shared/components/bar_chart.vue b/app/assets/javascripts/vue_shared/components/bar_chart.vue
new file mode 100644
index 00000000000..3ced4eb691a
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/bar_chart.vue
@@ -0,0 +1,391 @@
+<script>
+import * as d3 from 'd3';
+import tooltip from '../directives/tooltip';
+import Icon from './icon.vue';
+import SvgGradient from './svg_gradient.vue';
+import {
+ GRADIENT_COLORS,
+ GRADIENT_OPACITY,
+ INVERSE_GRADIENT_COLORS,
+ INVERSE_GRADIENT_OPACITY,
+} from './bar_chart_constants';
+
+/**
+ * Renders a bar chart that can be dragged(scrolled) when the number
+ * of elements to renders surpasses that of the available viewport space
+ * while keeping even padding and a width of 24px (customizable)
+ *
+ * It can render data with the following format:
+ * graphData: [{
+ * name: 'element' // x domain data
+ * value: 1 // y domain data
+ * }]
+ *
+ * Used in:
+ * - Contribution analytics - all of the rows describing pushes, merge requests and issues
+ */
+
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ Icon,
+ SvgGradient,
+ },
+ props: {
+ graphData: {
+ type: Array,
+ required: true,
+ },
+ barWidth: {
+ type: Number,
+ required: false,
+ default: 24,
+ },
+ yAxisLabel: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ minX: -40,
+ minY: 0,
+ vbWidth: 0,
+ vbHeight: 0,
+ vpWidth: 0,
+ vpHeight: 350,
+ preserveAspectRatioType: 'xMidYMid meet',
+ containerMargin: {
+ leftRight: 30,
+ },
+ viewBoxMargin: {
+ topBottom: 150,
+ },
+ panX: 0,
+ xScale: {},
+ yScale: {},
+ zoom: {},
+ bars: {},
+ xGraphRange: 0,
+ isLoading: true,
+ paddingThreshold: 50,
+ showScrollIndicator: false,
+ showLeftScrollIndicator: false,
+ isGrabbed: false,
+ isPanAvailable: false,
+ gradientColors: GRADIENT_COLORS,
+ gradientOpacity: GRADIENT_OPACITY,
+ inverseGradientColors: INVERSE_GRADIENT_COLORS,
+ inverseGradientOpacity: INVERSE_GRADIENT_OPACITY,
+ maxTextWidth: 72,
+ rectYAxisLabelDims: {},
+ xAxisTextElements: {},
+ yAxisRectTransformPadding: 20,
+ yAxisTextTransformPadding: 10,
+ yAxisTextRotation: 90,
+ };
+ },
+ computed: {
+ svgViewBox() {
+ return `${this.minX} ${this.minY} ${this.vbWidth} ${this.vbHeight}`;
+ },
+ xAxisLocation() {
+ return `translate(${this.panX}, ${this.vbHeight})`;
+ },
+ barTranslationTransform() {
+ return `translate(${this.panX}, 0)`;
+ },
+ scrollIndicatorTransform() {
+ return `translate(${this.vbWidth - 80}, 0)`;
+ },
+ activateGrabCursor() {
+ return {
+ 'svg-graph-container-with-grab': this.isPanAvailable,
+ 'svg-graph-container-grabbed': this.isPanAvailable && this.isGrabbed,
+ };
+ },
+ yAxisLabelRectTransform() {
+ const rectWidth =
+ this.rectYAxisLabelDims.height != null ? this.rectYAxisLabelDims.height / 2 : 0;
+ const yCoord = this.vbHeight / 2 - rectWidth;
+
+ return `translate(${this.minX - this.yAxisRectTransformPadding}, ${yCoord})`;
+ },
+ yAxisLabelTextTransform() {
+ const rectWidth =
+ this.rectYAxisLabelDims.height != null ? this.rectYAxisLabelDims.height / 2 : 0;
+ const yCoord = this.vbHeight / 2 + rectWidth - 5;
+
+ return `translate(${this.minX + this.yAxisTextTransformPadding}, ${yCoord}) rotate(-${this.yAxisTextRotation})`;
+ },
+ },
+ mounted() {
+ if (!this.allValuesEmpty) {
+ this.draw();
+ }
+ },
+ methods: {
+ draw() {
+ // update viewport
+ this.vpWidth = this.$refs.svgContainer.clientWidth - this.containerMargin.leftRight;
+ // update viewbox
+ this.vbWidth = this.vpWidth;
+ this.vbHeight = this.vpHeight - this.viewBoxMargin.topBottom;
+ let padding = 0;
+ if (this.graphData.length * this.barWidth > this.vbWidth) {
+ this.xGraphRange = this.graphData.length * this.barWidth;
+ padding = this.calculatePadding(this.barWidth);
+ this.showScrollIndicator = true;
+ this.isPanAvailable = true;
+ } else {
+ this.xGraphRange = this.vbWidth - Math.abs(this.minX);
+ }
+
+ this.xScale = d3
+ .scaleBand()
+ .range([0, this.xGraphRange])
+ .round(true)
+ .paddingInner(padding);
+ this.yScale = d3.scaleLinear().rangeRound([this.vbHeight, 0]);
+
+ this.xScale.domain(this.graphData.map(d => d.name));
+ this.yScale.domain([0, d3.max(this.graphData.map(d => d.value))]);
+
+ // Zoom/Panning Function
+ this.zoom = d3
+ .zoom()
+ .translateExtent([[0, 0], [this.xGraphRange, this.vbHeight]])
+ .on('zoom', this.panGraph)
+ .on('end', this.removeGrabStyling);
+
+ const xAxis = d3.axisBottom().scale(this.xScale);
+
+ const yAxis = d3
+ .axisLeft()
+ .scale(this.yScale)
+ .ticks(4);
+
+ const renderedXAxis = d3
+ .select(this.$refs.baseSvg)
+ .select('.x-axis')
+ .call(xAxis);
+
+ this.xAxisTextElements = this.$refs.xAxis.querySelectorAll('text');
+
+ renderedXAxis.select('.domain').remove();
+
+ renderedXAxis
+ .selectAll('text')
+ .style('text-anchor', 'end')
+ .attr('dx', '-.3em')
+ .attr('dy', '-.95em')
+ .attr('class', 'tick-text')
+ .attr('transform', 'rotate(-90)');
+
+ renderedXAxis.selectAll('line').remove();
+
+ const { maxTextWidth } = this;
+ renderedXAxis.selectAll('text').each(function formatText() {
+ const axisText = d3.select(this);
+ let textLength = axisText.node().getComputedTextLength();
+ let textContent = axisText.text();
+ while (textLength > maxTextWidth && textContent.length > 0) {
+ textContent = textContent.slice(0, -1);
+ axisText.text(`${textContent}...`);
+ textLength = axisText.node().getComputedTextLength();
+ }
+ });
+
+ const width = this.vbWidth;
+
+ const renderedYAxis = d3
+ .select(this.$refs.baseSvg)
+ .select('.y-axis')
+ .call(yAxis);
+
+ renderedYAxis.selectAll('.tick').each(function createTickLines(d, i) {
+ if (i > 0) {
+ d3
+ .select(this)
+ .select('line')
+ .attr('x2', width)
+ .attr('class', 'axis-tick');
+ }
+ });
+
+ // Add the panning capabilities
+ if (this.isPanAvailable) {
+ d3
+ .select(this.$refs.baseSvg)
+ .call(this.zoom)
+ .on('wheel.zoom', null); // This disables the pan of the graph with the scroll of the mouse wheel
+ }
+
+ this.isLoading = false;
+ // Update the yAxisLabel coordinates
+ const labelDims = this.$refs.yAxisLabel.getBBox();
+ this.rectYAxisLabelDims = {
+ height: labelDims.width + 10,
+ };
+ },
+ panGraph() {
+ const allowedRightScroll = this.xGraphRange - this.vbWidth - this.paddingThreshold;
+ const graphMaxPan = Math.abs(d3.event.transform.x) < allowedRightScroll;
+ this.isGrabbed = true;
+ this.panX = d3.event.transform.x;
+
+ if (d3.event.transform.x === 0) {
+ this.showLeftScrollIndicator = false;
+ } else {
+ this.showLeftScrollIndicator = true;
+ this.showScrollIndicator = true;
+ }
+
+ if (!graphMaxPan) {
+ this.panX = -1 * (this.xGraphRange - this.vbWidth + this.paddingThreshold);
+ this.showScrollIndicator = false;
+ }
+ },
+ setTooltipTitle(data) {
+ return data !== null ? `${data.name}: ${data.value}` : '';
+ },
+ calculatePadding(desiredBarWidth) {
+ const widthWithMargin = this.vbWidth - Math.abs(this.minX);
+ const dividend = widthWithMargin - this.graphData.length * desiredBarWidth;
+ const divisor = widthWithMargin - desiredBarWidth;
+
+ return dividend / divisor;
+ },
+ removeGrabStyling() {
+ this.isGrabbed = false;
+ },
+ barHoveredIn(index) {
+ this.xAxisTextElements[index].classList.add('x-axis-text');
+ },
+ barHoveredOut(index) {
+ this.xAxisTextElements[index].classList.remove('x-axis-text');
+ },
+ },
+};
+</script>
+<template>
+ <div
+ ref="svgContainer"
+ :class="activateGrabCursor"
+ class="svg-graph-container"
+ >
+ <svg
+ ref="baseSvg"
+ :width="vpWidth"
+ :height="vpHeight"
+ :viewBox="svgViewBox"
+ :preserveAspectRatio="preserveAspectRatioType">
+ <g
+ ref="xAxis"
+ :transform="xAxisLocation"
+ class="x-axis"
+ />
+ <g v-if="!isLoading">
+ <template
+ v-for="(data, index) in graphData">
+ <rect
+ v-tooltip
+ :key="index"
+ :width="xScale.bandwidth()"
+ :x="xScale(data.name)"
+ :y="yScale(data.value)"
+ :height="vbHeight - yScale(data.value)"
+ :transform="barTranslationTransform"
+ :title="setTooltipTitle(data)"
+ class="bar-rect"
+ data-placement="top"
+ @mouseover="barHoveredIn(index)"
+ @mouseout="barHoveredOut(index)"
+ />
+ </template>
+ </g>
+ <rect
+ :height="vbHeight + 100"
+ transform="translate(-100, -5)"
+ width="100"
+ fill="#fff"
+ />
+ <g class="y-axis-label">
+ <line
+ :x1="0"
+ :x2="0"
+ :y1="0"
+ :y2="vbHeight"
+ transform="translate(-35, 0)"
+ stroke="black"
+ />
+ <!--Get text length and change the height of this rect accordingly-->
+ <rect
+ :height="rectYAxisLabelDims.height"
+ :transform="yAxisLabelRectTransform"
+ :width="30"
+ fill="#fff"
+ />
+ <text
+ ref="yAxisLabel"
+ :transform="yAxisLabelTextTransform"
+ >
+ {{ yAxisLabel }}
+ </text>
+ </g>
+ <g
+ class="y-axis"
+ />
+ <g v-if="showScrollIndicator">
+ <rect
+ :height="vbHeight + 100"
+ :transform="`translate(${vpWidth - 60}, -5)`"
+ width="40"
+ fill="#fff"
+ />
+ <icon
+ :x="vpWidth - 50"
+ :y="vbHeight / 2"
+ :width="14"
+ :height="14"
+ name="chevron-right"
+ class="animate-flicker"
+ />
+ </g>
+ <!--The line that shows up when the data elements surpass the available width -->
+ <g
+ v-if="showScrollIndicator"
+ :transform="scrollIndicatorTransform">
+ <rect
+ :height="vbHeight"
+ x="0"
+ y="0"
+ width="20"
+ fill="url(#shadow-gradient)"
+ />
+ </g>
+ <!--Left scroll indicator-->
+ <g
+ v-if="showLeftScrollIndicator"
+ transform="translate(0, 0)">
+ <rect
+ :height="vbHeight"
+ x="0"
+ y="0"
+ width="20"
+ fill="url(#left-shadow-gradient)"
+ />
+ </g>
+ <svg-gradient
+ :colors="gradientColors"
+ :opacity="gradientOpacity"
+ identifier-name="shadow-gradient"/>
+ <svg-gradient
+ :colors="inverseGradientColors"
+ :opacity="inverseGradientOpacity"
+ identifier-name="left-shadow-gradient"/>
+ </svg>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/bar_chart_constants.js b/app/assets/javascripts/vue_shared/components/bar_chart_constants.js
new file mode 100644
index 00000000000..6957b112da6
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/bar_chart_constants.js
@@ -0,0 +1,4 @@
+export const GRADIENT_COLORS = ['#000', '#a7a7a7'];
+export const GRADIENT_OPACITY = ['0', '0.4'];
+export const INVERSE_GRADIENT_COLORS = ['#a7a7a7', '#000'];
+export const INVERSE_GRADIENT_OPACITY = ['0.4', '0'];
diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
index dc5760bce28..945a33d9622 100644
--- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue
+++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
@@ -13,17 +13,29 @@
* />
*/
import tooltip from '../directives/tooltip';
+import Icon from '../components/icon.vue';
export default {
name: 'ClipboardButton',
+
directives: {
tooltip,
},
+
+ components: {
+ Icon,
+ },
+
props: {
text: {
type: String,
required: true,
},
+ gfm: {
+ type: String,
+ required: false,
+ default: null,
+ },
title: {
type: String,
required: true,
@@ -44,6 +56,14 @@ export default {
default: 'btn-default',
},
},
+ computed: {
+ clipboardText() {
+ if (this.gfm !== null) {
+ return JSON.stringify({ text: this.text, gfm: this.gfm });
+ }
+ return this.text;
+ },
+ },
};
</script>
@@ -52,16 +72,12 @@ export default {
v-tooltip
:class="cssClass"
:title="title"
- :data-clipboard-text="text"
+ :data-clipboard-text="clipboardText"
:data-container="tooltipContainer"
:data-placement="tooltipPlacement"
type="button"
class="btn"
>
- <i
- aria-hidden="true"
- class="fa fa-clipboard"
- >
- </i>
+ <icon name="duplicate" />
</button>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/code_block.vue b/app/assets/javascripts/vue_shared/components/code_block.vue
new file mode 100644
index 00000000000..3cca7a86bef
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/code_block.vue
@@ -0,0 +1,16 @@
+<script>
+export default {
+ name: 'CodeBlock',
+ props: {
+ code: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <pre class="code-block rounded">
+ <code class="d-block">{{ code }}</code>
+ </pre>
+</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 133bdbb54f7..8163947cd0c 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
@@ -42,6 +42,9 @@ export default {
},
methods: {
onImgLoad() {
+ requestIdleCallback(this.calculateImgSize, { timeout: 1000 });
+ },
+ calculateImgSize() {
const { contentImg } = this.$refs;
if (contentImg) {
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
index 3cba0c5e633..af5ebcdc40a 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
@@ -38,9 +38,17 @@ export default {
v-show="isLoading"
:inline="true"
/>
- <span class="dropdown-toggle-text">
- {{ toggleText }}
- </span>
+ <template>
+ <slot
+ v-if="$slots.default"
+ ></slot>
+ <span
+ v-else
+ class="dropdown-toggle-text"
+ >
+ {{ toggleText }}
+ </span>
+ </template>
<span
v-show="!isLoading"
class="dropdown-toggle-icon"
diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue
index b298b989203..b023c5cfeb1 100644
--- a/app/assets/javascripts/vue_shared/components/gl_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue
@@ -1,4 +1,6 @@
<script>
+import $ from 'jquery';
+
const buttonVariants = ['danger', 'primary', 'success', 'warning'];
const sizeVariants = ['sm', 'md', 'lg', 'xl'];
@@ -38,6 +40,12 @@ export default {
return this.modalSize === 'md' ? '' : `modal-${this.modalSize}`;
},
},
+ mounted() {
+ $(this.$el).on('shown.bs.modal', this.opened).on('hidden.bs.modal', this.closed);
+ },
+ beforeDestroy() {
+ $(this.$el).off('shown.bs.modal', this.opened).off('hidden.bs.modal', this.closed);
+ },
methods: {
emitCancel(event) {
this.$emit('cancel', event);
@@ -45,6 +53,12 @@ export default {
emitSubmit(event) {
this.$emit('submit', event);
},
+ opened() {
+ this.$emit('open');
+ },
+ closed() {
+ this.$emit('closed');
+ },
},
};
</script>
diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
index 62d35f6547d..49fbce75110 100644
--- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue
+++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
@@ -113,6 +113,9 @@ export default {
{{ user.name }}
</a>
+ <span
+ v-if="user.status_tooltip_html"
+ v-html="user.status_tooltip_html"></span>
</template>
</section>
diff --git a/app/assets/javascripts/vue_shared/components/help_popover.vue b/app/assets/javascripts/vue_shared/components/help_popover.vue
new file mode 100644
index 00000000000..540df392e4e
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/help_popover.vue
@@ -0,0 +1,53 @@
+<script>
+import $ from 'jquery';
+import Icon from '~/vue_shared/components/icon.vue';
+import { inserted } from '~/feature_highlight/feature_highlight_helper';
+import { mouseenter, debouncedMouseleave, togglePopover } from '~/shared/popover';
+
+/**
+ * Render a button with a question mark icon
+ * On hover shows a popover. The popover will be dismissed on mouseleave
+ */
+export default {
+ name: 'HelpPopover',
+ components: {
+ Icon,
+ },
+ props: {
+ options: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ mounted() {
+ const $el = $(this.$el);
+
+ $el
+ .popover({
+ html: true,
+ trigger: 'focus',
+ container: 'body',
+ placement: 'top',
+ template:
+ '<div class="popover" role="tooltip"><div class="arrow"></div><p class="popover-header"></p><div class="popover-body"></div></div>',
+ ...this.options,
+ })
+ .on('mouseenter', mouseenter)
+ .on('mouseleave', debouncedMouseleave(300))
+ .on('inserted.bs.popover', inserted)
+ .on('show.bs.popover', () => {
+ window.addEventListener('scroll', togglePopover.bind($el, false), { once: true });
+ });
+ },
+};
+</script>
+<template>
+ <button
+ type="button"
+ class="btn btn-blank btn-transparent btn-help"
+ tabindex="0"
+ >
+ <icon name="question" />
+ </button>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/icon.vue b/app/assets/javascripts/vue_shared/components/icon.vue
index c42c4a1fbe7..5e0e7315e99 100644
--- a/app/assets/javascripts/vue_shared/components/icon.vue
+++ b/app/assets/javascripts/vue_shared/components/icon.vue
@@ -1,24 +1,40 @@
<script>
-/* This is a re-usable vue component for rendering a svg sprite
- icon
- Sample configuration:
-
- <icon
- name="retry"
- :size="32"
- css-classes="top"
- />
+// only allow classes in images.scss e.g. s12
+const validSizes = [8, 10, 12, 16, 18, 24, 32, 48, 72];
+let iconValidator = () => true;
+/*
+ During development/tests we want to validate that we are just using icons that are actually defined
*/
-// only allow classes in images.scss e.g. s12
-const validSizes = [8, 12, 16, 18, 24, 32, 48, 72];
+if (process.env.NODE_ENV !== 'production') {
+ // eslint-disable-next-line global-require
+ const data = require('@gitlab-org/gitlab-svgs/dist/icons.json');
+ const { icons } = data;
+ iconValidator = value => {
+ if (icons.includes(value)) {
+ return true;
+ }
+ // eslint-disable-next-line no-console
+ console.warn(`Icon '${value}' is not a known icon of @gitlab/gitlab-svg`);
+ return false;
+ };
+}
+/** This is a re-usable vue component for rendering a svg sprite icon
+ * @example
+ * <icon
+ * name="retry"
+ * :size="32"
+ * css-classes="top"
+ * />
+ */
export default {
props: {
name: {
type: String,
required: true,
+ validator: iconValidator,
},
size: {
@@ -59,6 +75,12 @@ export default {
required: false,
default: null,
},
+
+ tabIndex: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
computed: {
@@ -82,7 +104,8 @@ export default {
:height="height"
:x="x"
:y="y"
+ :tabindex="tabIndex"
>
- <use v-bind="{ 'xlink:href':spriteHref }" />
+ <use v-bind="{ 'xlink:href':spriteHref }"/>
</svg>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/identicon.vue b/app/assets/javascripts/vue_shared/components/identicon.vue
index 4ffc811e714..0862f2c0cff 100644
--- a/app/assets/javascripts/vue_shared/components/identicon.vue
+++ b/app/assets/javascripts/vue_shared/components/identicon.vue
@@ -1,4 +1,6 @@
<script>
+import { getIdenticonBackgroundClass, getIdenticonTitle } from '~/helpers/avatar_helper';
+
export default {
props: {
entityId: {
@@ -16,26 +18,11 @@ export default {
},
},
computed: {
- /**
- * This method is based on app/helpers/avatars_helper.rb#project_identicon
- */
- identiconStyles() {
- const allowedColors = [
- '#FFEBEE',
- '#F3E5F5',
- '#E8EAF6',
- '#E3F2FD',
- '#E0F2F1',
- '#FBE9E7',
- '#EEEEEE',
- ];
-
- const backgroundColor = allowedColors[this.entityId % 7];
-
- return `background-color: ${backgroundColor}; color: #555;`;
+ identiconBackgroundClass() {
+ return getIdenticonBackgroundClass(this.entityId);
},
identiconTitle() {
- return this.entityName.charAt(0).toUpperCase();
+ return getIdenticonTitle(this.entityName);
},
},
};
@@ -43,8 +30,7 @@ export default {
<template>
<div
- :class="sizeClass"
- :style="identiconStyles"
+ :class="[sizeClass, identiconBackgroundClass]"
class="avatar identicon">
{{ identiconTitle }}
</div>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index fba67681777..d62537021ca 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -1,5 +1,6 @@
<script>
import $ from 'jquery';
+ import { s__ } from '~/locale';
import Flash from '../../../flash';
import GLForm from '../../../gl_form';
import markdownHeader from './header.vue';
@@ -22,6 +23,11 @@
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
addSpacingClasses: {
type: Boolean,
required: false,
@@ -67,6 +73,7 @@
members: this.enableAutocomplete,
issues: this.enableAutocomplete,
mergeRequests: this.enableAutocomplete,
+ epics: this.enableAutocomplete,
milestones: this.enableAutocomplete,
labels: this.enableAutocomplete,
});
@@ -91,10 +98,11 @@
if (text) {
this.markdownPreviewLoading = true;
- this.$http.post(this.markdownPreviewPath, { text })
- .then(resp => resp.json())
- .then(data => this.renderMarkdown(data))
- .catch(() => new Flash('Error loading markdown preview'));
+ this.$http
+ .post(this.versionedPreviewPath(), { text })
+ .then(resp => resp.json())
+ .then(data => this.renderMarkdown(data))
+ .catch(() => new Flash(s__('Error loading markdown preview')));
} else {
this.renderMarkdown();
}
@@ -118,6 +126,13 @@
$(this.$refs['markdown-preview']).renderGFM();
});
},
+
+ versionedPreviewPath() {
+ const { markdownPreviewPath, markdownVersion } = this;
+ return `${markdownPreviewPath}${
+ markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
+ }markdown_version=${markdownVersion}`;
+ },
},
};
</script>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index 83171ae50b8..8c22f3f6536 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -29,8 +29,8 @@
methods: {
isValid(form) {
return !form ||
- form.find('.js-vue-markdown-field').length ||
- $(this.$el).closest('form') === form[0];
+ form.find('.js-vue-markdown-field').length &&
+ $(this.$el).closest('form')[0] === form[0];
},
previewMarkdownTab(event, form) {
diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.vue b/app/assets/javascripts/vue_shared/components/memory_graph.vue
index 522091ea889..552a92541be 100644
--- a/app/assets/javascripts/vue_shared/components/memory_graph.vue
+++ b/app/assets/javascripts/vue_shared/components/memory_graph.vue
@@ -126,7 +126,7 @@ export default {
:cx="dotX"
:cy="dotY"
r="1.5"
- tranform="translate(0 -1)"
+ transform="translate(0 -1)"
/>
</svg>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/panel_resizer.vue b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
index 8c2dcc2d902..7947ae1e4da 100644
--- a/app/assets/javascripts/vue_shared/components/panel_resizer.vue
+++ b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
@@ -32,7 +32,7 @@
},
computed: {
className() {
- return `drag${this.side}`;
+ return `drag-${this.side}`;
},
cursorStyle() {
if (this.enabled) {
@@ -44,8 +44,15 @@
methods: {
resetSize(e) {
e.preventDefault();
+ this.$emit('resize-start', this.size);
+
this.size = this.startSize;
this.$emit('update:size', this.size);
+
+ // End resizing on next tick so that listeners can react to DOM changes
+ this.$nextTick(() => {
+ this.$emit('resize-end', this.size);
+ });
},
startDrag(e) {
if (this.enabled) {
@@ -84,7 +91,7 @@
<div
:class="className"
:style="cursorStyle"
- class="dragHandle"
+ class="drag-handle"
@mousedown="startDrag"
@dblclick="resetSize"
></div>
diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/default.vue b/app/assets/javascripts/vue_shared/components/project_avatar/default.vue
new file mode 100644
index 00000000000..17927fabbcc
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/project_avatar/default.vue
@@ -0,0 +1,47 @@
+<script>
+import Identicon from '../identicon.vue';
+import ProjectAvatarImage from './image.vue';
+
+export default {
+ components: {
+ Identicon,
+ ProjectAvatarImage,
+ },
+ props: {
+ project: {
+ type: Object,
+ required: true,
+ },
+ size: {
+ type: Number,
+ default: 40,
+ },
+ },
+ computed: {
+ sizeClass() {
+ return `s${this.size}`;
+ },
+ },
+};
+</script>
+
+<template>
+ <span
+ :class="sizeClass"
+ class="avatar-container project-avatar"
+ >
+ <project-avatar-image
+ v-if="project.avatar_url"
+ :link-href="project.path"
+ :img-src="project.avatar_url"
+ :img-alt="project.name"
+ :img-size="size"
+ />
+ <identicon
+ v-else
+ :entity-id="project.id"
+ :entity-name="project.name"
+ :size-class="sizeClass"
+ />
+ </span>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
index ac2e99abe77..80dc7d3557c 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
@@ -12,6 +12,11 @@ export default {
type: Boolean,
required: true,
},
+ cssClasses: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
tooltipLabel() {
@@ -30,10 +35,12 @@ export default {
<button
v-tooltip
:title="tooltipLabel"
+ :class="cssClasses"
type="button"
class="btn btn-blank gutter-toggle btn-sidebar-action"
data-container="body"
data-placement="left"
+ data-boundary="viewport"
@click="toggle"
>
<i
diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
index b1c2df54ef6..f44d361c47e 100644
--- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
+++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
@@ -1,4 +1,5 @@
<script>
+import { roundOffFloat } from '~/lib/utils/common_utils';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
@@ -70,7 +71,7 @@ export default {
},
methods: {
getPercent(count) {
- return Math.ceil((count / this.totalCount) * 100);
+ return roundOffFloat((count / this.totalCount) * 100, 1);
},
barStyle(percent) {
return `width: ${percent}%;`;
diff --git a/app/assets/javascripts/vue_shared/components/svg_gradient.vue b/app/assets/javascripts/vue_shared/components/svg_gradient.vue
new file mode 100644
index 00000000000..b61a1befcd6
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/svg_gradient.vue
@@ -0,0 +1,37 @@
+<script>
+export default {
+ props: {
+ colors: {
+ type: Array,
+ required: true,
+ },
+ opacity: {
+ type: Array,
+ required: true,
+ },
+ identifierName: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <svg
+ height="0"
+ width="0">
+ <defs>
+ <linearGradient
+ :id="identifierName">
+ <stop
+ :stop-color="colors[0]"
+ :stop-opacity="opacity[0]"
+ offset="0%" />
+ <stop
+ :stop-color="colors[1]"
+ :stop-opacity="opacity[1]"
+ offset="100%" />
+ </linearGradient>
+ </defs>
+ </svg>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
index 3a413c74410..7737b9f2697 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
@@ -1,5 +1,4 @@
<script>
-
/* This is a re-usable vue component for rendering a user avatar that
does not need to link to the user's profile. The image and an optional
tooltip can be configured by props passed to this component.
@@ -67,7 +66,9 @@ export default {
// we provide an empty string when we use it inside user avatar link.
// In both cases we should render the defaultAvatarUrl
sanitizedSource() {
- return this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc;
+ let baseSrc = this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc;
+ if (baseSrc.indexOf('?') === -1) baseSrc += `?width=${this.size}`;
+ return baseSrc;
},
resultantSrcAttribute() {
return this.lazy ? placeholderImage : this.sanitizedSource;
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index ded33e8b151..c20738a20c3 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -110,7 +110,7 @@ code {
padding: 2px 4px;
color: $red-600;
background-color: $red-100;
- border-radius: 3px;
+ border-radius: $border-radius-default;
.code > & {
background-color: inherit;
@@ -128,7 +128,8 @@ table {
border-spacing: 0;
}
-.tooltip {
+.tooltip,
+.no-pointer-events {
// Fix bootstrap4 bug whereby tooltips flicker when they are hovered over their borders
pointer-events: none;
}
@@ -338,3 +339,13 @@ input[type=color].form-control {
vertical-align: unset;
}
}
+
+// Bootstrap 3 compatibility because bootstrap_form Gem is not updated yet
+.input-group-btn:first-child {
+ @extend .input-group-prepend;
+}
+
+// Bootstrap 3 compatibility because bootstrap_form Gem is not updated yet
+.input-group-btn:last-child {
+ @extend .input-group-append;
+}
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index c1ec11e434a..4c7c399a3ca 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -7,7 +7,7 @@
.avatar-circle {
float: left;
margin-right: 15px;
- border-radius: $avatar_radius;
+ border-radius: $avatar-radius;
border: 1px solid $avatar-border;
&.s16 { @include avatar-size(16px, 6px); }
&.s18 { @include avatar-size(18px, 6px); }
@@ -69,12 +69,16 @@
.identicon {
text-align: center;
vertical-align: top;
+ color: $identicon-fg-color;
+ background-color: $identicon-gray;
+ // Sizes
&.s16 { font-size: 12px; line-height: 1.33; }
&.s24 { font-size: 13px; line-height: 1.8; }
&.s26 { font-size: 20px; line-height: 1.33; }
&.s32 { font-size: 20px; line-height: 30px; }
&.s40 { font-size: 16px; line-height: 38px; }
+ &.s48 { font-size: 20px; line-height: 46px; }
&.s60 { font-size: 32px; line-height: 58px; }
&.s70 { font-size: 34px; line-height: 70px; }
&.s90 { font-size: 36px; line-height: 88px; }
@@ -82,6 +86,15 @@
&.s110 { font-size: 40px; line-height: 108px; font-weight: $gl-font-weight-normal; }
&.s140 { font-size: 72px; line-height: 138px; }
&.s160 { font-size: 96px; line-height: 158px; }
+
+ // Background colors
+ &.bg1 { background-color: $identicon-red; }
+ &.bg2 { background-color: $identicon-purple; }
+ &.bg3 { background-color: $identicon-indigo; }
+ &.bg4 { background-color: $identicon-blue; }
+ &.bg5 { background-color: $identicon-teal; }
+ &.bg6 { background-color: $identicon-orange; }
+ &.bg7 { background-color: $identicon-gray; }
}
.avatar-container {
@@ -90,6 +103,7 @@
display: flex;
a {
+ width: 100%;
display: flex;
}
@@ -110,7 +124,7 @@
color: $white-light;
border: 1px solid $avatar-border;
border-radius: 1em;
- font-family: $regular_font;
+ font-family: $regular-font;
font-size: 9px;
line-height: 16px;
text-align: center;
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 1d4828be223..7145a76db6d 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -350,11 +350,21 @@
}
}
-.flex-container-block {
- display: -webkit-flex;
- display: flex;
-}
-
.flex-right {
margin-left: auto;
}
+
+.code-block {
+ background: $black;
+ color: $gray-darkest;
+ white-space: pre;
+ overflow-x: auto;
+ font-size: 12px;
+ border: 0;
+ padding: $grid-size;
+
+ code {
+ background-color: inherit;
+ padding: inherit;
+ }
+}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 523fcb05a87..646cedd79ed 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -294,6 +294,10 @@
.btn-clipboard {
border: 0;
padding: 0 5px;
+
+ svg {
+ top: auto;
+ }
}
.input-group-prepend,
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 326499125fc..af17210f341 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -113,8 +113,6 @@ hr {
.item-title { font-weight: $gl-font-weight-bold; }
-/** FLASH message **/
-.author_link,
.author-link {
color: $gl-link-color;
}
@@ -262,12 +260,7 @@ li.note {
}
.milestone {
- &.milestone-closed {
- background: $gray-light;
- }
-
.progress {
- margin-bottom: 0;
margin-top: 4px;
box-shadow: none;
background-color: $border-gray-light;
@@ -377,11 +370,14 @@ img.emoji {
margin-right: 10px;
}
-.alert,
-.progress {
+.alert {
margin-bottom: $gl-padding;
}
+.progress {
+ height: 4px;
+}
+
.project-item-select-holder {
display: inline-block;
position: relative;
@@ -458,6 +454,7 @@ img.emoji {
.prepend-left-10 { margin-left: 10px; }
.prepend-left-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left: 20px; }
+.append-right-4 { margin-right: 4px; }
.append-right-5 { margin-right: 5px; }
.append-right-8 { margin-right: 8px; }
.append-right-10 { margin-right: 10px; }
@@ -473,3 +470,6 @@ img.emoji {
.inline { display: inline-block; }
.center { text-align: center; }
.vertical-align-middle { vertical-align: middle; }
+.flex-align-self-center { align-self: center; }
+.flex-grow { flex-grow: 1; }
+.flex-no-shrink { flex-shrink: 0; }
diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index 9cbaaa5dc8d..e2bbcc67a67 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -55,6 +55,11 @@
.sidebar-context-title {
overflow: hidden;
text-overflow: ellipsis;
+
+ &.text-secondary {
+ font-weight: normal;
+ font-size: 0.8em;
+ }
}
}
@@ -68,8 +73,7 @@
}
.nav-sidebar {
- transition: width $sidebar-transition-duration,
- left $sidebar-transition-duration;
+ transition: width $sidebar-transition-duration, left $sidebar-transition-duration;
position: fixed;
z-index: 400;
width: $contextual-sidebar-width;
@@ -77,12 +81,12 @@
bottom: 0;
left: 0;
background-color: $gray-light;
- box-shadow: inset -2px 0 0 $border-color;
+ box-shadow: inset -1px 0 0 $border-color;
transform: translate3d(0, 0, 0);
&:not(.sidebar-collapsed-desktop) {
@media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
- box-shadow: inset -2px 0 0 $border-color,
+ box-shadow: inset -1px 0 0 $border-color,
2px 1px 3px $dropdown-shadow-color;
}
}
@@ -214,7 +218,7 @@
> li {
> a {
@include media-breakpoint-up(sm) {
- margin-right: 2px;
+ margin-right: 1px;
}
&:hover {
@@ -224,7 +228,7 @@
&.is-showing-fly-out {
> a {
- margin-right: 2px;
+ margin-right: 1px;
}
.sidebar-sub-level-items {
@@ -317,14 +321,14 @@
.toggle-sidebar-button,
.close-nav-button {
- width: $contextual-sidebar-width - 2px;
+ width: $contextual-sidebar-width - 1px;
transition: width $sidebar-transition-duration;
position: fixed;
bottom: 0;
padding: $gl-padding;
background-color: $gray-light;
border: 0;
- border-top: 2px solid $border-color;
+ border-top: 1px solid $border-color;
color: $gl-text-color-secondary;
display: flex;
align-items: center;
@@ -379,7 +383,7 @@
.toggle-sidebar-button {
padding: 16px;
- width: $contextual-sidebar-collapsed-width - 2px;
+ width: $contextual-sidebar-collapsed-width - 1px;
.collapse-text,
.icon-angle-double-left {
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 74475daae14..eebce8b9011 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -36,7 +36,7 @@
width: 100%;
}
- &.projects-dropdown-menu {
+ &.frequent-items-dropdown-menu {
padding: 0;
overflow-y: initial;
max-height: initial;
@@ -571,7 +571,8 @@
margin-bottom: 10px;
padding: 0 10px;
- .fa {
+ .fa,
+ .input-icon {
position: absolute;
top: 10px;
right: 20px;
@@ -790,6 +791,7 @@
@include media-breakpoint-down(xs) {
.navbar-gitlab {
li.header-projects,
+ li.header-groups,
li.header-more,
li.header-new,
li.header-user {
@@ -813,18 +815,18 @@
}
}
-header.header-content .dropdown-menu.projects-dropdown-menu {
+header.header-content .dropdown-menu.frequent-items-dropdown-menu {
padding: 0;
}
-.projects-dropdown-container {
+.frequent-items-dropdown-container {
display: flex;
flex-direction: row;
width: 500px;
- height: 334px;
+ height: 354px;
- .project-dropdown-sidebar,
- .project-dropdown-content {
+ .frequent-items-dropdown-sidebar,
+ .frequent-items-dropdown-content {
padding: 8px 0;
}
@@ -832,12 +834,12 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
color: $almost-black;
}
- .project-dropdown-sidebar {
+ .frequent-items-dropdown-sidebar {
width: 30%;
border-right: 1px solid $border-color;
}
- .project-dropdown-content {
+ .frequent-items-dropdown-content {
position: relative;
width: 70%;
}
@@ -848,33 +850,36 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
height: auto;
flex: 1;
- .project-dropdown-sidebar,
- .project-dropdown-content {
+ .frequent-items-dropdown-sidebar,
+ .frequent-items-dropdown-content {
width: 100%;
}
- .project-dropdown-sidebar {
+ .frequent-items-dropdown-sidebar {
border-bottom: 1px solid $border-color;
border-right: 0;
}
}
- .projects-list-frequent-container,
- .projects-list-search-container {
+ .section-header,
+ .frequent-items-list-container li.section-empty {
+ padding: 0 $gl-padding;
+ color: $gl-text-color-secondary;
+ font-size: $gl-font-size;
+ }
+
+ .frequent-items-list-container {
+ height: 304px;
padding: 8px 0;
overflow-y: auto;
li.section-empty.section-failure {
color: $callout-danger-color;
}
- }
- .section-header,
- .projects-list-frequent-container li.section-empty,
- .projects-list-search-container li.section-empty {
- padding: 0 15px;
- color: $gl-text-color-secondary;
- font-size: $gl-font-size;
+ .frequent-items-list-item-container a {
+ display: flex;
+ }
}
.search-input-container {
@@ -894,12 +899,8 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
margin-top: 8px;
}
- .projects-list-search-container {
- height: 284px;
- }
-
@include media-breakpoint-down(xs) {
- .projects-list-frequent-container {
+ .frequent-items-list-container {
width: auto;
height: auto;
padding-bottom: 0;
@@ -907,32 +908,38 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
}
}
-.projects-list-item-container {
- .project-item-avatar-container .project-item-metadata-container {
+.frequent-items-list-item-container {
+ .frequent-items-item-avatar-container,
+ .frequent-items-item-metadata-container {
float: left;
}
- .project-title,
- .project-namespace {
+ .frequent-items-item-metadata-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
+
+ .frequent-items-item-title,
+ .frequent-items-item-namespace {
max-width: 250px;
- overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&:hover {
- .project-item-avatar-container .avatar {
+ .frequent-items-item-avatar-container .avatar {
border-color: $md-area-border;
}
}
- .project-title {
+ .frequent-items-item-title {
font-size: $gl-font-size;
font-weight: 400;
line-height: 16px;
}
- .project-namespace {
+ .frequent-items-item-namespace {
margin-top: 4px;
font-size: 12px;
line-height: 12px;
@@ -940,7 +947,7 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
}
@include media-breakpoint-down(xs) {
- .project-item-metadata-container {
+ .frequent-items-item-metadata-container {
float: none;
}
}
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 551a7e852ae..5d79610b21e 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -224,7 +224,10 @@
.form-control {
position: relative;
min-width: 200px;
- padding: 5px 25px 6px 0;
+ padding-right: 25px;
+ padding-left: 0;
+ height: $input-height;
+ line-height: inherit;
border-color: transparent;
&:focus,
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 282e424fc38..d7149d93622 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -31,7 +31,7 @@ label {
margin: 0;
}
- &.label-light {
+ &.label-bold {
font-weight: $gl-font-weight-bold;
}
}
@@ -80,7 +80,7 @@ label {
.form-control {
height: 29px;
background: $white-light;
- font-family: $monospace_font;
+ font-family: $monospace-font;
}
.input-group-prepend .btn,
@@ -255,3 +255,8 @@ label {
color: $theme-gray-600;
}
}
+
+.input-lg {
+ max-width: 320px;
+ width: 100%;
+}
diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss
index 1cf12b1a015..d2ba76f5160 100644
--- a/app/assets/stylesheets/framework/gfm.scss
+++ b/app/assets/stylesheets/framework/gfm.scss
@@ -9,12 +9,8 @@
.gfm-project_member {
padding: 0 2px;
- border-radius: #{$border-radius-default / 2};
- background-color: $user-mention-bg;
-
- &:hover {
- background-color: $user-mention-bg-hover;
- }
+ background-color: $blue-100;
+ border-radius: $border-radius-default;
}
.gfm-color_chip {
diff --git a/app/assets/stylesheets/framework/gitlab_theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss
index aaa8bed3df0..50ebc6d0dd1 100644
--- a/app/assets/stylesheets/framework/gitlab_theme.scss
+++ b/app/assets/stylesheets/framework/gitlab_theme.scss
@@ -3,7 +3,6 @@
*/
@mixin gitlab-theme(
- $location-badge-color,
$search-and-nav-links,
$active-tab-border,
$border-and-box-shadow,
@@ -29,15 +28,21 @@
.navbar-sub-nav,
.navbar-nav {
> li {
- > a:hover,
- > a:focus {
- background-color: rgba($search-and-nav-links, 0.2);
+ > a,
+ > button {
+ &:hover,
+ &:focus {
+ background-color: rgba($search-and-nav-links, 0.2);
+ }
}
- &.active > a,
- &.dropdown.show > a {
- color: $nav-svg-color;
- background-color: $color-alternate;
+ &.active,
+ &.dropdown.show {
+ > a,
+ > button {
+ color: $nav-svg-color;
+ background-color: $color-alternate;
+ }
}
&.line-separator {
@@ -113,12 +118,6 @@
}
}
- .location-badge {
- color: $location-badge-color;
- background-color: rgba($search-and-nav-links, 0.1);
- border-right: 1px solid $sidebar-text;
- }
-
.search-input::placeholder {
color: rgba($search-and-nav-links, 0.8);
}
@@ -135,10 +134,6 @@
background-color: $white-light;
}
- .location-badge {
- color: $gl-text-color;
- }
-
.search-input-wrap {
.search-icon {
fill: rgba($search-and-nav-links, 0.8);
@@ -147,7 +142,6 @@
}
}
-
// Sidebar
.nav-sidebar li.active {
box-shadow: inset 4px 0 0 $border-and-box-shadow;
@@ -195,7 +189,6 @@
body {
&.ui-indigo {
@include gitlab-theme(
- $indigo-100,
$indigo-200,
$indigo-500,
$indigo-700,
@@ -207,7 +200,6 @@ body {
&.ui-light-indigo {
@include gitlab-theme(
- $indigo-100,
$indigo-200,
$indigo-500,
$indigo-500,
@@ -219,7 +211,6 @@ body {
&.ui-blue {
@include gitlab-theme(
- $theme-blue-100,
$theme-blue-200,
$theme-blue-500,
$theme-blue-700,
@@ -231,7 +222,6 @@ body {
&.ui-light-blue {
@include gitlab-theme(
- $theme-light-blue-100,
$theme-light-blue-200,
$theme-light-blue-500,
$theme-light-blue-500,
@@ -243,7 +233,6 @@ body {
&.ui-green {
@include gitlab-theme(
- $theme-green-100,
$theme-green-200,
$theme-green-500,
$theme-green-700,
@@ -255,7 +244,6 @@ body {
&.ui-light-green {
@include gitlab-theme(
- $theme-green-100,
$theme-green-200,
$theme-green-500,
$theme-green-500,
@@ -267,7 +255,6 @@ body {
&.ui-red {
@include gitlab-theme(
- $theme-red-100,
$theme-red-200,
$theme-red-500,
$theme-red-700,
@@ -279,7 +266,6 @@ body {
&.ui-light-red {
@include gitlab-theme(
- $theme-light-red-100,
$theme-light-red-200,
$theme-light-red-500,
$theme-light-red-500,
@@ -291,7 +277,6 @@ body {
&.ui-dark {
@include gitlab-theme(
- $theme-gray-100,
$theme-gray-200,
$theme-gray-500,
$theme-gray-700,
@@ -303,7 +288,6 @@ body {
&.ui-light {
@include gitlab-theme(
- $theme-gray-900,
$theme-gray-700,
$theme-gray-800,
$theme-gray-700,
@@ -352,10 +336,6 @@ body {
&:hover {
background-color: $white-light;
box-shadow: inset 0 0 0 1px $blue-200;
-
- .location-badge {
- box-shadow: inset 0 0 0 1px $blue-200;
- }
}
}
@@ -368,13 +348,6 @@ body {
color: $gl-text-color;
}
}
-
- .location-badge {
- color: $theme-gray-700;
- box-shadow: inset 0 0 0 1px $border-color;
- background-color: $nav-badge-bg;
- border-right: 0;
- }
}
.nav-sidebar li.active {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 5789c3fa1b1..e7e13d35d8e 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -268,17 +268,9 @@
.navbar-sub-nav,
.navbar-nav {
- align-items: center;
-
> li {
- > a:hover,
- > a:focus {
- text-decoration: none;
- outline: 0;
- color: $white-light;
- }
-
- > a {
+ > a,
+ > button {
display: -webkit-flex;
display: flex;
align-items: center;
@@ -290,6 +282,18 @@
border-radius: $border-radius-default;
height: 32px;
font-weight: $gl-font-weight-bold;
+
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ outline: 0;
+ color: $white-light;
+ }
+ }
+
+ > button {
+ background: transparent;
+ border: 0;
}
&.line-separator {
@@ -313,7 +317,7 @@
font-size: 10px;
}
- .project-item-select-holder {
+ .frequent-items-item-select-holder {
display: inline;
}
@@ -563,9 +567,6 @@
border-bottom: 1px solid $white-normal;
.mx-auto {
- margin: 8px 0;
- text-align: center;
-
.tanuki-logo,
img {
height: 36px;
diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss
index 813a1711ea2..452e946f95f 100644
--- a/app/assets/stylesheets/framework/highlight.scss
+++ b/app/assets/stylesheets/framework/highlight.scss
@@ -9,8 +9,8 @@
padding: 10px 0;
border: 0;
border-radius: 0;
- font-family: $monospace_font;
- font-size: $code_font_size;
+ font-family: $monospace-font;
+ font-size: $code-font-size;
line-height: 19px;
margin: 0;
overflow: auto;
@@ -22,7 +22,7 @@
code {
display: inline-block;
min-width: 100%;
- font-family: $monospace_font;
+ font-family: $monospace-font;
white-space: normal;
word-wrap: normal;
padding: 0;
@@ -44,7 +44,7 @@
float: left;
a {
- font-family: $monospace_font;
+ font-family: $monospace-font;
display: block;
font-size: $code_font_size !important;
min-height: 19px;
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index 30314f3d6cb..d1f7ff4438b 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -3,12 +3,20 @@
svg {
fill: $green-500;
}
+
+ &.add-border {
+ @include borderless-status-icon($green-500);
+ }
}
.ci-status-icon-failed {
svg {
fill: $gl-danger;
}
+
+ &.add-border {
+ @include borderless-status-icon($red-500);
+ }
}
.ci-status-icon-pending,
@@ -17,12 +25,20 @@
svg {
fill: $orange-500;
}
+
+ &.add-border {
+ @include borderless-status-icon($orange-500);
+ }
}
.ci-status-icon-running {
svg {
fill: $blue-400;
}
+
+ &.add-border {
+ @include borderless-status-icon($blue-400);
+ }
}
.ci-status-icon-canceled,
@@ -30,6 +46,10 @@
svg {
fill: $gl-text-color;
}
+
+ &.add-border {
+ @include borderless-status-icon($gl-text-color);
+ }
}
.ci-status-icon-created,
@@ -38,6 +58,10 @@
svg {
fill: $gray-darkest;
}
+
+ &.add-border {
+ @include borderless-status-icon($gray-darkest);
+ }
}
.ci-status-icon-manual {
diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss
index ab3cceceae9..f878ec1ca91 100644
--- a/app/assets/stylesheets/framework/images.scss
+++ b/app/assets/stylesheets/framework/images.scss
@@ -39,7 +39,7 @@
svg {
fill: currentColor;
- $svg-sizes: 8 12 16 18 24 32 48 72;
+ $svg-sizes: 8 10 12 16 18 24 32 48 72;
@each $svg-size in $svg-sizes {
&.s#{$svg-size} {
@include svg-size(#{$svg-size}px);
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index 1d247671761..86de88729ee 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -45,4 +45,9 @@
&.status-box-upcoming {
background: $gl-text-color-secondary;
}
+
+ &.status-box-milestone {
+ color: $gl-text-color;
+ background: $gray-darker;
+ }
}
diff --git a/app/assets/stylesheets/framework/jquery.scss b/app/assets/stylesheets/framework/jquery.scss
index 300ba4f2de6..d1360a0c0eb 100644
--- a/app/assets/stylesheets/framework/jquery.scss
+++ b/app/assets/stylesheets/framework/jquery.scss
@@ -1,5 +1,5 @@
.ui-widget {
- font-family: $regular_font;
+ font-family: $regular-font;
font-size: $font-size-base;
.ui-state-default {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index d54490c87c6..4b67eab05b3 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -259,7 +259,7 @@ ul.controls {
margin-right: 0;
}
- .author_link {
+ .author-link {
.avatar-inline {
margin-left: 0;
margin-right: 0;
@@ -270,7 +270,7 @@ ul.controls {
.issuable-pipeline-broken a,
.issuable-pipeline-status a,
- .author_link {
+ .author-link {
display: flex;
}
}
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 0b645eb811b..98bf26a5222 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -3,13 +3,13 @@
* Mixins with fixed values
*/
-@mixin str-truncated($max_width: 82%) {
+@mixin str-truncated($max-width: 82%) {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: top;
white-space: nowrap;
- max-width: $max_width;
+ max-width: $max-width;
}
/*
@@ -22,7 +22,7 @@
border: 0;
border-color: $md-area-border;
- @supports(width: fit-content) {
+ @supports (width: fit-content) {
display: block;
width: fit-content;
}
@@ -116,7 +116,7 @@
/* http://phrappe.com/css/conditional-css-for-webkit-based-browsers/ */
@mixin on-webkit-only {
- @media screen and (-webkit-min-device-pixel-ratio:0) {
+ @media screen and (-webkit-min-device-pixel-ratio: 0) {
@content;
}
}
@@ -164,14 +164,18 @@
bottom: 12px;
width: 43px;
height: 30px;
- transition-duration: .3s;
+ transition-duration: 0.3s;
-webkit-transform: translateZ(0);
- background: linear-gradient(to $gradient-direction, $gradient-color 45%, rgba($gradient-color, 0.4));
+ background: linear-gradient(
+ to $gradient-direction,
+ $gradient-color 45%,
+ rgba($gradient-color, 0.4)
+ );
&.scrolling {
visibility: visible;
opacity: 1;
- transition-duration: .3s;
+ transition-duration: 0.3s;
}
.fa {
@@ -232,3 +236,33 @@
word-break: break-word;
max-width: 100%;
}
+
+@mixin build-loader-animation {
+ position: relative;
+ white-space: initial;
+
+ .dot {
+ display: inline-block;
+ width: 6px;
+ height: 6px;
+ margin: auto auto 12px;
+ border-radius: 50%;
+ animation: blinking-dot 1s linear infinite;
+ background: $white-light;
+
+ &:nth-child(2) {
+ animation-delay: 0.33s;
+ }
+
+ &:nth-child(3) {
+ animation-delay: 0.66s;
+ }
+ }
+}
+
+@mixin borderless-status-icon($color) {
+ svg {
+ border: 1px solid $color;
+ border-radius: 50%;
+ }
+}
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index a8e28104a94..5ca4d944d73 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -47,7 +47,6 @@
.card-body {
padding: $gl-padding;
- background-color: $white-light;
.form-actions {
margin: -$gl-padding;
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 9dbb04e5443..8bab8cf36b1 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -321,11 +321,18 @@
}
&.activities {
+ display: flex;
border-bottom: 1px solid $border-color;
+ overflow: hidden;
.nav-links {
border-bottom: 0;
}
+
+ @include media-breakpoint-down(xs) {
+ display: block;
+ overflow: visible;
+ }
}
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 8c716400913..c4dbcf2ddc9 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -33,11 +33,11 @@
@include media-breakpoint-up(sm) {
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
- padding-right: $gutter_collapsed_width;
+ padding-right: $gutter-collapsed-width;
}
.merge-request-tabs-holder.affix {
- right: $gutter_collapsed_width;
+ right: $gutter-collapsed-width;
}
}
@@ -67,21 +67,21 @@
@include media-breakpoint-only(sm) {
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
- padding-right: $gutter_collapsed_width;
+ padding-right: $gutter-collapsed-width;
}
}
@include media-breakpoint-up(md) {
.content-wrapper {
- padding-right: $gutter_width;
+ padding-right: $gutter-width;
}
&:not(.with-overlay) .merge-request-tabs-holder.affix {
- right: $gutter_width;
+ right: $gutter-width;
}
&.with-overlay .merge-request-tabs-holder.affix {
- right: $gutter_collapsed_width;
+ right: $gutter-collapsed-width;
}
}
}
diff --git a/app/assets/stylesheets/framework/stacked_progress_bar.scss b/app/assets/stylesheets/framework/stacked_progress_bar.scss
index 528ba53a48b..29a2d5881f7 100644
--- a/app/assets/stylesheets/framework/stacked_progress_bar.scss
+++ b/app/assets/stylesheets/framework/stacked_progress_bar.scss
@@ -10,7 +10,7 @@
.status-neutral,
.status-red, {
height: 100%;
- min-width: 30px;
+ min-width: 40px;
padding: 0 5px;
font-size: $tooltip-font-size;
font-weight: normal;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 9e77ea03a24..473ca408c04 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -44,7 +44,7 @@
// Single code lines should wrap
code {
- font-family: $monospace_font;
+ font-family: $monospace-font;
white-space: pre-wrap;
word-wrap: normal;
}
@@ -179,6 +179,10 @@
font-weight: inherit;
}
+ a > code {
+ color: $gl-link-color;
+ }
+
dd {
margin-left: $gl-padding;
}
@@ -321,7 +325,7 @@ h6 {
/** CODE **/
pre {
- font-family: $monospace_font;
+ font-family: $monospace-font;
display: block;
padding: $gl-padding-8;
margin: 0 0 $gl-padding-8;
@@ -342,7 +346,7 @@ code {
}
.monospace {
- font-family: $monospace_font;
+ font-family: $monospace-font;
}
.weight-normal {
@@ -350,7 +354,8 @@ code {
}
.commit-sha,
-.ref-name {
+.ref-name,
+.pipeline-number {
@extend .monospace;
font-size: 95%;
}
@@ -380,7 +385,7 @@ code {
*
*/
textarea.js-gfm-input {
- font-family: $monospace_font;
+ font-family: $monospace-font;
font-size: 13px;
}
@@ -439,3 +444,5 @@ textarea {
color: $placeholder-text-color;
}
}
+
+.lh-100 { line-height: 1; }
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 7808f6d3a25..4db9efff6ee 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -2,9 +2,9 @@
* Layout
*/
$grid-size: 8px;
-$gutter_collapsed_width: 62px;
-$gutter_width: 290px;
-$gutter_inner_width: 250px;
+$gutter-collapsed-width: 62px;
+$gutter-width: 290px;
+$gutter-inner-width: 250px;
$sidebar-transition-duration: 0.3s;
$sidebar-breakpoint: 1024px;
$default-transition-duration: 0.15s;
@@ -233,8 +233,8 @@ $md-area-border: #ddd;
/*
* Code
*/
-$code_font_size: 90%;
-$code_line_height: 1.6;
+$code-font-size: 90%;
+$code-line-height: 1.6;
/*
* Tooltips
@@ -297,7 +297,6 @@ $performance-bar-height: 35px;
$flash-height: 52px;
$context-header-height: 60px;
$breadcrumb-min-height: 48px;
-$gcp-signup-offer-icon-max-width: 125px;
/*
* Common component specific colors
@@ -372,9 +371,9 @@ $diff-jagged-border-gradient-color: darken($white-normal, 8%);
/*
* Fonts
*/
-$monospace_font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono',
+$monospace-font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono',
'Courier New', 'andale mono', 'lucida console', monospace;
-$regular_font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
+$regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
/*
@@ -468,7 +467,8 @@ $award-emoji-positive-add-lines: #bb9c13;
*/
$search-input-border-color: rgba($blue-400, 0.8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
-$search-input-width: 220px;
+$search-input-width: 240px;
+$search-input-active-width: 320px;
$location-badge-active-bg: $blue-500;
$location-icon-color: #e7e9ed;
@@ -488,6 +488,18 @@ $note-icon-gutter-width: 55px;
$zen-control-color: #555;
/*
+* Identicon
+*/
+$identicon-red: #ffebee;
+$identicon-purple: #f3e5f5;
+$identicon-indigo: #e8eaf6;
+$identicon-blue: #e3f2fd;
+$identicon-teal: #e0f2f1;
+$identicon-orange: #fbe9e7;
+$identicon-gray: $gray-darker;
+$identicon-fg-color: #555555;
+
+/*
* Calendar
*/
$calendar-hover-bg: #ecf3fe;
@@ -527,7 +539,7 @@ $issue-board-list-difference-md: $issue-board-list-difference-sm + $issue-boards
/*
* Avatar
*/
-$avatar_radius: 50%;
+$avatar-radius: 50%;
$avatar-border: $gray-normal;
$avatar-border-hover: $gray-darker;
$avatar-background: $gray-lightest;
@@ -743,6 +755,7 @@ Pipeline Graph
*/
$stage-hover-bg: $gray-darker;
$ci-action-icon-size: 22px;
+$ci-action-icon-size-lg: 24px;
$pipeline-dropdown-line-height: 20px;
$pipeline-dropdown-status-icon-size: 18px;
$ci-action-dropdown-button-size: 24px;
@@ -830,8 +843,10 @@ $secondary: $gray-light;
$input-disabled-bg: $gray-light;
$input-border-color: $theme-gray-200;
$input-color: $gl-text-color;
-$font-family-sans-serif: $regular_font;
-$font-family-monospace: $monospace_font;
+$font-family-sans-serif: $regular-font;
+$font-family-monospace: $monospace-font;
$input-line-height: 20px;
$btn-line-height: 20px;
$table-accent-bg: $gray-light;
+$card-border-color: $border-color;
+$card-cap-bg: $gray-light;
diff --git a/app/assets/stylesheets/mailers/highlighted_diff_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
index 1835c4364d3..8b234a5a656 100644
--- a/app/assets/stylesheets/mailers/highlighted_diff_email.scss
+++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
@@ -77,13 +77,13 @@ $highlighted-gc-bg: #eaf2f5;
.code {
background-color: $white-light;
font-family: monospace;
- font-size: $code_font_size;
+ font-size: $code-font-size;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
> tr {
- line-height: $code_line_height;
+ line-height: $code-line-height;
}
}
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
new file mode 100644
index 00000000000..2b8163b8c68
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -0,0 +1,1497 @@
+@import 'framework/variables';
+@import 'framework/mixins';
+
+$search-list-icon-width: 18px;
+$ide-activity-bar-width: 60px;
+$ide-context-header-padding: 10px;
+$ide-project-avatar-end: $ide-context-header-padding + 48px;
+$ide-tree-padding: $gl-padding;
+$ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
+
+.project-refs-form,
+.project-refs-target-form {
+ display: inline-block;
+}
+
+.fade-enter,
+.fade-leave-to {
+ opacity: 0;
+}
+
+.commit-message {
+ @include str-truncated(250px);
+}
+
+.editable-mode {
+ display: inline-block;
+}
+
+.ide-view {
+ position: relative;
+ display: flex;
+ height: calc(100vh - #{$header-height});
+ margin-top: 0;
+ padding-bottom: $ide-statusbar-height;
+ color: $gl-text-color;
+
+ &.is-collapsed {
+ .ide-file-list {
+ max-width: 250px;
+ }
+ }
+
+ .file-status-icon {
+ width: 10px;
+ height: 10px;
+ }
+}
+
+.ide-file-list {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+
+ .file {
+ height: 32px;
+ cursor: pointer;
+
+ &.file-active {
+ background: $theme-gray-100;
+ }
+
+ .ide-file-name {
+ flex: 1;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ max-width: inherit;
+ line-height: 16px;
+ display: inline-block;
+ height: 18px;
+
+ svg {
+ vertical-align: middle;
+ margin-right: 2px;
+ }
+
+ .loading-container {
+ margin-right: 4px;
+ display: inline-block;
+ }
+ }
+
+ .ide-file-icon-holder {
+ display: flex;
+ align-items: center;
+ color: $theme-gray-700;
+ }
+
+ .ide-file-changed-icon {
+ margin-left: auto;
+
+ > svg {
+ display: block;
+ }
+ }
+
+ .ide-new-btn {
+ display: none;
+
+ .btn {
+ padding: 2px 5px;
+ }
+ }
+
+ &:hover,
+ &:focus {
+ .ide-new-btn {
+ display: block;
+ }
+ }
+
+ .folder-icon {
+ fill: $gl-text-color-secondary;
+ }
+ }
+
+ a {
+ color: $gl-text-color;
+ }
+
+ th {
+ position: sticky;
+ top: 0;
+ }
+}
+
+.file-name {
+ display: flex;
+ overflow: visible;
+ align-items: center;
+ width: 100%;
+}
+
+.multi-file-loading-container {
+ margin-top: 10px;
+ padding: 10px;
+}
+
+.multi-file-table-col-commit-message {
+ white-space: nowrap;
+ width: 50%;
+}
+
+.multi-file-edit-pane {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ border-left: 1px solid $white-dark;
+ overflow: hidden;
+}
+
+.multi-file-tabs {
+ display: flex;
+ background-color: $white-normal;
+ box-shadow: inset 0 -1px $white-dark;
+
+ > ul {
+ display: flex;
+ overflow-x: auto;
+ }
+
+ li {
+ display: flex;
+ align-items: center;
+ padding: $grid-size $gl-padding;
+ background-color: $gray-normal;
+ border-right: 1px solid $white-dark;
+ border-bottom: 1px solid $white-dark;
+
+ &.active {
+ background-color: $white-light;
+ border-bottom-color: $white-light;
+ }
+
+ &:not(.disabled) {
+ .multi-file-tab {
+ cursor: pointer;
+ }
+ }
+
+ &.disabled {
+ .multi-file-tab-close {
+ cursor: default;
+ }
+ }
+ }
+}
+
+.multi-file-tab {
+ @include str-truncated(141px);
+
+ svg {
+ vertical-align: middle;
+ }
+}
+
+.multi-file-tab-close {
+ width: 16px;
+ height: 16px;
+ padding: 0;
+ margin-left: $grid-size;
+ background: none;
+ border: 0;
+ border-radius: $border-radius-default;
+ color: $theme-gray-900;
+
+ svg {
+ position: relative;
+ top: -2px;
+ }
+
+ .ide-file-changed-icon {
+ display: block;
+ position: relative;
+ top: 1px;
+ right: -2px;
+ }
+
+ &:not([disabled]):hover {
+ background-color: $theme-gray-200;
+ }
+
+ &:not([disabled]):focus {
+ background-color: $blue-500;
+ color: $white-light;
+ outline: 0;
+
+ svg {
+ fill: currentColor;
+ }
+ }
+}
+
+.multi-file-edit-pane-content {
+ flex: 1;
+ height: 0;
+}
+
+.blob-editor-container {
+ flex: 1;
+ height: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+
+ .vertical-center {
+ min-height: auto;
+ }
+
+ .monaco-editor .lines-content .cigr {
+ display: none;
+ }
+
+ .is-readonly,
+ .editor.original {
+ .view-lines {
+ cursor: default;
+ }
+
+ .cursors-layer {
+ display: none;
+ }
+ }
+
+ .is-deleted {
+ .editor.modified {
+ .margin-view-overlays,
+ .lines-content,
+ .decorationsOverviewRuler {
+ // !important to override monaco inline styles
+ display: none !important;
+ }
+ }
+
+ .diffOverviewRuler.modified {
+ // !important to override monaco inline styles
+ display: none !important;
+ }
+ }
+
+ .is-added {
+ .editor.original {
+ .margin-view-overlays,
+ .lines-content,
+ .decorationsOverviewRuler {
+ // !important to override monaco inline styles
+ display: none !important;
+ }
+ }
+
+ .diffOverviewRuler.original {
+ // !important to override monaco inline styles
+ display: none !important;
+ }
+ }
+
+ .monaco-diff-editor.vs {
+ .editor.modified {
+ box-shadow: none;
+ }
+
+ .diagonal-fill {
+ display: none !important;
+ }
+
+ .diffOverview {
+ background-color: $white-light;
+ border-left: 1px solid $white-dark;
+ cursor: ns-resize;
+ }
+
+ .diffViewport {
+ display: none;
+ }
+
+ .char-insert {
+ background-color: $line-added-dark;
+ }
+
+ .char-delete {
+ background-color: $line-removed-dark;
+ }
+
+ .line-numbers {
+ color: $black-transparent;
+ }
+
+ .view-overlays {
+ .line-insert {
+ background-color: $line-added;
+ }
+
+ .line-delete {
+ background-color: $line-removed;
+ }
+ }
+
+ .margin {
+ background-color: $white-light;
+ border-right: 1px solid $theme-gray-100;
+
+ .line-insert {
+ border-right: 1px solid $line-added-dark;
+ }
+
+ .line-delete {
+ border-right: 1px solid $line-removed-dark;
+ }
+ }
+
+ .margin-view-overlays .insert-sign,
+ .margin-view-overlays .delete-sign {
+ opacity: 0.4;
+ }
+ }
+}
+
+.multi-file-editor-holder {
+ height: 100%;
+ min-height: 0;
+
+ &.is-readonly,
+ .editor.original {
+ .monaco-editor,
+ .monaco-editor-background,
+ .monaco-editor .inputarea.ime-input {
+ background-color: $theme-gray-50;
+ }
+ }
+}
+
+.preview-container {
+ flex-grow: 1;
+ position: relative;
+
+ .md-previewer {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ padding: $gl-padding;
+ }
+
+ .file-container {
+ background-color: $gray-darker;
+ display: flex;
+ height: 100%;
+ align-items: center;
+ justify-content: center;
+
+ text-align: center;
+
+ .file-content {
+ padding: $gl-padding;
+ max-width: 100%;
+ max-height: 100%;
+
+ img {
+ max-width: 90%;
+ }
+
+ .isZoomable {
+ cursor: pointer;
+ cursor: zoom-in;
+
+ &.isZoomed {
+ cursor: pointer;
+ cursor: zoom-out;
+ max-width: none;
+ max-height: none;
+ margin-right: $gl-padding;
+ }
+ }
+ }
+
+ .file-info {
+ font-size: $label-font-size;
+ color: $diff-image-info-color;
+ }
+ }
+}
+
+.ide-mode-tabs {
+ border-bottom: 1px solid $white-dark;
+
+ .nav-links {
+ border-bottom: 0;
+
+ li a {
+ padding: $gl-padding-8 $gl-padding;
+ line-height: $gl-btn-line-height;
+ }
+ }
+}
+
+.ide-btn-group {
+ padding: $gl-padding-4 $gl-vert-padding;
+ line-height: 24px;
+}
+
+.ide-status-bar {
+ border-top: 1px solid $white-dark;
+ padding: 2px $gl-padding-8 0;
+ background: $white-light;
+ display: flex;
+ justify-content: space-between;
+ height: $ide-statusbar-height;
+
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+
+ font-size: 12px;
+ line-height: 22px;
+
+ * {
+ font-size: inherit;
+ }
+
+ > div + div {
+ padding-left: $gl-padding;
+ }
+
+ svg {
+ vertical-align: sub;
+ }
+}
+
+.ide-status-file {
+ text-align: right;
+
+ .ide-status-branch + &,
+ &:first-child {
+ margin-left: auto;
+ }
+}
+// Not great, but this is to deal with our current output
+.multi-file-preview-holder {
+ height: 100%;
+ overflow: scroll;
+
+ .file-content.code {
+ display: flex;
+
+ i {
+ margin-left: -10px;
+ }
+ }
+
+ .line-numbers {
+ min-width: 50px;
+ }
+
+ .file-content,
+ .line-numbers,
+ .blob-content,
+ .code {
+ min-height: 100%;
+ }
+}
+
+.file-content.blob-no-preview {
+ a {
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+.multi-file-commit-panel {
+ display: flex;
+ position: relative;
+ width: 340px;
+ padding: 0;
+ background-color: $gray-light;
+ padding-right: 1px;
+
+ .context-header {
+ width: auto;
+ margin-right: 0;
+
+ > a,
+ > button {
+ text-decoration: none;
+ padding-top: $gl-padding-8;
+ padding-bottom: $gl-padding-8;
+ }
+ }
+
+ .multi-file-commit-panel-inner {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+ min-width: 0;
+ width: 100%;
+ }
+
+ .multi-file-commit-panel-inner-content {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ background-color: $white-light;
+ border-left: 1px solid $white-dark;
+ border-top: 1px solid $white-dark;
+ border-top-left-radius: $border-radius-small;
+ min-height: 0;
+ }
+}
+
+.multi-file-commit-panel-section {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ max-height: 100%;
+ overflow: auto;
+}
+
+.ide-commit-empty-state {
+ padding: 0 $gl-padding;
+}
+
+.ide-commit-empty-state-container {
+ margin-top: auto;
+ margin-bottom: auto;
+}
+
+.multi-file-commit-panel-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 0;
+ border-bottom: 1px solid $white-dark;
+ padding: 12px 0;
+}
+
+.multi-file-commit-panel-header-title {
+ display: flex;
+ flex: 1;
+ align-items: center;
+
+ svg {
+ margin-right: $gl-btn-padding;
+ color: $theme-gray-700;
+ }
+}
+
+.multi-file-commit-panel-collapse-btn {
+ border-left: 1px solid $white-dark;
+ margin-left: auto;
+}
+
+.multi-file-commit-list {
+ flex: 1;
+ overflow: auto;
+ padding: $grid-size 0;
+ margin-left: -$grid-size;
+ margin-right: -$grid-size;
+ min-height: 60px;
+
+ &.form-text.text-muted {
+ margin-left: 0;
+ right: 0;
+ }
+}
+
+.ide-file-addition,
+.ide-file-addition-solid {
+ color: $green-500;
+}
+
+.ide-file-modified,
+.ide-file-modified-solid {
+ color: $orange-500;
+}
+
+.ide-file-deletion,
+.ide-file-deletion-solid {
+ color: $red-500;
+}
+
+.multi-file-commit-list-collapsed {
+ display: flex;
+ flex-direction: column;
+ padding: $gl-padding 0;
+
+ svg {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ color: $theme-gray-700;
+ }
+
+ .file-status-icon {
+ width: 10px;
+ height: 10px;
+ margin-left: 3px;
+ }
+}
+
+.multi-file-commit-list-path,
+.ide-file-list .file {
+ display: flex;
+ align-items: center;
+ margin-left: -$grid-size;
+ margin-right: -$grid-size;
+ padding: $grid-size / 2 $grid-size;
+ border-radius: $border-radius-default;
+ text-align: left;
+
+ &:hover,
+ &:focus {
+ background: $theme-gray-100;
+ }
+
+ &:active {
+ background: $theme-gray-200;
+ }
+}
+
+.multi-file-commit-list-path {
+ cursor: pointer;
+
+ &.is-active {
+ background-color: $white-normal;
+ }
+
+ &:hover,
+ &:focus {
+ outline: 0;
+ }
+
+ svg {
+ min-width: 16px;
+ vertical-align: middle;
+ display: inline-block;
+ }
+}
+
+.multi-file-commit-list-file-path {
+ @include str-truncated(calc(100% - 30px));
+
+ &:active {
+ text-decoration: none;
+ }
+}
+
+.multi-file-discard-btn {
+ top: 4px;
+ right: 8px;
+ bottom: 4px;
+
+ svg {
+ top: 0;
+ }
+}
+
+.multi-file-commit-form {
+ position: relative;
+ background-color: $white-light;
+ border-left: 1px solid $white-dark;
+ transition: all 0.3s ease;
+
+ > form,
+ > .commit-form-compact {
+ padding: $gl-padding 0;
+ margin-left: $gl-padding;
+ margin-right: $gl-padding;
+ border-top: 1px solid $white-dark;
+ }
+
+ .btn {
+ font-size: $gl-font-size;
+ }
+
+ .multi-file-commit-panel-success-message {
+ top: 0;
+ }
+}
+
+.multi-file-commit-panel-bottom {
+ position: relative;
+}
+
+.dirty-diff {
+ // !important need to override monaco inline style
+ width: 4px !important;
+ left: 0 !important;
+
+ &-modified {
+ background-color: $blue-500;
+ }
+
+ &-added {
+ background-color: $green-600;
+ }
+
+ &-removed {
+ height: 0 !important;
+ width: 0 !important;
+ bottom: -2px;
+ border-style: solid;
+ border-width: 5px;
+ border-color: transparent transparent transparent $red-500;
+
+ &::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100px;
+ height: 1px;
+ background-color: rgba($red-500, 0.5);
+ }
+ }
+}
+
+.ide-loading {
+ display: flex;
+ height: 100vh;
+ align-items: center;
+ justify-content: center;
+}
+
+.ide-empty-state {
+ display: flex;
+ height: 100vh;
+ align-items: center;
+ justify-content: center;
+}
+
+.ide {
+ overflow: hidden;
+
+ &.nav-only {
+ padding-top: $header-height;
+
+ .with-performance-bar & {
+ padding-top: $header-height + $performance-bar-height;
+ }
+
+ .flash-container {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+
+ .alert-wrapper .flash-container .flash-alert:last-child,
+ .alert-wrapper .flash-container .flash-notice:last-child {
+ margin-bottom: 0;
+ }
+
+ .content-wrapper {
+ margin-top: 0;
+ padding-bottom: 0;
+ }
+
+ &.flash-shown {
+ .content-wrapper {
+ margin-top: 0;
+ }
+
+ .ide-view {
+ height: calc(100vh - #{$header-height + $flash-height});
+ }
+ }
+ }
+}
+
+.with-performance-bar .ide.nav-only {
+ .flash-container {
+ margin-top: 0;
+ }
+
+ .content-wrapper {
+ margin-top: 0;
+ padding-bottom: 0;
+ }
+
+ .ide-view {
+ height: calc(100vh - #{$header-height + $performance-bar-height});
+ }
+
+ &.flash-shown {
+ .ide-view {
+ height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height});
+ }
+ }
+}
+
+.drag-handle {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 4px;
+
+ &:hover {
+ background-color: $white-normal;
+ }
+
+ &.drag-right {
+ right: 0;
+ }
+
+ &.drag-left {
+ left: 0;
+ }
+}
+
+.ide-commit-list-container {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ min-height: 140px;
+ margin-left: $gl-padding;
+ margin-right: $gl-padding;
+
+ &.is-first {
+ border-bottom: 1px solid $white-dark;
+ }
+}
+
+.ide-staged-action-btn {
+ width: 22px;
+ margin-left: -1px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+
+ > svg {
+ top: 0;
+ }
+}
+
+.ide-commit-file-count {
+ min-width: 22px;
+ background-color: $gray-light;
+ border: 1px solid $white-dark;
+}
+
+.ide-commit-radios {
+ label {
+ font-weight: normal;
+
+ &.is-disabled {
+ .ide-radio-label {
+ text-decoration: line-through;
+ }
+ }
+ }
+
+ .form-text.text-muted {
+ margin-top: 0;
+ line-height: 0;
+ }
+}
+
+.ide-commit-new-branch {
+ margin-left: 25px;
+}
+
+.ide-sidebar-link {
+ display: flex;
+ align-items: center;
+ position: relative;
+ height: 60px;
+ width: 100%;
+ padding: 0 $gl-padding;
+ color: $gl-text-color-secondary;
+ background-color: transparent;
+ border: 0;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ outline: 0;
+ cursor: pointer;
+
+ svg {
+ margin: 0 auto;
+ }
+
+ &:hover {
+ color: $gl-text-color;
+ background-color: $theme-gray-100;
+ }
+
+ &:focus {
+ color: $gl-text-color;
+ background-color: $theme-gray-200;
+ }
+
+ &.active {
+ // extend width over border of sidebar section
+ width: calc(100% + 1px);
+ padding-right: $gl-padding + 1px;
+ background-color: $white-light;
+ border-top-color: $white-dark;
+ border-bottom-color: $white-dark;
+
+ &::after {
+ content: '';
+ position: absolute;
+ right: -1px;
+ top: 0;
+ bottom: 0;
+ width: 1px;
+ background: $white-light;
+ }
+
+ &.is-right {
+ padding-right: $gl-padding;
+ padding-left: $gl-padding + 1px;
+
+ &::after {
+ right: auto;
+ left: -1px;
+ }
+ }
+ }
+}
+
+.ide-activity-bar {
+ position: relative;
+ flex: 0 0 $ide-activity-bar-width;
+ z-index: 1;
+}
+
+.ide-file-finder-overlay {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 100;
+}
+
+.ide-file-finder {
+ top: 10px;
+ left: 50%;
+ transform: translateX(-50%);
+
+ .highlighted {
+ color: $blue-500;
+ font-weight: $gl-font-weight-bold;
+ }
+}
+
+.ide-commit-message-field {
+ height: 200px;
+ background-color: $white-light;
+
+ .md-area {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ }
+
+ .nav-links {
+ height: 30px;
+ }
+
+ .form-text.text-muted {
+ margin-top: 2px;
+ color: $blue-500;
+ cursor: pointer;
+ }
+}
+
+.ide-commit-message-textarea-container {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+
+ .note-textarea {
+ font-family: $monospace-font;
+ }
+}
+
+.ide-commit-message-highlights-container {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: -100px;
+ bottom: 0;
+ padding-right: 100px;
+ pointer-events: none;
+ z-index: 1;
+
+ .highlights {
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ color: transparent;
+ }
+
+ mark {
+ margin-left: -1px;
+ padding: 0 2px;
+ border-radius: $border-radius-small;
+ background-color: $orange-200;
+ color: transparent;
+ opacity: 0.6;
+ }
+}
+
+.ide-commit-message-textarea {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 2;
+ background: transparent;
+ resize: none;
+}
+
+.ide-tree-header {
+ flex: 0 0 auto;
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ padding: 12px 0;
+ margin-left: $ide-tree-padding;
+ margin-right: $ide-tree-padding;
+ border-bottom: 1px solid $white-dark;
+
+ .ide-new-btn {
+ margin-left: auto;
+ }
+
+ .ide-nav-dropdown {
+ width: 100%;
+ margin-bottom: 12px;
+
+ .dropdown-menu {
+ width: 385px;
+ max-height: initial;
+ }
+
+ .dropdown-menu-toggle {
+ svg {
+ vertical-align: middle;
+ }
+
+ &:hover {
+ background-color: $white-normal;
+ }
+ }
+
+ &.show {
+ .dropdown-menu-toggle {
+ background-color: $white-dark;
+ }
+ }
+ }
+
+ button {
+ color: $gl-text-color;
+ }
+}
+
+.ide-tree-body {
+ overflow: auto;
+ padding-left: $ide-tree-padding;
+ padding-right: $ide-tree-padding;
+}
+
+.ide-sidebar-branch-title {
+ font-weight: $gl-font-weight-normal;
+
+ svg {
+ position: relative;
+ top: 3px;
+ margin-top: -1px;
+ }
+}
+
+.commit-form-compact {
+ .btn {
+ margin-bottom: 8px;
+ }
+
+ p {
+ margin-bottom: 0;
+ }
+}
+
+.commit-form-slide-up-enter-active,
+.commit-form-slide-up-leave-active {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ transition: all 0.3s ease;
+}
+
+.is-full .commit-form-slide-up-enter,
+.is-compact .commit-form-slide-up-leave-to {
+ transform: translateY(100%);
+}
+
+.is-full .commit-form-slide-up-enter-to,
+.is-compact .commit-form-slide-up-leave {
+ transform: translateY(0);
+}
+
+.commit-form-slide-up-enter,
+.commit-form-slide-up-leave-to {
+ opacity: 0;
+}
+
+.ide-review-header {
+ flex-direction: column;
+ align-items: flex-start;
+
+ .dropdown {
+ margin-left: auto;
+ }
+
+ a {
+ color: $gl-link-color;
+ }
+}
+
+.ide-review-sub-header {
+ color: $gl-text-color-secondary;
+}
+
+.ide-tree-changes {
+ display: flex;
+ align-items: center;
+ font-size: 12px;
+}
+
+.multi-file-commit-panel-success-message {
+ position: absolute;
+ top: 61px;
+ left: 1px;
+ bottom: 0;
+ right: 0;
+ z-index: 10;
+ background: $white-light;
+ overflow: auto;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.ide-review-button-holder {
+ display: flex;
+ width: 100%;
+ align-items: center;
+}
+
+.ide-context-header {
+ .ide-merge-requests-dropdown.dropdown-menu {
+ width: 385px;
+ max-height: initial;
+ }
+
+ .avatar-container {
+ flex: initial;
+ margin-right: 0;
+ }
+
+ .ide-sidebar-project-title {
+ margin-left: $ide-tree-text-start - $ide-project-avatar-end;
+ }
+}
+
+.ide-context-body {
+ min-height: 0;
+}
+
+.ide-sidebar-project-title {
+ min-width: 0;
+
+ .sidebar-context-title {
+ white-space: nowrap;
+ display: block;
+
+ &.text-secondary {
+ font-weight: normal;
+ }
+ }
+}
+
+.ide-external-link {
+ position: relative;
+
+ svg {
+ display: none;
+ position: absolute;
+ top: 2px;
+ right: -$gl-padding;
+ }
+
+ &:hover,
+ &:focus {
+ svg {
+ display: inline-block;
+ }
+ }
+}
+
+.ide-right-sidebar {
+ width: auto;
+ min-width: 60px;
+
+ .ide-activity-bar {
+ border-left: 1px solid $white-dark;
+ }
+
+ .multi-file-commit-panel-inner {
+ width: 350px;
+ padding: $grid-size $gl-padding;
+ background-color: $white-light;
+ border-left: 1px solid $white-dark;
+ }
+
+ .ide-right-sidebar-clientside {
+ padding: 0;
+ }
+}
+
+.ide-pipeline {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ margin-top: -$grid-size;
+ margin-bottom: -$grid-size;
+
+ .empty-state {
+ margin-top: auto;
+ margin-bottom: auto;
+
+ p {
+ margin: $grid-size 0;
+ text-align: center;
+ line-height: 24px;
+ }
+
+ .btn,
+ h4 {
+ margin: 0;
+ }
+ }
+
+ .build-trace,
+ .top-bar {
+ margin-left: -$gl-padding;
+ }
+
+ &.build-page .top-bar {
+ top: 0;
+ font-size: 12px;
+ border-top-right-radius: $border-radius-default;
+ }
+}
+
+.ide-pipeline-list {
+ flex: 1;
+ overflow: auto;
+}
+
+.ide-pipeline-header {
+ min-height: 55px;
+ padding-left: $gl-padding;
+ padding-right: $gl-padding;
+
+ .ci-status-icon {
+ display: flex;
+ }
+}
+
+.ide-job-item {
+ display: flex;
+ padding: 16px;
+
+ &:not(:last-child) {
+ border-bottom: 1px solid $border-color;
+ }
+
+ .ci-status-icon {
+ display: flex;
+ justify-content: center;
+ min-width: 24px;
+ overflow: hidden;
+ }
+}
+
+.ide-stage {
+ .card-header {
+ display: flex;
+ cursor: pointer;
+
+ .ci-status-icon {
+ display: flex;
+ align-items: center;
+ }
+ }
+
+ .card-body {
+ padding: 0;
+ }
+}
+
+.ide-stage-collapse-icon {
+ margin: auto 0 auto auto;
+}
+
+.ide-stage-title {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.ide-job-header {
+ min-height: 60px;
+}
+
+.ide-nav-form {
+ .nav-links li {
+ width: 50%;
+ padding-left: 0;
+ padding-right: 0;
+
+ a {
+ text-align: center;
+
+ &:not(.active) {
+ background-color: $gray-light;
+ }
+ }
+ }
+
+ .dropdown-input {
+ padding-left: $gl-padding;
+ padding-right: $gl-padding;
+
+ .input-icon {
+ right: auto;
+ left: 10px;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+ }
+
+ .dropdown-input-field {
+ padding-left: $search-list-icon-width + $gl-padding;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ }
+
+ .tokens-container {
+ padding-left: $search-list-icon-width + $gl-padding;
+ overflow-x: hidden;
+ }
+
+ .btn-link {
+ padding-top: $gl-padding;
+ padding-bottom: $gl-padding;
+ }
+}
+
+.ide-search-list-current-icon {
+ min-width: $search-list-icon-width;
+}
+
+.ide-search-list-empty {
+ height: 230px;
+}
+
+.ide-merge-requests-dropdown-content {
+ min-height: 230px;
+ max-height: 470px;
+}
+
+.ide-merge-request-project-path {
+ font-size: 12px;
+ line-height: 16px;
+ color: $gl-text-color-secondary;
+}
+
+.ide-merge-request-info {
+ .detail-page-header {
+ line-height: initial;
+ min-height: 38px;
+ }
+
+ .issuable-details {
+ overflow: auto;
+ }
+}
+
+.ide-entry-dropdown-toggle {
+ padding: $gl-padding-4;
+ color: $gl-text-color;
+ background-color: $theme-gray-100;
+
+ &:hover {
+ background-color: $theme-gray-200;
+ }
+
+ &:active,
+ &:focus {
+ color: $white-normal;
+ background-color: $blue-500;
+ outline: 0;
+ }
+
+ svg {
+ fill: currentColor;
+ }
+}
+
+.ide-new-btn .dropdown.show .ide-entry-dropdown-toggle {
+ color: $white-normal;
+ background-color: $blue-500;
+}
+
+.ide-preview-header {
+ padding: 0 $grid-size;
+ border-bottom: 1px solid $white-dark;
+ background-color: $gray-light;
+ min-height: 44px;
+}
+
+.ide-navigator-btn {
+ height: 24px;
+ min-width: 24px;
+ max-width: 24px;
+ padding: 0;
+ margin: 0 ($grid-size / 2);
+ color: $gl-gray-light;
+
+ &:first-child {
+ margin-left: 0;
+ }
+}
+
+.ide-navigator-location {
+ padding-top: ($grid-size / 2);
+ padding-bottom: ($grid-size / 2);
+
+ &:focus {
+ outline: 0;
+ box-shadow: none;
+ border-color: $theme-gray-200;
+ }
+}
+
+.ide-preview-loading-icon {
+ right: $grid-size;
+ top: 50%;
+ transform: translateY(-50%);
+}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 750d2c8b990..a68b47b1d02 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -63,7 +63,7 @@
width: 100%;
&.is-compact {
- width: calc(100% - #{$gutter_width});
+ width: calc(100% - #{$gutter-width});
}
}
}
@@ -80,7 +80,6 @@
overflow-x: scroll;
white-space: nowrap;
min-height: 200px;
- display: flex;
@include media-breakpoint-only(sm) {
height: calc(100vh - #{$issue-board-list-difference-sm});
@@ -111,15 +110,17 @@
.board {
display: inline-block;
- flex: 1;
- min-width: 300px;
- max-width: 400px;
+ width: calc(85vw - 15px);
height: 100%;
padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2);
white-space: normal;
vertical-align: top;
+ @include media-breakpoint-up(sm) {
+ width: 400px;
+ }
+
&.is-expandable {
.board-header {
cursor: pointer;
@@ -127,8 +128,6 @@
}
&.is-collapsed {
- flex: none;
- min-width: 0;
width: 50px;
.board-header {
@@ -206,7 +205,7 @@
.board-title {
margin: 0;
- padding: 12px $gl-padding;
+ padding: $gl-padding-8 $gl-padding;
font-size: 1em;
border-bottom: 1px solid $border-color;
display: flex;
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index f030189af06..e8158cd7f6b 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -9,25 +9,21 @@
}
}
-@keyframes blinking-dots {
+@keyframes blinking-dot {
0% {
- background-color: rgba($white-light, 1);
- box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 0.2);
+ opacity: 1;
}
25% {
- background-color: rgba($white-light, 0.4);
- box-shadow: 12px 0 0 0 rgba($white-light, 2), 24px 0 0 0 rgba($white-light, 0.2);
+ opacity: 0.4;
}
75% {
- background-color: rgba($white-light, 0.4);
- box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 1);
+ opacity: 0.4;
}
100% {
- background-color: rgba($white-light, 1);
- box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 0.2);
+ opacity: 1;
}
}
@@ -182,12 +178,7 @@
}
.build-loader-animation {
- position: relative;
- width: 6px;
- height: 6px;
- margin: auto auto 12px 2px;
- border-radius: 50%;
- animation: blinking-dots 1s linear infinite;
+ @include build-loader-animation;
}
}
@@ -270,6 +261,7 @@
.block {
width: 100%;
+ word-break: break-word;
&:last-child {
border-bottom: 1px solid $border-gray-normal;
diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss
index 56beb7718a4..0f22fe21143 100644
--- a/app/assets/stylesheets/pages/clusters.scss
+++ b/app/assets/stylesheets/pages/clusters.scss
@@ -32,49 +32,23 @@
}
.gcp-signup-offer {
- background-color: $blue-50;
- border: 1px solid $blue-300;
- border-radius: $border-radius-default;
+ border-left-color: $blue-500;
- // TODO: To be superceded by cssLab
- &.alert {
- padding: 24px 16px;
-
- &-dismissable {
- padding-right: 32px;
-
- .close {
- top: -8px;
- right: -16px;
- color: $blue-500;
- opacity: 1;
- }
- }
- }
-
- .gcp-logo {
- margin-bottom: $gl-padding;
- text-align: center;
- }
-
- img {
- max-width: $gcp-signup-offer-icon-max-width;
+ svg {
+ fill: $blue-500;
+ vertical-align: middle;
}
- a:not(.btn) {
- color: $gl-link-color;
- font-weight: normal;
- text-decoration: none;
- }
+ .gcp-signup-offer--content {
+ display: flex;
- @include media-breakpoint-up(sm) {
- > div {
- display: flex;
- align-items: center;
+ h4 {
+ font-size: 16px;
+ line-height: 24px;
}
- .gcp-logo {
- margin: 0;
+ .gcp-signup-offer--icon {
+ align-self: flex-start;
}
}
}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 49226ae8eac..bce83bf0dd0 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -1,9 +1,17 @@
-.commit-description {
- background: none;
+%commit-description-base {
+ padding: $gl-padding-8 0 $gl-padding-8 $gl-padding-8;
+ margin-top: $gl-padding-8;
border: 0;
- padding: 0;
+ border-radius: unset;
+ background: none;
word-break: normal;
- white-space: pre-wrap;
+ overflow-x: auto;
+ border-left: 3px solid $white-dark;
+ color: $gl-text-color-secondary;
+}
+
+.commit-description {
+ @extend %commit-description-base;
}
.js-details-expand {
@@ -79,7 +87,7 @@
.commit-message-container {
background-color: $body-bg;
position: relative;
- font-family: $monospace_font;
+ font-family: $monospace-font;
$left: 12px;
overflow: hidden; // See https://gitlab.com/gitlab-org/gitlab-ce/issues/13987
.max-width-marker {
@@ -175,11 +183,17 @@
justify-content: space-between;
align-items: start;
flex-grow: 1;
+ min-width: 0;
+
+ .project_namespace {
+ color: $gl-text-color-secondary;
+ }
}
.commit-content {
padding-right: 10px;
white-space: normal;
+ overflow: hidden;
.commit-title {
display: flex;
@@ -205,7 +219,7 @@
> .ci-status-link,
> .btn,
> .commit-sha-group {
- margin-left: $gl-padding-8;
+ margin-left: $gl-padding;
}
}
@@ -235,10 +249,6 @@
fill: $gl-text-color-secondary;
}
- .fa-clipboard {
- color: $gl-text-color-secondary;
- }
-
:first-child {
border-bottom-left-radius: $border-radius-default;
border-top-left-radius: $border-radius-default;
@@ -261,26 +271,22 @@
vertical-align: baseline;
}
- a.autodevops-badge {
- color: $white-light;
- }
+ a {
+ color: $gl-text-color;
- a.autodevops-link {
- color: $gl-link-color;
+ &.autodevops-badge {
+ color: $white-light;
+ }
+
+ &.autodevops-link {
+ color: $gl-link-color;
+ }
}
.commit-row-description {
- font-size: 14px;
- padding: 0 0 0 $gl-padding-8;
- border: 0;
+ @extend %commit-description-base;
display: none;
- white-space: pre-wrap;
- word-break: normal;
- color: $gl-text-color-secondary;
- background: none;
- font-family: inherit;
- border-left: 2px solid $theme-gray-300;
- border-radius: unset;
+ flex: 1;
a {
color: $gl-text-color;
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index a22c666a525..e2c0a7a6225 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -368,7 +368,7 @@
.fa {
color: $gl-text-color-secondary;
- font-size: $code_font_size;
+ font-size: $code-font-size;
}
}
}
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index 2e007c52592..37ed5ae674a 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -10,7 +10,7 @@
}
.issue_created_ago,
- .author_link {
+ .author-link {
white-space: nowrap;
}
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index a90a9c6e486..591e21243ed 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -15,7 +15,8 @@
}
svg {
- vertical-align: text-bottom;
+ vertical-align: middle;
+ top: -1px;
}
}
@@ -56,7 +57,7 @@
table {
width: 100%;
- font-family: $monospace_font;
+ font-family: $monospace-font;
border: 0;
border-collapse: separate;
margin: 0;
@@ -73,8 +74,8 @@
}
.line_holder td {
- line-height: $code_line_height;
- font-size: $code_font_size;
+ line-height: $code-line-height;
+ font-size: $code-font-size;
&.noteable_line {
position: relative;
@@ -518,6 +519,12 @@
outline: none;
color: $gl-link-hover-color;
}
+
+ .caret-icon {
+ position: relative;
+ top: 2px;
+ left: -1px;
+ }
}
// Mobile
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 437621299e0..ddd1f8cc98a 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -84,7 +84,7 @@
.soft-wrap-toggle {
display: inline-block;
vertical-align: top;
- font-family: $regular_font;
+ font-family: $regular-font;
}
.soft-wrap-toggle {
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 06f08ae2215..8a074017344 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -23,7 +23,7 @@
}
.btn-group {
- > a {
+ > .btn:not(.btn-danger) {
color: $gl-text-color-secondary;
}
@@ -222,6 +222,23 @@
}
}
+.prometheus-graphs {
+ .environments {
+ .dropdown-menu-toggle {
+ svg {
+ position: absolute;
+ right: 5%;
+ top: 25%;
+ }
+ }
+
+ .dropdown-menu-toggle,
+ .dropdown-menu {
+ width: 240px;
+ }
+ }
+}
+
.environments-actions {
.external-url,
.monitoring-url,
@@ -276,6 +293,8 @@
.prometheus-graph-flag {
display: block;
min-width: 160px;
+ border: 0;
+ box-shadow: 0 1px 4px 0 $black-transparent;
h5 {
padding: 0;
@@ -295,7 +314,6 @@
&.popover {
padding: 0;
- border: 1px solid $border-color;
&.left {
left: auto;
@@ -303,12 +321,19 @@
margin-right: 10px;
> .arrow {
- right: -16px;
+ right: -14px;
border-left-color: $border-color;
}
> .arrow::after {
- border-left-color: $theme-gray-50;
+ border-top: 6px solid transparent;
+ border-bottom: 6px solid transparent;
+ border-left: 4px solid $theme-gray-50;
+ }
+
+ .arrow-shadow {
+ right: -3px;
+ box-shadow: 1px 0 9px 0 $black-transparent;
}
}
@@ -318,19 +343,35 @@
margin-left: 10px;
> .arrow {
- left: -16px;
+ left: -7px;
border-right-color: $border-color;
}
> .arrow::after {
- border-right-color: $theme-gray-50;
+ border-top: 6px solid transparent;
+ border-bottom: 6px solid transparent;
+ border-right: 4px solid $theme-gray-50;
+ }
+
+ .arrow-shadow {
+ left: -3px;
+ box-shadow: 1px 0 8px 0 $black-transparent;
}
}
> .arrow {
- top: 16px;
- margin-top: -8px;
- border-width: 8px;
+ top: 10px;
+ margin: 0;
+ }
+
+ .arrow-shadow {
+ content: "";
+ position: absolute;
+ width: 7px;
+ height: 7px;
+ background-color: transparent;
+ transform: rotate(45deg);
+ top: 13px;
}
> .popover-title,
@@ -338,10 +379,12 @@
padding: 8px;
font-size: 12px;
white-space: nowrap;
+ position: relative;
}
> .popover-title {
background-color: $theme-gray-50;
+ border-radius: $border-radius-default $border-radius-default 0 0;
}
}
@@ -435,7 +478,7 @@
}
.deploy-info-text-link {
- font-family: $monospace_font;
+ font-family: $monospace-font;
fill: $gl-link-color;
&:hover {
diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss
index 84da9180f93..49d8a5d959b 100644
--- a/app/assets/stylesheets/pages/graph.scss
+++ b/app/assets/stylesheets/pages/graph.scss
@@ -31,3 +31,61 @@
color: $gl-text-red;
}
}
+
+.svg-graph-container {
+ width: 100%;
+
+ .axis-tick {
+ opacity: 0.4;
+ }
+
+ .tick-text {
+ fill: $gl-text-color-secondary;
+ }
+
+ .x-axis-text {
+ fill: $theme-gray-900;
+ }
+
+ .bar-rect {
+ fill: rgba($blue-500, 0.1);
+ stroke: $blue-500;
+ }
+
+ .bar-rect:hover {
+ fill: rgba($blue-700, 0.3);
+ }
+
+ .y-axis-label {
+ line {
+ stroke: $stat-graph-axis-fill;
+ }
+
+ text {
+ font-weight: bold;
+ font-size: 12px;
+ fill: $theme-gray-800;
+ }
+ }
+}
+
+.svg-graph-container-with-grab {
+ cursor: grab;
+ cursor: -webkit-grab;
+}
+
+.svg-graph-container-grabbed {
+ cursor: grabbing;
+ cursor: -webkit-grabbing;
+}
+
+@keyframes flickerAnimation {
+ 0% { opacity: 1; }
+ 50% { opacity: 0; }
+ 100% { opacity: 1; }
+}
+
+.animate-flicker {
+ animation: flickerAnimation 1.5s infinite;
+ fill: $theme-gray-500;
+}
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 05bf5596fb3..1587aebfe1d 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -290,9 +290,8 @@
}
.folder-toggle-wrap {
- float: left;
- line-height: $list-text-height;
font-size: 0;
+ flex-shrink: 0;
span {
font-size: $gl-font-size;
@@ -308,7 +307,7 @@
width: 15px;
svg {
- margin-bottom: 2px;
+ margin-bottom: 1px;
}
}
@@ -391,9 +390,17 @@
cursor: pointer;
}
- .avatar-container > a {
- width: 100%;
- text-decoration: none;
+ .group-text {
+ min-width: 0; // allows for truncated text within flex children
+ }
+
+ .avatar-container {
+ flex-shrink: 0;
+
+ > a {
+ width: 100%;
+ text-decoration: none;
+ }
}
&.has-more-items {
@@ -401,9 +408,18 @@
padding: 20px 10px;
}
+ .description {
+ p {
+ @include str-truncated;
+
+ max-width: none;
+ }
+ }
+
.stats {
position: relative;
- line-height: 46px;
+ line-height: normal;
+ flex-shrink: 0;
> span {
display: inline-flex;
@@ -422,14 +438,20 @@
}
.controls {
- margin-left: 5px;
+ flex-shrink: 0;
> .btn {
- margin-right: $btn-margin-5;
+ margin: 0 0 0 $btn-margin-5;
}
}
}
+ @include media-breakpoint-down(xs) {
+ .group-stats {
+ display: none;
+ }
+ }
+
.project-row-contents .stats {
line-height: inherit;
@@ -451,18 +473,6 @@
}
}
-ul.group-list-tree {
- li.group-row {
- > .group-row-contents .title {
- line-height: $list-text-height;
- }
-
- &.has-description > .group-row-contents .title {
- line-height: inherit;
- }
- }
-}
-
.js-groups-list-holder {
.groups-list-loading {
font-size: 34px;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index f9fd9f1ab8b..8e78d9f65eb 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -166,7 +166,7 @@
border-bottom: 1px solid $border-gray-normal;
// This prevents the mess when resizing the sidebar
// of elements repositioning themselves..
- width: $gutter_inner_width;
+ width: $gutter-inner-width;
// --
&.issuable-sidebar-header {
@@ -197,7 +197,7 @@
}
&.assignee {
- .author_link {
+ .author-link {
display: block;
padding-left: 42px;
position: relative;
@@ -290,7 +290,7 @@
}
&.right-sidebar-expanded {
- width: $gutter_width;
+ width: $gutter-width;
.value {
line-height: 1;
@@ -377,11 +377,11 @@
display: block;
}
- width: $gutter_collapsed_width;
+ width: $gutter-collapsed-width;
padding: 0;
.block {
- width: $gutter_collapsed_width - 2px;
+ width: $gutter-collapsed-width - 2px;
padding: 15px 0 0;
border-bottom: 0;
overflow: hidden;
@@ -449,6 +449,7 @@
.todo-undone {
color: $gl-link-color;
+ fill: $gl-link-color;
}
.author {
@@ -486,7 +487,7 @@
padding-bottom: 0;
margin-bottom: 10px;
- .author_link {
+ .author-link {
padding-left: 0;
.avatar {
@@ -595,7 +596,7 @@
margin: 16px 0 0;
font-size: 85%;
- .author_link {
+ .author-link {
color: $gray-darkest;
}
}
@@ -620,7 +621,7 @@
padding-right: 0;
}
- .author_link {
+ .author-link {
display: block;
}
@@ -834,17 +835,7 @@
}
.compare-meter {
- &.within_estimate {
- .meter-fill {
- background: $gl-primary;
- }
- }
-
&.over_estimate {
- .meter-fill {
- background: $red-500;
- }
-
.time-remaining,
.compare-value.spent {
color: $red-500;
@@ -852,18 +843,6 @@
}
}
- .meter-container {
- background: $border-gray-light;
- border-radius: 3px;
-
- .meter-fill {
- max-width: 100%;
- height: 5px;
- border-radius: 3px;
- background: $gl-primary;
- }
- }
-
.compare-display-container {
display: flex;
justify-content: space-between;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 19fb99bfa93..212e5979273 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -12,7 +12,7 @@
}
.issuable-meta {
- .author_link {
+ .author-link {
display: inline-block;
}
diff --git a/app/assets/stylesheets/pages/issues/issue_count_badge.scss b/app/assets/stylesheets/pages/issues/issue_count_badge.scss
index ccb62bfed18..4fba89e956b 100644
--- a/app/assets/stylesheets/pages/issues/issue_count_badge.scss
+++ b/app/assets/stylesheets/pages/issues/issue_count_badge.scss
@@ -1,29 +1,11 @@
.issue-count-badge {
display: inline-flex;
- align-items: stretch;
- height: 24px;
-}
-
-.issue-count-badge-count {
- display: flex;
- align-items: center;
- padding-right: 10px;
- padding-left: 10px;
- border: 1px solid $border-color;
border-radius: $border-radius-base;
- line-height: 1;
-
- &.has-btn {
- border-right: 0;
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- }
+ border: 1px solid $border-color;
+ padding: 5px $gl-padding-8;
}
-.issue-count-badge-add-button {
- display: flex;
+.issue-count-badge-count {
+ display: inline-flex;
align-items: center;
- border: 1px solid $border-color;
- border-radius: 0 $border-radius-base $border-radius-base 0;
- line-height: 1;
}
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 79cac7f4ff0..b25dc4f419a 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -72,6 +72,9 @@
}
.manage-labels-list {
+ padding: 0;
+ margin-bottom: 0;
+
> li:not(.empty-message):not(.is-not-draggable) {
background-color: $white-light;
margin-bottom: 5px;
@@ -79,6 +82,11 @@
justify-content: space-between;
padding: $gl-padding;
border-radius: $border-radius-default;
+ border: 1px solid $theme-gray-100;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
&.sortable-ghost {
opacity: 0.3;
@@ -89,6 +97,7 @@
cursor: move;
cursor: -webkit-grab;
cursor: -moz-grab;
+ border: 0;
&:active {
cursor: -webkit-grabbing;
@@ -241,7 +250,10 @@
.label-actions-list {
list-style: none;
flex-shrink: 0;
+ text-align: right;
padding: 0;
+ position: relative;
+ margin: 0;
}
.label-badge {
@@ -262,6 +274,7 @@
.label-links {
list-style: none;
+ margin: 0;
padding: 0;
white-space: nowrap;
}
@@ -270,6 +283,16 @@
padding: 0;
}
+.label-description {
+ .description-text {
+ margin-bottom: 10px;
+
+ .admin-labels & {
+ margin-bottom: 0;
+ }
+ }
+}
+
.label-list-item {
.content-list &::before,
.content-list &::after {
@@ -317,6 +340,64 @@
fill: $blue-600;
}
}
+
+ &.remove-row {
+ &:hover {
+ color: $gl-text-red;
+
+ svg {
+ fill: $gl-text-red;
+ }
+ }
+ }
+ }
+}
+
+@media (max-width: map-get($grid-breakpoints, md)-1) {
+ .manage-labels-list {
+ > li:not(.empty-message):not(.is-not-draggable) {
+ flex-wrap: wrap;
+ }
+
+ .label-name {
+ order: 1;
+ flex-grow: 1;
+ width: auto;
+ max-width: 100%;
+ }
+
+ .label-actions-list {
+ order: 2;
+ flex-shrink: 1;
+ text-align: left;
+ }
+
+ .label-links {
+ white-space: normal;
+ }
+
+ .label-description {
+ order: 3;
+ width: 100%;
+
+ > .append-right-default.prepend-left-default {
+ margin-left: 0;
+ margin-right: 0;
+ }
+ }
+ }
+}
+
+@media (max-width: 910px) {
+ .priority-badge {
+ display: block;
+ width: 100%;
+ margin-left: 0;
+ margin-top: $gl-padding;
+
+ .label-badge {
+ display: inline-block;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index c1b1d2e028d..8a4a2caa6c9 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -237,7 +237,7 @@
}
.login-page-broadcast {
- margin-top: 50px;
+ margin-top: 40px;
}
.navless-container {
diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss
index e76525fdbf6..d26659701e1 100644
--- a/app/assets/stylesheets/pages/merge_conflicts.scss
+++ b/app/assets/stylesheets/pages/merge_conflicts.scss
@@ -1,167 +1,162 @@
// Disabled to use the color map for creating color schemes
// scss-lint:disable ColorVariable
$colors: (
- white_header_head_neutral : #e1fad7,
- white_line_head_neutral : #effdec,
- white_button_head_neutral : #9adb84,
+ white-header-head-neutral : #e1fad7,
+ white-line-head-neutral : #effdec,
+ white-button-head-neutral : #9adb84,
- white_header_head_chosen : #baf0a8,
- white_line_head_chosen : #e1fad7,
- white_button_head_chosen : #52c22d,
+ white-header-head-chosen : #baf0a8,
+ white-line-head-chosen : #e1fad7,
+ white-button-head-chosen : #52c22d,
- white_header_origin_neutral : #e0f0ff,
- white_line_origin_neutral : #f2f9ff,
- white_button_origin_neutral : #87c2fa,
+ white-header-origin-neutral : #e0f0ff,
+ white-line-origin-neutral : #f2f9ff,
+ white-button-origin-neutral : #87c2fa,
- white_header_origin_chosen : #add8ff,
- white_line_origin_chosen : #e0f0ff,
- white_button_origin_chosen : #268ced,
+ white-header-origin-chosen : #add8ff,
+ white-line-origin-chosen : #e0f0ff,
+ white-button-origin-chosen : #268ced,
- white_header_not_chosen : #f0f0f0,
- white_line_not_chosen : $gray-light,
+ white-header-not-chosen : #f0f0f0,
+ white-line-not-chosen : $gray-light,
+ dark-header-head-neutral : rgba(#3f3, .2),
+ dark-line-head-neutral : rgba(#3f3, .1),
+ dark-button-head-neutral : #40874f,
- dark_header_head_neutral : rgba(#3f3, .2),
- dark_line_head_neutral : rgba(#3f3, .1),
- dark_button_head_neutral : #40874f,
+ dark-header-head-chosen : rgba(#3f3, .33),
+ dark-line-head-chosen : rgba(#3f3, .2),
+ dark-button-head-chosen : #258537,
- dark_header_head_chosen : rgba(#3f3, .33),
- dark_line_head_chosen : rgba(#3f3, .2),
- dark_button_head_chosen : #258537,
+ dark-header-origin-neutral : rgba(#2878c9, .4),
+ dark-line-origin-neutral : rgba(#2878c9, .3),
+ dark-button-origin-neutral : #2a5c8c,
- dark_header_origin_neutral : rgba(#2878c9, .4),
- dark_line_origin_neutral : rgba(#2878c9, .3),
- dark_button_origin_neutral : #2a5c8c,
+ dark-header-origin-chosen : rgba(#2878c9, .6),
+ dark-line-origin-chosen : rgba(#2878c9, .4),
+ dark-button-origin-chosen : #1d6cbf,
- dark_header_origin_chosen : rgba(#2878c9, .6),
- dark_line_origin_chosen : rgba(#2878c9, .4),
- dark_button_origin_chosen : #1d6cbf,
+ dark-header-not-chosen : rgba(#fff, .25),
+ dark-line-not-chosen : rgba(#fff, .1),
- dark_header_not_chosen : rgba(#fff, .25),
- dark_line_not_chosen : rgba(#fff, .1),
+ monokai-header-head-neutral : rgba(#a6e22e, .25),
+ monokai-line-head-neutral : rgba(#a6e22e, .1),
+ monokai-button-head-neutral : #376b20,
+ monokai-header-head-chosen : rgba(#a6e22e, .4),
+ monokai-line-head-chosen : rgba(#a6e22e, .25),
+ monokai-button-head-chosen : #39800d,
- monokai_header_head_neutral : rgba(#a6e22e, .25),
- monokai_line_head_neutral : rgba(#a6e22e, .1),
- monokai_button_head_neutral : #376b20,
+ monokai-header-origin-neutral : rgba(#60d9f1, .35),
+ monokai-line-origin-neutral : rgba(#60d9f1, .15),
+ monokai-button-origin-neutral : #38848c,
- monokai_header_head_chosen : rgba(#a6e22e, .4),
- monokai_line_head_chosen : rgba(#a6e22e, .25),
- monokai_button_head_chosen : #39800d,
+ monokai-header-origin-chosen : rgba(#60d9f1, .5),
+ monokai-line-origin-chosen : rgba(#60d9f1, .35),
+ monokai-button-origin-chosen : #3ea4b2,
- monokai_header_origin_neutral : rgba(#60d9f1, .35),
- monokai_line_origin_neutral : rgba(#60d9f1, .15),
- monokai_button_origin_neutral : #38848c,
+ monokai-header-not-chosen : rgba(#76715d, .24),
+ monokai-line-not-chosen : rgba(#76715d, .1),
- monokai_header_origin_chosen : rgba(#60d9f1, .5),
- monokai_line_origin_chosen : rgba(#60d9f1, .35),
- monokai_button_origin_chosen : #3ea4b2,
+ solarized-light-header-head-neutral : rgba(#859900, .37),
+ solarized-light-line-head-neutral : rgba(#859900, .2),
+ solarized-light-button-head-neutral : #afb262,
- monokai_header_not_chosen : rgba(#76715d, .24),
- monokai_line_not_chosen : rgba(#76715d, .1),
+ solarized-light-header-head-chosen : rgba(#859900, .5),
+ solarized-light-line-head-chosen : rgba(#859900, .37),
+ solarized-light-button-head-chosen : #94993d,
+ solarized-light-header-origin-neutral : rgba(#2878c9, .37),
+ solarized-light-line-origin-neutral : rgba(#2878c9, .15),
+ solarized-light-button-origin-neutral : #60a1bf,
- solarized_light_header_head_neutral : rgba(#859900, .37),
- solarized_light_line_head_neutral : rgba(#859900, .2),
- solarized_light_button_head_neutral : #afb262,
+ solarized-light-header-origin-chosen : rgba(#2878c9, .6),
+ solarized-light-line-origin-chosen : rgba(#2878c9, .37),
+ solarized-light-button-origin-chosen : #2482b2,
- solarized_light_header_head_chosen : rgba(#859900, .5),
- solarized_light_line_head_chosen : rgba(#859900, .37),
- solarized_light_button_head_chosen : #94993d,
+ solarized-light-header-not-chosen : rgba(#839496, .37),
+ solarized-light-line-not-chosen : rgba(#839496, .2),
- solarized_light_header_origin_neutral : rgba(#2878c9, .37),
- solarized_light_line_origin_neutral : rgba(#2878c9, .15),
- solarized_light_button_origin_neutral : #60a1bf,
+ solarized-dark-header-head-neutral : rgba(#859900, .35),
+ solarized-dark-line-head-neutral : rgba(#859900, .15),
+ solarized-dark-button-head-neutral : #376b20,
- solarized_light_header_origin_chosen : rgba(#2878c9, .6),
- solarized_light_line_origin_chosen : rgba(#2878c9, .37),
- solarized_light_button_origin_chosen : #2482b2,
+ solarized-dark-header-head-chosen : rgba(#859900, .5),
+ solarized-dark-line-head-chosen : rgba(#859900, .35),
+ solarized-dark-button-head-chosen : #39800d,
- solarized_light_header_not_chosen : rgba(#839496, .37),
- solarized_light_line_not_chosen : rgba(#839496, .2),
+ solarized-dark-header-origin-neutral : rgba(#2878c9, .35),
+ solarized-dark-line-origin-neutral : rgba(#2878c9, .15),
+ solarized-dark-button-origin-neutral : #086799,
+ solarized-dark-header-origin-chosen : rgba(#2878c9, .6),
+ solarized-dark-line-origin-chosen : rgba(#2878c9, .35),
+ solarized-dark-button-origin-chosen : #0082cc,
- solarized_dark_header_head_neutral : rgba(#859900, .35),
- solarized_dark_line_head_neutral : rgba(#859900, .15),
- solarized_dark_button_head_neutral : #376b20,
-
- solarized_dark_header_head_chosen : rgba(#859900, .5),
- solarized_dark_line_head_chosen : rgba(#859900, .35),
- solarized_dark_button_head_chosen : #39800d,
-
- solarized_dark_header_origin_neutral : rgba(#2878c9, .35),
- solarized_dark_line_origin_neutral : rgba(#2878c9, .15),
- solarized_dark_button_origin_neutral : #086799,
-
- solarized_dark_header_origin_chosen : rgba(#2878c9, .6),
- solarized_dark_line_origin_chosen : rgba(#2878c9, .35),
- solarized_dark_button_origin_chosen : #0082cc,
-
- solarized_dark_header_not_chosen : rgba(#839496, .25),
- solarized_dark_line_not_chosen : rgba(#839496, .15)
+ solarized-dark-header-not-chosen : rgba(#839496, .25),
+ solarized-dark-line-not-chosen : rgba(#839496, .15)
);
// scss-lint:enable ColorVariable
-
@mixin color-scheme($color) {
.header.line_content,
.diff-line-num {
&.origin {
- background-color: map-get($colors, #{$color}_header_origin_neutral);
- border-color: map-get($colors, #{$color}_header_origin_neutral);
+ background-color: map-get($colors, #{$color}-header-origin-neutral);
+ border-color: map-get($colors, #{$color}-header-origin-neutral);
button {
- background-color: map-get($colors, #{$color}_button_origin_neutral);
- border-color: darken(map-get($colors, #{$color}_button_origin_neutral), 15);
+ background-color: map-get($colors, #{$color}-button-origin-neutral);
+ border-color: darken(map-get($colors, #{$color}-button-origin-neutral), 15);
}
&.selected {
- background-color: map-get($colors, #{$color}_header_origin_chosen);
- border-color: map-get($colors, #{$color}_header_origin_chosen);
+ background-color: map-get($colors, #{$color}-header-origin-chosen);
+ border-color: map-get($colors, #{$color}-header-origin-chosen);
button {
- background-color: map-get($colors, #{$color}_button_origin_chosen);
- border-color: darken(map-get($colors, #{$color}_button_origin_chosen), 15);
+ background-color: map-get($colors, #{$color}-button-origin-chosen);
+ border-color: darken(map-get($colors, #{$color}-button-origin-chosen), 15);
}
}
&.unselected {
- background-color: map-get($colors, #{$color}_header_not_chosen);
- border-color: map-get($colors, #{$color}_header_not_chosen);
+ background-color: map-get($colors, #{$color}-header-not-chosen);
+ border-color: map-get($colors, #{$color}-header-not-chosen);
button {
- background-color: lighten(map-get($colors, #{$color}_button_origin_neutral), 15);
- border-color: map-get($colors, #{$color}_button_origin_neutral);
+ background-color: lighten(map-get($colors, #{$color}-button-origin-neutral), 15);
+ border-color: map-get($colors, #{$color}-button-origin-neutral);
}
}
}
&.head {
- background-color: map-get($colors, #{$color}_header_head_neutral);
- border-color: map-get($colors, #{$color}_header_head_neutral);
+ background-color: map-get($colors, #{$color}-header-head-neutral);
+ border-color: map-get($colors, #{$color}-header-head-neutral);
button {
- background-color: map-get($colors, #{$color}_button_head_neutral);
- border-color: darken(map-get($colors, #{$color}_button_head_neutral), 15);
+ background-color: map-get($colors, #{$color}-button-head-neutral);
+ border-color: darken(map-get($colors, #{$color}-button-head-neutral), 15);
}
&.selected {
- background-color: map-get($colors, #{$color}_header_head_chosen);
- border-color: map-get($colors, #{$color}_header_head_chosen);
+ background-color: map-get($colors, #{$color}-header-head-chosen);
+ border-color: map-get($colors, #{$color}-header-head-chosen);
button {
- background-color: map-get($colors, #{$color}_button_head_chosen);
- border-color: darken(map-get($colors, #{$color}_button_head_chosen), 15);
+ background-color: map-get($colors, #{$color}-button-head-chosen);
+ border-color: darken(map-get($colors, #{$color}-button-head-chosen), 15);
}
}
&.unselected {
- background-color: map-get($colors, #{$color}_header_not_chosen);
- border-color: map-get($colors, #{$color}_header_not_chosen);
+ background-color: map-get($colors, #{$color}-header-not-chosen);
+ border-color: map-get($colors, #{$color}-header-not-chosen);
button {
- background-color: lighten(map-get($colors, #{$color}_button_head_neutral), 15);
- border-color: map-get($colors, #{$color}_button_head_neutral);
+ background-color: lighten(map-get($colors, #{$color}-button-head-neutral), 15);
+ border-color: map-get($colors, #{$color}-button-head-neutral);
}
}
}
@@ -169,32 +164,31 @@ $colors: (
.line_content {
&.origin {
- background-color: map-get($colors, #{$color}_line_origin_neutral);
+ background-color: map-get($colors, #{$color}-line-origin-neutral);
&.selected {
- background-color: map-get($colors, #{$color}_line_origin_chosen);
+ background-color: map-get($colors, #{$color}-line-origin-chosen);
}
&.unselected {
- background-color: map-get($colors, #{$color}_line_not_chosen);
+ background-color: map-get($colors, #{$color}-line-not-chosen);
}
}
&.head {
- background-color: map-get($colors, #{$color}_line_head_neutral);
+ background-color: map-get($colors, #{$color}-line-head-neutral);
&.selected {
- background-color: map-get($colors, #{$color}_line_head_chosen);
+ background-color: map-get($colors, #{$color}-line-head-chosen);
}
&.unselected {
- background-color: map-get($colors, #{$color}_line_not_chosen);
+ background-color: map-get($colors, #{$color}-line-not-chosen);
}
}
}
}
-
#conflicts {
.white {
@@ -210,11 +204,11 @@ $colors: (
}
.solarized-light {
- @include color-scheme('solarized_light')
+ @include color-scheme('solarized-light')
}
.solarized-dark {
- @include color-scheme('solarized_dark')
+ @include color-scheme('solarized-dark')
}
.diff-wrap-lines .line_content {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index ccf5d632614..621321101cd 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -15,16 +15,71 @@
}
}
+.mr-widget-border-top {
+ border-top: 1px solid $border-color;
+}
+
+.media-section {
+ @include media-breakpoint-down(md) {
+ align-items: flex-start;
+
+ .media-body {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+ }
+
+ .code-text {
+ @include media-breakpoint-up(lg) {
+ align-self: center;
+ flex: 1;
+ }
+ }
+}
+
+.mr-widget-section {
+ .media {
+ align-items: center;
+ }
+
+ .code-text {
+ flex: 1;
+ }
+}
+
+
+.mr-widget-heading {
+ position: relative;
+ border: 1px solid $border-color;
+ border-radius: 4px;
+
+ &:not(.deploy-heading)::before {
+ content: '';
+ border-left: 1px solid $theme-gray-200;
+ position: absolute;
+ left: 32px;
+ top: -17px;
+ height: 16px;
+ }
+}
+
+.mr-section-container {
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+ border-top: 0;
+}
+
+.mr-widget-heading,
+.mr-widget-section,
+.mr-widget-footer {
+ padding: $gl-padding;
+}
+
.mr-state-widget {
color: $gl-text-color;
- border: 1px solid $border-color;
- border-radius: 2px;
- line-height: 28px;
- .mr-widget-heading,
.mr-widget-section,
.mr-widget-footer {
- padding: $gl-padding;
border-top: solid 1px $border-color;
}
@@ -32,6 +87,14 @@
padding: 0;
}
+ .mr-report {
+ padding: 0;
+
+ > .media {
+ padding: $gl-padding;
+ }
+ }
+
form {
margin-bottom: 0;
@@ -94,10 +157,8 @@
.modify-merge-commit-link {
padding: 0;
-
background-color: transparent;
border: 0;
-
color: $gl-text-color;
&:hover,
@@ -124,10 +185,17 @@
.ci-widget {
color: $gl-text-color;
display: flex;
+ align-items: center;
+ justify-content: space-between;
@include media-breakpoint-down(xs) {
flex-wrap: wrap;
}
+
+ .ci-widget-content {
+ display: flex;
+ align-items: center;
+ }
}
.mr-widget-icon {
@@ -136,8 +204,6 @@
}
.ci-status-icon svg {
- width: $status-icon-size;
- height: $status-icon-size;
margin: 3px 0;
position: relative;
overflow: visible;
@@ -145,8 +211,6 @@
}
.mr-widget-pipeline-graph {
- padding: 0 4px;
-
.dropdown-menu {
z-index: 300;
}
@@ -157,7 +221,7 @@
}
.normal {
- line-height: 28px;
+ flex: 1;
}
.capitalize {
@@ -168,7 +232,7 @@
@extend .ref-name;
color: $gl-text-color;
- font-weight: $gl-font-weight-bold;
+ font-weight: normal;
overflow: hidden;
word-break: break-all;
@@ -185,13 +249,19 @@
position: absolute;
content: '...';
right: 0;
- font-family: $regular_font;
+ font-family: $regular-font;
background-color: $gray-light;
}
}
}
+ .widget-status-icon {
+ align-self: flex-start;
+ }
+
.mr-widget-body {
+ line-height: 28px;
+
@include clearfix;
&.media > *:first-child {
@@ -477,15 +547,60 @@
.mr-source-target {
display: flex;
flex-wrap: wrap;
- justify-content: space-between;
- align-items: center;
- background-color: $gray-light;
- border-radius: $border-radius-default $border-radius-default 0 0;
- padding: $gl-padding / 2 $gl-padding;
+ border-radius: $border-radius-default;
+ padding: $gl-padding;
+ border: 1px solid $border-color;
+ min-height: 69px;
+
+ @include media-breakpoint-up(md) {
+ align-items: center;
+ }
.dropdown-toggle .fa {
color: $gl-text-color;
}
+
+ .git-merge-icon-container {
+ border: 1px solid $theme-gray-400;
+ border-radius: 50%;
+ height: 32px;
+ width: 32px;
+ color: $theme-gray-700;
+ line-height: 28px;
+
+ .ic-git-merge {
+ vertical-align: middle;
+ width: 31px;
+ }
+ }
+
+ .git-merge-container {
+ justify-content: space-between;
+ flex: 1;
+ flex-direction: row;
+ align-items: center;
+
+ @include media-breakpoint-down(md) {
+ flex-direction: column;
+ align-items: flex-start;
+
+ .branch-actions {
+ margin-top: 16px;
+ }
+ }
+
+ @include media-breakpoint-up(lg) {
+ .branch-actions {
+ align-self: center;
+ margin-left: $gl-padding;
+ }
+ }
+ }
+
+ .diverged-commits-count {
+ color: $gl-text-color-secondary;
+ font-size: 12px;
+ }
}
.card-new-merge-request {
@@ -720,13 +835,25 @@
}
.deploy-heading {
+ margin-top: -19px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ background-color: $gray-light;
+
+ @include media-breakpoint-up(md) {
+ padding: $gl-padding-8 $gl-padding;
+ }
+
.media-body {
min-width: 0;
+ font-size: 12px;
+ margin-left: 48px;
}
}
.deploy-body {
display: flex;
+ align-items: center;
flex-wrap: wrap;
@include media-breakpoint-up(xs) {
@@ -734,21 +861,38 @@
white-space: nowrap;
}
+ @include media-breakpoint-down(md) {
+ flex-direction: column;
+ align-items: flex-start;
+
+ .deployment-info {
+ margin-bottom: $gl-padding;
+ }
+ }
+
> *:not(:last-child) {
margin-right: .3em;
}
-}
-.deploy-link {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- min-width: 100px;
- max-width: 150px;
+ svg {
+ vertical-align: text-top;
+ }
- @include media-breakpoint-up(xs) {
- min-width: 0;
- max-width: 100%;
+ .deployment-info {
+ flex: 1;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ min-width: 100px;
+
+ @include media-breakpoint-up(xs) {
+ min-width: 0;
+ max-width: 100%;
+ }
+ }
+
+ .btn svg {
+ fill: $theme-gray-700;
}
}
@@ -768,3 +912,33 @@
}
}
}
+
+.ci-widget-container {
+ justify-content: space-between;
+ flex: 1;
+ flex-direction: row;
+
+ @include media-breakpoint-down(md) {
+ flex-direction: column;
+
+ .stage-cell .stage-container {
+ margin-top: 16px;
+ }
+
+ .dropdown .mini-pipeline-graph-dropdown-menu.dropdown-menu {
+ transform: initial;
+ }
+ }
+
+ .coverage {
+ font-size: 12px;
+ color: $theme-gray-700;
+ line-height: initial;
+ }
+
+ .mini-pipeline-graph-dropdown-toggle,
+ .stage-cell .mini-pipeline-graph-dropdown-toggle svg {
+ height: $ci-action-icon-size-lg;
+ width: $ci-action-icon-size-lg;
+ }
+}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index dba83e56d72..46437ce5841 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -3,8 +3,20 @@
}
.milestones {
+ padding: $gl-padding-8;
+ margin-top: $gl-padding-8;
+ border-radius: $border-radius-default;
+ background-color: $theme-gray-100;
+
.milestone {
- padding: 10px 16px;
+ border: 0;
+ padding: $gl-padding-top $gl-padding;
+ border-radius: $border-radius-default;
+ background-color: $white-light;
+
+ &:not(:last-child) {
+ margin-bottom: $gl-padding-4;
+ }
h4 {
font-weight: $gl-font-weight-bold;
@@ -13,6 +25,24 @@
.progress {
width: 100%;
height: 6px;
+ margin-bottom: $gl-padding-4;
+ }
+
+ .milestone-progress {
+ a {
+ color: $gl-link-color;
+ }
+ }
+
+ .status-box {
+ font-size: $tooltip-font-size;
+ margin-top: 0;
+ margin-right: $gl-padding-4;
+
+ @include media-breakpoint-down(xs) {
+ line-height: unset;
+ padding: $gl-padding-4 $gl-input-padding;
+ }
}
}
}
@@ -229,6 +259,10 @@
}
}
+.milestone-range {
+ color: $gl-text-color-tertiary;
+}
+
@include media-breakpoint-down(xs) {
.milestone-banner-text,
.milestone-banner-link {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 5e5696b1602..dcf590e7331 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -42,7 +42,7 @@
display: block;
padding: 10px 0;
color: $gl-text-color;
- font-family: $regular_font;
+ font-family: $regular-font;
border: 0;
&:focus {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 32d14049067..c369d89d63c 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -328,7 +328,7 @@ ul.notes {
}
.notes_holder {
- font-family: $regular_font;
+ font-family: $regular-font;
td {
border: 1px solid $white-normal;
@@ -403,7 +403,7 @@ ul.notes {
}
}
- .author_link {
+ .author-link {
color: $gl-text-color;
}
}
@@ -546,6 +546,7 @@ ul.notes {
svg {
@include btn-svg;
+ margin: 0;
}
.award-control-icon-positive,
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 52332ac97dd..b68c89c25d8 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -301,6 +301,21 @@
border-bottom: 2px solid $border-color;
}
}
+
+ //delete when all pipelines are updated to new size
+ &.mr-widget-pipeline-stages {
+ + .stage-container {
+ margin-left: 4px;
+ }
+
+ &:not(:last-child) {
+ &::after {
+ width: 4px;
+ right: -4px;
+ top: 11px;
+ }
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 5d0d59e12f2..b45e305897c 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -418,3 +418,23 @@ table.u2f-registrations {
}
}
}
+
+.edit-user {
+ .clear-user-status {
+ svg {
+ fill: $gl-text-color-secondary;
+ }
+ }
+
+ .emoji-menu-toggle-button {
+ @extend .note-action-button;
+
+ .no-emoji-placeholder {
+ position: relative;
+
+ svg {
+ fill: $gl-text-color-secondary;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index aa83e5bdebc..6eaa0523387 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -490,30 +490,31 @@
margin-bottom: 0;
}
+ .tab-pane {
+ padding-top: 0;
+ padding-bottom: 0;
+ }
+
.template-option {
- padding: $gl-padding $gl-padding $gl-padding ($gl-padding * 4);
- position: relative;
+ .logo {
+ .btn-template-icon {
+ width: 40px !important;
+ }
+ }
+
+ padding: 16px 0;
&:not(:first-child) {
border-top: 1px solid $border-color;
}
- .btn-template-icon {
- position: absolute;
- left: $gl-padding;
- top: $gl-padding;
+ .controls {
+ margin-left: auto;
}
- }
-
- .template-title {
- font-size: 16px;
- }
- .template-description {
- margin: 6px 0 12px;
}
- .template-button {
+ .choose-template {
input {
position: absolute;
clip: rect(0, 0, 0, 0);
@@ -540,8 +541,6 @@
}
.selected-icon {
- padding-right: $gl-padding;
-
svg {
display: none;
top: 7px;
@@ -754,6 +753,11 @@
}
}
+.repository-languages-bar {
+ height: 6px;
+ margin-bottom: 8px;
+}
+
pre.light-well {
border-color: $well-light-border;
}
@@ -819,10 +823,6 @@ pre.light-well {
.avatar-container {
align-self: flex-start;
-
- > a {
- width: 100%;
- }
}
.project-details {
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
deleted file mode 100644
index 3c24aaa65e8..00000000000
--- a/app/assets/stylesheets/pages/repo.scss
+++ /dev/null
@@ -1,1331 +0,0 @@
-.project-refs-form,
-.project-refs-target-form {
- display: inline-block;
-}
-
-.fade-enter,
-.fade-leave-to {
- opacity: 0;
-}
-
-.commit-message {
- @include str-truncated(250px);
-}
-
-.editable-mode {
- display: inline-block;
-}
-
-.ide-view {
- position: relative;
- display: flex;
- height: calc(100vh - #{$header-height});
- margin-top: 0;
- border-top: 1px solid $white-dark;
- padding-bottom: $ide-statusbar-height;
- color: $gl-text-color;
-
- &.is-collapsed {
- .ide-file-list {
- max-width: 250px;
- }
- }
-
- .file-status-icon {
- width: 10px;
- height: 10px;
- }
-}
-
-.ide-file-list {
- flex: 1;
- padding-left: $gl-padding;
- padding-right: $gl-padding;
- padding-bottom: $grid-size;
-
- .file {
- cursor: pointer;
-
- &.file-active {
- background: $theme-gray-100;
- }
-
- .ide-file-name {
- flex: 1;
- white-space: nowrap;
- text-overflow: ellipsis;
- max-width: inherit;
- line-height: 16px;
- display: inline-block;
- height: 18px;
-
- svg {
- vertical-align: middle;
- margin-right: 2px;
- }
-
- .loading-container {
- margin-right: 4px;
- display: inline-block;
- }
- }
-
- .ide-file-icon-holder {
- display: flex;
- align-items: center;
- }
-
- .ide-file-changed-icon {
- margin-left: auto;
-
- > svg {
- display: block;
- }
- }
-
- .ide-new-btn {
- display: none;
-
- .btn {
- padding: 2px 5px;
- }
- }
-
- &:hover,
- &:focus {
- .ide-new-btn {
- display: block;
- }
- }
-
- .folder-icon {
- fill: $gl-text-color-secondary;
- }
- }
-
- a {
- color: $gl-text-color;
- }
-
- th {
- position: sticky;
- top: 0;
- }
-}
-
-.file-name {
- display: flex;
- overflow: visible;
- align-items: center;
- width: 100%;
-}
-
-.multi-file-loading-container {
- margin-top: 10px;
- padding: 10px;
-}
-
-.multi-file-table-col-commit-message {
- white-space: nowrap;
- width: 50%;
-}
-
-.multi-file-edit-pane {
- display: flex;
- flex-direction: column;
- flex: 1;
- border-left: 1px solid $white-dark;
- overflow: hidden;
-}
-
-.multi-file-tabs {
- display: flex;
- background-color: $white-normal;
- box-shadow: inset 0 -1px $white-dark;
-
- > ul {
- display: flex;
- overflow-x: auto;
- }
-
- li {
- display: flex;
- align-items: center;
- padding: $grid-size $gl-padding;
- background-color: $gray-normal;
- border-right: 1px solid $white-dark;
- border-bottom: 1px solid $white-dark;
-
- &.active {
- background-color: $white-light;
- border-bottom-color: $white-light;
- }
- }
-}
-
-.multi-file-tab {
- @include str-truncated(141px);
- cursor: pointer;
-
- svg {
- vertical-align: middle;
- }
-}
-
-.multi-file-tab-close {
- width: 16px;
- height: 16px;
- padding: 0;
- margin-left: $grid-size;
- background: none;
- border: 0;
- border-radius: $border-radius-default;
- color: $theme-gray-900;
-
- svg {
- position: relative;
- top: -2px;
- }
-
- .ide-file-changed-icon {
- display: block;
- position: relative;
- top: 1px;
- right: -2px;
- }
-
- &:not([disabled]):hover {
- background-color: $theme-gray-200;
- }
-
- &:not([disabled]):focus {
- background-color: $blue-500;
- color: $white-light;
- outline: 0;
-
- svg {
- fill: currentColor;
- }
- }
-}
-
-.multi-file-edit-pane-content {
- flex: 1;
- height: 0;
-}
-
-.blob-editor-container {
- flex: 1;
- height: 0;
- display: flex;
- flex-direction: column;
- justify-content: center;
-
- .vertical-center {
- min-height: auto;
- }
-
- .monaco-editor .lines-content .cigr {
- display: none;
- }
-
- .is-readonly,
- .editor.original {
- .view-lines {
- cursor: default;
- }
-
- .cursors-layer {
- display: none;
- }
- }
-
- .monaco-diff-editor.vs {
- .editor.modified {
- box-shadow: none;
- }
-
- .diagonal-fill {
- display: none !important;
- }
-
- .diffOverview {
- background-color: $white-light;
- border-left: 1px solid $white-dark;
- cursor: ns-resize;
- }
-
- .diffViewport {
- display: none;
- }
-
- .char-insert {
- background-color: $line-added-dark;
- }
-
- .char-delete {
- background-color: $line-removed-dark;
- }
-
- .line-numbers {
- color: $black-transparent;
- }
-
- .view-overlays {
- .line-insert {
- background-color: $line-added;
- }
-
- .line-delete {
- background-color: $line-removed;
- }
- }
-
- .margin {
- background-color: $white-light;
- border-right: 1px solid $theme-gray-100;
-
- .line-insert {
- border-right: 1px solid $line-added-dark;
- }
-
- .line-delete {
- border-right: 1px solid $line-removed-dark;
- }
- }
-
- .margin-view-overlays .insert-sign,
- .margin-view-overlays .delete-sign {
- opacity: 0.4;
- }
- }
-}
-
-.multi-file-editor-holder {
- height: 100%;
- min-height: 0;
-
- &.is-readonly,
- .editor.original {
- .monaco-editor,
- .monaco-editor-background,
- .monaco-editor .inputarea.ime-input {
- background-color: $theme-gray-50;
- }
- }
-}
-
-.preview-container {
- flex-grow: 1;
- position: relative;
-
- .md-previewer {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- overflow: auto;
- padding: $gl-padding;
- }
-
- .file-container {
- background-color: $gray-darker;
- display: flex;
- height: 100%;
- align-items: center;
- justify-content: center;
-
- text-align: center;
-
- .file-content {
- padding: $gl-padding;
- max-width: 100%;
- max-height: 100%;
-
- img {
- max-width: 90%;
- }
-
- .isZoomable {
- cursor: pointer;
- cursor: zoom-in;
-
- &.isZoomed {
- cursor: pointer;
- cursor: zoom-out;
- max-width: none;
- max-height: none;
- margin-right: $gl-padding;
- }
- }
- }
-
- .file-info {
- font-size: $label-font-size;
- color: $diff-image-info-color;
- }
- }
-}
-
-.ide-mode-tabs {
- border-bottom: 1px solid $white-dark;
-
- .nav-links {
- border-bottom: 0;
-
- li a {
- padding: $gl-padding-8 $gl-padding;
- line-height: $gl-btn-line-height;
- }
- }
-}
-
-.ide-btn-group {
- padding: $gl-padding-4 $gl-vert-padding;
- line-height: 24px;
-}
-
-.ide-status-bar {
- border-top: 1px solid $white-dark;
- padding: 2px $gl-padding-8 0;
- background: $white-light;
- display: flex;
- justify-content: space-between;
- height: $ide-statusbar-height;
-
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
-
- font-size: 12px;
- line-height: 22px;
-
- * {
- font-size: inherit;
- }
-
- > div + div {
- padding-left: $gl-padding;
- }
-
- svg {
- vertical-align: sub;
- }
-}
-
-.ide-status-file {
- text-align: right;
-
- .ide-status-branch + &,
- &:first-child {
- margin-left: auto;
- }
-}
-// Not great, but this is to deal with our current output
-.multi-file-preview-holder {
- height: 100%;
- overflow: scroll;
-
- .file-content.code {
- display: flex;
-
- i {
- margin-left: -10px;
- }
- }
-
- .line-numbers {
- min-width: 50px;
- }
-
- .file-content,
- .line-numbers,
- .blob-content,
- .code {
- min-height: 100%;
- }
-}
-
-.file-content.blob-no-preview {
- a {
- margin-left: auto;
- margin-right: auto;
- }
-}
-
-.multi-file-commit-panel {
- display: flex;
- position: relative;
- width: 340px;
- padding: 0;
- background-color: $gray-light;
- padding-right: 1px;
-
- .context-header {
- width: auto;
- margin-right: 0;
-
- > a,
- > button {
- height: 60px;
- }
- }
-
- .projects-sidebar {
- min-height: 0;
- display: flex;
- flex-direction: column;
- flex: 1;
- }
-
- .multi-file-commit-panel-inner {
- position: relative;
- display: flex;
- flex-direction: column;
- height: 100%;
- min-width: 0;
- width: 100%;
- }
-
- .multi-file-commit-panel-inner-scroll {
- display: flex;
- flex: 1;
- flex-direction: column;
- overflow: auto;
- background-color: $white-light;
- border-left: 1px solid $white-dark;
- border-top: 1px solid $white-dark;
- border-top-left-radius: $border-radius-small;
- }
-}
-
-.multi-file-commit-panel-section {
- display: flex;
- flex-direction: column;
- flex: 1;
- max-height: 100%;
- overflow: auto;
-}
-
-.ide-commit-empty-state {
- padding: 0 $gl-padding;
-}
-
-.ide-commit-empty-state-container {
- margin-top: auto;
- margin-bottom: auto;
-}
-
-.multi-file-commit-panel-header {
- display: flex;
- align-items: center;
- margin-bottom: 0;
- border-bottom: 1px solid $white-dark;
- padding: 12px 0;
-}
-
-.multi-file-commit-panel-header-title {
- display: flex;
- flex: 1;
- align-items: center;
-
- svg {
- margin-right: $gl-btn-padding;
- color: $theme-gray-700;
- }
-}
-
-.multi-file-commit-panel-collapse-btn {
- border-left: 1px solid $white-dark;
- margin-left: auto;
-}
-
-.multi-file-commit-list {
- flex: 1;
- overflow: auto;
- padding: $grid-size 0;
- margin-left: -$grid-size;
- margin-right: -$grid-size;
- min-height: 60px;
-
- &.form-text.text-muted {
- margin-left: 0;
- right: 0;
- }
-}
-
-.multi-file-addition,
-.multi-file-addition-solid {
- color: $green-500;
-}
-
-.multi-file-modified,
-.multi-file-modified-solid {
- color: $orange-500;
-}
-
-.multi-file-commit-list-collapsed {
- display: flex;
- flex-direction: column;
- padding: $gl-padding 0;
-
- svg {
- display: block;
- margin-left: auto;
- margin-right: auto;
- color: $theme-gray-700;
- }
-
- .file-status-icon {
- width: 10px;
- height: 10px;
- margin-left: 3px;
- }
-}
-
-.multi-file-commit-list-path,
-.ide-file-list .file {
- display: flex;
- align-items: center;
- margin-left: -$grid-size;
- margin-right: -$grid-size;
- padding: $grid-size / 2 $grid-size;
- border-radius: $border-radius-default;
- text-align: left;
-
- &:hover,
- &:focus {
- background: $theme-gray-100;
- }
-
- &:active {
- background: $theme-gray-200;
- }
-}
-
-.multi-file-commit-list-path {
- cursor: pointer;
-
- &.is-active {
- background-color: $white-normal;
- }
-
- &:hover,
- &:focus {
- outline: 0;
- }
-
- svg {
- min-width: 16px;
- vertical-align: middle;
- display: inline-block;
- }
-}
-
-.multi-file-commit-list-file-path {
- @include str-truncated(calc(100% - 30px));
-
- &:active {
- text-decoration: none;
- }
-}
-
-.multi-file-discard-btn {
- top: 4px;
- right: 8px;
- bottom: 4px;
-
- svg {
- top: 0;
- }
-}
-
-.multi-file-commit-form {
- position: relative;
- background-color: $white-light;
- border-left: 1px solid $white-dark;
- transition: all 0.3s ease;
-
- > form,
- > .commit-form-compact {
- padding: $gl-padding 0;
- margin-left: $gl-padding;
- margin-right: $gl-padding;
- border-top: 1px solid $white-dark;
- }
-
- .btn {
- font-size: $gl-font-size;
- }
-
- .multi-file-commit-panel-success-message {
- top: 0;
- }
-}
-
-.multi-file-commit-panel-bottom {
- position: relative;
-}
-
-.dirty-diff {
- // !important need to override monaco inline style
- width: 4px !important;
- left: 0 !important;
-
- &-modified {
- background-color: $blue-500;
- }
-
- &-added {
- background-color: $green-600;
- }
-
- &-removed {
- height: 0 !important;
- width: 0 !important;
- bottom: -2px;
- border-style: solid;
- border-width: 5px;
- border-color: transparent transparent transparent $red-500;
-
- &::before {
- content: '';
- position: absolute;
- left: 0;
- top: 0;
- width: 100px;
- height: 1px;
- background-color: rgba($red-500, 0.5);
- }
- }
-}
-
-.ide-loading {
- display: flex;
- height: 100vh;
- align-items: center;
- justify-content: center;
-}
-
-.ide-empty-state {
- display: flex;
- height: 100vh;
- align-items: center;
- justify-content: center;
-}
-
-.ide-new-btn {
- .btn {
- padding-top: 3px;
- padding-bottom: 3px;
- }
-
- .dropdown {
- display: flex;
- }
-
- .dropdown-toggle svg {
- top: 0;
- }
-
- .dropdown-menu {
- left: auto;
- right: 0;
-
- label {
- font-weight: $gl-font-weight-normal;
- padding: 5px 8px;
- margin-bottom: 0;
- }
- }
-}
-
-.ide {
- overflow: hidden;
-
- &.nav-only {
- padding-top: $header-height;
-
- .with-performance-bar & {
- padding-top: $header-height + $performance-bar-height;
- }
-
- .flash-container {
- margin-top: 0;
- margin-bottom: 0;
- }
-
- .alert-wrapper .flash-container .flash-alert:last-child,
- .alert-wrapper .flash-container .flash-notice:last-child {
- margin-bottom: 0;
- }
-
- .content-wrapper {
- margin-top: 0;
- padding-bottom: 0;
- }
-
- &.flash-shown {
- .content-wrapper {
- margin-top: 0;
- }
-
- .ide-view {
- height: calc(100vh - #{$header-height + $flash-height});
- }
- }
-
- .projects-sidebar {
- .multi-file-commit-panel-inner-scroll {
- flex: 1;
- }
- }
- }
-}
-
-.with-performance-bar .ide.nav-only {
- .flash-container {
- margin-top: 0;
- }
-
- .content-wrapper {
- margin-top: 0;
- padding-bottom: 0;
- }
-
- .ide-view {
- height: calc(100vh - #{$header-height + $performance-bar-height});
- }
-
- &.flash-shown {
- .ide-view {
- height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height});
- }
- }
-}
-
-.dragHandle {
- position: absolute;
- top: 0;
- bottom: 0;
- width: 1px;
- background-color: $white-dark;
-
- &.dragright {
- right: 0;
- }
-
- &.dragleft {
- left: 0;
- }
-}
-
-.ide-commit-list-container {
- display: flex;
- flex: 1;
- flex-direction: column;
- min-height: 140px;
- margin-left: $gl-padding;
- margin-right: $gl-padding;
-
- &.is-first {
- border-bottom: 1px solid $white-dark;
- }
-}
-
-.ide-staged-action-btn {
- width: 22px;
- margin-left: -1px;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
-
- > svg {
- top: 0;
- }
-}
-
-.ide-commit-file-count {
- min-width: 22px;
- background-color: $gray-light;
- border: 1px solid $white-dark;
-}
-
-.ide-commit-radios {
- label {
- font-weight: normal;
-
- &.is-disabled {
- .ide-radio-label {
- text-decoration: line-through;
- }
- }
- }
-
- .form-text.text-muted {
- margin-top: 0;
- line-height: 0;
- }
-}
-
-.ide-commit-new-branch {
- margin-left: 25px;
-}
-
-.ide-sidebar-link {
- display: flex;
- align-items: center;
- position: relative;
- height: 60px;
- width: 100%;
- padding: 0 $gl-padding;
- color: $gl-text-color-secondary;
- background-color: transparent;
- border: 0;
- border-top: 1px solid transparent;
- border-bottom: 1px solid transparent;
- outline: 0;
- cursor: pointer;
-
- svg {
- margin: 0 auto;
- }
-
- &:hover {
- color: $gl-text-color;
- background-color: $theme-gray-100;
- }
-
- &:focus {
- color: $gl-text-color;
- background-color: $theme-gray-200;
- }
-
- &.active {
- // extend width over border of sidebar section
- width: calc(100% + 1px);
- padding-right: $gl-padding + 1px;
- background-color: $white-light;
- border-top-color: $white-dark;
- border-bottom-color: $white-dark;
-
- &::after {
- content: '';
- position: absolute;
- right: -1px;
- top: 0;
- bottom: 0;
- width: 1px;
- background: $white-light;
- }
-
- &.is-right {
- padding-right: $gl-padding;
- padding-left: $gl-padding + 1px;
-
- &::after {
- right: auto;
- left: -1px;
- }
- }
- }
-}
-
-.ide-activity-bar {
- position: relative;
- flex: 0 0 60px;
- z-index: 1;
-}
-
-.ide-file-finder-overlay {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 100;
-}
-
-.ide-file-finder {
- top: 10px;
- left: 50%;
- transform: translateX(-50%);
-
- .highlighted {
- color: $blue-500;
- font-weight: $gl-font-weight-bold;
- }
-}
-
-.ide-commit-message-field {
- height: 200px;
- background-color: $white-light;
-
- .md-area {
- display: flex;
- flex-direction: column;
- height: 100%;
- }
-
- .nav-links {
- height: 30px;
- }
-
- .form-text.text-muted {
- margin-top: 2px;
- color: $blue-500;
- cursor: pointer;
- }
-}
-
-.ide-commit-message-textarea-container {
- position: relative;
- width: 100%;
- height: 100%;
- overflow: hidden;
-
- .note-textarea {
- font-family: $monospace_font;
- }
-}
-
-.ide-commit-message-highlights-container {
- position: absolute;
- left: 0;
- top: 0;
- right: -100px;
- bottom: 0;
- padding-right: 100px;
- pointer-events: none;
- z-index: 1;
-
- .highlights {
- white-space: pre-wrap;
- word-wrap: break-word;
- color: transparent;
- }
-
- mark {
- margin-left: -1px;
- padding: 0 2px;
- border-radius: $border-radius-small;
- background-color: $orange-200;
- color: transparent;
- opacity: 0.6;
- }
-}
-
-.ide-commit-message-textarea {
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- width: 100%;
- height: 100%;
- z-index: 2;
- background: transparent;
- resize: none;
-}
-
-.ide-tree-header {
- display: flex;
- align-items: center;
- margin-bottom: 8px;
- padding: 12px 0;
- border-bottom: 1px solid $white-dark;
-
- .ide-new-btn {
- margin-left: auto;
- }
-}
-
-.ide-sidebar-branch-title {
- font-weight: $gl-font-weight-normal;
-
- svg {
- position: relative;
- top: 3px;
- margin-top: -1px;
- }
-}
-
-.commit-form-compact {
- .btn {
- margin-bottom: 8px;
- }
-
- p {
- margin-bottom: 0;
- }
-}
-
-.commit-form-slide-up-enter-active,
-.commit-form-slide-up-leave-active {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- transition: all 0.3s ease;
-}
-
-.is-full .commit-form-slide-up-enter,
-.is-compact .commit-form-slide-up-leave-to {
- transform: translateY(100%);
-}
-
-.is-full .commit-form-slide-up-enter-to,
-.is-compact .commit-form-slide-up-leave {
- transform: translateY(0);
-}
-
-.commit-form-slide-up-enter,
-.commit-form-slide-up-leave-to {
- opacity: 0;
-}
-
-.ide-review-header {
- flex-direction: column;
- align-items: flex-start;
-
- .dropdown {
- margin-left: auto;
- }
-
- a {
- color: $gl-link-color;
- }
-}
-
-.ide-review-sub-header {
- color: $gl-text-color-secondary;
-}
-
-.ide-tree-changes {
- display: flex;
- align-items: center;
- font-size: 12px;
-}
-
-.multi-file-commit-panel-success-message {
- position: absolute;
- top: 61px;
- left: 1px;
- bottom: 0;
- right: 0;
- z-index: 10;
- background: $white-light;
- overflow: auto;
- display: flex;
- flex-direction: column;
- justify-content: center;
-}
-
-.ide-review-button-holder {
- display: flex;
- width: 100%;
- align-items: center;
-}
-
-.ide-context-header {
- .avatar {
- flex: 0 0 38px;
- }
-
- .ide-merge-requests-dropdown.dropdown-menu {
- width: 385px;
- max-height: initial;
- }
-}
-
-.ide-sidebar-project-title {
- min-width: 0;
-
- .sidebar-context-title {
- white-space: nowrap;
- }
-
- .ide-sidebar-branch-title {
- min-width: 50px;
- }
-}
-
-.ide-external-link {
- position: relative;
-
- svg {
- display: none;
- position: absolute;
- top: 2px;
- right: -$gl-padding;
- }
-
- &:hover,
- &:focus {
- svg {
- display: inline-block;
- }
- }
-}
-
-.ide-right-sidebar {
- width: auto;
- min-width: 60px;
-
- .ide-activity-bar {
- border-left: 1px solid $white-dark;
- }
-
- .multi-file-commit-panel-inner {
- width: 350px;
- padding: $grid-size $gl-padding;
- background-color: $white-light;
- border-left: 1px solid $white-dark;
- }
-}
-
-.ide-pipeline {
- display: flex;
- flex-direction: column;
- height: 100%;
- margin-top: -$grid-size;
- margin-bottom: -$grid-size;
-
- .empty-state {
- margin-top: auto;
- margin-bottom: auto;
-
- p {
- margin: $grid-size 0;
- text-align: center;
- line-height: 24px;
- }
-
- .btn,
- h4 {
- margin: 0;
- }
- }
-
- .build-trace,
- .top-bar {
- margin-left: -$gl-padding;
- }
-
- &.build-page .top-bar {
- top: 0;
- font-size: 12px;
- border-top-right-radius: $border-radius-default;
- }
-}
-
-.ide-pipeline-list {
- flex: 1;
- overflow: auto;
-}
-
-.ide-pipeline-header {
- min-height: 55px;
- padding-left: $gl-padding;
- padding-right: $gl-padding;
-
- .ci-status-icon {
- display: flex;
- }
-}
-
-.ide-job-item {
- display: flex;
- padding: 16px;
-
- &:not(:last-child) {
- border-bottom: 1px solid $border-color;
- }
-
- .ci-status-icon {
- display: flex;
- justify-content: center;
- min-width: 24px;
- overflow: hidden;
- }
-}
-
-.ide-stage {
- .card-header {
- display: flex;
- cursor: pointer;
-
- .ci-status-icon {
- display: flex;
- align-items: center;
- }
- }
-
- .card-body {
- padding: 0;
- }
-}
-
-.ide-stage-collapse-icon {
- margin: auto 0 auto auto;
-}
-
-.ide-stage-title {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.ide-job-header {
- min-height: 60px;
-}
-
-.ide-merge-requests-dropdown {
- .nav-links li {
- width: 50%;
- padding-left: 0;
- padding-right: 0;
-
- a {
- text-align: center;
-
- &:not(.active) {
- background-color: $gray-light;
- }
- }
- }
-
- .dropdown-input {
- padding-left: $gl-padding;
- padding-right: $gl-padding;
-
- .fa {
- right: 26px;
- }
- }
-
- .btn-link {
- padding-top: $gl-padding;
- padding-bottom: $gl-padding;
- }
-}
-
-.ide-merge-request-current-icon {
- min-width: 18px;
-}
-
-.ide-merge-requests-empty {
- height: 230px;
-}
-
-.ide-merge-requests-dropdown-content {
- min-height: 230px;
- max-height: 470px;
-}
-
-.ide-merge-request-project-path {
- font-size: 12px;
- line-height: 16px;
- color: $gl-text-color-secondary;
-}
diff --git a/app/assets/stylesheets/pages/reports.scss b/app/assets/stylesheets/pages/reports.scss
new file mode 100644
index 00000000000..ecd51aa06a4
--- /dev/null
+++ b/app/assets/stylesheets/pages/reports.scss
@@ -0,0 +1,155 @@
+.split-report-section {
+ border-bottom: 1px solid $gray-darker;
+
+ .report-block-container {
+ max-height: 500px;
+ overflow: auto;
+ }
+
+ .space-children,
+ .space-children > span {
+ display: flex;
+ align-self: center;
+ }
+
+ .media {
+ align-items: center;
+ padding: 10px;
+ line-height: 20px;
+
+ /*
+ This fixes the wrapping div of the icon in the report header.
+ Apparently the borderless status icons are half the size of the status icons with border.
+ This means we have to double the size of the wrapping div for borderless icons.
+ */
+ .space-children:first-child {
+ width: 32px;
+ height: 32px;
+ align-items: center;
+ justify-content: center;
+ margin-right: 5px;
+ margin-left: 1px;
+ }
+ }
+
+ .code-text {
+ width: 100%;
+ flex: 1;
+ }
+}
+
+.mr-widget-grouped-section {
+ .report-block-container {
+ max-height: 170px;
+ overflow: auto;
+ }
+
+ .report-block-list-issue-parent {
+ padding: $gl-padding-top $gl-padding;
+ border-top: 1px solid $border-color;
+ }
+
+ .report-block-list-icon .loading-container {
+ position: relative;
+ left: -2px;
+ // needed to make the next element align with the
+ // elements below that have a svg with 16px width
+ .fa-spinner {
+ width: 16px;
+ }
+ }
+}
+
+.report-block-container {
+ border-top: 1px solid $border-color;
+ padding: $gl-padding-top;
+ background-color: $gray-light;
+
+ // Clean MR widget CSS
+ line-height: 20px;
+}
+
+.report-block-list {
+ list-style: none;
+ padding: 0 1px;
+ margin: 0;
+
+ .license-item {
+ line-height: $gl-padding-32;
+
+ .license-packages {
+ font-size: $label-font-size;
+ }
+
+ }
+}
+
+.report-block-list-icon {
+ display: flex;
+
+ &.failed svg {
+ color: $red-500;
+ }
+
+ &.success svg {
+ color: $green-500;
+ }
+
+ &.neutral svg {
+ color: $theme-gray-700;
+ }
+
+ .ci-status-icon {
+ svg {
+ width: 16px;
+ height: 16px;
+ left: -2px;
+ }
+ }
+}
+
+.report-block-list-issue {
+ display: flex;
+ align-items: flex-start;
+ align-content: flex-start;
+}
+
+.is-dismissed .report-block-list-issue-description,
+.is-dismissed .vulnerability-name-button {
+ text-decoration: line-through;
+}
+
+.report-block-list-issue-description-text::after {
+ content: '\00a0';
+}
+
+.report-block-list-issue-description {
+ align-content: space-around;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ display: flex;
+ align-self: center;
+}
+
+.report-block {
+ .break-link {
+ word-wrap: break-word;
+ word-break: break-all;
+ }
+}
+
+.report-block-issue-code {
+ width: 600px;
+}
+
+.modal-security-report-dast {
+ .modal-dialog {
+ width: $modal-lg;
+ max-width: $modal-lg;
+ }
+
+ // This is temporary till we get the new modals hooked up
+ &.modal-hide-footer .modal-footer {
+ display: none;
+ }
+}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 2d66f336076..60b280fd12e 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -1,3 +1,6 @@
+$search-dropdown-max-height: 400px;
+$search-avatar-size: 16px;
+
.search-results {
.search-result-row {
border-bottom: 1px solid $border-color;
@@ -24,8 +27,9 @@
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
}
-input[type="checkbox"]:hover {
- box-shadow: 0 0 2px 2px lighten($search-input-focus-shadow-color, 20%), 0 0 0 1px lighten($search-input-focus-shadow-color, 20%);
+input[type='checkbox']:hover {
+ box-shadow: 0 0 2px 2px lighten($search-input-focus-shadow-color, 20%),
+ 0 0 0 1px lighten($search-input-focus-shadow-color, 20%);
}
.search {
@@ -40,24 +44,15 @@ input[type="checkbox"]:hover {
height: 32px;
border: 0;
border-radius: $border-radius-default;
- transition: border-color ease-in-out $default-transition-duration, background-color ease-in-out $default-transition-duration;
+ transition: border-color ease-in-out $default-transition-duration,
+ background-color ease-in-out $default-transition-duration,
+ width ease-in-out $default-transition-duration;
&:hover {
box-shadow: none;
}
}
- .location-badge {
- white-space: nowrap;
- height: 32px;
- font-size: 12px;
- margin: -4px 4px -4px -4px;
- line-height: 25px;
- padding: 4px 8px;
- border-radius: $border-radius-default 0 0 $border-radius-default;
- transition: border-color ease-in-out $default-transition-duration;
- }
-
.search-input {
border: 0;
font-size: 14px;
@@ -104,17 +99,28 @@ input[type="checkbox"]:hover {
}
.dropdown-header {
- text-transform: uppercase;
- font-size: 11px;
+ // Necessary because glDropdown doesn't support a second style of headers
+ font-weight: $gl-font-weight-bold;
+ // .dropdown-menu li has 1px side padding
+ padding: $gl-padding-8 17px;
+ color: $gl-text-color;
+ font-size: $gl-font-size;
+ line-height: 16px;
}
// Custom dropdown positioning
.dropdown-menu {
left: -5px;
+ max-height: $search-dropdown-max-height;
+ overflow: auto;
+
+ @include media-breakpoint-up(xl) {
+ width: $search-input-active-width;
+ }
}
.dropdown-content {
- max-height: none;
+ max-height: $search-dropdown-max-height - 18px;
}
}
@@ -124,6 +130,10 @@ input[type="checkbox"]:hover {
border-color: $dropdown-input-focus-border;
box-shadow: none;
+ @include media-breakpoint-up(xl) {
+ width: $search-input-active-width;
+ }
+
.search-input-wrap {
.search-icon,
.clear-icon {
@@ -141,12 +151,6 @@ input[type="checkbox"]:hover {
color: $gl-text-color-tertiary;
}
}
-
- .location-badge {
- transition: all $default-transition-duration;
- background-color: $nav-badge-bg;
- border-color: $border-color;
- }
}
&.has-value {
@@ -160,10 +164,24 @@ input[type="checkbox"]:hover {
}
}
- &.has-location-badge {
- .search-input-wrap {
- width: 68%;
- }
+ .inline-search-icon {
+ position: relative;
+ margin-right: 4px;
+ color: $gl-text-color-secondary;
+ }
+
+ .identicon,
+ .search-item-avatar {
+ flex-basis: $search-avatar-size;
+ flex-shrink: 0;
+ margin-right: 4px;
+ }
+
+ .search-item-avatar {
+ width: $search-avatar-size;
+ height: $search-avatar-size;
+ border-radius: 50%;
+ border: 1px solid $avatar-border;
}
}
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index e264b06c4b2..839ac5ba59b 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -191,6 +191,22 @@
}
}
+.initialize-with-readme-setting {
+ .form-check {
+ margin-bottom: 10px;
+
+ .option-title {
+ font-weight: $gl-font-weight-normal;
+ display: inline-block;
+ color: $gl-text-color;
+ }
+
+ .option-description {
+ color: $project-option-descr-color;
+ }
+ }
+}
+
.prometheus-metrics-monitoring {
.card {
.card-toggle {
diff --git a/app/assets/stylesheets/pages/settings_ci_cd.scss b/app/assets/stylesheets/pages/settings_ci_cd.scss
index 777fdb3581e..239123fc3ab 100644
--- a/app/assets/stylesheets/pages/settings_ci_cd.scss
+++ b/app/assets/stylesheets/pages/settings_ci_cd.scss
@@ -19,9 +19,4 @@
.auto-devops-card {
margin-bottom: $gl-vert-padding;
-
- > .card-body {
- border-radius: $card-border-radius;
- padding: $gl-padding $gl-padding-24;
- }
}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index e5d7dd13915..010a2c05a1c 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -174,6 +174,18 @@
}
}
+@include media-breakpoint-down(lg) {
+ .todos-filters {
+ .filter-categories {
+ width: 75%;
+
+ .filter-item {
+ margin-bottom: 10px;
+ }
+ }
+ }
+}
+
@include media-breakpoint-down(xs) {
.todo {
.avatar {
@@ -199,6 +211,10 @@
}
.todos-filters {
+ .filter-categories {
+ width: auto;
+ }
+
.dropdown-menu-toggle {
width: 100%;
}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index efd26cb1f81..1cc26d40ba9 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -109,24 +109,6 @@
line-height: 21px;
}
- .last-commit {
- @include str-truncated(506px);
-
- .fa-angle-right {
- margin-left: 5px;
- }
-
- @include media-breakpoint-only(md) {
- @include str-truncated(450px);
- }
-
- }
-
- .commit-history-link-spacer {
- margin: 0 10px;
- color: $white-normal;
- }
-
&:hover:not(.tree-truncated-warning) {
td {
background-color: $row-hover;
diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss
index 0d6b0735f70..bd777c66b56 100644
--- a/app/assets/stylesheets/snippets.scss
+++ b/app/assets/stylesheets/snippets.scss
@@ -6,9 +6,9 @@
$border-style: 1px solid $border-color;
- font-family: $regular_font;
+ font-family: $regular-font;
font-size: $gl-font-size;
- line-height: $code_line_height;
+ line-height: $code-line-height;
color: $gl-text-color;
margin: 20px;
font-weight: 200;
@@ -22,8 +22,8 @@
height: 16px;
background-size: cover;
- &.gl-snippet-icon-doc_code { background-position: 0 0; }
- &.gl-snippet-icon-doc_text { background-position: 0 -16px; }
+ &.gl-snippet-icon-doc-code { background-position: 0 0; }
+ &.gl-snippet-icon-doc-text { background-position: 0 -16px; }
&.gl-snippet-icon-download { background-position: 0 -32px; }
}
@@ -48,9 +48,9 @@
padding: 10px;
border: 0;
border-radius: 0;
- font-family: $monospace_font;
- font-size: $code_font_size;
- line-height: $code_line_height;
+ font-family: $monospace-font;
+ font-size: $code-font-size;
+ line-height: $code-line-height;
margin: 0;
overflow: auto;
overflow-y: hidden;
@@ -66,10 +66,10 @@
float: left;
.diff-line-num {
- font-family: $monospace_font;
+ font-family: $monospace-font;
display: block;
- font-size: $code_font_size;
- min-height: $code_line_height;
+ font-size: $code-font-size;
+ min-height: $code-line-height;
white-space: nowrap;
color: $black-transparent;
min-width: 30px;
diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb
deleted file mode 100644
index 10d9d1b5345..00000000000
--- a/app/controllers/admin/cohorts_controller.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class Admin::CohortsController < Admin::ApplicationController
- def index
- if Gitlab::CurrentSettings.usage_ping_enabled
- cohorts_results = Rails.cache.fetch('cohorts', expires_in: 1.day) do
- CohortsService.new.execute
- end
-
- @cohorts = CohortsSerializer.new.represent(cohorts_results)
- end
- end
-end
diff --git a/app/controllers/admin/conversational_development_index_controller.rb b/app/controllers/admin/conversational_development_index_controller.rb
deleted file mode 100644
index 921169d3e2b..00000000000
--- a/app/controllers/admin/conversational_development_index_controller.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class Admin::ConversationalDevelopmentIndexController < Admin::ApplicationController
- def show
- @metric = ConversationalDevelopmentIndex::Metric.order(:created_at).last&.present
- end
-end
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index b0c4c31cffc..5c2025c1988 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -22,7 +22,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
end
def update
- if deploy_key.update_attributes(update_params)
+ if deploy_key.update(update_params)
flash[:notice] = 'Deploy key was successfully updated.'
redirect_to admin_deploy_keys_path
else
@@ -34,7 +34,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
deploy_key.destroy
respond_to do |format|
- format.html { redirect_to admin_deploy_keys_path, status: 302 }
+ format.html { redirect_to admin_deploy_keys_path, status: :found }
format.json { head :ok }
end
end
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 96b7bc65ac9..d7a5b745d3f 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -39,7 +39,7 @@ class Admin::GroupsController < Admin::ApplicationController
end
def update
- if @group.update_attributes(group_params)
+ if @group.update(group_params)
redirect_to [:admin, @group], notice: 'Group was successfully updated.'
else
render "edit"
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index fb788c47ef1..a98c355c7ba 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -23,7 +23,7 @@ class Admin::HooksController < Admin::ApplicationController
end
def update
- if hook.update_attributes(hook_params)
+ if hook.update(hook_params)
flash[:notice] = 'System hook was successfully updated.'
redirect_to admin_hooks_path
else
@@ -34,7 +34,7 @@ class Admin::HooksController < Admin::ApplicationController
def destroy
hook.destroy
- redirect_to admin_hooks_path, status: 302
+ redirect_to admin_hooks_path, status: :found
end
def test
@@ -52,8 +52,7 @@ class Admin::HooksController < Admin::ApplicationController
end
def hook_logs
- @hook_logs ||=
- Kaminari.paginate_array(hook.web_hook_logs.order(created_at: :desc)).page(params[:page])
+ @hook_logs ||= hook.web_hook_logs.recent.page(params[:page])
end
def hook_params
diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb
index 43b4e3a2cc3..ceb45865804 100644
--- a/app/controllers/admin/identities_controller.rb
+++ b/app/controllers/admin/identities_controller.rb
@@ -25,7 +25,7 @@ class Admin::IdentitiesController < Admin::ApplicationController
end
def update
- if @identity.update_attributes(identity_params)
+ if @identity.update(identity_params)
RepairLdapBlockedUserService.new(@user).execute
redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully updated.'
else
diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb
index 39dbf85f6c0..d2f947d2c66 100644
--- a/app/controllers/admin/impersonations_controller.rb
+++ b/app/controllers/admin/impersonations_controller.rb
@@ -11,7 +11,7 @@ class Admin::ImpersonationsController < Admin::ApplicationController
session[:impersonator_id] = nil
- redirect_to admin_user_path(original_user), status: 302
+ redirect_to admin_user_path(original_user), status: :found
end
private
diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb
index ae7a7f6279c..e355d5fdea7 100644
--- a/app/controllers/admin/jobs_controller.rb
+++ b/app/controllers/admin/jobs_controller.rb
@@ -2,7 +2,7 @@ class Admin::JobsController < Admin::ApplicationController
def index
@scope = params[:scope]
@all_builds = Ci::Build
- @builds = @all_builds.order('created_at DESC')
+ @builds = @all_builds.order('id DESC')
@builds =
case @scope
when 'pending'
@@ -20,6 +20,6 @@ class Admin::JobsController < Admin::ApplicationController
def cancel_all
Ci::Build.running_or_pending.each(&:cancel)
- redirect_to admin_jobs_path, status: 303
+ redirect_to admin_jobs_path, status: :see_other
end
end
diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb
index 7aba77d8129..51d5799cd89 100644
--- a/app/controllers/admin/runner_projects_controller.rb
+++ b/app/controllers/admin/runner_projects_controller.rb
@@ -16,7 +16,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController
runner = rp.runner
rp.destroy
- redirect_to admin_runner_path(runner), status: 302
+ redirect_to admin_runner_path(runner), status: :found
end
private
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 4b01904f2a1..6c76c55a9d4 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -28,7 +28,7 @@ class Admin::RunnersController < Admin::ApplicationController
def destroy
@runner.destroy
- redirect_to admin_runners_path, status: 302
+ redirect_to admin_runners_path, status: :found
end
def resume
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index a7025b62ad7..91a36af34f3 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::ServicesController < Admin::ApplicationController
include ServiceParams
@@ -16,7 +18,7 @@ class Admin::ServicesController < Admin::ApplicationController
end
def update
- if service.update_attributes(service_params[:service])
+ if service.update(service_params[:service])
PropagateServiceTemplateWorker.perform_async(service.id) if service.active?
redirect_to admin_application_settings_services_path,
@@ -30,7 +32,7 @@ class Admin::ServicesController < Admin::ApplicationController
def services_templates
Service.available_services_names.map do |service_name|
- service_template = service_name.concat("_service").camelize.constantize
+ service_template = "#{service_name}_service".camelize.constantize
service_template.where(template: true).first_or_create
end
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 653f3dfffc4..a51a8c3ed4a 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -163,7 +163,7 @@ class Admin::UsersController < Admin::ApplicationController
format.json { head :ok }
else
format.html { redirect_back_or_admin_user(alert: 'There was an error removing the e-mail.') }
- format.json { render json: 'There was an error removing the e-mail.', status: 400 }
+ format.json { render json: 'There was an error removing the e-mail.', status: :bad_request }
end
end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 21cc6dfdd16..05ed3669a41 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base
include EnforcesTwoFactorAuthentication
include WithPerformanceBar
+ before_action :limit_unauthenticated_session_times
before_action :authenticate_sessionless_user!
before_action :authenticate_user!
before_action :enforce_terms!, if: :should_enforce_terms?
@@ -19,18 +20,25 @@ class ApplicationController < ActionController::Base
before_action :ldap_security_check
before_action :sentry_context
before_action :default_headers
- before_action :add_gon_variables, unless: :peek_request?
+ before_action :add_gon_variables, unless: [:peek_request?, :json_request?]
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :require_email, unless: :devise_controller?
around_action :set_locale
- after_action :set_page_title_header, if: -> { request.format == :json }
+ after_action :set_page_title_header, if: :json_request?
protect_from_forgery with: :exception, prepend: true
helper_method :can?
- helper_method :import_sources_enabled?, :github_import_enabled?, :gitea_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
+ helper_method :import_sources_enabled?, :github_import_enabled?,
+ :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?,
+ :git_import_enabled?, :gitlab_project_import_enabled?,
+ :manifest_import_enabled?
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
@@ -79,10 +87,29 @@ class ApplicationController < ActionController::Base
end
end
+ # By default, all sessions are given the same expiration time configured in
+ # the session store (e.g. 1 week). However, unauthenticated users can
+ # generate a lot of sessions, primarily for CSRF verification. It makes
+ # sense to reduce the TTL for unauthenticated to something much lower than
+ # the default (e.g. 1 hour) to limit Redis memory. In addition, Rails
+ # creates a new session after login, so the short TTL doesn't even need to
+ # be extended.
+ def limit_unauthenticated_session_times
+ return if current_user
+
+ # Rack sets this header, but not all tests may have it: https://github.com/rack/rack/blob/fdcd03a3c5a1c51d1f96fc97f9dfa1a9deac0c77/lib/rack/session/abstract/id.rb#L251-L259
+ return unless request.env['rack.session.options']
+
+ # This works because Rack uses these options every time a request is handled:
+ # https://github.com/rack/rack/blob/fdcd03a3c5a1c51d1f96fc97f9dfa1a9deac0c77/lib/rack/session/abstract/id.rb#L342
+ request.env['rack.session.options'][:expire_after] = Settings.gitlab['unauthenticated_session_expire_delay']
+ end
+
protected
def append_info_to_payload(payload)
super
+
payload[:remote_ip] = request.remote_ip
logged_user = auth_user
@@ -97,12 +124,16 @@ class ApplicationController < ActionController::Base
end
end
+ ##
# Controllers such as GitHttpController may use alternative methods
- # (e.g. tokens) to authenticate the user, whereas Devise sets current_user
+ # (e.g. tokens) to authenticate the user, whereas Devise sets current_user.
+ #
def auth_user
- return current_user if current_user.present?
-
- return try(:authenticated_user)
+ if user_signed_in?
+ current_user
+ else
+ try(:authenticated_user)
+ end
end
# This filter handles personal access tokens, and atom requests with rss tokens
@@ -307,6 +338,10 @@ class ApplicationController < ActionController::Base
!Gitlab::CurrentSettings.import_sources.empty?
end
+ def bitbucket_server_import_enabled?
+ Gitlab::CurrentSettings.import_sources.include?('bitbucket_server')
+ end
+
def github_import_enabled?
Gitlab::CurrentSettings.import_sources.include?('github')
end
@@ -351,6 +386,10 @@ class ApplicationController < ActionController::Base
Gitlab::CurrentSettings.import_sources.include?('gitlab_project')
end
+ def manifest_import_enabled?
+ Group.supports_nested_groups? && Gitlab::CurrentSettings.import_sources.include?('manifest')
+ end
+
# U2F (universal 2nd factor) devices need a unique identifier for the application
# to perform authentication.
# https://developers.yubico.com/U2F/App_ID.html
@@ -368,7 +407,7 @@ class ApplicationController < ActionController::Base
# actually stored in the session and a token is needed
# for every request. If you want the token to work as a
# sign in token, you can simply remove store: false.
- sign_in user, store: false
+ sign_in(user, store: false, message: :sessionless_sign_in)
end
end
@@ -385,6 +424,10 @@ class ApplicationController < ActionController::Base
request.path.start_with?('/-/peek')
end
+ def json_request?
+ request.format.json?
+ end
+
def should_enforce_terms?
return false unless Gitlab::CurrentSettings.current_application_settings.enforce_terms
diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb
index 09e143c23e8..7dd19f87ef5 100644
--- a/app/controllers/boards/issues_controller.rb
+++ b/app/controllers/boards/issues_controller.rb
@@ -12,8 +12,9 @@ module Boards
skip_before_action :authenticate_user!, only: [:index]
def index
- issues = Boards::Issues::ListService.new(board_parent, current_user, filter_params).execute
- issues = issues.page(params[:page]).per(params[:per] || 20)
+ list_service = Boards::Issues::ListService.new(board_parent, current_user, filter_params)
+ issues = list_service.execute
+ issues = issues.page(params[:page]).per(params[:per] || 20).without_count
make_sure_position_is_set(issues) if Gitlab::Database.read_write?
issues = issues.preload(:project,
:milestone,
@@ -22,10 +23,7 @@ module Boards
notes: [:award_emoji, :author]
)
- render json: {
- issues: serialize_as_json(issues),
- size: issues.total_count
- }
+ render_issues(issues, list_service.metadata)
end
def create
@@ -51,6 +49,13 @@ module Boards
private
+ def render_issues(issues, metadata)
+ data = { issues: serialize_as_json(issues) }
+ data.merge!(metadata)
+
+ render json: data
+ end
+
def make_sure_position_is_set(issues)
issues.each do |issue|
issue.move_to_end && issue.save unless issue.relative_position
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index 69a053d4246..dfa1da7872c 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -60,7 +60,7 @@ module AuthenticatesWithTwoFactor
remember_me(user) if user_params[:remember_me] == '1'
user.save!
- sign_in(user)
+ sign_in(user, message: :two_factor_authenticated)
else
user.increment_failed_attempts!
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP")
@@ -77,7 +77,7 @@ module AuthenticatesWithTwoFactor
session.delete(:challenge)
remember_me(user) if user_params[:remember_me] == '1'
- sign_in(user)
+ sign_in(user, message: :two_factor_authenticated)
else
user.increment_failed_attempts!
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=U2F")
diff --git a/app/controllers/concerns/group_tree.rb b/app/controllers/concerns/group_tree.rb
index 56770a17406..6ec6897e707 100644
--- a/app/controllers/concerns/group_tree.rb
+++ b/app/controllers/concerns/group_tree.rb
@@ -1,21 +1,16 @@
module GroupTree
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def render_group_tree(groups)
- @groups = if params[:filter].present?
- # We find the ancestors by ID of the search results here.
- # Otherwise the ancestors would also have filters applied,
- # which would cause them not to be preloaded.
- group_ids = groups.search(params[:filter]).select(:id)
- Gitlab::GroupHierarchy.new(Group.where(id: group_ids))
- .base_and_ancestors
- else
- # Only show root groups if no parent-id is given
- groups.where(parent_id: params[:parent_id])
- end
+ groups = groups.sort_by_attribute(@sort = params[:sort])
- @groups = @groups.with_selects_for_list(archived: params[:archived])
- .sort_by_attribute(@sort = params[:sort])
- .page(params[:page])
+ groups = if params[:filter].present?
+ filtered_groups_with_ancestors(groups)
+ else
+ # If `params[:parent_id]` is `nil`, we will only show root-groups
+ groups.where(parent_id: params[:parent_id]).page(params[:page])
+ end
+
+ @groups = groups.with_selects_for_list(archived: params[:archived])
respond_to do |format|
format.html
@@ -28,4 +23,21 @@ module GroupTree
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
+
+ def filtered_groups_with_ancestors(groups)
+ filtered_groups = groups.search(params[:filter]).page(params[:page])
+
+ if Group.supports_nested_groups?
+ # We find the ancestors by ID of the search results here.
+ # Otherwise the ancestors would also have filters applied,
+ # which would cause them not to be preloaded.
+ #
+ # Pagination needs to be applied before loading the ancestors to
+ # make sure ancestors are not cut off by pagination.
+ Gitlab::GroupHierarchy.new(Group.where(id: filtered_groups.select(:id)))
+ .base_and_ancestors
+ else
+ filtered_groups
+ end
+ end
end
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index ba510968684..37e03d70b6f 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -127,7 +127,7 @@ module IssuableActions
errors: [
"Someone edited this #{issuable.human_class_name} at the same time you did. Please refresh your browser and make sure your changes will not unintentionally remove theirs."
]
- }, status: 409
+ }, status: :conflict
end
end
end
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 5e4e8a87153..4584ff782a3 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -27,7 +27,7 @@ module LfsRequest
message: 'Git LFS is not enabled on this GitLab server, contact your admin.',
documentation_url: help_url
},
- status: 501
+ status: :not_implemented
)
end
@@ -71,7 +71,22 @@ module LfsRequest
def lfs_download_access?
return false unless project.lfs_enabled?
- ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
+ ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code? || deploy_token_can_download_code?
+ end
+
+ def deploy_token_can_download_code?
+ deploy_token_present? &&
+ deploy_token.project == project &&
+ deploy_token.active? &&
+ deploy_token.read_repository?
+ end
+
+ def deploy_token_present?
+ user && user.is_a?(DeployToken)
+ end
+
+ def deploy_token
+ user
end
def lfs_upload_access?
@@ -86,7 +101,7 @@ module LfsRequest
end
def user_can_download_code?
- has_authentication_ability?(:download_code) && can?(user, :download_code, project)
+ has_authentication_ability?(:download_code) && can?(user, :download_code, project) && !deploy_token_present?
end
def build_can_download_code?
diff --git a/app/controllers/concerns/members_presentation.rb b/app/controllers/concerns/members_presentation.rb
index c0622516fd3..215e0bdf3cb 100644
--- a/app/controllers/concerns/members_presentation.rb
+++ b/app/controllers/concerns/members_presentation.rb
@@ -2,10 +2,18 @@ module MembersPresentation
extend ActiveSupport::Concern
def present_members(members)
+ preload_associations(members)
Gitlab::View::Presenter::Factory.new(
members,
current_user: current_user,
presenter_class: MembersPresenter
).fabricate!
end
+
+ def preload_associations(members)
+ ActiveRecord::Associations::Preloader.new.preload(members, :user)
+ ActiveRecord::Associations::Preloader.new.preload(members, :source)
+ ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :status)
+ ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :u2f_registrations)
+ end
end
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index 7a6a00b8e13..409e6d4c4d2 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -1,4 +1,5 @@
module MembershipActions
+ include MembersPresentation
extend ActiveSupport::Concern
def create
@@ -20,6 +21,7 @@ module MembershipActions
.execute(member)
.present(current_user: current_user)
+ present_members([member])
respond_to do |format|
format.js { render 'shared/members/update', locals: { member: member } }
end
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index fe9a030cdf2..5127db3f5fb 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -41,7 +41,7 @@ module NotesActions
@note = Notes::CreateService.new(note_project, current_user, create_params).execute
if @note.is_a?(Note)
- Notes::RenderService.new(current_user).execute([@note])
+ prepare_notes_for_rendering([@note], noteable)
end
respond_to do |format|
@@ -56,7 +56,7 @@ module NotesActions
@note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
if @note.is_a?(Note)
- Notes::RenderService.new(current_user).execute([@note])
+ prepare_notes_for_rendering([@note])
end
respond_to do |format|
diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb
index 90bb7a87b45..99123fcb3b0 100644
--- a/app/controllers/concerns/preview_markdown.rb
+++ b/app/controllers/concerns/preview_markdown.rb
@@ -10,9 +10,12 @@ module PreviewMarkdown
when 'wikis' then { pipeline: :wiki, project_wiki: @project_wiki, page_slug: params[:id] }
when 'snippets' then { skip_project_check: true }
when 'groups' then { group: group }
+ when 'projects' then { issuable_state_filter_enabled: true }
else {}
end
+ markdown_params[:markdown_engine] = result[:markdown_engine]
+
render json: {
body: view_context.markdown(result[:text], markdown_params),
references: {
diff --git a/app/controllers/concerns/renders_notes.rb b/app/controllers/concerns/renders_notes.rb
index 36e3d76ecfe..cf04023080a 100644
--- a/app/controllers/concerns/renders_notes.rb
+++ b/app/controllers/concerns/renders_notes.rb
@@ -4,6 +4,7 @@ module RendersNotes
preload_noteable_for_regular_notes(notes)
preload_max_access_for_authors(notes, @project)
preload_first_time_contribution_for_authors(noteable, notes)
+ preload_author_status(notes)
Notes::RenderService.new(current_user).execute(notes)
notes
@@ -28,4 +29,8 @@ module RendersNotes
notes.each {|n| n.specialize_for_first_contribution!(noteable)}
end
+
+ def preload_author_status(notes)
+ ActiveRecord::Associations::Preloader.new.preload(notes, { author: :status })
+ end
end
diff --git a/app/controllers/concerns/repository_settings_redirect.rb b/app/controllers/concerns/repository_settings_redirect.rb
index 0576f0e6e70..f3db3cd563b 100644
--- a/app/controllers/concerns/repository_settings_redirect.rb
+++ b/app/controllers/concerns/repository_settings_redirect.rb
@@ -1,7 +1,7 @@
module RepositorySettingsRedirect
extend ActiveSupport::Concern
- def redirect_to_repository_settings(project)
- redirect_to project_settings_repository_path(project)
+ def redirect_to_repository_settings(project, anchor: nil)
+ redirect_to project_settings_repository_path(project, anchor: anchor)
end
end
diff --git a/app/controllers/concerns/todos_actions.rb b/app/controllers/concerns/todos_actions.rb
new file mode 100644
index 00000000000..c0acdb3498d
--- /dev/null
+++ b/app/controllers/concerns/todos_actions.rb
@@ -0,0 +1,12 @@
+module TodosActions
+ extend ActiveSupport::Concern
+
+ def create
+ todo = TodoService.new.mark_todo(issuable, current_user)
+
+ render json: {
+ count: TodosFinder.new(current_user, state: :pending).execute.count,
+ delete_path: dashboard_todo_path(todo)
+ }
+ end
+end
diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb
index 16374146ae4..434459a225a 100644
--- a/app/controllers/concerns/uploads_actions.rb
+++ b/app/controllers/concerns/uploads_actions.rb
@@ -45,6 +45,16 @@ module UploadsActions
send_upload(uploader, attachment: uploader.filename, disposition: disposition)
end
+ def authorize
+ set_workhorse_internal_api_content_type
+
+ authorized = uploader_class.workhorse_authorize(
+ has_length: false,
+ maximum_size: Gitlab::CurrentSettings.max_attachment_size.megabytes.to_i)
+
+ render json: authorized
+ end
+
private
# Explicitly set the format.
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 751dbbd8e96..0469e7e1e1f 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -21,8 +21,19 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
private
+ def group_milestones
+ groups = GroupsFinder.new(current_user, all_available: true).execute
+
+ DashboardGroupMilestone.build_collection(groups)
+ end
+
+ # See [#39545](https://gitlab.com/gitlab-org/gitlab-ce/issues/39545) for info about the deprecation of dynamic milestones
+ def dynamic_milestones
+ DashboardMilestone.build_collection(@projects, params)
+ end
+
def milestones
- @milestones = DashboardMilestone.build_collection(@projects, params)
+ @milestones = group_milestones + dynamic_milestones
end
def milestone
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 4d4ac025f8c..ccfcbbdc776 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -7,7 +7,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
skip_cross_project_access_check :index, :starred
def index
- @projects = load_projects(params.merge(non_public: true)).page(params[:page])
+ @projects = load_projects(params.merge(non_public: true))
respond_to do |format|
format.html
@@ -25,7 +25,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
def starred
@projects = load_projects(params.merge(starred: true))
- .includes(:forked_from_project, :tags).page(params[:page])
+ .includes(:forked_from_project, :tags)
@groups = []
@@ -51,6 +51,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
.new(params: finder_params, current_user: current_user)
.execute
.includes(:route, :creator, namespace: [:route, :owner])
+ .page(finder_params[:page])
prepare_projects_for_rendering(projects)
end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index f9e8fe624e8..bd7111e28bc 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -70,7 +70,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
def todo_params
- params.permit(:action_id, :author_id, :project_id, :type, :sort, :state)
+ params.permit(:action_id, :author_id, :project_id, :type, :sort, :state, :group_id)
end
def redirect_out_of_range(todos)
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index cc5ba5878f8..35a61b359c8 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -7,6 +7,6 @@ class Groups::AvatarsController < Groups::ApplicationController
@group.remove_avatar!
@group.save
- redirect_to edit_group_path(@group), status: 302
+ redirect_to edit_group_path(@group), status: :found
end
end
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index ef5d5e5c742..7dc51f4c357 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -30,7 +30,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
end
@members = @members.page(params[:page]).per(50)
- @members = present_members(@members.includes(:user))
+ @members = present_members(@members)
@requesters = present_members(
AccessRequestsFinder.new(@group).execute(current_user))
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index 78992ec7f46..1036b4e6ed3 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -23,7 +23,7 @@ class Groups::RunnersController < Groups::ApplicationController
def destroy
@runner.destroy
- redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: 302
+ redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found
end
def resume
diff --git a/app/controllers/groups/uploads_controller.rb b/app/controllers/groups/uploads_controller.rb
index f1578f75e88..74760194a1f 100644
--- a/app/controllers/groups/uploads_controller.rb
+++ b/app/controllers/groups/uploads_controller.rb
@@ -1,9 +1,11 @@
class Groups::UploadsController < Groups::ApplicationController
include UploadsActions
+ include WorkhorseRequest
skip_before_action :group, if: -> { action_name == 'show' && image_or_video? }
- before_action :authorize_upload_file!, only: [:create]
+ before_action :authorize_upload_file!, only: [:create, :authorize]
+ before_action :verify_workhorse_api!, only: [:authorize]
private
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 79fa5818359..83169636ccf 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -85,7 +85,7 @@ class GroupsController < Groups::ApplicationController
def update
if Groups::UpdateService.new(@group, current_user, group_params).execute
- redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
+ redirect_to edit_group_path(@group, anchor: params[:update_section]), notice: "Group '#{@group.name}' was successfully updated."
else
@group.restore_path!
diff --git a/app/controllers/import/bitbucket_server_controller.rb b/app/controllers/import/bitbucket_server_controller.rb
new file mode 100644
index 00000000000..798daeca6c9
--- /dev/null
+++ b/app/controllers/import/bitbucket_server_controller.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+class Import::BitbucketServerController < Import::BaseController
+ before_action :verify_bitbucket_server_import_enabled
+ before_action :bitbucket_auth, except: [:new, :configure]
+ before_action :validate_import_params, only: [:create]
+
+ # As a basic sanity check to prevent URL injection, restrict project
+ # repository input and repository slugs to allowed characters. For Bitbucket:
+ #
+ # Project keys must start with a letter and may only consist of ASCII letters, numbers and underscores (A-Z, a-z, 0-9, _).
+ #
+ # Repository names are limited to 128 characters. They must start with a
+ # letter or number and may contain spaces, hyphens, underscores, and periods.
+ # (https://community.atlassian.com/t5/Answers-Developer-Questions/stash-repository-names/qaq-p/499054)
+ VALID_BITBUCKET_CHARS = /\A[\w\-_\.\s]+\z/
+
+ def new
+ end
+
+ def create
+ repo = bitbucket_client.repo(@project_key, @repo_slug)
+
+ unless repo
+ return render json: { errors: "Project #{@project_key}/#{@repo_slug} could not be found" }, status: :unprocessable_entity
+ end
+
+ project_name = params[:new_name].presence || repo.name
+ namespace_path = params[:new_namespace].presence || current_user.username
+ target_namespace = find_or_create_namespace(namespace_path, current_user)
+
+ if current_user.can?(:create_projects, target_namespace)
+ project = Gitlab::BitbucketServerImport::ProjectCreator.new(@project_key, @repo_slug, repo, project_name, target_namespace, current_user, credentials).execute
+
+ if project.persisted?
+ render json: ProjectSerializer.new.represent(project)
+ else
+ render json: { errors: project_save_error(project) }, status: :unprocessable_entity
+ end
+ else
+ render json: { errors: 'This namespace has already been taken! Please choose another one.' }, status: :unprocessable_entity
+ end
+ rescue BitbucketServer::Client::ServerError => e
+ render json: { errors: "Unable to connect to server: #{e}" }, status: :unprocessable_entity
+ end
+
+ def configure
+ session[personal_access_token_key] = params[:personal_access_token]
+ session[bitbucket_server_username_key] = params[:bitbucket_username]
+ session[bitbucket_server_url_key] = params[:bitbucket_server_url]
+
+ redirect_to status_import_bitbucket_server_path
+ end
+
+ def status
+ repos = bitbucket_client.repos
+
+ @repos, @incompatible_repos = repos.partition { |repo| repo.valid? }
+
+ @already_added_projects = find_already_added_projects('bitbucket_server')
+ already_added_projects_names = @already_added_projects.pluck(:import_source)
+
+ @repos.to_a.reject! { |repo| already_added_projects_names.include?(repo.browse_url) }
+ rescue BitbucketServer::Connection::ConnectionError, BitbucketServer::Client::ServerError => e
+ flash[:alert] = "Unable to connect to server: #{e}"
+ clear_session_data
+ redirect_to new_import_bitbucket_server_path
+ end
+
+ def jobs
+ render json: find_jobs('bitbucket_server')
+ end
+
+ private
+
+ def bitbucket_client
+ @bitbucket_client ||= BitbucketServer::Client.new(credentials)
+ end
+
+ def validate_import_params
+ @project_key = params[:project]
+ @repo_slug = params[:repository]
+
+ return render_validation_error('Missing project key') unless @project_key.present? && @repo_slug.present?
+ return render_validation_error('Missing repository slug') unless @repo_slug.present?
+ return render_validation_error('Invalid project key') unless @project_key =~ VALID_BITBUCKET_CHARS
+ return render_validation_error('Invalid repository slug') unless @repo_slug =~ VALID_BITBUCKET_CHARS
+ end
+
+ def render_validation_error(message)
+ render json: { errors: message }, status: :unprocessable_entity
+ end
+
+ def bitbucket_auth
+ unless session[bitbucket_server_url_key].present? &&
+ session[bitbucket_server_username_key].present? &&
+ session[personal_access_token_key].present?
+ redirect_to new_import_bitbucket_server_path
+ end
+ end
+
+ def verify_bitbucket_server_import_enabled
+ render_404 unless bitbucket_server_import_enabled?
+ end
+
+ def bitbucket_server_url_key
+ :bitbucket_server_url
+ end
+
+ def bitbucket_server_username_key
+ :bitbucket_server_username
+ end
+
+ def personal_access_token_key
+ :bitbucket_server_personal_access_token
+ end
+
+ def clear_session_data
+ session[bitbucket_server_url_key] = nil
+ session[bitbucket_server_username_key] = nil
+ session[personal_access_token_key] = nil
+ end
+
+ def credentials
+ {
+ base_uri: session[bitbucket_server_url_key],
+ user: session[bitbucket_server_username_key],
+ password: session[personal_access_token_key]
+ }
+ end
+end
diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb
index fccbdbca0f6..53f70446d95 100644
--- a/app/controllers/import/gitlab_controller.rb
+++ b/app/controllers/import/gitlab_controller.rb
@@ -1,4 +1,7 @@
class Import::GitlabController < Import::BaseController
+ MAX_PROJECT_PAGES = 15
+ PER_PAGE_PROJECTS = 100
+
before_action :verify_gitlab_import_enabled
before_action :gitlab_auth, except: :callback
@@ -10,7 +13,7 @@ class Import::GitlabController < Import::BaseController
end
def status
- @repos = client.projects
+ @repos = client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS)
@already_added_projects = find_already_added_projects('gitlab')
already_added_projects_names = @already_added_projects.pluck(:import_source)
diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb
new file mode 100644
index 00000000000..e5a719fa0df
--- /dev/null
+++ b/app/controllers/import/manifest_controller.rb
@@ -0,0 +1,93 @@
+class Import::ManifestController < Import::BaseController
+ before_action :whitelist_query_limiting, only: [:create]
+ before_action :verify_import_enabled
+ before_action :ensure_import_vars, only: [:create, :status]
+
+ def new
+ end
+
+ def status
+ @already_added_projects = find_already_added_projects
+ already_added_import_urls = @already_added_projects.pluck(:import_url)
+
+ @pending_repositories = repositories.to_a.reject do |repository|
+ already_added_import_urls.include?(repository[:url])
+ end
+ end
+
+ def upload
+ group = Group.find(params[:group_id])
+
+ unless can?(current_user, :create_projects, group)
+ @errors = ["You don't have enough permissions to create projects in the selected group"]
+
+ render :new && return
+ end
+
+ manifest = Gitlab::ManifestImport::Manifest.new(params[:manifest].tempfile)
+
+ if manifest.valid?
+ session[:manifest_import_repositories] = manifest.projects
+ session[:manifest_import_group_id] = group.id
+
+ redirect_to status_import_manifest_path
+ else
+ @errors = manifest.errors
+
+ render :new
+ end
+ end
+
+ def jobs
+ render json: find_jobs
+ end
+
+ def create
+ repository = repositories.find do |project|
+ project[:id] == params[:repo_id].to_i
+ end
+
+ project = Gitlab::ManifestImport::ProjectCreator.new(repository, group, current_user).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 ensure_import_vars
+ unless group && repositories.present?
+ redirect_to(new_import_manifest_path)
+ end
+ end
+
+ def group
+ @group ||= Group.find_by(id: session[:manifest_import_group_id])
+ end
+
+ def repositories
+ @repositories ||= session[:manifest_import_repositories]
+ end
+
+ def find_jobs
+ find_already_added_projects.to_json(only: [:id], methods: [:import_status])
+ end
+
+ def find_already_added_projects
+ group.all_projects
+ .where(import_type: 'manifest')
+ .where(creator_id: current_user)
+ .includes(:import_state)
+ end
+
+ def verify_import_enabled
+ render_404 unless manifest_import_enabled?
+ end
+
+ def whitelist_query_limiting
+ Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/48939')
+ end
+end
diff --git a/app/controllers/instance_statistics/application_controller.rb b/app/controllers/instance_statistics/application_controller.rb
new file mode 100644
index 00000000000..a273dde105c
--- /dev/null
+++ b/app/controllers/instance_statistics/application_controller.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class InstanceStatistics::ApplicationController < ApplicationController
+ before_action :authorize_read_instance_statistics!
+ layout 'instance_statistics'
+
+ def authorize_read_instance_statistics!
+ render_404 unless can?(current_user, :read_instance_statistics)
+ end
+end
diff --git a/app/controllers/instance_statistics/cohorts_controller.rb b/app/controllers/instance_statistics/cohorts_controller.rb
new file mode 100644
index 00000000000..7eba0a5ecdd
--- /dev/null
+++ b/app/controllers/instance_statistics/cohorts_controller.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class InstanceStatistics::CohortsController < InstanceStatistics::ApplicationController
+ def index
+ if Gitlab::CurrentSettings.usage_ping_enabled
+ cohorts_results = Rails.cache.fetch('cohorts', expires_in: 1.day) do
+ CohortsService.new.execute
+ end
+
+ @cohorts = CohortsSerializer.new.represent(cohorts_results)
+ end
+ end
+end
diff --git a/app/controllers/instance_statistics/conversational_development_index_controller.rb b/app/controllers/instance_statistics/conversational_development_index_controller.rb
new file mode 100644
index 00000000000..d6d2191849f
--- /dev/null
+++ b/app/controllers/instance_statistics/conversational_development_index_controller.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class InstanceStatistics::ConversationalDevelopmentIndexController < InstanceStatistics::ApplicationController
+ def index
+ @metric = ConversationalDevelopmentIndex::Metric.order(:created_at).last&.present
+ end
+end
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 67057b5b126..d172aee5436 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -41,7 +41,7 @@ class JwtController < ApplicationController
"You must use a personal access token with 'api' scope for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}" }
]
- }, status: 401
+ }, status: :unauthorized
end
def render_unauthorized
@@ -50,10 +50,26 @@ class JwtController < ApplicationController
{ code: 'UNAUTHORIZED',
message: 'HTTP Basic: Access denied' }
]
- }, status: 401
+ }, status: :unauthorized
end
def auth_params
- params.permit(:service, :scope, :account, :client_id)
+ params.permit(:service, :account, :client_id)
+ .merge(additional_params)
+ end
+
+ def additional_params
+ { scopes: scopes_param }.compact
+ end
+
+ # We have to parse scope here, because Docker Client does not send an array of scopes,
+ # but rather a flat list and we loose second scope when being processed by Rails:
+ # scope=scopeA&scope=scopeB
+ #
+ # This method makes to always return an array of scopes
+ def scopes_param
+ return unless params[:scope].present?
+
+ Array(Rack::Utils.parse_query(request.query_string)['scope'])
end
end
diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb
index 8ec4bb1233f..ed20302487c 100644
--- a/app/controllers/notification_settings_controller.rb
+++ b/app/controllers/notification_settings_controller.rb
@@ -5,14 +5,14 @@ class NotificationSettingsController < ApplicationController
return render_404 unless can_read?(resource)
@notification_setting = current_user.notification_settings_for(resource)
- @saved = @notification_setting.update_attributes(notification_setting_params)
+ @saved = @notification_setting.update(notification_setting_params)
render_response
end
def update
@notification_setting = current_user.notification_settings.find(params[:id])
- @saved = @notification_setting.update_attributes(notification_setting_params)
+ @saved = @notification_setting.update(notification_setting_params)
render_response
end
diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb
index f0cdc228366..f1e77d68acd 100644
--- a/app/controllers/profiles/active_sessions_controller.rb
+++ b/app/controllers/profiles/active_sessions_controller.rb
@@ -7,7 +7,7 @@ class Profiles::ActiveSessionsController < Profiles::ApplicationController
ActiveSession.destroy(current_user, params[:id])
respond_to do |format|
- format.html { redirect_to profile_active_sessions_url, status: 302 }
+ format.html { redirect_to profile_active_sessions_url, status: :found }
format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
index 39b9f8a84d1..4f030ded80f 100644
--- a/app/controllers/profiles/avatars_controller.rb
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -4,6 +4,6 @@ class Profiles::AvatarsController < Profiles::ApplicationController
Users::UpdateService.new(current_user, user: @user).execute { |user| user.remove_avatar! }
- redirect_to profile_path, status: 302
+ redirect_to profile_path, status: :found
end
end
diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb
index 2353f0840d6..a186c5f36a8 100644
--- a/app/controllers/profiles/chat_names_controller.rb
+++ b/app/controllers/profiles/chat_names_controller.rb
@@ -39,7 +39,7 @@ class Profiles::ChatNamesController < Profiles::ApplicationController
flash[:alert] = "Could not delete chat nickname #{@chat_name.chat_name}."
end
- redirect_to profile_chat_names_path, status: 302
+ redirect_to profile_chat_names_path, status: :found
end
private
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index bbd7ba49d77..a39824ec9c8 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -19,7 +19,7 @@ class Profiles::EmailsController < Profiles::ApplicationController
Emails::DestroyService.new(current_user, user: current_user).execute(@email)
respond_to do |format|
- format.html { redirect_to profile_emails_url, status: 302 }
+ format.html { redirect_to profile_emails_url, status: :found }
format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb
index 38e3eacd229..c32507756e8 100644
--- a/app/controllers/profiles/gpg_keys_controller.rb
+++ b/app/controllers/profiles/gpg_keys_controller.rb
@@ -21,7 +21,7 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
@gpg_key.destroy
respond_to do |format|
- format.html { redirect_to profile_gpg_keys_url, status: 302 }
+ format.html { redirect_to profile_gpg_keys_url, status: :found }
format.js { head :ok }
end
end
@@ -30,7 +30,7 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
@gpg_key.revoke
respond_to do |format|
- format.html { redirect_to profile_gpg_keys_url, status: 302 }
+ format.html { redirect_to profile_gpg_keys_url, status: :found }
format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 12a6cd11f80..6035258667e 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -26,7 +26,7 @@ class Profiles::KeysController < Profiles::ApplicationController
Keys::DestroyService.new(current_user).execute(@key)
respond_to do |format|
- format.html { redirect_to profile_keys_url, status: 302 }
+ format.html { redirect_to profile_keys_url, status: :found }
format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index aa9789f8a0f..29ff18a1219 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -78,7 +78,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def destroy
current_user.disable_two_factor!
- redirect_to profile_account_path, status: 302
+ redirect_to profile_account_path, status: :found
end
def skip
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 074db361949..6f50cbb4a36 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -99,7 +99,9 @@ class ProfilesController < Profiles::ApplicationController
:username,
:website_url,
:organization,
- :preferred_language
+ :preferred_language,
+ :private_profile,
+ status: [:emoji, :message]
)
end
end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 5ab6d103c89..b4f814fd3a4 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -61,7 +61,7 @@ class Projects::ApplicationController < ApplicationController
def require_non_empty_project
# Be sure to return status code 303 to avoid a double DELETE:
# http://api.rubyonrails.org/classes/ActionController/Redirecting.html
- redirect_to project_path(@project), status: 303 if @project.empty_repo?
+ redirect_to project_path(@project), status: :see_other if @project.empty_repo?
end
def require_branch_head
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index 992c8ea6992..07627ffb69f 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -14,7 +14,7 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
end
def labels
- render json: @autocomplete_service.labels(target)
+ render json: @autocomplete_service.labels_as_hash(target)
end
def milestones
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index 21a403f3765..53fdc5843b5 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -21,6 +21,6 @@ class Projects::AvatarsController < Projects::ApplicationController
@project.save
- redirect_to edit_project_path(@project), status: 302
+ redirect_to edit_project_path(@project, anchor: 'js-general-project-settings'), status: :found
end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index cd7250b10fc..d1dc9fe9600 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -98,7 +98,7 @@ class Projects::BranchesController < Projects::ApplicationController
flash_type = result[:status] == :error ? :alert : :notice
flash[flash_type] = result[:message]
- redirect_to project_branches_path(@project), status: 303
+ redirect_to project_branches_path(@project), status: :see_other
end
format.js { render nothing: true, status: result[:return_code] }
diff --git a/app/controllers/projects/clusters/gcp_controller.rb b/app/controllers/projects/clusters/gcp_controller.rb
deleted file mode 100644
index c2c5ad61e01..00000000000
--- a/app/controllers/projects/clusters/gcp_controller.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-class Projects::Clusters::GcpController < Projects::ApplicationController
- before_action :authorize_read_cluster!
- before_action :authorize_create_cluster!, only: [:new, :create]
- before_action :authorize_google_api, except: :login
- helper_method :token_in_session
-
- def login
- begin
- state = generate_session_key_redirect(gcp_new_namespace_project_clusters_path.to_s)
-
- @authorize_url = GoogleApi::CloudPlatform::Client.new(
- nil, callback_google_api_auth_url,
- state: state).authorize_url
- rescue GoogleApi::Auth::ConfigMissingError
- # no-op
- end
- end
-
- def new
- @cluster = ::Clusters::Cluster.new.tap do |cluster|
- cluster.build_provider_gcp
- end
- end
-
- def create
- @cluster = ::Clusters::CreateService
- .new(project, current_user, create_params)
- .execute(token_in_session)
-
- if @cluster.persisted?
- redirect_to project_cluster_path(project, @cluster)
- else
- render :new
- end
- end
-
- private
-
- def create_params
- params.require(:cluster).permit(
- :enabled,
- :name,
- :environment_scope,
- provider_gcp_attributes: [
- :gcp_project_id,
- :zone,
- :num_nodes,
- :machine_type
- ]).merge(
- provider_type: :gcp,
- platform_type: :kubernetes
- )
- end
-
- def authorize_google_api
- unless GoogleApi::CloudPlatform::Client.new(token_in_session, nil)
- .validate_token(expires_at_in_session)
- redirect_to action: 'login'
- end
- end
-
- def token_in_session
- session[GoogleApi::CloudPlatform::Client.session_key_for_token]
- end
-
- def expires_at_in_session
- @expires_at_in_session ||=
- session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at]
- end
-
- def generate_session_key_redirect(uri)
- GoogleApi::CloudPlatform::Client.new_session_key_for_redirect_uri do |key|
- session[key] = uri
- end
- end
-end
diff --git a/app/controllers/projects/clusters/user_controller.rb b/app/controllers/projects/clusters/user_controller.rb
deleted file mode 100644
index d0db64b2fa9..00000000000
--- a/app/controllers/projects/clusters/user_controller.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-class Projects::Clusters::UserController < Projects::ApplicationController
- before_action :authorize_read_cluster!
- before_action :authorize_create_cluster!, only: [:new, :create]
-
- def new
- @cluster = ::Clusters::Cluster.new.tap do |cluster|
- cluster.build_platform_kubernetes
- end
- end
-
- def create
- @cluster = ::Clusters::CreateService
- .new(project, current_user, create_params)
- .execute
-
- if @cluster.persisted?
- redirect_to project_cluster_path(project, @cluster)
- else
- render :new
- end
- end
-
- private
-
- def create_params
- params.require(:cluster).permit(
- :enabled,
- :name,
- :environment_scope,
- platform_kubernetes_attributes: [
- :namespace,
- :api_url,
- :token,
- :ca_cert
- ]).merge(
- provider_type: :user,
- platform_type: :kubernetes
- )
- end
-end
diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb
index d58039b7d42..358fe59618b 100644
--- a/app/controllers/projects/clusters_controller.rb
+++ b/app/controllers/projects/clusters_controller.rb
@@ -1,10 +1,15 @@
class Projects::ClustersController < Projects::ApplicationController
- before_action :cluster, except: [:index, :new]
+ before_action :cluster, except: [:index, :new, :create_gcp, :create_user]
before_action :authorize_read_cluster!
+ before_action :generate_gcp_authorize_url, only: [:new]
+ before_action :validate_gcp_token, only: [:new]
+ before_action :gcp_cluster, only: [:new]
+ before_action :user_cluster, only: [:new]
before_action :authorize_create_cluster!, only: [:new]
before_action :authorize_update_cluster!, only: [:update]
before_action :authorize_admin_cluster!, only: [:destroy]
before_action :update_applications_status, only: [:status]
+ helper_method :token_in_session
STATUS_POLLING_INTERVAL = 10_000
@@ -57,13 +62,45 @@ class Projects::ClustersController < Projects::ApplicationController
def destroy
if cluster.destroy
flash[:notice] = _('Kubernetes cluster integration was successfully removed.')
- redirect_to project_clusters_path(project), status: 302
+ redirect_to project_clusters_path(project), status: :found
else
flash[:notice] = _('Kubernetes cluster integration was not removed.')
render :show
end
end
+ def create_gcp
+ @gcp_cluster = ::Clusters::CreateService
+ .new(project, current_user, create_gcp_cluster_params)
+ .execute(token_in_session)
+
+ if @gcp_cluster.persisted?
+ redirect_to project_cluster_path(project, @gcp_cluster)
+ else
+ generate_gcp_authorize_url
+ validate_gcp_token
+ user_cluster
+
+ render :new, locals: { active_tab: 'gcp' }
+ end
+ end
+
+ def create_user
+ @user_cluster = ::Clusters::CreateService
+ .new(project, current_user, create_user_cluster_params)
+ .execute(token_in_session)
+
+ if @user_cluster.persisted?
+ redirect_to project_cluster_path(project, @user_cluster)
+ else
+ generate_gcp_authorize_url
+ validate_gcp_token
+ gcp_cluster
+
+ render :new, locals: { active_tab: 'user' }
+ end
+ end
+
private
def cluster
@@ -95,6 +132,80 @@ class Projects::ClustersController < Projects::ApplicationController
end
end
+ def create_gcp_cluster_params
+ params.require(:cluster).permit(
+ :enabled,
+ :name,
+ :environment_scope,
+ provider_gcp_attributes: [
+ :gcp_project_id,
+ :zone,
+ :num_nodes,
+ :machine_type
+ ]).merge(
+ provider_type: :gcp,
+ platform_type: :kubernetes
+ )
+ end
+
+ def create_user_cluster_params
+ params.require(:cluster).permit(
+ :enabled,
+ :name,
+ :environment_scope,
+ platform_kubernetes_attributes: [
+ :namespace,
+ :api_url,
+ :token,
+ :ca_cert
+ ]).merge(
+ provider_type: :user,
+ platform_type: :kubernetes
+ )
+ end
+
+ def generate_gcp_authorize_url
+ state = generate_session_key_redirect(new_project_cluster_path(@project).to_s)
+
+ @authorize_url = GoogleApi::CloudPlatform::Client.new(
+ nil, callback_google_api_auth_url,
+ state: state).authorize_url
+ rescue GoogleApi::Auth::ConfigMissingError
+ # no-op
+ end
+
+ def gcp_cluster
+ @gcp_cluster = ::Clusters::Cluster.new.tap do |cluster|
+ cluster.build_provider_gcp
+ end
+ end
+
+ def user_cluster
+ @user_cluster = ::Clusters::Cluster.new.tap do |cluster|
+ cluster.build_platform_kubernetes
+ end
+ end
+
+ def validate_gcp_token
+ @valid_gcp_token = GoogleApi::CloudPlatform::Client.new(token_in_session, nil)
+ .validate_token(expires_at_in_session)
+ end
+
+ def token_in_session
+ session[GoogleApi::CloudPlatform::Client.session_key_for_token]
+ end
+
+ def expires_at_in_session
+ @expires_at_in_session ||=
+ session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at]
+ end
+
+ def generate_session_key_redirect(uri)
+ GoogleApi::CloudPlatform::Client.new_session_key_for_redirect_uri do |key|
+ session[key] = uri
+ end
+ end
+
def authorize_update_cluster!
access_denied! unless can?(current_user, :update_cluster, cluster)
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 1d1184d46d1..44b176d304e 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -22,7 +22,9 @@ class Projects::CommitController < Projects::ApplicationController
apply_diff_view_cookie!
respond_to do |format|
- format.html { render }
+ format.html do
+ render
+ end
format.diff do
send_git_diff(@project.repository, @commit.diff_refs)
end
@@ -124,7 +126,10 @@ class Projects::CommitController < Projects::ApplicationController
end
def commit
- @noteable = @commit ||= @project.commit_by(oid: params[:id])
+ @noteable = @commit ||= @project.commit_by(oid: params[:id]).tap do |commit|
+ # preload author and their status for rendering
+ commit&.author&.status
+ end
end
def define_commit_vars
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index 9e495061f4e..36faea8056e 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -4,13 +4,17 @@ class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath
include RendersCommits
- before_action :whitelist_query_limiting
+ before_action :whitelist_query_limiting, except: :commits_root
before_action :require_non_empty_project
- before_action :assign_ref_vars
+ before_action :assign_ref_vars, except: :commits_root
before_action :authorize_download_code!
- before_action :set_commits
+ before_action :set_commits, except: :commits_root
before_action :set_request_format, only: :show
+ def commits_root
+ redirect_to project_commits_path(@project, @project.default_branch)
+ end
+
def show
@merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
.find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index f43ef2e5f2f..28fea322334 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -10,7 +10,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
def index
respond_to do |format|
- format.html { redirect_to_repository_settings(@project) }
+ format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') }
format.json do
render json: Projects::Settings::DeployKeysPresenter.new(@project, current_user: current_user).as_json
end
@@ -18,7 +18,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def new
- redirect_to_repository_settings(@project)
+ redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings')
end
def create
@@ -28,16 +28,16 @@ class Projects::DeployKeysController < Projects::ApplicationController
flash[:alert] = @key.errors.full_messages.join(', ').html_safe
end
- redirect_to_repository_settings(@project)
+ redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings')
end
def edit
end
def update
- if deploy_key.update_attributes(update_params)
+ if deploy_key.update(update_params)
flash[:notice] = 'Deploy key was successfully updated.'
- redirect_to_repository_settings(@project)
+ redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings')
else
render 'edit'
end
@@ -47,7 +47,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
Projects::EnableDeployKeyService.new(@project, current_user, params).execute
respond_to do |format|
- format.html { redirect_to_repository_settings(@project) }
+ format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') }
format.json { head :ok }
end
end
@@ -59,7 +59,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
deploy_key_project.destroy!
respond_to do |format|
- format.html { redirect_to_repository_settings(@project) }
+ format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') }
format.json { head :ok }
end
end
diff --git a/app/controllers/projects/deploy_tokens_controller.rb b/app/controllers/projects/deploy_tokens_controller.rb
index 2f91b8f36de..83abda64fe0 100644
--- a/app/controllers/projects/deploy_tokens_controller.rb
+++ b/app/controllers/projects/deploy_tokens_controller.rb
@@ -5,6 +5,6 @@ class Projects::DeployTokensController < Projects::ApplicationController
@token = @project.deploy_tokens.find(params[:id])
@token.revoke!
- redirect_to project_settings_repository_path(project)
+ redirect_to project_settings_repository_path(project, anchor: 'js-deploy-tokens')
end
end
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 0821362f5df..68353e6a210 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -2,7 +2,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
layout 'project'
before_action :authorize_read_environment!
before_action :authorize_create_environment!, only: [:new, :create]
- before_action :authorize_create_deployment!, only: [:stop]
+ before_action :authorize_stop_environment!, only: [:stop]
before_action :authorize_update_environment!, only: [:edit, :update]
before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :metrics]
@@ -116,7 +116,17 @@ class Projects::EnvironmentsController < Projects::ApplicationController
set_workhorse_internal_api_content_type
render json: Gitlab::Workhorse.terminal_websocket(terminal)
else
- render text: 'Not found', status: 404
+ render text: 'Not found', status: :not_found
+ end
+ end
+
+ def metrics_redirect
+ environment = project.default_environment
+
+ if environment
+ redirect_to environment_metrics_path(environment)
+ else
+ render :empty
end
end
@@ -165,4 +175,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def environment
@environment ||= project.environments.find(params[:id])
end
+
+ def authorize_stop_environment!
+ access_denied! unless can?(current_user, :stop_environment, environment)
+ end
end
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index 07249fe3182..a52814e6e52 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -53,7 +53,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
end
send_challenges
- render plain: "HTTP Basic: Access denied\n", status: 401
+ render plain: "HTTP Basic: Access denied\n", status: :unauthorized
rescue Gitlab::Auth::MissingPersonalAccessTokenError
render_missing_personal_access_token
end
@@ -83,7 +83,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
render plain: "HTTP Basic: Access denied\n" \
"You must use a personal access token with 'api' scope for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}",
- status: 401
+ status: :unauthorized
end
def repository
diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb
index f58ee3e9109..bc5f38f3c2b 100644
--- a/app/controllers/projects/group_links_controller.rb
+++ b/app/controllers/projects/group_links_controller.rb
@@ -24,7 +24,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
def update
@group_link = @project.project_group_links.find(params[:id])
- @group_link.update_attributes(group_link_params)
+ @group_link.update(group_link_params)
end
def destroy
@@ -34,7 +34,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
respond_to do |format|
format.html do
- redirect_to project_project_members_path(project), status: 302
+ redirect_to project_project_members_path(project), status: :found
end
format.js { head :ok }
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index dd7aa1a67b9..2da2aad9b33 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -29,7 +29,7 @@ class Projects::HooksController < Projects::ApplicationController
end
def update
- if hook.update_attributes(hook_params)
+ if hook.update(hook_params)
flash[:notice] = 'Hook was successfully updated.'
redirect_to project_settings_integrations_path(@project)
else
@@ -48,7 +48,7 @@ class Projects::HooksController < Projects::ApplicationController
def destroy
hook.destroy
- redirect_to project_settings_integrations_path(@project), status: 302
+ redirect_to project_settings_integrations_path(@project), status: :found
end
private
@@ -58,8 +58,7 @@ class Projects::HooksController < Projects::ApplicationController
end
def hook_logs
- @hook_logs ||=
- Kaminari.paginate_array(hook.web_hook_logs.order(created_at: :desc)).page(params[:page])
+ @hook_logs ||= hook.web_hook_logs.recent.page(params[:page])
end
def hook_params
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 7c897b2d86c..ef8159aa553 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -165,7 +165,7 @@ class Projects::IssuesController < Projects::ApplicationController
return @issue if defined?(@issue)
# The Sortable default scope causes performance issues when used with find_by
- @issuable = @noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take!
+ @issuable = @noteable = @issue ||= @project.issues.includes(author: :status).where(iid: params[:id]).reorder(nil).take!
@note = @project.notes.new(noteable: @issuable)
return render_404 unless can?(current_user, :read_issue, @issue)
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 63f0aea3195..e69faae754a 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -2,11 +2,12 @@ class Projects::JobsController < Projects::ApplicationController
include SendFileUpload
before_action :build, except: [:index, :cancel_all]
- before_action :authorize_read_build!,
- only: [:index, :show, :status, :raw, :trace]
+ before_action :authorize_read_build!
before_action :authorize_update_build!,
except: [:index, :show, :status, :raw, :trace, :cancel_all, :erase]
before_action :authorize_erase_build!, only: [:erase]
+ before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_workhorse_authorize]
+ before_action :verify_api_request!, only: :terminal_websocket_authorize
layout 'project'
@@ -44,12 +45,10 @@ class Projects::JobsController < Projects::ApplicationController
end
def show
- @builds = @project.pipelines
- .find_by_sha(@build.sha)
- .builds
+ @pipeline = @build.pipeline
+ @builds = @pipeline.builds
.order('id DESC')
.present(current_user: current_user)
- @pipeline = @build.pipeline
respond_to do |format|
format.html
@@ -136,6 +135,15 @@ class Projects::JobsController < Projects::ApplicationController
end
end
+ def terminal
+ end
+
+ # GET .../terminal.ws : implemented in gitlab-workhorse
+ def terminal_websocket_authorize
+ set_workhorse_internal_api_content_type
+ render json: Gitlab::Workhorse.terminal_websocket(@build.terminal_specification)
+ end
+
private
def authorize_update_build!
@@ -146,6 +154,14 @@ class Projects::JobsController < Projects::ApplicationController
return access_denied! unless can?(current_user, :erase_build, build)
end
+ def authorize_use_build_terminal!
+ return access_denied! unless can?(current_user, :create_build_terminal, build)
+ end
+
+ def verify_api_request!
+ Gitlab::Workhorse.verify_api_request!(request.headers)
+ end
+
def raw_send_params
{ type: 'text/plain; charset=utf-8', disposition: 'inline' }
end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 91016f6494e..8a2bce6e7b5 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -39,7 +39,7 @@ class Projects::LabelsController < Projects::ApplicationController
else
respond_to do |format|
format.html { render :new }
- format.json { render json: { message: @label.errors.messages }, status: 400 }
+ format.json { render json: { message: @label.errors.messages }, status: :bad_request }
end
end
end
@@ -112,10 +112,10 @@ class Projects::LabelsController < Projects::ApplicationController
begin
return render_404 unless promote_service.execute(@label)
- flash[:notice] = "#{@label.title} promoted to <a href=\"#{group_labels_path(@project.group)}\">group label</a>.".html_safe
+ flash[:notice] = flash_notice_for(@label, @project.group)
respond_to do |format|
format.html do
- redirect_to(project_labels_path(@project), status: 303)
+ redirect_to(project_labels_path(@project), status: :see_other)
end
format.json do
render json: { url: project_labels_path(@project) }
@@ -135,6 +135,15 @@ class Projects::LabelsController < Projects::ApplicationController
end
end
+ def flash_notice_for(label, group)
+ notice = ''.html_safe
+ notice << label.title
+ notice << ' promoted to '
+ notice << view_context.link_to('<u>group label</u>'.html_safe, group_labels_path(group))
+ notice << '.'
+ notice
+ end
+
protected
def label_params
@@ -151,7 +160,10 @@ class Projects::LabelsController < Projects::ApplicationController
def find_labels
@available_labels ||=
- LabelsFinder.new(current_user, project_id: @project.id, include_ancestor_groups: params[:include_ancestor_groups]).execute
+ LabelsFinder.new(current_user,
+ project_id: @project.id,
+ include_ancestor_groups: params[:include_ancestor_groups],
+ search: params[:search]).execute
end
def authorize_admin_labels!
diff --git a/app/controllers/projects/lfs_api_controller.rb b/app/controllers/projects/lfs_api_controller.rb
index ee4ed674110..a01351ba292 100644
--- a/app/controllers/projects/lfs_api_controller.rb
+++ b/app/controllers/projects/lfs_api_controller.rb
@@ -1,6 +1,8 @@
class Projects::LfsApiController < Projects::GitHttpClientController
include LfsRequest
+ LFS_TRANSFER_CONTENT_TYPE = 'application/octet-stream'.freeze
+
skip_before_action :lfs_check_access!, only: [:deprecated]
before_action :lfs_check_batch_operation!, only: [:batch]
@@ -25,7 +27,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController
message: 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
documentation_url: "#{Gitlab.config.gitlab.url}/help"
},
- status: 501
+ status: :not_implemented
)
end
@@ -86,14 +88,17 @@ class Projects::LfsApiController < Projects::GitHttpClientController
upload: {
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}/#{object[:size]}",
header: {
- Authorization: request.headers['Authorization']
+ Authorization: request.headers['Authorization'],
+ # 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
}
}
end
def lfs_check_batch_operation!
- if upload_request? && Gitlab::Database.read_only?
+ if batch_operation_disallowed?
render(
json: {
message: lfs_read_only_message
@@ -105,6 +110,11 @@ class Projects::LfsApiController < Projects::GitHttpClientController
end
# Overridden in EE
+ def batch_operation_disallowed?
+ upload_request? && Gitlab::Database.read_only?
+ end
+
+ # Overridden in EE
def lfs_read_only_message
_('You cannot write to this read-only GitLab instance.')
end
diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb
index 45c98d60822..dd7e673ec75 100644
--- a/app/controllers/projects/lfs_storage_controller.rb
+++ b/app/controllers/projects/lfs_storage_controller.rb
@@ -28,7 +28,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
if store_file!(oid, size)
head 200
else
- render plain: 'Unprocessable entity', status: 422
+ render plain: 'Unprocessable entity', status: :unprocessable_entity
end
rescue ActiveRecord::RecordInvalid
render_lfs_forbidden
diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb
index 8e4aeec16dc..fead81dd472 100644
--- a/app/controllers/projects/merge_requests/application_controller.rb
+++ b/app/controllers/projects/merge_requests/application_controller.rb
@@ -6,7 +6,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
private
def merge_request
- @issuable = @merge_request ||= @project.merge_requests.find_by!(iid: params[:id])
+ @issuable = @merge_request ||= @project.merge_requests.includes(author: :status).find_by!(iid: params[:id])
end
def merge_request_params
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index a7c5f858c42..1b069fe507b 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -99,6 +99,23 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
}
end
+ def test_reports
+ result = @merge_request.compare_test_reports
+
+ case result[:status]
+ when :parsing
+ Gitlab::PollingInterval.set_header(response, interval: 3000)
+
+ render json: '', status: :no_content
+ when :parsed
+ render json: result[:data].to_json, status: :ok
+ when :error
+ render json: { status_reason: result[:status_reason] }, status: :bad_request
+ else
+ render json: { status_reason: 'Unknown error' }, status: :internal_server_error
+ end
+ end
+
def edit
define_edit_vars
end
@@ -192,7 +209,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
deployment = environment.first_deployment_for(@merge_request.diff_head_sha)
stop_url =
- if environment.stop_action? && can?(current_user, :create_deployment, environment)
+ if can?(current_user, :stop_environment, environment)
stop_project_environment_path(project, environment)
end
@@ -227,7 +244,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def rebase
RebaseWorker.perform_async(@merge_request.id, current_user.id)
- render nothing: true, status: 200
+ render nothing: true, status: :ok
end
protected
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index f85dcfe6bfc..b9b3dcd5a85 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -76,8 +76,8 @@ class Projects::MilestonesController < Projects::ApplicationController
def promote
promoted_milestone = Milestones::PromoteService.new(project, current_user).execute(milestone)
+ flash[:notice] = flash_notice_for(promoted_milestone, project.group)
- flash[:notice] = "#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, promoted_milestone.iid)}\">group milestone</a>.".html_safe
respond_to do |format|
format.html do
redirect_to project_milestones_path(project)
@@ -90,13 +90,22 @@ class Projects::MilestonesController < Projects::ApplicationController
redirect_to milestone, alert: error.message
end
+ def flash_notice_for(milestone, group)
+ notice = ''.html_safe
+ notice << milestone.title
+ notice << ' promoted to '
+ notice << view_context.link_to('<u>group milestone</u>'.html_safe, group_milestone_path(group, milestone.iid))
+ notice << '.'
+ notice
+ end
+
def destroy
return access_denied! unless can?(current_user, :admin_milestone, @project)
Milestones::DestroyService.new(project, current_user).execute(milestone)
respond_to do |format|
- format.html { redirect_to namespace_project_milestones_path, status: 303 }
+ format.html { redirect_to namespace_project_milestones_path, status: :see_other }
format.js { head :ok }
end
end
diff --git a/app/controllers/projects/mirrors_controller.rb b/app/controllers/projects/mirrors_controller.rb
index 5698ff4e706..3739608e4c0 100644
--- a/app/controllers/projects/mirrors_controller.rb
+++ b/app/controllers/projects/mirrors_controller.rb
@@ -9,18 +9,20 @@ class Projects::MirrorsController < Projects::ApplicationController
layout "project_settings"
def show
- redirect_to_repository_settings(project)
+ redirect_to_repository_settings(project, anchor: 'js-push-remote-settings')
end
def update
- if project.update_attributes(mirror_params)
+ result = ::Projects::UpdateService.new(project, current_user, mirror_params).execute
+
+ if result[:status] == :success
flash[:notice] = 'Mirroring settings were successfully updated.'
else
flash[:alert] = project.errors.full_messages.join(', ').html_safe
end
respond_to do |format|
- format.html { redirect_to_repository_settings(project) }
+ format.html { redirect_to_repository_settings(project, anchor: 'js-push-remote-settings') }
format.json do
if project.errors.present?
render json: project.errors, status: :unprocessable_entity
@@ -37,7 +39,7 @@ class Projects::MirrorsController < Projects::ApplicationController
flash[:notice] = "The remote repository is being updated..."
end
- redirect_to_repository_settings(project)
+ redirect_to_repository_settings(project, anchor: 'js-push-remote-settings')
end
private
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 4d4c2af2415..21e2145b73b 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -1,4 +1,5 @@
class Projects::NotesController < Projects::ApplicationController
+ include RendersNotes
include NotesActions
include NotesHelper
include ToggleAwardEmoji
@@ -53,7 +54,7 @@ class Projects::NotesController < Projects::ApplicationController
private
def render_json_with_notes_serializer
- Notes::RenderService.new(current_user).execute([note])
+ prepare_notes_for_rendering([note])
render json: note_serializer.represent(note)
end
diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb
index fa258f3d9af..aeda7b3edf5 100644
--- a/app/controllers/projects/pipeline_schedules_controller.rb
+++ b/app/controllers/projects/pipeline_schedules_controller.rb
@@ -64,7 +64,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
def destroy
if schedule.destroy
- redirect_to pipeline_schedules_path(@project), status: 302
+ redirect_to pipeline_schedules_path(@project), status: :found
else
redirect_to pipeline_schedules_path(@project),
status: :forbidden,
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 768595ceeb4..b5db646bf57 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -1,7 +1,6 @@
class Projects::PipelinesController < Projects::ApplicationController
before_action :whitelist_query_limiting, only: [:create, :retry]
before_action :pipeline, except: [:index, :new, :create, :charts]
- before_action :commit, only: [:show, :builds, :failures]
before_action :authorize_read_pipeline!
before_action :authorize_create_pipeline!, only: [:new, :create]
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
@@ -13,7 +12,7 @@ class Projects::PipelinesController < Projects::ApplicationController
def index
@scope = params[:scope]
@pipelines = PipelinesFinder
- .new(project, scope: @scope)
+ .new(project, current_user, scope: @scope)
.execute
.page(params[:page])
.per(30)
@@ -161,11 +160,11 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def pipeline
- @pipeline ||= project.pipelines.find_by!(id: params[:id]).present(current_user: current_user)
- end
-
- def commit
- @commit ||= @pipeline.commit
+ @pipeline ||= project
+ .pipelines
+ .includes(user: :status)
+ .find_by!(id: params[:id])
+ .present(current_user: current_user)
end
def whitelist_query_limiting
@@ -178,7 +177,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def limited_pipelines_count(project, scope = nil)
- finder = PipelinesFinder.new(project, scope: scope)
+ finder = PipelinesFinder.new(project, current_user, scope: scope)
view_context.limited_counter_with_delimiter(finder.execute)
end
diff --git a/app/controllers/projects/protected_refs_controller.rb b/app/controllers/projects/protected_refs_controller.rb
index 9e757a8d25f..cc62ce2f11b 100644
--- a/app/controllers/projects/protected_refs_controller.rb
+++ b/app/controllers/projects/protected_refs_controller.rb
@@ -19,7 +19,7 @@ class Projects::ProtectedRefsController < Projects::ApplicationController
flash[:alert] = protected_ref.errors.full_messages.join(', ').html_safe
end
- redirect_to_repository_settings(@project)
+ redirect_to_repository_settings(@project, anchor: params[:update_section])
end
def show
@@ -40,7 +40,7 @@ class Projects::ProtectedRefsController < Projects::ApplicationController
destroy_service_class.new(@project, current_user).execute(@protected_ref)
respond_to do |format|
- format.html { redirect_to_repository_settings(@project) }
+ format.html { redirect_to_repository_settings(@project, anchor: params[:update_section]) }
format.js { head :ok }
end
end
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 9bc774b7636..1cba0011304 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -10,7 +10,6 @@ class Projects::RawController < Projects::ApplicationController
def show
@blob = @repository.blob_at(@commit.id, @path)
-
if @blob
headers['X-Content-Type-Options'] = 'nosniff'
@@ -19,7 +18,7 @@ class Projects::RawController < Projects::ApplicationController
if @blob.stored_externally?
send_lfs_object
else
- send_git_blob @repository, @blob
+ send_git_blob @repository, @blob, inline: (params[:inline] != 'false')
end
else
render_404
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
index 3e0a530fdb9..19e09b3af6f 100644
--- a/app/controllers/projects/releases_controller.rb
+++ b/app/controllers/projects/releases_controller.rb
@@ -14,7 +14,7 @@ class Projects::ReleasesController < Projects::ApplicationController
# it exists only to save a description to each Tag.
# If description is empty we should destroy the existing record.
if release_params[:description].present?
- release.update_attributes(release_params)
+ release.update(release_params)
else
release.destroy
end
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index d01f324e6fd..ecb2ece7532 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -24,7 +24,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
send_git_archive @repository, ref: @ref, format: params[:format], append_sha: append_sha
rescue => ex
logger.error("#{self.class.name}: #{ex}")
- return git_not_found!
+ git_not_found!
end
def assign_archive_vars
diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb
index a080724634b..c098c82081e 100644
--- a/app/controllers/projects/runner_projects_controller.rb
+++ b/app/controllers/projects/runner_projects_controller.rb
@@ -21,6 +21,6 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
runner_project = project.runner_projects.find(params[:id])
runner_project.destroy
- redirect_to project_runners_path(project), status: 302
+ redirect_to project_runners_path(project), status: :found
end
end
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index bef94cea989..d118cec977c 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -5,7 +5,7 @@ class Projects::RunnersController < Projects::ApplicationController
layout 'project_settings'
def index
- redirect_to project_settings_ci_cd_path(@project)
+ redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings')
end
def edit
@@ -24,7 +24,7 @@ class Projects::RunnersController < Projects::ApplicationController
@runner.destroy
end
- redirect_to project_runners_path(@project), status: 302
+ redirect_to project_runners_path(@project), status: :found
end
def resume
@@ -50,13 +50,13 @@ class Projects::RunnersController < Projects::ApplicationController
def toggle_shared_runners
project.toggle!(:shared_runners_enabled)
- redirect_to project_settings_ci_cd_path(@project)
+ redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings')
end
def toggle_group_runners
project.toggle_ci_cd_settings!(:group_runners_enabled)
- redirect_to project_settings_ci_cd_path(@project)
+ redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings')
end
protected
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 690596b12db..d55046047ae 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -34,7 +34,7 @@ class Projects::ServicesController < Projects::ApplicationController
private
def service_test_response
- if @service.update_attributes(service_params[:service])
+ if @service.update(service_params[:service])
data = @service.test_data(project, current_user)
outcome = @service.test(data)
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index fb3f6eec2bd..322ec096ffb 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -74,7 +74,7 @@ module Projects
.ordered
.page(params[:page]).per(20)
- @shared_runners = ::Ci::Runner.shared.active
+ @shared_runners = ::Ci::Runner.instance_type.active
@shared_runners_count = @shared_runners.count(:all)
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 208a1d19862..7c03d8ce827 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -82,13 +82,13 @@ class Projects::SnippetsController < Projects::ApplicationController
@snippet.destroy
- redirect_to project_snippets_path(@project), status: 302
+ redirect_to project_snippets_path(@project), status: :found
end
protected
def snippet
- @snippet ||= @project.snippets.find(params[:id])
+ @snippet ||= @project.snippets.inc_relations_for_view.find(params[:id])
end
alias_method :awardable, :snippet
alias_method :spammable, :snippet
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index b62d7d9b7c5..b17753222a0 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -50,7 +50,7 @@ class Projects::TagsController < Projects::ApplicationController
respond_to do |format|
if result[:status] == :success
format.html do
- redirect_to project_tags_path(@project), status: 303
+ redirect_to project_tags_path(@project), status: :see_other
end
format.js
diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb
index 694b468c8d3..52d6fb82093 100644
--- a/app/controllers/projects/templates_controller.rb
+++ b/app/controllers/projects/templates_controller.rb
@@ -14,6 +14,6 @@ class Projects::TemplatesController < Projects::ApplicationController
def get_template_class
template_types = { issue: Gitlab::Template::IssueTemplate, merge_request: Gitlab::Template::MergeRequestTemplate }.with_indifferent_access
@template_type = template_types[params[:template_type]]
- render json: [], status: 404 unless @template_type
+ render json: [], status: :not_found unless @template_type
end
end
diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb
index a41fcb85c40..93fb9da6510 100644
--- a/app/controllers/projects/todos_controller.rb
+++ b/app/controllers/projects/todos_controller.rb
@@ -1,19 +1,13 @@
class Projects::TodosController < Projects::ApplicationController
- before_action :authenticate_user!, only: [:create]
-
- def create
- todo = TodoService.new.mark_todo(issuable, current_user)
+ include Gitlab::Utils::StrongMemoize
+ include TodosActions
- render json: {
- count: TodosFinder.new(current_user, state: :pending).execute.count,
- delete_path: dashboard_todo_path(todo)
- }
- end
+ before_action :authenticate_user!, only: [:create]
private
def issuable
- @issuable ||= begin
+ strong_memoize(:issuable) do
case params[:issuable_type]
when "issue"
IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id])
diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb
index e04145dd0b3..cb12b707087 100644
--- a/app/controllers/projects/triggers_controller.rb
+++ b/app/controllers/projects/triggers_controller.rb
@@ -7,7 +7,7 @@ class Projects::TriggersController < Projects::ApplicationController
layout 'project_settings'
def index
- redirect_to project_settings_ci_cd_path(@project)
+ redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
end
def create
@@ -19,7 +19,7 @@ class Projects::TriggersController < Projects::ApplicationController
flash[:alert] = 'You could not create a new trigger.'
end
- redirect_to project_settings_ci_cd_path(@project)
+ redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
end
def take_ownership
@@ -29,7 +29,7 @@ class Projects::TriggersController < Projects::ApplicationController
flash[:alert] = 'You could not take ownership of trigger.'
end
- redirect_to project_settings_ci_cd_path(@project)
+ redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
end
def edit
@@ -37,7 +37,7 @@ class Projects::TriggersController < Projects::ApplicationController
def update
if trigger.update(trigger_params)
- redirect_to project_settings_ci_cd_path(@project), notice: 'Trigger was successfully updated.'
+ redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers'), notice: 'Trigger was successfully updated.'
else
render action: "edit"
end
@@ -50,7 +50,7 @@ class Projects::TriggersController < Projects::ApplicationController
flash[:alert] = "Could not remove the trigger."
end
- redirect_to project_settings_ci_cd_path(@project), status: 302
+ redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers'), status: :found
end
private
diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb
index f5cf089ad98..7a85046164c 100644
--- a/app/controllers/projects/uploads_controller.rb
+++ b/app/controllers/projects/uploads_controller.rb
@@ -1,11 +1,13 @@
class Projects::UploadsController < Projects::ApplicationController
include UploadsActions
+ include WorkhorseRequest
# These will kick you out if you don't have access.
skip_before_action :project, :repository,
if: -> { action_name == 'show' && image_or_video? }
- before_action :authorize_upload_file!, only: [:create]
+ before_action :authorize_upload_file!, only: [:create, :authorize]
+ before_action :verify_workhorse_api!, only: [:authorize]
private
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index aa844e94d89..da7aeb26a75 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -1,10 +1,16 @@
class Projects::WikisController < Projects::ApplicationController
include PreviewMarkdown
+ include Gitlab::Utils::StrongMemoize
before_action :authorize_read_wiki!
before_action :authorize_create_wiki!, only: [:edit, :create, :history]
before_action :authorize_admin_wiki!, only: :destroy
before_action :load_project_wiki
+ before_action :load_page, only: [:show, :edit, :update, :history, :destroy]
+ before_action :valid_encoding?, only: [:show, :edit, :update], if: :load_page
+ before_action only: [:edit, :update], unless: :valid_encoding? do
+ redirect_to(project_wiki_path(@project, @page))
+ end
def pages
@wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page])
@@ -12,11 +18,11 @@ class Projects::WikisController < Projects::ApplicationController
end
def show
- @page = @project_wiki.find_page(params[:id], params[:version_id])
-
view_param = @project_wiki.empty? ? params[:view] : 'create'
if @page
+ set_encoding_error unless valid_encoding?
+
render 'show'
elsif file = @project_wiki.find_file(params[:id], params[:version_id])
response.headers['Content-Security-Policy'] = "default-src 'none'"
@@ -38,13 +44,11 @@ class Projects::WikisController < Projects::ApplicationController
end
def edit
- @page = @project_wiki.find_page(params[:id])
end
def update
return render('empty') unless can?(current_user, :create_wiki, @project)
- @page = @project_wiki.find_page(params[:id])
@page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page)
if @page.valid?
@@ -79,8 +83,6 @@ class Projects::WikisController < Projects::ApplicationController
end
def history
- @page = @project_wiki.find_page(params[:id])
-
if @page
@page_versions = Kaminari.paginate_array(@page.versions(page: params[:page].to_i),
total_count: @page.count_versions)
@@ -94,8 +96,6 @@ class Projects::WikisController < Projects::ApplicationController
end
def destroy
- @page = @project_wiki.find_page(params[:id])
-
WikiPages::DestroyService.new(@project, current_user).execute(@page)
redirect_to project_wiki_path(@project, :home),
@@ -112,15 +112,24 @@ class Projects::WikisController < Projects::ApplicationController
private
def load_project_wiki
- @project_wiki = ProjectWiki.new(@project, current_user)
+ @project_wiki = load_wiki
# Call #wiki to make sure the Wiki Repo is initialized
@project_wiki.wiki
- @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages(limit: 15))
+
+ @sidebar_page = @project_wiki.find_sidebar(params[:version_id])
+
+ unless @sidebar_page # Fallback to default sidebar
+ @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages(limit: 15))
+ end
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
redirect_to project_path(@project)
- return false
+ false
+ end
+
+ def load_wiki
+ ProjectWiki.new(@project, current_user)
end
def wiki_params
@@ -129,7 +138,28 @@ class Projects::WikisController < Projects::ApplicationController
def build_page(args)
WikiPage.new(@project_wiki).tap do |page|
- page.update_attributes(args)
+ page.update_attributes(args) # rubocop:disable Rails/ActiveRecordAliases
end
end
+
+ def load_page
+ @page ||= @project_wiki.find_page(*page_params)
+ end
+
+ def page_params
+ keys = [:id]
+ keys << :version_id if params[:action] == 'show'
+
+ params.values_at(*keys)
+ end
+
+ def valid_encoding?
+ strong_memoize(:valid_encoding) do
+ @page.content.encoding == Encoding::UTF_8
+ end
+ end
+
+ def set_encoding_error
+ flash.now[:notice] = "The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository."
+ end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index c2492a137fb..e9ae8c13142 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -2,6 +2,7 @@ class ProjectsController < Projects::ApplicationController
include IssuableCollections
include ExtractsPath
include PreviewMarkdown
+ include SendFileUpload
before_action :whitelist_query_limiting, only: [:create]
before_action :authenticate_user!, except: [:index, :show, :activity, :refs]
@@ -60,7 +61,7 @@ class ProjectsController < Projects::ApplicationController
flash[:notice] = _("Project '%{project_name}' was successfully updated.") % { project_name: @project.name }
format.html do
- redirect_to(edit_project_path(@project))
+ redirect_to(edit_project_path(@project, anchor: 'js-general-project-settings'))
end
else
flash.now[:alert] = result[:message]
@@ -132,7 +133,7 @@ class ProjectsController < Projects::ApplicationController
::Projects::DestroyService.new(@project, current_user, {}).async_execute
flash[:notice] = _("Project '%{project_name}' is in the process of being deleted.") % { project_name: @project.full_name }
- redirect_to dashboard_projects_path, status: 302
+ redirect_to dashboard_projects_path, status: :found
rescue Projects::DestroyService::DestroyError => ex
redirect_to edit_project_path(@project), status: 302, alert: ex.message
end
@@ -147,7 +148,7 @@ class ProjectsController < Projects::ApplicationController
def archive
return access_denied! unless can?(current_user, :archive_project, @project)
- @project.archive!
+ ::Projects::UpdateService.new(@project, current_user, archived: true).execute
respond_to do |format|
format.html { redirect_to project_path(@project) }
@@ -157,7 +158,7 @@ class ProjectsController < Projects::ApplicationController
def unarchive
return access_denied! unless can?(current_user, :archive_project, @project)
- @project.unarchive!
+ ::Projects::UpdateService.new(@project, current_user, archived: false).execute
respond_to do |format|
format.html { redirect_to project_path(@project) }
@@ -173,7 +174,7 @@ class ProjectsController < Projects::ApplicationController
)
rescue ::Projects::HousekeepingService::LeaseTaken => ex
redirect_to(
- edit_project_path(@project),
+ edit_project_path(@project, anchor: 'js-project-advanced-settings'),
alert: ex.to_s
)
end
@@ -182,19 +183,19 @@ class ProjectsController < Projects::ApplicationController
@project.add_export_job(current_user: current_user)
redirect_to(
- edit_project_path(@project),
+ edit_project_path(@project, anchor: 'js-export-project'),
notice: _("Project export started. A download link will be sent by email.")
)
end
def download_export
- export_project_path = @project.export_project_path
-
- if export_project_path
+ if export_project_object_storage?
+ send_upload(@project.import_export_upload.export_file)
+ elsif export_project_path
send_file export_project_path, disposition: 'attachment'
else
redirect_to(
- edit_project_path(@project),
+ edit_project_path(@project, anchor: 'js-export-project'),
alert: _("Project export link has expired. Please generate a new export from your project settings.")
)
end
@@ -207,7 +208,7 @@ class ProjectsController < Projects::ApplicationController
flash[:alert] = _("Project export could not be deleted.")
end
- redirect_to(edit_project_path(@project))
+ redirect_to(edit_project_path(@project, anchor: 'js-export-project'))
end
def generate_new_export
@@ -215,7 +216,7 @@ class ProjectsController < Projects::ApplicationController
export
else
redirect_to(
- edit_project_path(@project),
+ edit_project_path(@project, anchor: 'js-export-project'),
alert: _("Project export could not be deleted.")
)
end
@@ -265,8 +266,6 @@ class ProjectsController < Projects::ApplicationController
render json: options.to_json
end
- private
-
# Render project landing depending of which features are available
# So if page is not availble in the list it renders the next page
#
@@ -347,6 +346,7 @@ class ProjectsController < Projects::ApplicationController
:visibility_level,
:template_name,
:merge_method,
+ :initialize_with_readme,
project_feature_attributes: %i[
builds_access_level
@@ -423,4 +423,12 @@ class ProjectsController < Projects::ApplicationController
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42440')
end
+
+ def export_project_path
+ @export_project_path ||= @project.export_project_path
+ end
+
+ def export_project_object_storage?
+ @project.export_project_object_exists?
+ end
end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 7aa277b3614..ab8e2e35b98 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -32,8 +32,8 @@ class SessionsController < Devise::SessionsController
super do |resource|
# User has successfully signed in, so clear any unused reset token
if resource.reset_password_token.present?
- resource.update_attributes(reset_password_token: nil,
- reset_password_sent_at: nil)
+ resource.update(reset_password_token: nil,
+ reset_password_sent_at: nil)
end
# hide the signed-in notification
@@ -62,7 +62,11 @@ class SessionsController < Devise::SessionsController
return unless captcha_enabled?
return unless Gitlab::Recaptcha.load_configurations!
- unless verify_recaptcha
+ if verify_recaptcha
+ increment_successful_login_captcha_counter
+ else
+ increment_failed_login_captcha_counter
+
self.resource = resource_class.new
flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
flash.delete :recaptcha_error
@@ -71,18 +75,36 @@ class SessionsController < Devise::SessionsController
end
end
- def log_failed_login
- Gitlab::AppLogger.info("Failed Login: username=#{user_params[:login]} ip=#{request.remote_ip}")
+ def increment_failed_login_captcha_counter
+ Gitlab::Metrics.counter(
+ :failed_login_captcha_total,
+ 'Number of failed CAPTCHA attempts for logins'.freeze
+ ).increment
end
- def failed_login?
- (options = env["warden.options"]) && options[:action] == "unauthenticated"
+ def increment_successful_login_captcha_counter
+ Gitlab::Metrics.counter(
+ :successful_login_captcha_total,
+ 'Number of successful CAPTCHA attempts for logins'.freeze
+ ).increment
end
+ ##
+ # We do have some duplication between lib/gitlab/auth/activity.rb here, but
+ # leaving this method here because of backwards compatibility.
+ #
def login_counter
@login_counter ||= Gitlab::Metrics.counter(:user_session_logins_total, 'User sign in count')
end
+ def log_failed_login
+ Gitlab::AppLogger.info("Failed Login: username=#{user_params[:login]} ip=#{request.remote_ip}")
+ end
+
+ def failed_login?
+ (options = env["warden.options"]) && options[:action] == "unauthenticated"
+ end
+
# Handle an "initial setup" state, where there's only one user, it's an admin,
# and they require a password change.
def check_initial_setup
@@ -139,6 +161,8 @@ class SessionsController < Devise::SessionsController
end
def auto_sign_in_with_provider
+ return unless Gitlab::Auth.omniauth_enabled?
+
provider = Gitlab.config.omniauth.auto_sign_in_with_provider
return unless provider.present?
diff --git a/app/controllers/sherlock/transactions_controller.rb b/app/controllers/sherlock/transactions_controller.rb
index cb6c3a7cd98..ae4953c3259 100644
--- a/app/controllers/sherlock/transactions_controller.rb
+++ b/app/controllers/sherlock/transactions_controller.rb
@@ -13,7 +13,7 @@ module Sherlock
def destroy_all
Gitlab::Sherlock.collection.clear
- redirect_to :back, status: 302
+ redirect_to :back, status: :found
end
end
end
diff --git a/app/controllers/snippets/notes_controller.rb b/app/controllers/snippets/notes_controller.rb
index c8b4682e6dc..217da89a1fd 100644
--- a/app/controllers/snippets/notes_controller.rb
+++ b/app/controllers/snippets/notes_controller.rb
@@ -9,7 +9,7 @@ class Snippets::NotesController < ApplicationController
private
def note
- @note ||= snippet.notes.find(params[:id])
+ @note ||= snippet.notes.inc_relations_for_view.find(params[:id])
end
alias_method :awardable, :note
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 3d51520ddf4..dcf18c1f751 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -89,13 +89,13 @@ class SnippetsController < ApplicationController
@snippet.destroy
- redirect_to snippets_path, status: 302
+ redirect_to snippets_path, status: :found
end
protected
def snippet
- @snippet ||= PersonalSnippet.find_by(id: params[:id])
+ @snippet ||= PersonalSnippet.inc_relations_for_view.find_by(id: params[:id])
end
alias_method :awardable, :snippet
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 31f47a7aa7c..2f65f4a7403 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -13,6 +13,8 @@ class UsersController < ApplicationController
skip_before_action :authenticate_user!
before_action :user, except: [:exists]
+ before_action :authorize_read_user_profile!,
+ only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :snippets]
def show
respond_to do |format|
@@ -148,4 +150,8 @@ class UsersController < ApplicationController
def build_canonical_path(user)
url_for(safe_params.merge(username: user.to_param))
end
+
+ def authorize_read_user_profile!
+ access_denied! unless can?(current_user, :read_user_profile, user)
+ end
end
diff --git a/app/finders/admin/projects_finder.rb b/app/finders/admin/projects_finder.rb
index 53b77f5fed9..543bf1a1415 100644
--- a/app/finders/admin/projects_finder.rb
+++ b/app/finders/admin/projects_finder.rb
@@ -7,7 +7,7 @@ class Admin::ProjectsFinder
end
def execute
- items = Project.without_deleted.with_statistics
+ items = Project.without_deleted.with_statistics.with_route
items = by_namespace_id(items)
items = by_visibilty_level(items)
items = by_with_push(items)
@@ -16,7 +16,7 @@ class Admin::ProjectsFinder
items = by_archived(items)
items = by_personal(items)
items = by_name(items)
- items = items.includes(namespace: [:owner])
+ items = items.includes(namespace: [:owner, :route])
sort(items).page(params[:page])
end
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index 0754123a3cf..0eeba1d2428 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -8,6 +8,7 @@
# owned: boolean
# parent: Group
# all_available: boolean (defaults to true)
+# min_access_level: integer
#
# Users with full private access can see all groups. The `owned` and `parent`
# params can be used to restrict the groups that are returned.
@@ -39,6 +40,7 @@ class GroupsFinder < UnionFinder
def all_groups
return [owned_groups] if params[:owned]
+ return [groups_with_min_access_level] if min_access_level?
return [Group.all] if current_user&.full_private_access? && all_available?
groups = []
@@ -56,6 +58,16 @@ class GroupsFinder < UnionFinder
current_user.groups
end
+ def groups_with_min_access_level
+ groups = current_user
+ .groups
+ .where('members.access_level >= ?', params[:min_access_level])
+
+ Gitlab::GroupHierarchy
+ .new(groups)
+ .base_and_descendants
+ end
+
def by_parent(groups)
return groups unless params[:parent]
@@ -73,4 +85,8 @@ class GroupsFinder < UnionFinder
def all_available?
params.fetch(:all_available, true)
end
+
+ def min_access_level?
+ current_user && params[:min_access_level].present?
+ end
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 5d5f72c4d86..372e2a96c2c 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -7,7 +7,7 @@
# current_user - which user use
# params:
# scope: 'created_by_me' or 'assigned_to_me' or 'all'
-# state: 'opened' or 'closed' or 'all'
+# state: 'opened' or 'closed' or 'locked' or 'all'
# group_id: integer
# project_id: integer
# milestone_title: string
@@ -130,7 +130,7 @@ class IssuableFinder
counts[:all] = counts.values.sum
- counts
+ counts.with_indifferent_access
end
def group
@@ -311,6 +311,8 @@ class IssuableFinder
items.respond_to?(:merged) ? items.merged : items.closed
when 'opened'
items.opened
+ when 'locked'
+ items.where(state: 'locked')
else
items
end
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index afd1f824b32..1d05bf28438 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -14,6 +14,7 @@ class LabelsFinder < UnionFinder
@skip_authorization = skip_authorization
items = find_union(label_ids, Label) || Label.none
items = with_title(items)
+ items = by_search(items)
sort(items)
end
@@ -63,6 +64,12 @@ class LabelsFinder < UnionFinder
items.where(title: title)
end
+ def by_search(labels)
+ return labels unless search?
+
+ labels.search(params[:search])
+ end
+
# Gets redacted array of group ids
# which can include the ancestors and descendants of the requested group.
def group_ids_for(group)
@@ -106,6 +113,10 @@ class LabelsFinder < UnionFinder
params[:only_group_labels]
end
+ def search?
+ params[:search].present?
+ end
+
def title
params[:title] || params[:name]
end
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 8d84ed4bdfb..40089c082c1 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -6,7 +6,7 @@
# current_user - which user use
# params:
# scope: 'created_by_me' or 'assigned_to_me' or 'all'
-# state: 'open', 'closed', 'merged', or 'all'
+# state: 'open', 'closed', 'merged', 'locked', or 'all'
# group_id: integer
# project_id: integer
# milestone_title: string
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
index 5aea0cb8192..a56a3a1e1a9 100644
--- a/app/finders/personal_projects_finder.rb
+++ b/app/finders/personal_projects_finder.rb
@@ -1,6 +1,9 @@
class PersonalProjectsFinder < UnionFinder
- def initialize(user)
+ include Gitlab::Allowable
+
+ def initialize(user, params = {})
@user = user
+ @params = params
end
# Finds the projects belonging to the user in "@user", limited to either
@@ -8,9 +11,13 @@ class PersonalProjectsFinder < UnionFinder
#
# current_user - When given the list of projects is limited to those only
# visible by this user.
+ # params - Optional query parameters
+ # min_access_level: integer
#
# Returns an ActiveRecord::Relation.
def execute(current_user = nil)
+ return Project.none unless can?(current_user, :read_user_profile, @user)
+
segments = all_projects(current_user)
find_union(segments, Project).includes(:namespace).order_updated_desc
@@ -19,11 +26,21 @@ class PersonalProjectsFinder < UnionFinder
private
def all_projects(current_user)
- projects = []
+ return [projects_with_min_access_level(current_user)] if current_user && min_access_level?
+ projects = []
projects << @user.personal_projects.visible_to_user(current_user) if current_user
projects << @user.personal_projects.public_to_user(current_user)
-
projects
end
+
+ def projects_with_min_access_level(current_user)
+ @user
+ .personal_projects
+ .visible_to_user_and_access_level(current_user, @params[:min_access_level])
+ end
+
+ def min_access_level?
+ @params[:min_access_level].present?
+ end
end
diff --git a/app/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb
index 0a487839aff..a99a889a7e9 100644
--- a/app/finders/pipelines_finder.rb
+++ b/app/finders/pipelines_finder.rb
@@ -1,15 +1,20 @@
class PipelinesFinder
- attr_reader :project, :pipelines, :params
+ attr_reader :project, :pipelines, :params, :current_user
ALLOWED_INDEXED_COLUMNS = %w[id status ref user_id].freeze
- def initialize(project, params = {})
+ def initialize(project, current_user, params = {})
@project = project
+ @current_user = current_user
@pipelines = project.pipelines
@params = params
end
def execute
+ unless Ability.allowed?(current_user, :read_pipeline, project)
+ return Ci::Pipeline.none
+ end
+
items = pipelines
items = by_scope(items)
items = by_status(items)
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index c7d6bc6cfdc..cac6643eff3 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -16,6 +16,8 @@
# personal: boolean
# search: string
# non_archived: boolean
+# archived: 'only' or boolean
+# min_access_level: integer
#
class ProjectsFinder < UnionFinder
include CustomAttributesFilter
@@ -33,7 +35,7 @@ class ProjectsFinder < UnionFinder
user = params.delete(:user)
collection =
if user
- PersonalProjectsFinder.new(user).execute(current_user)
+ PersonalProjectsFinder.new(user, finder_params).execute(current_user)
else
init_collection
end
@@ -64,6 +66,8 @@ class ProjectsFinder < UnionFinder
def collection_with_user
if owned_projects?
current_user.owned_projects
+ elsif min_access_level?
+ current_user.authorized_projects.where('project_authorizations.access_level >= ?', params[:min_access_level])
else
if private_only?
current_user.authorized_projects
@@ -75,7 +79,7 @@ class ProjectsFinder < UnionFinder
# Builds a collection for an anonymous user.
def collection_without_user
- if private_only? || owned_projects?
+ if private_only? || owned_projects? || min_access_level?
Project.none
else
Project.public_to_user
@@ -90,6 +94,10 @@ class ProjectsFinder < UnionFinder
params[:non_public].present?
end
+ def min_access_level?
+ params[:min_access_level].present?
+ end
+
def by_ids(items)
project_ids_relation ? items.where(id: project_ids_relation) : items
end
@@ -130,7 +138,7 @@ class ProjectsFinder < UnionFinder
def by_archived(projects)
if params[:non_archived]
projects.non_archived
- elsif params.key?(:archived) # Back-compatibility with the places where `params[:archived]` can be set explicitly to `false`
+ elsif params.key?(:archived)
if params[:archived] == 'only'
projects.archived
elsif Gitlab::Utils.to_boolean(params[:archived])
@@ -142,4 +150,10 @@ class ProjectsFinder < UnionFinder
projects
end
end
+
+ def finder_params
+ return {} unless min_access_level?
+
+ { min_access_level: params[:min_access_level] }
+ end
end
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 09e2c586f2a..6e9c8ea6fde 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -15,6 +15,7 @@
class TodosFinder
prepend FinderWithCrossProjectAccess
include FinderMethods
+ include Gitlab::Utils::StrongMemoize
requires_cross_project_access unless: -> { project? }
@@ -34,6 +35,7 @@ class TodosFinder
items = by_author(items)
items = by_state(items)
items = by_type(items)
+ items = by_group(items)
# Filtering by project HAS TO be the last because we use
# the project IDs yielded by the todos query thus far
items = by_project(items)
@@ -82,6 +84,10 @@ class TodosFinder
params[:project_id].present?
end
+ def group?
+ params[:group_id].present?
+ end
+
def project
return @project if defined?(@project)
@@ -89,10 +95,6 @@ class TodosFinder
@project = Project.find(params[:project_id])
@project = nil if @project.pending_delete?
-
- unless Ability.allowed?(current_user, :read_project, @project)
- @project = nil
- end
else
@project = nil
end
@@ -100,18 +102,14 @@ class TodosFinder
@project
end
- def project_ids(items)
- ids = items.except(:order).select(:project_id)
- if Gitlab::Database.mysql?
- # To make UPDATE work on MySQL, wrap it in a SELECT with an alias
- ids = Todo.except(:order).select('*').from("(#{ids.to_sql}) AS t")
+ def group
+ strong_memoize(:group) do
+ Group.find(params[:group_id])
end
-
- ids
end
def type?
- type.present? && %w(Issue MergeRequest).include?(type)
+ type.present? && %w(Issue MergeRequest Epic).include?(type)
end
def type
@@ -148,12 +146,23 @@ class TodosFinder
def by_project(items)
if project?
- items.where(project: project)
- else
- projects = Project.public_or_visible_to_user(current_user)
+ items = items.where(project: project)
+ end
- items.joins(:project).merge(projects)
+ items
+ end
+
+ def by_group(items)
+ if group?
+ groups = group.self_and_descendants
+ project_todos = items.where(project_id: Project.where(group: groups).select(:id))
+ group_todos = items.where(group_id: groups.select(:id))
+
+ union = Gitlab::SQL::Union.new([project_todos, group_todos])
+ items = Todo.from("(#{union.to_sql}) #{Todo.table_name}")
end
+
+ items
end
def by_state(items)
diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb
index 74776b2ed1f..876f086a3ef 100644
--- a/app/finders/user_recent_events_finder.rb
+++ b/app/finders/user_recent_events_finder.rb
@@ -7,6 +7,7 @@
class UserRecentEventsFinder
prepend FinderWithCrossProjectAccess
include FinderMethods
+ include Gitlab::Allowable
requires_cross_project_access
@@ -21,6 +22,8 @@ class UserRecentEventsFinder
end
def execute
+ return Event.none unless can?(current_user, :read_user_profile, target_user)
+
recent_events(params[:offset] || 0)
.joins(:project)
.with_associations
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb
index de4fc1d8e32..8755a1a62e7 100644
--- a/app/graphql/gitlab_schema.rb
+++ b/app/graphql/gitlab_schema.rb
@@ -2,7 +2,10 @@ class GitlabSchema < GraphQL::Schema
use BatchLoader::GraphQL
use Gitlab::Graphql::Authorize
use Gitlab::Graphql::Present
+ use Gitlab::Graphql::Connections
query(Types::QueryType)
- # mutation(Types::MutationType)
+
+ default_max_page_size 100
+ mutation(Types::MutationType)
end
diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb
new file mode 100644
index 00000000000..eb03dfe1624
--- /dev/null
+++ b/app/graphql/mutations/base_mutation.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Mutations
+ class BaseMutation < GraphQL::Schema::RelayClassicMutation
+ field :errors, [GraphQL::STRING_TYPE],
+ null: false,
+ description: "Reasons why the mutation failed."
+
+ def current_user
+ context[:current_user]
+ end
+ end
+end
diff --git a/app/graphql/mutations/concerns/mutations/resolves_project.rb b/app/graphql/mutations/concerns/mutations/resolves_project.rb
new file mode 100644
index 00000000000..0dd1f264a52
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/resolves_project.rb
@@ -0,0 +1,13 @@
+module Mutations
+ module ResolvesProject
+ extend ActiveSupport::Concern
+
+ def resolve_project(full_path:)
+ resolver.resolve(full_path: full_path)
+ end
+
+ def resolver
+ Resolvers::ProjectResolver.new(object: nil, context: context)
+ end
+ end
+end
diff --git a/app/graphql/mutations/merge_requests/base.rb b/app/graphql/mutations/merge_requests/base.rb
new file mode 100644
index 00000000000..2149e72e2df
--- /dev/null
+++ b/app/graphql/mutations/merge_requests/base.rb
@@ -0,0 +1,32 @@
+module Mutations
+ module MergeRequests
+ class Base < BaseMutation
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+ include Mutations::ResolvesProject
+
+ argument :project_path, GraphQL::ID_TYPE,
+ required: true,
+ description: "The project the merge request to mutate is in"
+
+ argument :iid, GraphQL::ID_TYPE,
+ required: true,
+ description: "The iid of the merge request to mutate"
+
+ field :merge_request,
+ Types::MergeRequestType,
+ null: true,
+ description: "The merge request after mutation"
+
+ authorize :update_merge_request
+
+ private
+
+ def find_object(project_path:, iid:)
+ project = resolve_project(full_path: project_path)
+ resolver = Resolvers::MergeRequestResolver.new(object: project, context: context)
+
+ resolver.resolve(iid: iid)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/merge_requests/set_wip.rb b/app/graphql/mutations/merge_requests/set_wip.rb
new file mode 100644
index 00000000000..a2aa0c84ee4
--- /dev/null
+++ b/app/graphql/mutations/merge_requests/set_wip.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Mutations
+ module MergeRequests
+ class SetWip < Base
+ graphql_name 'MergeRequestSetWip'
+
+ argument :wip,
+ GraphQL::BOOLEAN_TYPE,
+ required: true,
+ description: <<~DESC
+ Whether or not to set the merge request as a WIP.
+ DESC
+
+ def resolve(project_path:, iid:, wip: nil)
+ merge_request = authorized_find!(project_path: project_path, iid: iid)
+ project = merge_request.project
+
+ ::MergeRequests::UpdateService.new(project, current_user, wip_event: wip_event(merge_request, wip))
+ .execute(merge_request)
+
+ {
+ merge_request: merge_request,
+ errors: merge_request.errors.full_messages
+ }
+ end
+
+ private
+
+ def wip_event(merge_request, wip)
+ wip ? 'wip' : 'unwip'
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/resolves_pipelines.rb b/app/graphql/resolvers/concerns/resolves_pipelines.rb
new file mode 100644
index 00000000000..9ec45378d8e
--- /dev/null
+++ b/app/graphql/resolvers/concerns/resolves_pipelines.rb
@@ -0,0 +1,23 @@
+module ResolvesPipelines
+ extend ActiveSupport::Concern
+
+ included do
+ type [Types::Ci::PipelineType], null: false
+ argument :status,
+ Types::Ci::PipelineStatusEnum,
+ required: false,
+ description: "Filter pipelines by their status"
+ argument :ref,
+ GraphQL::STRING_TYPE,
+ required: false,
+ description: "Filter pipelines by the ref they are run for"
+ argument :sha,
+ GraphQL::STRING_TYPE,
+ required: false,
+ description: "Filter pipelines by the sha of the commit they are run for"
+ end
+
+ def resolve_pipelines(project, params = {})
+ PipelinesFinder.new(project, context[:current_user], params).execute
+ end
+end
diff --git a/app/graphql/resolvers/merge_request_pipelines_resolver.rb b/app/graphql/resolvers/merge_request_pipelines_resolver.rb
new file mode 100644
index 00000000000..00b51ee1381
--- /dev/null
+++ b/app/graphql/resolvers/merge_request_pipelines_resolver.rb
@@ -0,0 +1,16 @@
+module Resolvers
+ class MergeRequestPipelinesResolver < BaseResolver
+ include ::ResolvesPipelines
+
+ alias_method :merge_request, :object
+
+ def resolve(**args)
+ resolve_pipelines(project, args)
+ .merge(merge_request.all_pipelines)
+ end
+
+ def project
+ merge_request.source_project
+ end
+ end
+end
diff --git a/app/graphql/resolvers/project_pipelines_resolver.rb b/app/graphql/resolvers/project_pipelines_resolver.rb
new file mode 100644
index 00000000000..7f175a3b26c
--- /dev/null
+++ b/app/graphql/resolvers/project_pipelines_resolver.rb
@@ -0,0 +1,11 @@
+module Resolvers
+ class ProjectPipelinesResolver < BaseResolver
+ include ResolvesPipelines
+
+ alias_method :project, :object
+
+ def resolve(**args)
+ resolve_pipelines(project, args)
+ end
+ end
+end
diff --git a/app/graphql/types/ci/pipeline_status_enum.rb b/app/graphql/types/ci/pipeline_status_enum.rb
new file mode 100644
index 00000000000..2c12e5001d8
--- /dev/null
+++ b/app/graphql/types/ci/pipeline_status_enum.rb
@@ -0,0 +1,9 @@
+module Types
+ module Ci
+ class PipelineStatusEnum < BaseEnum
+ ::Ci::Pipeline.all_state_names.each do |state_symbol|
+ value state_symbol.to_s.upcase, value: state_symbol.to_s
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
new file mode 100644
index 00000000000..bbb7d9354d0
--- /dev/null
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -0,0 +1,31 @@
+module Types
+ module Ci
+ class PipelineType < BaseObject
+ expose_permissions Types::PermissionTypes::Ci::Pipeline
+
+ graphql_name 'Pipeline'
+
+ field :id, GraphQL::ID_TYPE, null: false
+ field :iid, GraphQL::ID_TYPE, null: false
+
+ field :sha, GraphQL::STRING_TYPE, null: false
+ field :before_sha, GraphQL::STRING_TYPE, null: true
+ field :status, PipelineStatusEnum, null: false
+ field :duration,
+ GraphQL::INT_TYPE,
+ null: true,
+ description: "Duration of the pipeline in seconds"
+ field :coverage,
+ GraphQL::FLOAT_TYPE,
+ null: true,
+ description: "Coverage percentage"
+ field :created_at, Types::TimeType, null: false
+ field :updated_at, Types::TimeType, null: false
+ field :started_at, Types::TimeType, null: true
+ field :finished_at, Types::TimeType, null: true
+ field :committed_at, Types::TimeType, null: true
+
+ # TODO: Add triggering user as a type
+ end
+ end
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index a1f3c0dd8c0..88cd2adc6dc 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -45,5 +45,11 @@ module Types
field :upvotes, GraphQL::INT_TYPE, null: false
field :downvotes, GraphQL::INT_TYPE, null: false
field :subscribed, GraphQL::BOOLEAN_TYPE, method: :subscribed?, null: false
+
+ field :head_pipeline, Types::Ci::PipelineType, null: true, method: :actual_head_pipeline do
+ authorize :read_pipeline
+ end
+ field :pipelines, Types::Ci::PipelineType.connection_type,
+ resolver: Resolvers::MergeRequestPipelinesResolver
end
end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 06ed91c1658..2b4ef299296 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
module Types
class MutationType < BaseObject
+ include Gitlab::Graphql::MountMutation
+
graphql_name "Mutation"
- # TODO: Add Mutations as fields
+ mount_mutation Mutations::MergeRequests::SetWip
end
end
diff --git a/app/graphql/types/permission_types/ci/pipeline.rb b/app/graphql/types/permission_types/ci/pipeline.rb
new file mode 100644
index 00000000000..942539c7cf7
--- /dev/null
+++ b/app/graphql/types/permission_types/ci/pipeline.rb
@@ -0,0 +1,11 @@
+module Types
+ module PermissionTypes
+ module Ci
+ class Pipeline < BasePermissionType
+ graphql_name 'PipelinePermissions'
+
+ abilities :update_pipeline, :admin_pipeline, :destroy_pipeline
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index a832e8b4bde..97707215b4e 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -70,5 +70,10 @@ module Types
resolver: Resolvers::MergeRequestResolver do
authorize :read_merge_request
end
+
+ field :pipelines,
+ Types::Ci::PipelineType.connection_type,
+ null: false,
+ resolver: Resolvers::ProjectPipelinesResolver
end
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index ef1bf283d0c..2bdf2c2c120 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -148,6 +148,7 @@ module ApplicationSettingsHelper
:after_sign_up_text,
:akismet_api_key,
:akismet_enabled,
+ :allow_local_requests_from_hooks_and_services,
:authorized_keys_enabled,
:auto_devops_enabled,
:auto_devops_domain,
@@ -174,6 +175,7 @@ module ApplicationSettingsHelper
:ed25519_key_restriction,
:email_author_in_body,
:enabled_git_access_protocol,
+ :enforce_terms,
:gitaly_timeout_default,
:gitaly_timeout_medium,
:gitaly_timeout_fast,
@@ -182,6 +184,7 @@ module ApplicationSettingsHelper
:help_page_hide_commercial_content,
:help_page_support_url,
:help_page_text,
+ :hide_third_party_offers,
:home_page_url,
:housekeeping_bitmaps_enabled,
:housekeeping_enabled,
@@ -203,6 +206,7 @@ module ApplicationSettingsHelper
:metrics_port,
:metrics_sample_interval,
:metrics_timeout,
+ :mirror_available,
:pages_domain_verification_enabled,
:password_authentication_enabled_for_web,
:password_authentication_enabled_for_git,
@@ -233,27 +237,26 @@ module ApplicationSettingsHelper
:sign_in_text,
:signup_enabled,
:terminal_max_session_time,
- :throttle_unauthenticated_enabled,
- :throttle_unauthenticated_requests_per_period,
- :throttle_unauthenticated_period_in_seconds,
- :throttle_authenticated_web_enabled,
- :throttle_authenticated_web_requests_per_period,
- :throttle_authenticated_web_period_in_seconds,
+ :terms,
:throttle_authenticated_api_enabled,
- :throttle_authenticated_api_requests_per_period,
:throttle_authenticated_api_period_in_seconds,
+ :throttle_authenticated_api_requests_per_period,
+ :throttle_authenticated_web_enabled,
+ :throttle_authenticated_web_period_in_seconds,
+ :throttle_authenticated_web_requests_per_period,
+ :throttle_unauthenticated_enabled,
+ :throttle_unauthenticated_period_in_seconds,
+ :throttle_unauthenticated_requests_per_period,
:two_factor_grace_period,
:unique_ips_limit_enabled,
:unique_ips_limit_per_user,
:unique_ips_limit_time_window,
:usage_ping_enabled,
+ :instance_statistics_visibility_private,
:user_default_external,
:user_oauth_applications,
:version_check_enabled,
- :allow_local_requests_from_hooks_and_services,
- :enforce_terms,
- :terms,
- :mirror_available
+ :web_ide_clientside_preview_enabled
]
end
end
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index d2daee22aba..18f0979fc86 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -7,7 +7,7 @@ module AuthHelper
end
def omniauth_enabled?
- Gitlab.config.omniauth.enabled
+ Gitlab::Auth.omniauth_enabled?
end
def provider_has_icon?(name)
diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb
index 43d92bde064..494f785e305 100644
--- a/app/helpers/avatars_helper.rb
+++ b/app/helpers/avatars_helper.rb
@@ -1,38 +1,10 @@
module AvatarsHelper
def project_icon(project_id, options = {})
- project =
- if project_id.respond_to?(:avatar_url)
- project_id
- else
- Project.find_by_full_path(project_id)
- end
-
- if project.avatar_url
- image_tag project.avatar_url, options
- else # generated icon
- project_identicon(project, options)
- end
+ source_icon(Project, project_id, options)
end
- def project_identicon(project, options = {})
- allowed_colors = {
- red: 'FFEBEE',
- purple: 'F3E5F5',
- indigo: 'E8EAF6',
- blue: 'E3F2FD',
- teal: 'E0F2F1',
- orange: 'FBE9E7',
- gray: 'EEEEEE'
- }
-
- options[:class] ||= ''
- options[:class] << ' identicon'
- bg_key = project.id % 7
- style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555"
-
- content_tag(:div, class: options[:class], style: style) do
- project.name[0, 1].upcase
- end
+ def group_icon(group_id, options = {})
+ source_icon(Group, group_id, options)
end
# Takes both user and email and returns the avatar_icon by
@@ -133,4 +105,32 @@ module AvatarsHelper
mail_to(options[:user_email], avatar)
end
end
+
+ private
+
+ def source_icon(klass, source_id, options = {})
+ source =
+ if source_id.respond_to?(:avatar_url)
+ source_id
+ else
+ klass.find_by_full_path(source_id)
+ end
+
+ if source.avatar_url
+ image_tag source.avatar_url, options
+ else
+ source_identicon(source, options)
+ end
+ end
+
+ def source_identicon(source, options = {})
+ bg_key = (source.id % 7) + 1
+ options[:class] ||= ''
+ options[:class] << ' identicon'
+ options[:class] << " bg#{bg_key}"
+
+ content_tag(:div, class: options[:class].strip) do
+ source.name[0, 1].upcase
+ end
+ end
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 3db28fd6da3..7eb45ddd117 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -114,22 +114,22 @@ module BlobHelper
icon("#{file_type_icon_class('file', mode, name)} fw")
end
- def blob_raw_url(only_path: false)
+ def blob_raw_url(**kwargs)
if @build && @entry
- raw_project_job_artifacts_url(@project, @build, path: @entry.path, only_path: only_path)
+ raw_project_job_artifacts_url(@project, @build, path: @entry.path, **kwargs)
elsif @snippet
if @snippet.project_id
- raw_project_snippet_url(@project, @snippet, only_path: only_path)
+ raw_project_snippet_url(@project, @snippet, **kwargs)
else
- raw_snippet_url(@snippet, only_path: only_path)
+ raw_snippet_url(@snippet, **kwargs)
end
elsif @blob
- project_raw_url(@project, @id, only_path: only_path)
+ project_raw_url(@project, @id, **kwargs)
end
end
- def blob_raw_path
- blob_raw_url(only_path: true)
+ def blob_raw_path(**kwargs)
+ blob_raw_url(**kwargs, only_path: true)
end
# SVGs can contain malicious JavaScript; only include whitelisted
@@ -226,16 +226,17 @@ module BlobHelper
def open_raw_blob_button(blob)
return if blob.empty?
+ return if blob.raw_binary? || blob.stored_externally?
- if blob.raw_binary? || blob.stored_externally?
- icon = sprite_icon('download')
- title = 'Download'
- else
- icon = icon('file-code-o')
- title = 'Open raw'
- end
+ title = 'Open raw'
+ link_to icon('file-code-o'), blob_raw_path, class: 'btn btn-sm has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: title, data: { container: 'body' }
+ end
+
+ def download_blob_button(blob)
+ return if blob.empty?
- link_to icon, blob_raw_path, class: 'btn btn-sm has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: title, data: { container: 'body' }
+ title = 'Download'
+ link_to sprite_icon('download'), blob_raw_path(inline: false), download: @path, class: 'btn btn-sm has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: title, data: { container: 'body' }
end
def blob_render_error_reason(viewer)
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 3605d6a3c95..0171a880164 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -51,7 +51,7 @@ module ButtonHelper
}
content_tag :button, button_attributes do
- concat(icon('clipboard', 'aria-hidden': 'true')) unless hide_button_icon
+ concat(sprite_icon('duplicate')) unless hide_button_icon
concat(button_text)
end
end
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 5fce97164ae..330959e536d 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -56,7 +56,7 @@ module CiStatusHelper
status.humanize
end
- def ci_icon_for_status(status)
+ def ci_icon_for_status(status, size: 16)
if detailed_status?(status)
return sprite_icon(status.icon)
end
@@ -85,7 +85,7 @@ module CiStatusHelper
'status_canceled'
end
- sprite_icon(icon_name, size: 16)
+ sprite_icon(icon_name, size: size)
end
def pipeline_status_cache_key(pipeline_status)
@@ -111,7 +111,8 @@ module CiStatusHelper
'commit',
commit.status(ref),
path,
- tooltip_placement: tooltip_placement)
+ tooltip_placement: tooltip_placement,
+ icon_size: 24)
end
def render_pipeline_status(pipeline, tooltip_placement: 'left')
@@ -122,19 +123,19 @@ module CiStatusHelper
def no_runners_for_project?(project)
project.runners.blank? &&
- Ci::Runner.shared.blank?
+ Ci::Runner.instance_type.blank?
end
- def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body')
+ def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16)
klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}"
title = "#{type.titleize}: #{ci_label_for_status(status)}"
data = { toggle: 'tooltip', placement: tooltip_placement, container: container }
if path
- link_to ci_icon_for_status(status), path,
+ link_to ci_icon_for_status(status, size: icon_size), path,
class: klass, title: title, data: data
else
- content_tag :span, ci_icon_for_status(status),
+ content_tag :span, ci_icon_for_status(status, size: icon_size),
class: klass, title: title, data: data
end
end
diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb
index c24d340d184..8fd0b6f14c6 100644
--- a/app/helpers/clusters_helper.rb
+++ b/app/helpers/clusters_helper.rb
@@ -4,6 +4,7 @@ module ClustersHelper
end
def render_gcp_signup_offer
+ return if Gitlab::CurrentSettings.current_application_settings.hide_third_party_offers?
return unless show_gcp_signup_offer?
content_tag :section, class: 'no-animate expanded' do
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index e5c3be47801..89fe90fd801 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -145,15 +145,14 @@ module CommitsHelper
person_name
end
- options = {
- class: "commit-#{options[:source]}-link has-tooltip",
- title: source_email
+ link_options = {
+ class: "commit-#{options[:source]}-link"
}
if user.nil?
- mail_to(source_email, text, options)
+ mail_to(source_email, text, link_options)
else
- link_to(text, user_path(user), options)
+ link_to(text, user_path(user), link_options)
end
end
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index 4ce89f89fa9..c005ecbb56b 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -4,4 +4,23 @@ module EnvironmentsHelper
endpoint: project_environments_path(@project, format: :json)
}
end
+
+ def metrics_data(project, environment)
+ {
+ "settings-path" => edit_project_service_path(project, 'prometheus'),
+ "clusters-path" => project_clusters_path(project),
+ "current-environment-name": environment.name,
+ "documentation-path" => help_page_path('administration/monitoring/prometheus/index.md'),
+ "empty-getting-started-svg-path" => image_path('illustrations/monitoring/getting_started.svg'),
+ "empty-loading-svg-path" => image_path('illustrations/monitoring/loading.svg'),
+ "empty-no-data-svg-path" => image_path('illustrations/monitoring/no_data.svg'),
+ "empty-unable-to-connect-svg-path" => image_path('illustrations/monitoring/unable_to_connect.svg'),
+ "metrics-endpoint" => additional_metrics_project_environment_path(project, environment, format: :json),
+ "deployment-endpoint" => project_environment_deployments_path(project, environment, format: :json),
+ "environments-endpoint": project_environments_path(project, format: :json),
+ "project-path" => project_path(project),
+ "tags-path" => project_tags_path(project),
+ "has-metrics" => "#{environment.has_metrics?}"
+ }
+ end
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 95fea2f18d1..5b51d2f2425 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -33,11 +33,6 @@ module GroupsHelper
.count
end
- def group_icon(group, options = {})
- img_path = group_icon_url(group, options)
- image_tag img_path, options
- end
-
def group_icon_url(group, options = {})
if group.is_a?(String)
group = Group.find_by_full_path(group)
@@ -128,8 +123,10 @@ module GroupsHelper
def get_group_sidebar_links
links = [:overview, :group_members]
- if can?(current_user, :read_cross_project)
- links += [:activity, :issues, :boards, :labels, :milestones, :merge_requests]
+ resources = [:activity, :issues, :boards, :labels, :milestones,
+ :merge_requests]
+ links += resources.select do |resource|
+ can?(current_user, "read_group_#{resource}".to_sym, @group)
end
if can?(current_user, :admin_group, @group)
diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb
index 551b9cca6b1..0a356ba55d2 100644
--- a/app/helpers/hooks_helper.rb
+++ b/app/helpers/hooks_helper.rb
@@ -10,7 +10,7 @@ module HooksHelper
trigger_human_name = trigger.to_s.tr('_', ' ').camelize
- link_to path, rel: 'nofollow' do
+ link_to path, rel: 'nofollow', method: :post do
content_tag(:span, trigger_human_name)
end
end
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 2f304b040c7..41084ec686f 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -1,3 +1,5 @@
+require 'json'
+
module IconsHelper
extend self
include FontAwesome::Rails::IconHelper
@@ -38,6 +40,13 @@ module IconsHelper
end
def sprite_icon(icon_name, size: nil, css_class: nil)
+ if Gitlab::Sentry.should_raise?
+ unless known_sprites.include?(icon_name)
+ exception = ArgumentError.new("#{icon_name} is not a known icon in @gitlab-org/gitlab-svg")
+ raise exception
+ end
+ end
+
css_classes = size ? "s#{size}" : ""
css_classes << " #{css_class}" unless css_class.blank?
content_tag(:svg, content_tag(:use, "", { "xlink:href" => "#{sprite_icon_path}##{icon_name}" } ), class: css_classes.empty? ? nil : css_classes)
@@ -134,4 +143,10 @@ module IconsHelper
icon_class
end
+
+ private
+
+ def known_sprites
+ @known_sprites ||= JSON.parse(File.read(Rails.root.join('node_modules/@gitlab-org/gitlab-svgs/dist/icons.json')))['icons']
+ end
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 9f501ea55fb..c84ed8091c3 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -131,6 +131,19 @@ module IssuablesHelper
end
end
+ def group_dropdown_label(group_id, default_label)
+ return default_label if group_id.nil?
+ return "Any group" if group_id == "0"
+
+ group = ::Group.find_by(id: group_id)
+
+ if group
+ group.full_name
+ else
+ default_label
+ end
+ end
+
def milestone_dropdown_label(milestone_title, default_label = "Milestone")
title =
case milestone_title
@@ -159,6 +172,12 @@ module IssuablesHelper
output << content_tag(:strong) do
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "d-none d-sm-inline", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "d-block d-sm-none")
+
+ if status = user_status(issuable.author)
+ author_output << "&ensp; #{status}".html_safe
+ end
+
+ author_output
end
output << "&ensp;".html_safe
@@ -236,6 +255,7 @@ module IssuablesHelper
issuableRef: issuable.to_reference,
markdownPreviewPath: preview_markdown_path(parent),
markdownDocsPath: help_page_path('user/markdown'),
+ markdownVersion: issuable.cached_markdown_version,
issuableTemplates: issuable_templates(issuable),
initialTitleHtml: markdown_field(issuable, :title),
initialTitleText: issuable.title,
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index 39e7a7fd396..cbb971cf8b7 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -107,6 +107,7 @@ module MarkupHelper
def markup(file_name, text, context = {})
context[:project] ||= @project
+ context[:markdown_engine] ||= :redcarpet
html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
prepare_for_rendering(html, context)
end
@@ -120,7 +121,8 @@ module MarkupHelper
project: @project,
project_wiki: @project_wiki,
page_slug: wiki_page.slug,
- issuable_state_filter_enabled: true
+ issuable_state_filter_enabled: true,
+ markdown_engine: :redcarpet
}
html =
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 82a7931c557..097be8a0643 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -108,7 +108,7 @@ module MergeRequestsHelper
data_attrs = {
action: tab.to_s,
target: "##{tab}",
- toggle: options.fetch(:force_link, false) ? '' : 'tab'
+ toggle: options.fetch(:force_link, false) ? '' : 'tabvue'
}
url = case tab
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 15a15405f1d..95da8f00aff 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -233,4 +233,12 @@ module MilestonesHelper
group_milestone_path(@group, milestone.iid, milestone: params)
end
end
+
+ def group_or_dashboard_milestone_path(milestone)
+ if milestone.group_milestone?
+ group_milestone_path(milestone.group, milestone.iid, milestone: { title: milestone.title })
+ else
+ dashboard_milestone_path(milestone.safe_title, title: milestone.title)
+ end
+ end
end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index 9be93fa69ae..30585cb403d 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -3,30 +3,42 @@ module NamespacesHelper
params.dig(:project, :namespace_id) || params[:namespace_id]
end
- def namespaces_options(selected = :current_user, display_path: false, extra_group: nil)
- groups = current_user.manageable_groups
- .joins(:route)
- .includes(:route)
- .order('routes.path')
+ def namespaces_options(selected = :current_user, display_path: false, groups: nil, extra_group: nil, groups_only: false)
+ groups ||= current_user.manageable_groups
+ .eager_load(:route)
+ .order('routes.path')
users = [current_user.namespace]
+ selected_id = selected
unless extra_group.nil? || extra_group.is_a?(Group)
extra_group = Group.find(extra_group) if Namespace.find(extra_group).kind == 'group'
end
- if extra_group && extra_group.is_a?(Group) && (!Group.exists?(name: extra_group.name) || Ability.allowed?(current_user, :read_group, extra_group))
- groups |= [extra_group]
+ if extra_group && extra_group.is_a?(Group)
+ extra_group = dedup_extra_group(extra_group)
+
+ if Ability.allowed?(current_user, :read_group, extra_group)
+ # Assign the value to an invalid primary ID so that the select box works
+ extra_group.id = -1 unless extra_group.persisted?
+ selected_id = extra_group.id if selected == :extra_group
+ groups |= [extra_group]
+ else
+ selected_id = current_user.namespace.id
+ end
end
options = []
options << options_for_group(groups, display_path: display_path, type: 'group')
- options << options_for_group(users, display_path: display_path, type: 'user')
- if selected == :current_user && current_user.namespace
- selected = current_user.namespace.id
+ unless groups_only
+ options << options_for_group(users, display_path: display_path, type: 'user')
+
+ if selected == :current_user && current_user.namespace
+ selected_id = current_user.namespace.id
+ end
end
- grouped_options_for_select(options, selected)
+ grouped_options_for_select(options, selected_id)
end
def namespace_icon(namespace, size = 40)
@@ -39,6 +51,17 @@ module NamespacesHelper
private
+ # Many importers create a temporary Group, so use the real
+ # group if one exists by that name to prevent duplicates.
+ def dedup_extra_group(extra_group)
+ unless extra_group.persisted?
+ existing_group = Group.find_by(name: extra_group.name)
+ extra_group = existing_group if existing_group&.persisted?
+ end
+
+ extra_group
+ end
+
def options_for_group(namespaces, display_path:, type:)
group_label = type.pluralize
elements = namespaces.sort_by(&:human_name).map! do |n|
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index e1a0cf1604c..5404ead44f3 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -148,6 +148,7 @@ module NotesHelper
members: autocomplete,
issues: autocomplete,
mergeRequests: autocomplete,
+ epics: autocomplete,
milestones: autocomplete,
labels: autocomplete
}
@@ -168,6 +169,7 @@ module NotesHelper
registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'),
newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'),
markdownDocsPath: help_page_path('user/markdown'),
+ markdownVersion: issuable.cached_markdown_version,
quickActionsDocsPath: help_page_path('user/project/quick_actions'),
closePath: close_issuable_path(issuable),
reopenPath: reopen_issuable_path(issuable),
diff --git a/app/helpers/pipeline_schedules_helper.rb b/app/helpers/pipeline_schedules_helper.rb
index 6edaf78de1b..4b9f6bd2caf 100644
--- a/app/helpers/pipeline_schedules_helper.rb
+++ b/app/helpers/pipeline_schedules_helper.rb
@@ -3,7 +3,7 @@ module PipelineSchedulesHelper
ActiveSupport::TimeZone.all.map do |timezone|
{
name: timezone.name,
- offset: timezone.utc_offset,
+ offset: timezone.now.utc_offset,
identifier: timezone.tzinfo.identifier
}
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index c7a434ea092..aaf9dff43ee 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -63,10 +63,10 @@ module ProjectsHelper
author_html = author_html.html_safe
if opts[:name]
- link_to(author_html, user_path(author), class: "author_link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
+ link_to(author_html, user_path(author), class: "author-link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
else
title = opts[:title].sub(":name", sanitize(author.name))
- link_to(author_html, user_path(author), class: "author_link has-tooltip", title: title, data: { container: 'body' }).html_safe
+ link_to(author_html, user_path(author), class: "author-link has-tooltip", title: title, data: { container: 'body' }).html_safe
end
end
@@ -177,6 +177,7 @@ module ProjectsHelper
controller.action_name,
Gitlab::CurrentSettings.cache_key,
"cross-project:#{can?(current_user, :read_cross_project)}",
+ max_project_member_access_cache_key(project),
'v2.6'
]
@@ -412,20 +413,6 @@ module ProjectsHelper
@ref || @repository.try(:root_ref)
end
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1235
- def sanitize_repo_path(project, message)
- return '' unless message.present?
-
- exports_path = File.join(Settings.shared['path'], 'tmp/project_exports')
- filtered_message = message.strip.gsub(exports_path, "[REPO EXPORT PATH]")
-
- disk_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path
- end
-
- filtered_message.gsub(disk_path.chomp('/'), "[REPOS PATH]")
- end
-
def project_child_container_class(view_path)
view_path == "projects/issues/issues" ? "prepend-top-default" : "project-show-#{view_path}"
end
diff --git a/app/helpers/repository_languages_helper.rb b/app/helpers/repository_languages_helper.rb
new file mode 100644
index 00000000000..9a842cf5ce0
--- /dev/null
+++ b/app/helpers/repository_languages_helper.rb
@@ -0,0 +1,16 @@
+module RepositoryLanguagesHelper
+ def repository_languages_bar(languages)
+ return if languages.none?
+
+ content_tag :div, class: 'progress repository-languages-bar' do
+ safe_join(languages.map { |lang| language_progress(lang) })
+ end
+ end
+
+ def language_progress(lang)
+ content_tag :div, nil,
+ class: "progress-bar has-tooltip",
+ style: "width: #{lang.share}%; background-color:#{lang.color}",
+ title: lang.name
+ end
+end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index f7dafca7834..98074a4c0c5 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -82,16 +82,16 @@ module SearchHelper
ref = @ref || @project.repository.root_ref
[
- { category: "Current Project", label: "Files", url: project_tree_path(@project, ref) },
- { category: "Current Project", label: "Commits", url: project_commits_path(@project, ref) },
- { category: "Current Project", label: "Network", url: project_network_path(@project, ref) },
- { category: "Current Project", label: "Graph", url: project_graph_path(@project, ref) },
- { category: "Current Project", label: "Issues", url: project_issues_path(@project) },
- { category: "Current Project", label: "Merge Requests", url: project_merge_requests_path(@project) },
- { category: "Current Project", label: "Milestones", url: project_milestones_path(@project) },
- { category: "Current Project", label: "Snippets", url: project_snippets_path(@project) },
- { category: "Current Project", label: "Members", url: project_project_members_path(@project) },
- { category: "Current Project", label: "Wiki", url: project_wikis_path(@project) }
+ { category: "In this project", label: "Files", url: project_tree_path(@project, ref) },
+ { category: "In this project", label: "Commits", url: project_commits_path(@project, ref) },
+ { category: "In this project", label: "Network", url: project_network_path(@project, ref) },
+ { category: "In this project", label: "Graph", url: project_graph_path(@project, ref) },
+ { category: "In this project", label: "Issues", url: project_issues_path(@project) },
+ { category: "In this project", label: "Merge Requests", url: project_merge_requests_path(@project) },
+ { category: "In this project", label: "Milestones", url: project_milestones_path(@project) },
+ { category: "In this project", label: "Snippets", url: project_snippets_path(@project) },
+ { category: "In this project", label: "Members", url: project_project_members_path(@project) },
+ { category: "In this project", label: "Wiki", url: project_wikis_path(@project) }
]
else
[]
@@ -105,7 +105,8 @@ module SearchHelper
category: "Groups",
id: group.id,
label: "#{search_result_sanitize(group.full_name)}",
- url: group_path(group)
+ url: group_path(group),
+ avatar_url: group.avatar_url || ''
}
end
end
@@ -119,7 +120,8 @@ module SearchHelper
id: p.id,
value: "#{search_result_sanitize(p.name)}",
label: "#{search_result_sanitize(p.full_name)}",
- url: project_path(p)
+ url: project_path(p),
+ avatar_url: p.avatar_url || ''
}
end
end
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index 733832c1bbb..a05640773ad 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -116,7 +116,7 @@ module SnippetsHelper
raw_project_snippet_url(@snippet.project, @snippet)
end
- link_to external_snippet_icon('doc_code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw'
+ link_to external_snippet_icon('doc-code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw'
end
def embedded_snippet_download_button
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 9151543dfdc..ebfde993456 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -8,7 +8,7 @@ module SubmoduleHelper
url = repository.submodule_url_for(ref, submodule_item.path)
if url == '.' || url == './'
- url = File.join(Gitlab.config.gitlab.url, @project.full_path)
+ url = File.join(Gitlab.config.gitlab.url, repository.project.full_path)
end
if url =~ %r{([^/:]+)/([^/]+(?:\.git)?)\Z}
@@ -31,7 +31,7 @@ module SubmoduleHelper
[namespace_project_path(namespace, project),
namespace_project_tree_path(namespace, project, submodule_item.id)]
elsif relative_self_url?(url)
- relative_self_links(url, submodule_item.id)
+ relative_self_links(url, submodule_item.id, repository.project)
elsif github_dot_com_url?(url)
standard_links('github.com', namespace, project, submodule_item.id)
elsif gitlab_dot_com_url?(url)
@@ -73,7 +73,7 @@ module SubmoduleHelper
[base, [base, '/tree/', commit].join('')]
end
- def relative_self_links(url, commit)
+ def relative_self_links(url, commit, project)
url.rstrip!
# Map relative links to a namespace and project
# For example:
@@ -85,7 +85,7 @@ module SubmoduleHelper
namespace = components.pop.gsub(/^\.\.$/, '')
if namespace.empty?
- namespace = @project.namespace.full_path
+ namespace = project.namespace.full_path
end
begin
diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb
index 271e839692a..336385f6798 100644
--- a/app/helpers/time_helper.rb
+++ b/app/helpers/time_helper.rb
@@ -5,9 +5,13 @@ module TimeHelper
seconds = interval_in_seconds - minutes * 60
if minutes >= 1
- "#{pluralize(minutes, "minute")} #{pluralize(seconds, "second")}"
+ if seconds % 60 == 0
+ pluralize(minutes, "minute")
+ else
+ [pluralize(minutes, "minute"), pluralize(seconds, "second")].to_sentence
+ end
else
- "#{pluralize(seconds, "second")}"
+ pluralize(seconds, "second")
end
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index f7620e0b6b8..7cd74358168 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -43,7 +43,7 @@ module TodosHelper
project_commit_path(todo.project,
todo.target, anchor: anchor)
else
- path = [todo.project.namespace.becomes(Namespace), todo.project, todo.target]
+ path = [todo.parent, todo.target]
path.unshift(:pipelines) if todo.build_failed?
@@ -167,4 +167,12 @@ module TodosHelper
def show_todo_state?(todo)
(todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && %w(closed merged).include?(todo.target.state)
end
+
+ def todo_group_options
+ groups = current_user.authorized_groups.map do |group|
+ { id: group.id, text: group.full_name }
+ end
+
+ groups.unshift({ id: '', text: 'Any Group' }).to_json
+ end
end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index ce9373f5883..ceea4384f91 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -31,10 +31,42 @@ module UsersHelper
current_user_menu_items.include?(item)
end
+ def max_project_member_access(project)
+ current_user&.max_member_access_for_project(project.id) || Gitlab::Access::NO_ACCESS
+ end
+
+ def max_project_member_access_cache_key(project)
+ "access:#{max_project_member_access(project)}"
+ end
+
+ def user_status(user)
+ return unless user
+
+ unless user.association(:status).loaded?
+ exception = RuntimeError.new("Status was not preloaded")
+ Gitlab::Sentry.track_exception(exception, extra: { user: user.inspect })
+ end
+
+ return unless user.status
+
+ content_tag :span,
+ class: 'user-status-emoji has-tooltip',
+ title: user.status.message_html,
+ data: { html: true, placement: 'top' } do
+ emoji_icon user.status.emoji
+ end
+ end
+
private
def get_profile_tabs
- [:activity, :groups, :contributed, :projects, :snippets]
+ tabs = []
+
+ if can?(current_user, :read_user_profile, @user)
+ tabs += [:activity, :groups, :contributed, :projects, :snippets]
+ end
+
+ tabs
end
def get_current_user_menu_items
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index e395cda03d3..cf2fe5a2019 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -126,10 +126,9 @@ module VisibilityLevelHelper
end
def visibility_icon_description(form_model)
- case form_model
- when Project
+ if form_model.respond_to?(:visibility_level_allowed_as_fork?)
project_visibility_icon_description(form_model.visibility_level)
- when Group
+ elsif form_model.respond_to?(:visibility_level_allowed_by_sub_groups?)
group_visibility_icon_description(form_model.visibility_level)
end
end
diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb
index a82271ce0ee..fd1d78bd9b8 100644
--- a/app/helpers/workhorse_helper.rb
+++ b/app/helpers/workhorse_helper.rb
@@ -2,9 +2,9 @@
# Workhorse will also serve files when using `send_file`.
module WorkhorseHelper
# Send a Git blob through Workhorse
- def send_git_blob(repository, blob)
+ def send_git_blob(repository, blob, inline: true)
headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob))
- headers['Content-Disposition'] = 'inline'
+ headers['Content-Disposition'] = inline ? 'inline' : 'attachment'
headers['Content-Type'] = safe_content_type(blob)
render plain: ""
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 1db1482d6b7..0e1e39501f5 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -124,7 +124,7 @@ class Notify < BaseMailer
fallback_reply_message_id = "<reply-#{reply_key}@#{Gitlab.config.gitlab.host}>".freeze
headers['References'] ||= []
- headers['References'] << fallback_reply_message_id
+ headers['References'].unshift(fallback_reply_message_id)
@reply_by_email = true
end
@@ -158,7 +158,7 @@ class Notify < BaseMailer
def mail_answer_thread(model, headers = {})
headers['Message-ID'] = "<#{SecureRandom.hex}@#{Gitlab.config.gitlab.host}>"
headers['In-Reply-To'] = message_id(model)
- headers['References'] = message_id(model)
+ headers['References'] = [message_id(model)]
headers[:subject]&.prepend('Re: ')
diff --git a/spec/mailers/previews/devise_mailer_preview.rb b/app/mailers/previews/devise_mailer_preview.rb
index d6588efc486..d6588efc486 100644
--- a/spec/mailers/previews/devise_mailer_preview.rb
+++ b/app/mailers/previews/devise_mailer_preview.rb
diff --git a/spec/mailers/previews/email_rejection_mailer_preview.rb b/app/mailers/previews/email_rejection_mailer_preview.rb
index 639e8471232..639e8471232 100644
--- a/spec/mailers/previews/email_rejection_mailer_preview.rb
+++ b/app/mailers/previews/email_rejection_mailer_preview.rb
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
new file mode 100644
index 00000000000..3615cde8026
--- /dev/null
+++ b/app/mailers/previews/notify_preview.rb
@@ -0,0 +1,170 @@
+class NotifyPreview < ActionMailer::Preview
+ def note_merge_request_email_for_individual_note
+ note_email(:note_merge_request_email) do
+ note = <<-MD.strip_heredoc
+ This is an individual note on a merge request :smiley:
+
+ In this notification email, we expect to see:
+
+ - The note contents (that's what you're looking at)
+ - A link to view this note on Gitlab
+ - An explanation for why the user is receiving this notification
+ MD
+
+ create_note(noteable_type: 'merge_request', noteable_id: merge_request.id, note: note)
+ end
+ end
+
+ def note_merge_request_email_for_discussion
+ note_email(:note_merge_request_email) do
+ note = <<-MD.strip_heredoc
+ This is a new discussion on a merge request :smiley:
+
+ In this notification email, we expect to see:
+
+ - A line saying who started this discussion
+ - The note contents (that's what you're looking at)
+ - A link to view this discussion on Gitlab
+ - An explanation for why the user is receiving this notification
+ MD
+
+ create_note(noteable_type: 'merge_request', noteable_id: merge_request.id, type: 'DiscussionNote', note: note)
+ end
+ end
+
+ def note_merge_request_email_for_diff_discussion
+ note_email(:note_merge_request_email) do
+ note = <<-MD.strip_heredoc
+ This is a new discussion on a merge request :smiley:
+
+ In this notification email, we expect to see:
+
+ - A line saying who started this discussion and on what file
+ - The diff
+ - The note contents (that's what you're looking at)
+ - A link to view this discussion on Gitlab
+ - An explanation for why the user is receiving this notification
+ MD
+
+ position = Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ diff_refs: merge_request.diff_refs
+ )
+
+ create_note(noteable_type: 'merge_request', noteable_id: merge_request.id, type: 'DiffNote', position: position, note: note)
+ end
+ end
+
+ def closed_issue_email
+ Notify.closed_issue_email(user.id, issue.id, user.id).message
+ end
+
+ def issue_status_changed_email
+ Notify.issue_status_changed_email(user.id, issue.id, 'closed', user.id).message
+ end
+
+ def closed_merge_request_email
+ Notify.closed_merge_request_email(user.id, issue.id, user.id).message
+ end
+
+ def merge_request_status_email
+ Notify.merge_request_status_email(user.id, merge_request.id, 'closed', user.id).message
+ end
+
+ def merged_merge_request_email
+ Notify.merged_merge_request_email(user.id, merge_request.id, user.id).message
+ end
+
+ def member_access_denied_email
+ Notify.member_access_denied_email('project', project.id, user.id).message
+ end
+
+ def member_access_granted_email
+ Notify.member_access_granted_email('project', user.id).message
+ end
+
+ def member_access_requested_email
+ Notify.member_access_requested_email('group', user.id, 'some@example.com').message
+ end
+
+ def member_invite_accepted_email
+ Notify.member_invite_accepted_email('project', user.id).message
+ end
+
+ def member_invite_declined_email
+ Notify.member_invite_declined_email(
+ 'project',
+ project.id,
+ 'invite@example.com',
+ user.id
+ ).message
+ end
+
+ def member_invited_email
+ Notify.member_invited_email('project', user.id, '1234').message
+ end
+
+ def pages_domain_enabled_email
+ cleanup do
+ pages_domain = PagesDomain.new(domain: 'my.example.com', project: project, verified_at: Time.now, enabled_until: 1.week.from_now)
+
+ Notify.pages_domain_enabled_email(pages_domain, user).message
+ end
+ end
+
+ def pipeline_success_email
+ Notify.pipeline_success_email(pipeline, pipeline.user.try(:email))
+ end
+
+ def pipeline_failed_email
+ Notify.pipeline_failed_email(pipeline, pipeline.user.try(:email))
+ end
+
+ private
+
+ def project
+ @project ||= Project.find_by_full_path('gitlab-org/gitlab-test')
+ end
+
+ def issue
+ @merge_request ||= project.issues.first
+ end
+
+ def merge_request
+ @merge_request ||= project.merge_requests.first
+ end
+
+ def pipeline
+ @pipeline = Ci::Pipeline.last
+ end
+
+ def user
+ @user ||= User.last
+ end
+
+ def create_note(params)
+ Notes::CreateService.new(project, user, params).execute
+ end
+
+ def note_email(method)
+ cleanup do
+ note = yield
+
+ Notify.public_send(method, user.id, note) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+
+ def cleanup
+ email = nil
+
+ ActiveRecord::Base.transaction do
+ email = yield
+ raise ActiveRecord::Rollback
+ end
+
+ email
+ end
+end
diff --git a/spec/mailers/previews/repository_check_mailer_preview.rb b/app/mailers/previews/repository_check_mailer_preview.rb
index 19d4eab1805..19d4eab1805 100644
--- a/spec/mailers/previews/repository_check_mailer_preview.rb
+++ b/app/mailers/previews/repository_check_mailer_preview.rb
diff --git a/app/models/ability.rb b/app/models/ability.rb
index bb600eaccba..a853106e5bd 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency 'declarative_policy'
class Ability
diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb
index 4cbd90c5817..1b78fd04ebb 100644
--- a/app/models/abuse_report.rb
+++ b/app/models/abuse_report.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AbuseReport < ActiveRecord::Base
include CacheMarkdownField
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index b4a86dbb331..0d9c6a4a1f0 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ActiveSession
include ActiveModel::Model
diff --git a/app/models/appearance.rb b/app/models/appearance.rb
index b770aadef0e..bffba3e13fa 100644
--- a/app/models/appearance.rb
+++ b/app/models/appearance.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Appearance < ActiveRecord::Base
include CacheableAttributes
include CacheMarkdownField
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index bddeb8b0352..bbe7811841a 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ApplicationSetting < ActiveRecord::Base
include CacheableAttributes
include CacheMarkdownField
@@ -228,25 +230,27 @@ class ApplicationSetting < ActiveRecord::Base
{
after_sign_up_text: nil,
akismet_enabled: false,
+ allow_local_requests_from_hooks_and_services: false,
authorized_keys_enabled: true, # TODO default to false if the instance is configured to use AuthorizedKeysCommand
container_registry_token_expire_delay: 5,
default_artifacts_expire_in: '30 days',
default_branch_protection: Settings.gitlab['default_branch_protection'],
+ default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
- default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
disabled_oauth_sign_in_sources: [],
domain_whitelist: Settings.gitlab['domain_whitelist'],
dsa_key_restriction: 0,
ecdsa_key_restriction: 0,
ed25519_key_restriction: 0,
+ gitaly_timeout_default: 55,
+ gitaly_timeout_fast: 10,
+ gitaly_timeout_medium: 30,
gravatar_enabled: Settings.gravatar['enabled'],
- help_page_text: nil,
help_page_hide_commercial_content: false,
- unique_ips_limit_per_user: 10,
- unique_ips_limit_time_window: 3600,
- unique_ips_limit_enabled: false,
+ help_page_text: nil,
+ hide_third_party_offers: false,
housekeeping_bitmaps_enabled: true,
housekeeping_enabled: true,
housekeeping_full_repack_period: 50,
@@ -257,12 +261,14 @@ class ApplicationSetting < ActiveRecord::Base
koding_url: nil,
max_artifacts_size: Settings.artifacts['max_size'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
- password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'],
+ mirror_available: true,
password_authentication_enabled_for_git: true,
+ password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'],
performance_bar_allowed_group_id: nil,
rsa_key_restriction: 0,
plantuml_enabled: false,
plantuml_url: nil,
+ polling_interval_multiplier: 1,
project_export_enabled: true,
recaptcha_enabled: false,
repository_checks_enabled: true,
@@ -277,24 +283,22 @@ class ApplicationSetting < ActiveRecord::Base
sign_in_text: nil,
signup_enabled: Settings.gitlab['signup_enabled'],
terminal_max_session_time: 0,
- throttle_unauthenticated_enabled: false,
- throttle_unauthenticated_requests_per_period: 3600,
- throttle_unauthenticated_period_in_seconds: 3600,
- throttle_authenticated_web_enabled: false,
- throttle_authenticated_web_requests_per_period: 7200,
- throttle_authenticated_web_period_in_seconds: 3600,
throttle_authenticated_api_enabled: false,
- throttle_authenticated_api_requests_per_period: 7200,
throttle_authenticated_api_period_in_seconds: 3600,
+ throttle_authenticated_api_requests_per_period: 7200,
+ throttle_authenticated_web_enabled: false,
+ throttle_authenticated_web_period_in_seconds: 3600,
+ throttle_authenticated_web_requests_per_period: 7200,
+ throttle_unauthenticated_enabled: false,
+ throttle_unauthenticated_period_in_seconds: 3600,
+ throttle_unauthenticated_requests_per_period: 3600,
two_factor_grace_period: 48,
- user_default_external: false,
- polling_interval_multiplier: 1,
+ unique_ips_limit_enabled: false,
+ unique_ips_limit_per_user: 10,
+ unique_ips_limit_time_window: 3600,
usage_ping_enabled: Settings.gitlab['usage_ping_enabled'],
- gitaly_timeout_fast: 10,
- gitaly_timeout_medium: 30,
- gitaly_timeout_default: 55,
- allow_local_requests_from_hooks_and_services: false,
- mirror_available: true
+ instance_statistics_visibility_private: false,
+ user_default_external: false
}
end
diff --git a/app/models/application_setting/term.rb b/app/models/application_setting/term.rb
index 3b1dfe7e4ef..498701ba22b 100644
--- a/app/models/application_setting/term.rb
+++ b/app/models/application_setting/term.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ApplicationSetting
class Term < ActiveRecord::Base
include CacheMarkdownField
diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb
index 112a8778b4e..8508c88d406 100644
--- a/app/models/audit_event.rb
+++ b/app/models/audit_event.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AuditEvent < ActiveRecord::Base
serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize
diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb
index 4d1a15c53aa..99c7866d636 100644
--- a/app/models/award_emoji.rb
+++ b/app/models/award_emoji.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AwardEmoji < ActiveRecord::Base
DOWNVOTE_NAME = "thumbsdown".freeze
UPVOTE_NAME = "thumbsup".freeze
diff --git a/app/models/badge.rb b/app/models/badge.rb
index 265c5d872d4..7e3b6b659e4 100644
--- a/app/models/badge.rb
+++ b/app/models/badge.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Badge < ActiveRecord::Base
# This structure sets the placeholders that the urls
# can have. This hash also sets which action to ask when
diff --git a/app/models/badges/group_badge.rb b/app/models/badges/group_badge.rb
index f4b2bdecdcc..c0712f452df 100644
--- a/app/models/badges/group_badge.rb
+++ b/app/models/badges/group_badge.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupBadge < Badge
belongs_to :group
diff --git a/app/models/badges/project_badge.rb b/app/models/badges/project_badge.rb
index 3945b376052..59638df6fad 100644
--- a/app/models/badges/project_badge.rb
+++ b/app/models/badges/project_badge.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectBadge < Badge
belongs_to :project
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 71c974b4c09..acc64ffca67 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects
class Blob < SimpleDelegator
CACHE_TIME = 60 # Cache raw blobs referred to by a (mutable) ref for 1 minute
diff --git a/app/models/blob_viewer/auxiliary.rb b/app/models/blob_viewer/auxiliary.rb
index 1bea225f17c..263d51b4e36 100644
--- a/app/models/blob_viewer/auxiliary.rb
+++ b/app/models/blob_viewer/auxiliary.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
module Auxiliary
extend ActiveSupport::Concern
diff --git a/app/models/blob_viewer/balsamiq.rb b/app/models/blob_viewer/balsamiq.rb
index f982521db99..1af6c5474d7 100644
--- a/app/models/blob_viewer/balsamiq.rb
+++ b/app/models/blob_viewer/balsamiq.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Balsamiq < Base
include Rich
diff --git a/app/models/blob_viewer/base.rb b/app/models/blob_viewer/base.rb
index bf3453b3063..eaaf9af1330 100644
--- a/app/models/blob_viewer/base.rb
+++ b/app/models/blob_viewer/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Base
PARTIAL_PATH_PREFIX = 'projects/blob/viewers'.freeze
diff --git a/app/models/blob_viewer/binary_stl.rb b/app/models/blob_viewer/binary_stl.rb
index 80393471ef2..425f72decae 100644
--- a/app/models/blob_viewer/binary_stl.rb
+++ b/app/models/blob_viewer/binary_stl.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class BinarySTL < Base
include Rich
diff --git a/app/models/blob_viewer/cartfile.rb b/app/models/blob_viewer/cartfile.rb
index d8471bc33c0..ea0494033bf 100644
--- a/app/models/blob_viewer/cartfile.rb
+++ b/app/models/blob_viewer/cartfile.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Cartfile < DependencyManager
include Static
diff --git a/app/models/blob_viewer/changelog.rb b/app/models/blob_viewer/changelog.rb
index 0464ae27f71..8810bd25809 100644
--- a/app/models/blob_viewer/changelog.rb
+++ b/app/models/blob_viewer/changelog.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Changelog < Base
include Auxiliary
diff --git a/app/models/blob_viewer/client_side.rb b/app/models/blob_viewer/client_side.rb
index 079cfbe3616..f7efd4b8e50 100644
--- a/app/models/blob_viewer/client_side.rb
+++ b/app/models/blob_viewer/client_side.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
module ClientSide
extend ActiveSupport::Concern
diff --git a/app/models/blob_viewer/composer_json.rb b/app/models/blob_viewer/composer_json.rb
index def4879fbb5..9d1376de0cb 100644
--- a/app/models/blob_viewer/composer_json.rb
+++ b/app/models/blob_viewer/composer_json.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class ComposerJson < DependencyManager
include ServerSide
diff --git a/app/models/blob_viewer/contributing.rb b/app/models/blob_viewer/contributing.rb
index fbd1dd48697..fa224309e31 100644
--- a/app/models/blob_viewer/contributing.rb
+++ b/app/models/blob_viewer/contributing.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Contributing < Base
include Auxiliary
diff --git a/app/models/blob_viewer/dependency_manager.rb b/app/models/blob_viewer/dependency_manager.rb
index cc4950240af..711465c7c79 100644
--- a/app/models/blob_viewer/dependency_manager.rb
+++ b/app/models/blob_viewer/dependency_manager.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class DependencyManager < Base
include Auxiliary
diff --git a/app/models/blob_viewer/download.rb b/app/models/blob_viewer/download.rb
index 074e7204814..8228a83c2b1 100644
--- a/app/models/blob_viewer/download.rb
+++ b/app/models/blob_viewer/download.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Download < Base
include Simple
diff --git a/app/models/blob_viewer/empty.rb b/app/models/blob_viewer/empty.rb
index 2380578ed72..766be349d83 100644
--- a/app/models/blob_viewer/empty.rb
+++ b/app/models/blob_viewer/empty.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Empty < Base
include Simple
diff --git a/app/models/blob_viewer/gemfile.rb b/app/models/blob_viewer/gemfile.rb
index fae8c8df23f..77220cdbd08 100644
--- a/app/models/blob_viewer/gemfile.rb
+++ b/app/models/blob_viewer/gemfile.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Gemfile < DependencyManager
include Static
diff --git a/app/models/blob_viewer/gemspec.rb b/app/models/blob_viewer/gemspec.rb
index 7802edeb754..274859a7710 100644
--- a/app/models/blob_viewer/gemspec.rb
+++ b/app/models/blob_viewer/gemspec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Gemspec < DependencyManager
include ServerSide
diff --git a/app/models/blob_viewer/gitlab_ci_yml.rb b/app/models/blob_viewer/gitlab_ci_yml.rb
index 53bc247dec1..1a86f04b1b9 100644
--- a/app/models/blob_viewer/gitlab_ci_yml.rb
+++ b/app/models/blob_viewer/gitlab_ci_yml.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class GitlabCiYml < Base
include ServerSide
diff --git a/app/models/blob_viewer/godeps_json.rb b/app/models/blob_viewer/godeps_json.rb
index e19a602603b..743c759aea5 100644
--- a/app/models/blob_viewer/godeps_json.rb
+++ b/app/models/blob_viewer/godeps_json.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class GodepsJson < DependencyManager
include Static
diff --git a/app/models/blob_viewer/image.rb b/app/models/blob_viewer/image.rb
index c4eae5c79c2..56e27839fca 100644
--- a/app/models/blob_viewer/image.rb
+++ b/app/models/blob_viewer/image.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Image < Base
include Rich
diff --git a/app/models/blob_viewer/license.rb b/app/models/blob_viewer/license.rb
index 57355f2c3aa..3427227ad26 100644
--- a/app/models/blob_viewer/license.rb
+++ b/app/models/blob_viewer/license.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class License < Base
include Auxiliary
diff --git a/app/models/blob_viewer/markup.rb b/app/models/blob_viewer/markup.rb
index 33b59c4f512..f525180048e 100644
--- a/app/models/blob_viewer/markup.rb
+++ b/app/models/blob_viewer/markup.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Markup < Base
include Rich
diff --git a/app/models/blob_viewer/notebook.rb b/app/models/blob_viewer/notebook.rb
index e00b47e6c17..57d6d802db3 100644
--- a/app/models/blob_viewer/notebook.rb
+++ b/app/models/blob_viewer/notebook.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Notebook < Base
include Rich
diff --git a/app/models/blob_viewer/package_json.rb b/app/models/blob_viewer/package_json.rb
index 46cd2f04f4d..d12dd93ce2e 100644
--- a/app/models/blob_viewer/package_json.rb
+++ b/app/models/blob_viewer/package_json.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class PackageJson < DependencyManager
include ServerSide
diff --git a/app/models/blob_viewer/pdf.rb b/app/models/blob_viewer/pdf.rb
index 65805f5f388..2cf7752585c 100644
--- a/app/models/blob_viewer/pdf.rb
+++ b/app/models/blob_viewer/pdf.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class PDF < Base
include Rich
diff --git a/app/models/blob_viewer/podfile.rb b/app/models/blob_viewer/podfile.rb
index 507bc734cb4..73d714f48ca 100644
--- a/app/models/blob_viewer/podfile.rb
+++ b/app/models/blob_viewer/podfile.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Podfile < DependencyManager
include Static
diff --git a/app/models/blob_viewer/podspec.rb b/app/models/blob_viewer/podspec.rb
index a4c242db3a9..2303471583d 100644
--- a/app/models/blob_viewer/podspec.rb
+++ b/app/models/blob_viewer/podspec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Podspec < DependencyManager
include ServerSide
diff --git a/app/models/blob_viewer/podspec_json.rb b/app/models/blob_viewer/podspec_json.rb
index 602f4a51fd9..d3f6ae269da 100644
--- a/app/models/blob_viewer/podspec_json.rb
+++ b/app/models/blob_viewer/podspec_json.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class PodspecJson < Podspec
self.file_types = %i(podspec_json)
diff --git a/app/models/blob_viewer/readme.rb b/app/models/blob_viewer/readme.rb
index 4604a9934a0..f1a5c6a6acc 100644
--- a/app/models/blob_viewer/readme.rb
+++ b/app/models/blob_viewer/readme.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Readme < Base
include Auxiliary
diff --git a/app/models/blob_viewer/requirements_txt.rb b/app/models/blob_viewer/requirements_txt.rb
index 83ac55f61d0..58161e83493 100644
--- a/app/models/blob_viewer/requirements_txt.rb
+++ b/app/models/blob_viewer/requirements_txt.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class RequirementsTxt < DependencyManager
include Static
diff --git a/app/models/blob_viewer/rich.rb b/app/models/blob_viewer/rich.rb
index be373dbc948..0f66a672102 100644
--- a/app/models/blob_viewer/rich.rb
+++ b/app/models/blob_viewer/rich.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
module Rich
extend ActiveSupport::Concern
diff --git a/app/models/blob_viewer/route_map.rb b/app/models/blob_viewer/route_map.rb
index 153b4eeb2c9..6731536dfe1 100644
--- a/app/models/blob_viewer/route_map.rb
+++ b/app/models/blob_viewer/route_map.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class RouteMap < Base
include ServerSide
diff --git a/app/models/blob_viewer/server_side.rb b/app/models/blob_viewer/server_side.rb
index 86afcc86aa0..29501f50396 100644
--- a/app/models/blob_viewer/server_side.rb
+++ b/app/models/blob_viewer/server_side.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
module ServerSide
extend ActiveSupport::Concern
diff --git a/app/models/blob_viewer/simple.rb b/app/models/blob_viewer/simple.rb
index 454a20495fc..c176784df46 100644
--- a/app/models/blob_viewer/simple.rb
+++ b/app/models/blob_viewer/simple.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
module Simple
extend ActiveSupport::Concern
diff --git a/app/models/blob_viewer/sketch.rb b/app/models/blob_viewer/sketch.rb
index 818456778e1..659ab11f30b 100644
--- a/app/models/blob_viewer/sketch.rb
+++ b/app/models/blob_viewer/sketch.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Sketch < Base
include Rich
diff --git a/app/models/blob_viewer/static.rb b/app/models/blob_viewer/static.rb
index c9e257e5388..5cac08aa189 100644
--- a/app/models/blob_viewer/static.rb
+++ b/app/models/blob_viewer/static.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
module Static
extend ActiveSupport::Concern
diff --git a/app/models/blob_viewer/svg.rb b/app/models/blob_viewer/svg.rb
index b7e5cd71e6b..454c6a57568 100644
--- a/app/models/blob_viewer/svg.rb
+++ b/app/models/blob_viewer/svg.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class SVG < Base
include Rich
diff --git a/app/models/blob_viewer/text.rb b/app/models/blob_viewer/text.rb
index f68cbb7e212..e0c586a6680 100644
--- a/app/models/blob_viewer/text.rb
+++ b/app/models/blob_viewer/text.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Text < Base
include Simple
diff --git a/app/models/blob_viewer/text_stl.rb b/app/models/blob_viewer/text_stl.rb
index 8184dc0104c..fb6b26d2e44 100644
--- a/app/models/blob_viewer/text_stl.rb
+++ b/app/models/blob_viewer/text_stl.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class TextSTL < BinarySTL
self.binary = false
diff --git a/app/models/blob_viewer/video.rb b/app/models/blob_viewer/video.rb
index 057f9fe516f..48bb2a13518 100644
--- a/app/models/blob_viewer/video.rb
+++ b/app/models/blob_viewer/video.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class Video < Base
include Rich
diff --git a/app/models/blob_viewer/yarn_lock.rb b/app/models/blob_viewer/yarn_lock.rb
index 31588ddcbab..196d9f96f23 100644
--- a/app/models/blob_viewer/yarn_lock.rb
+++ b/app/models/blob_viewer/yarn_lock.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobViewer
class YarnLock < DependencyManager
include Static
diff --git a/app/models/board.rb b/app/models/board.rb
index 3cede6fc99a..a137863456c 100644
--- a/app/models/board.rb
+++ b/app/models/board.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Board < ActiveRecord::Base
belongs_to :group
belongs_to :project
@@ -26,4 +28,8 @@ class Board < ActiveRecord::Base
def closed_list
lists.merge(List.closed).take
end
+
+ def scoped?
+ false
+ end
end
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index 4aa236555cb..baf8adb318b 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BroadcastMessage < ActiveRecord::Base
include CacheMarkdownField
include Sortable
diff --git a/app/models/chat_name.rb b/app/models/chat_name.rb
index fbd0f123341..03b0af53046 100644
--- a/app/models/chat_name.rb
+++ b/app/models/chat_name.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ChatName < ActiveRecord::Base
LAST_USED_AT_INTERVAL = 1.hour
diff --git a/app/models/chat_team.rb b/app/models/chat_team.rb
index 25ecf2d5937..4e724f9adf7 100644
--- a/app/models/chat_team.rb
+++ b/app/models/chat_team.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ChatTeam < ActiveRecord::Base
validates :team_id, presence: true
validates :namespace, uniqueness: true
diff --git a/app/models/ci/artifact_blob.rb b/app/models/ci/artifact_blob.rb
index 760f01f225b..cd0b31482d2 100644
--- a/app/models/ci/artifact_blob.rb
+++ b/app/models/ci/artifact_blob.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class ArtifactBlob
include BlobLike
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 41446946a5e..9292929be98 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class Build < CommitStatus
prepend ArtifactMigratable
@@ -8,8 +10,6 @@ module Ci
include Importable
include Gitlab::Utils::StrongMemoize
- MissingDependenciesError = Class.new(StandardError)
-
belongs_to :project, inverse_of: :builds
belongs_to :runner
belongs_to :trigger_request
@@ -17,17 +17,28 @@ module Ci
has_many :deployments, as: :deployable
+ RUNNER_FEATURES = {
+ upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? }
+ }.freeze
+
has_one :last_deployment, -> { order('deployments.id DESC') }, as: :deployable, class_name: 'Deployment'
has_many :trace_sections, class_name: 'Ci::BuildTraceSection'
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id
has_many :job_artifacts, class_name: 'Ci::JobArtifact', foreign_key: :job_id, dependent: :destroy, inverse_of: :job # rubocop:disable Cop/ActiveRecordDependent
- has_one :job_artifacts_archive, -> { where(file_type: Ci::JobArtifact.file_types[:archive]) }, class_name: 'Ci::JobArtifact', inverse_of: :job, foreign_key: :job_id
- has_one :job_artifacts_metadata, -> { where(file_type: Ci::JobArtifact.file_types[:metadata]) }, class_name: 'Ci::JobArtifact', inverse_of: :job, foreign_key: :job_id
- has_one :job_artifacts_trace, -> { where(file_type: Ci::JobArtifact.file_types[:trace]) }, class_name: 'Ci::JobArtifact', inverse_of: :job, foreign_key: :job_id
+
+ Ci::JobArtifact.file_types.each do |key, value|
+ has_one :"job_artifacts_#{key}", -> { where(file_type: value) }, class_name: 'Ci::JobArtifact', inverse_of: :job, foreign_key: :job_id
+ end
has_one :metadata, class_name: 'Ci::BuildMetadata'
+ has_one :runner_session, class_name: 'Ci::BuildRunnerSession', validate: true, inverse_of: :build
+
+ accepts_nested_attributes_for :runner_session
+
delegate :timeout, to: :metadata, prefix: true, allow_nil: true
+ delegate :url, to: :runner_session, prefix: true, allow_nil: true
+ delegate :terminal_specification, to: :runner_session, allow_nil: true
delegate :gitlab_deploy_token, to: :project
##
@@ -60,6 +71,11 @@ module Ci
where('NOT EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').trace)
end
+ scope :with_test_reports, ->() do
+ includes(:job_artifacts_junit) # Prevent N+1 problem when iterating each ci_job_artifact row
+ .where('EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').test_reports)
+ end
+
scope :with_artifacts_stored_locally, -> { with_artifacts_archive.where(artifacts_file_store: [nil, LegacyArtifactUploader::Store::LOCAL]) }
scope :with_artifacts_not_expired, ->() { with_artifacts_archive.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts_archive.where('artifacts_expire_at < ?', Time.now) }
@@ -167,13 +183,13 @@ module Ci
end
end
- before_transition any => [:running] do |build|
- build.validates_dependencies! unless Feature.enabled?('ci_disable_validates_dependencies')
- end
-
after_transition pending: :running do |build|
build.ensure_metadata.update_timeout_state
end
+
+ after_transition running: any do |build|
+ Ci::BuildRunnerSession.where(build: build).delete_all
+ end
end
def ensure_metadata
@@ -361,7 +377,7 @@ module Ci
def update_coverage
coverage = trace.extract_coverage(coverage_regex)
- update_attributes(coverage: coverage) if coverage.present?
+ update(coverage: coverage) if coverage.present?
end
def parse_trace_sections!
@@ -376,6 +392,14 @@ module Ci
trace.exist?
end
+ def has_test_reports?
+ job_artifacts.test_reports.any?
+ end
+
+ def has_old_trace?
+ old_trace.present?
+ end
+
def trace=(data)
raise NotImplementedError
end
@@ -385,6 +409,8 @@ module Ci
end
def erase_old_trace!
+ return unless has_old_trace?
+
update_column(:trace, nil)
end
@@ -421,9 +447,9 @@ module Ci
end
def artifacts_metadata_entry(path, **options)
- artifacts_metadata.use_file do |metadata_path|
+ artifacts_metadata.open do |metadata_stream|
metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
- metadata_path,
+ metadata_stream,
path,
**options)
@@ -437,16 +463,22 @@ module Ci
save
end
+ def erase_test_reports!
+ # TODO: Use fast_destroy_all in the context of https://gitlab.com/gitlab-org/gitlab-ce/issues/35240
+ job_artifacts_junit&.destroy
+ end
+
def erase(opts = {})
return false unless erasable?
erase_artifacts!
+ erase_test_reports!
erase_trace!
update_erased!(opts[:erased_by])
end
def erasable?
- complete? && (artifacts? || has_trace?)
+ complete? && (artifacts? || has_test_reports? || has_trace?)
end
def erased?
@@ -523,10 +555,6 @@ module Ci
Gitlab::Ci::Build::Image.from_services(self)
end
- def artifacts
- [options[:artifacts]]
- end
-
def cache
cache = options[:cache]
@@ -558,10 +586,10 @@ module Ci
options[:dependencies]&.empty?
end
- def validates_dependencies!
- dependencies.each do |dependency|
- raise MissingDependenciesError unless dependency.valid_dependency?
- end
+ def has_valid_build_dependencies?
+ return true if Feature.enabled?('ci_disable_validates_dependencies')
+
+ dependencies.all?(&:valid_dependency?)
end
def valid_dependency?
@@ -571,6 +599,24 @@ module Ci
true
end
+ def runner_required_feature_names
+ strong_memoize(:runner_required_feature_names) do
+ RUNNER_FEATURES.select do |feature, method|
+ method.call(self)
+ end.keys
+ end
+ end
+
+ def supported_runner?(features)
+ runner_required_feature_names.all? do |feature_name|
+ features&.dig(feature_name)
+ end
+ end
+
+ def publishes_artifacts_reports?
+ options&.dig(:artifacts, :reports)&.any?
+ end
+
def hide_secrets(trace)
return unless trace
@@ -584,8 +630,28 @@ module Ci
super(options).merge(when: read_attribute(:when))
end
+ def has_terminal?
+ running? && runner_session_url.present?
+ end
+
+ def collect_test_reports!(test_reports)
+ test_reports.get_suite(group_name).tap do |test_suite|
+ each_test_report do |file_type, blob|
+ Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, test_suite)
+ end
+ end
+ end
+
private
+ def each_test_report
+ Ci::JobArtifact::TEST_REPORT_FILE_TYPES.each do |file_type|
+ public_send("job_artifacts_#{file_type}").each_blob do |blob| # rubocop:disable GitlabSecurity/PublicSend
+ yield file_type, blob
+ end
+ end
+ end
+
def update_artifacts_size
self.artifacts_size = legacy_artifacts_file&.size
end
@@ -633,6 +699,7 @@ module Ci
variables.append(key: 'CI_JOB_NAME', value: name)
variables.append(key: 'CI_JOB_STAGE', value: stage)
variables.append(key: 'CI_COMMIT_SHA', value: sha)
+ variables.append(key: 'CI_COMMIT_BEFORE_SHA', value: before_sha)
variables.append(key: 'CI_COMMIT_REF_NAME', value: ref)
variables.append(key: 'CI_COMMIT_REF_SLUG', value: ref_slug)
variables.append(key: "CI_COMMIT_TAG", value: ref) if tag?
diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb
index 96762f8845c..9d588b862bd 100644
--- a/app/models/ci/build_metadata.rb
+++ b/app/models/ci/build_metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
# The purpose of this class is to store Build related data that can be disposed.
# Data that should be persisted forever, should be stored with Ci::Build model.
diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb
new file mode 100644
index 00000000000..457d7eeab6a
--- /dev/null
+++ b/app/models/ci/build_runner_session.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Ci
+ # The purpose of this class is to store Build related runner session.
+ # Data will be removed after transitioning from running to any state.
+ class BuildRunnerSession < ActiveRecord::Base
+ extend Gitlab::Ci::Model
+
+ self.table_name = 'ci_builds_runner_session'
+
+ belongs_to :build, class_name: 'Ci::Build', inverse_of: :runner_session
+
+ validates :build, presence: true
+ validates :url, url: { protocols: %w(https) }
+
+ def terminal_specification
+ return {} unless url.present?
+
+ {
+ subprotocols: ['terminal.gitlab.com'].freeze,
+ url: "#{url}/exec".sub("https://", "wss://"),
+ headers: { Authorization: [authorization.presence] }.compact,
+ ca_pem: certificate.presence
+ }
+ end
+ end
+end
diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb
index 4856f10846c..108874b75a6 100644
--- a/app/models/ci/build_trace_chunk.rb
+++ b/app/models/ci/build_trace_chunk.rb
@@ -1,54 +1,60 @@
+# frozen_string_literal: true
+
module Ci
class BuildTraceChunk < ActiveRecord::Base
include FastDestroyAll
+ include ::Gitlab::ExclusiveLeaseHelpers
extend Gitlab::Ci::Model
belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id
default_value_for :data_store, :redis
- WriteError = Class.new(StandardError)
-
CHUNK_SIZE = 128.kilobytes
- CHUNK_REDIS_TTL = 1.week
WRITE_LOCK_RETRY = 10
WRITE_LOCK_SLEEP = 0.01.seconds
WRITE_LOCK_TTL = 1.minute
+ # Note: The ordering of this enum is related to the precedence of persist store.
+ # The bottom item takes the higest precedence, and the top item takes the lowest precedence.
enum data_store: {
redis: 1,
- db: 2
+ database: 2,
+ fog: 3
}
class << self
- def redis_data_key(build_id, chunk_index)
- "gitlab:ci:trace:#{build_id}:chunks:#{chunk_index}"
+ def all_stores
+ @all_stores ||= self.data_stores.keys
end
- def redis_data_keys
- redis.pluck(:build_id, :chunk_index).map do |data|
- redis_data_key(data.first, data.second)
- end
+ def persistable_store
+ # get first available store from the back of the list
+ all_stores.reverse.find { |store| get_store_class(store).available? }
end
- def redis_delete_data(keys)
- return if keys.empty?
-
- Gitlab::Redis::SharedState.with do |redis|
- redis.del(keys)
- end
+ def get_store_class(store)
+ @stores ||= {}
+ @stores[store] ||= "Ci::BuildTraceChunks::#{store.capitalize}".constantize.new
end
##
# FastDestroyAll concerns
def begin_fast_destroy
- redis_data_keys
+ all_stores.each_with_object({}) do |store, result|
+ relation = public_send(store) # rubocop:disable GitlabSecurity/PublicSend
+ keys = get_store_class(store).keys(relation)
+
+ result[store] = keys if keys.present?
+ end
end
##
# FastDestroyAll concerns
def finalize_fast_destroy(keys)
- redis_delete_data(keys)
+ keys.each do |store, value|
+ get_store_class(store).delete_keys(value)
+ end
end
end
@@ -66,10 +72,15 @@ module Ci
end
def append(new_data, offset)
+ raise ArgumentError, 'New data is missing' unless new_data
raise ArgumentError, 'Offset is out of range' if offset > size || offset < 0
raise ArgumentError, 'Chunk size overflow' if CHUNK_SIZE < (offset + new_data.bytesize)
- set_data(data.byteslice(0, offset) + new_data)
+ in_lock(*lock_params) do # Write opetation is atomic
+ unsafe_set_data!(data.byteslice(0, offset) + new_data)
+ end
+
+ schedule_to_persist if full?
end
def size
@@ -88,93 +99,63 @@ module Ci
(start_offset...end_offset)
end
- def use_database!
- in_lock do
- break if db?
- break unless size > 0
-
- self.update!(raw_data: data, data_store: :db)
- self.class.redis_delete_data([redis_data_key])
+ def persist_data!
+ in_lock(*lock_params) do # Write opetation is atomic
+ unsafe_persist_to!(self.class.persistable_store)
end
end
private
- def get_data
- if redis?
- redis_data
- elsif db?
- raw_data
- else
- raise 'Unsupported data store'
- end&.force_encoding(Encoding::BINARY) # Redis/Database return UTF-8 string as default
- end
-
- def set_data(value)
- raise ArgumentError, 'too much data' if value.bytesize > CHUNK_SIZE
-
- in_lock do
- if redis?
- redis_set_data(value)
- elsif db?
- self.raw_data = value
- else
- raise 'Unsupported data store'
- end
+ def unsafe_persist_to!(new_store)
+ return if data_store == new_store.to_s
+ raise ArgumentError, 'Can not persist empty data' unless size > 0
- @data = value
+ old_store_class = self.class.get_store_class(data_store)
- save! if changed?
+ get_data.tap do |the_data|
+ self.raw_data = nil
+ self.data_store = new_store
+ unsafe_set_data!(the_data)
end
- schedule_to_db if full?
+ old_store_class.delete_data(self)
end
- def schedule_to_db
- return if db?
-
- Ci::BuildTraceChunkFlushWorker.perform_async(id)
- end
-
- def full?
- size == CHUNK_SIZE
+ def get_data
+ self.class.get_store_class(data_store).data(self)&.force_encoding(Encoding::BINARY) # Redis/Database return UTF-8 string as default
+ rescue Excon::Error::NotFound
+ # If the data store is :fog and the file does not exist in the object storage, this method returns nil.
end
- def redis_data
- Gitlab::Redis::SharedState.with do |redis|
- redis.get(redis_data_key)
- end
- end
+ def unsafe_set_data!(value)
+ raise ArgumentError, 'New data size exceeds chunk size' if value.bytesize > CHUNK_SIZE
- def redis_set_data(data)
- Gitlab::Redis::SharedState.with do |redis|
- redis.set(redis_data_key, data, ex: CHUNK_REDIS_TTL)
- end
- end
+ self.class.get_store_class(data_store).set_data(self, value)
+ @data = value
- def redis_data_key
- self.class.redis_data_key(build_id, chunk_index)
+ save! if changed?
end
- def in_lock
- write_lock_key = "trace_write:#{build_id}:chunks:#{chunk_index}"
+ def schedule_to_persist
+ return if data_persisted?
- lease = Gitlab::ExclusiveLease.new(write_lock_key, timeout: WRITE_LOCK_TTL)
- retry_count = 0
+ Ci::BuildTraceChunkFlushWorker.perform_async(id)
+ end
- until uuid = lease.try_obtain
- # Keep trying until we obtain the lease. To prevent hammering Redis too
- # much we'll wait for a bit between retries.
- sleep(WRITE_LOCK_SLEEP)
- break if WRITE_LOCK_RETRY < (retry_count += 1)
- end
+ def data_persisted?
+ !redis?
+ end
- raise WriteError, 'Failed to obtain write lock' unless uuid
+ def full?
+ size == CHUNK_SIZE
+ end
- self.reload if self.persisted?
- return yield
- ensure
- Gitlab::ExclusiveLease.cancel(write_lock_key, uuid)
+ def lock_params
+ ["trace_write:#{build_id}:chunks:#{chunk_index}",
+ { ttl: WRITE_LOCK_TTL,
+ retries: WRITE_LOCK_RETRY,
+ sleep_sec: WRITE_LOCK_SLEEP }]
end
end
end
diff --git a/app/models/ci/build_trace_chunks/database.rb b/app/models/ci/build_trace_chunks/database.rb
new file mode 100644
index 00000000000..73cb8abf381
--- /dev/null
+++ b/app/models/ci/build_trace_chunks/database.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Ci
+ module BuildTraceChunks
+ class Database
+ def available?
+ true
+ end
+
+ def keys(relation)
+ []
+ end
+
+ def delete_keys(keys)
+ # no-op
+ end
+
+ def data(model)
+ model.raw_data
+ end
+
+ def set_data(model, data)
+ model.raw_data = data
+ end
+
+ def delete_data(model)
+ model.update_columns(raw_data: nil) unless model.raw_data.nil?
+ end
+ end
+ end
+end
diff --git a/app/models/ci/build_trace_chunks/fog.rb b/app/models/ci/build_trace_chunks/fog.rb
new file mode 100644
index 00000000000..a849bd08427
--- /dev/null
+++ b/app/models/ci/build_trace_chunks/fog.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module Ci
+ module BuildTraceChunks
+ class Fog
+ def available?
+ object_store.enabled
+ end
+
+ def data(model)
+ connection.get_object(bucket_name, key(model))[:body]
+ end
+
+ def set_data(model, data)
+ connection.put_object(bucket_name, key(model), data)
+ end
+
+ def delete_data(model)
+ delete_keys([[model.build_id, model.chunk_index]])
+ end
+
+ def keys(relation)
+ return [] unless available?
+
+ relation.pluck(:build_id, :chunk_index)
+ end
+
+ def delete_keys(keys)
+ keys.each do |key|
+ connection.delete_object(bucket_name, key_raw(*key))
+ end
+ end
+
+ private
+
+ def key(model)
+ key_raw(model.build_id, model.chunk_index)
+ end
+
+ def key_raw(build_id, chunk_index)
+ "tmp/builds/#{build_id.to_i}/chunks/#{chunk_index.to_i}.log"
+ end
+
+ def bucket_name
+ return unless available?
+
+ object_store.remote_directory
+ end
+
+ def connection
+ return unless available?
+
+ @connection ||= ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys)
+ end
+
+ def object_store
+ Gitlab.config.artifacts.object_store
+ end
+ end
+ end
+end
diff --git a/app/models/ci/build_trace_chunks/redis.rb b/app/models/ci/build_trace_chunks/redis.rb
new file mode 100644
index 00000000000..813eaf5d839
--- /dev/null
+++ b/app/models/ci/build_trace_chunks/redis.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Ci
+ module BuildTraceChunks
+ class Redis
+ CHUNK_REDIS_TTL = 1.week
+
+ def available?
+ true
+ end
+
+ def data(model)
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.get(key(model))
+ end
+ end
+
+ def set_data(model, data)
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set(key(model), data, ex: CHUNK_REDIS_TTL)
+ end
+ end
+
+ def delete_data(model)
+ delete_keys([[model.build_id, model.chunk_index]])
+ end
+
+ def keys(relation)
+ relation.pluck(:build_id, :chunk_index)
+ end
+
+ def delete_keys(keys)
+ return if keys.empty?
+
+ keys = keys.map { |key| key_raw(*key) }
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.del(keys)
+ end
+ end
+
+ private
+
+ def key(model)
+ key_raw(model.build_id, model.chunk_index)
+ end
+
+ def key_raw(build_id, chunk_index)
+ "gitlab:ci:trace:#{build_id.to_i}:chunks:#{chunk_index.to_i}"
+ end
+ end
+ end
+end
diff --git a/app/models/ci/build_trace_section.rb b/app/models/ci/build_trace_section.rb
index ccdb95546c8..a4bee59c83b 100644
--- a/app/models/ci/build_trace_section.rb
+++ b/app/models/ci/build_trace_section.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class BuildTraceSection < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/build_trace_section_name.rb b/app/models/ci/build_trace_section_name.rb
index 0fdcb1ea329..cbdf3c4b673 100644
--- a/app/models/ci/build_trace_section_name.rb
+++ b/app/models/ci/build_trace_section_name.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class BuildTraceSectionName < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/group.rb b/app/models/ci/group.rb
index 9c1046e8715..9b2c3c807ac 100644
--- a/app/models/ci/group.rb
+++ b/app/models/ci/group.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
##
# This domain model is a representation of a group of jobs that are related
diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb
index 44cb583e1bd..492d1d0329e 100644
--- a/app/models/ci/group_variable.rb
+++ b/app/models/ci/group_variable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class GroupVariable < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 3b952391b7e..d7c5f29be96 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -1,14 +1,24 @@
+# frozen_string_literal: true
+
module Ci
class JobArtifact < ActiveRecord::Base
include AfterCommitQueue
include ObjectStorage::BackgroundMove
extend Gitlab::Ci::Model
+ NotSupportedAdapterError = Class.new(StandardError)
+
+ TEST_REPORT_FILE_TYPES = %w[junit].freeze
+ DEFAULT_FILE_NAMES = { junit: 'junit.xml' }.freeze
+ TYPE_AND_FORMAT_PAIRS = { archive: :zip, metadata: :gzip, trace: :raw, junit: :gzip }.freeze
+
belongs_to :project
belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id
mount_uploader :file, JobArtifactUploader
+ validates :file_format, presence: true, unless: :trace?, on: :create
+ validate :valid_file_format?, unless: :trace?, on: :create
before_save :set_size, if: :file_changed?
after_save :update_project_statistics_after_save, if: :size_changed?
after_destroy :update_project_statistics_after_destroy, unless: :project_destroyed?
@@ -17,14 +27,37 @@ module Ci
scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
+ scope :test_reports, -> do
+ types = self.file_types.select { |file_type| TEST_REPORT_FILE_TYPES.include?(file_type) }.values
+
+ where(file_type: types)
+ end
+
delegate :exists?, :open, to: :file
enum file_type: {
archive: 1,
metadata: 2,
- trace: 3
+ trace: 3,
+ junit: 4
+ }
+
+ enum file_format: {
+ raw: 1,
+ zip: 2,
+ gzip: 3
}
+ FILE_FORMAT_ADAPTERS = {
+ gzip: Gitlab::Ci::Build::Artifacts::GzipFileAdapter
+ }.freeze
+
+ def valid_file_format?
+ unless TYPE_AND_FORMAT_PAIRS[self.file_type&.to_sym] == self.file_format&.to_sym
+ errors.add(:file_format, 'Invalid file format with specified file type')
+ end
+ end
+
def update_file_store
# The file.object_store is set during `uploader.store!`
# which happens after object is inserted/updated
@@ -50,8 +83,22 @@ module Ci
end
end
+ def each_blob(&blk)
+ unless file_format_adapter_class
+ raise NotSupportedAdapterError, 'This file format requires a dedicated adapter'
+ end
+
+ file.open do |stream|
+ file_format_adapter_class.new(stream).each_blob(&blk)
+ end
+ end
+
private
+ def file_format_adapter_class
+ FILE_FORMAT_ADAPTERS[file_format.to_sym]
+ end
+
def set_size
self.size = file.size
end
diff --git a/app/models/ci/legacy_stage.rb b/app/models/ci/legacy_stage.rb
index ce691875e42..96dbc7b6895 100644
--- a/app/models/ci/legacy_stage.rb
+++ b/app/models/ci/legacy_stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
# Currently this is artificial object, constructed dynamically
# We should migrate this object to actual database record in the future
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index e5caa3ffa41..e4aed76f611 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class Pipeline < ActiveRecord::Base
extend Gitlab::Ci::Model
@@ -603,6 +605,18 @@ module Ci
@latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a
end
+ def has_test_reports?
+ complete? && builds.latest.with_test_reports.any?
+ end
+
+ def test_reports
+ Gitlab::Ci::Reports::TestReports.new.tap do |test_reports|
+ builds.latest.with_test_reports.each do |build|
+ build.collect_test_reports!(test_reports)
+ end
+ end
+ end
+
private
def ci_yaml_from_repo
diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb
index b6abc3d7681..1c1f203bdb2 100644
--- a/app/models/ci/pipeline_schedule.rb
+++ b/app/models/ci/pipeline_schedule.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PipelineSchedule < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/pipeline_schedule_variable.rb b/app/models/ci/pipeline_schedule_variable.rb
index 03df4e3e638..fbb9987cab2 100644
--- a/app/models/ci/pipeline_schedule_variable.rb
+++ b/app/models/ci/pipeline_schedule_variable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PipelineScheduleVariable < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb
index 38e14ffbc0c..017ec0b145a 100644
--- a/app/models/ci/pipeline_variable.rb
+++ b/app/models/ci/pipeline_variable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PipelineVariable < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 8c9aacca8de..f41955f43e7 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
module Ci
class Runner < ActiveRecord::Base
extend Gitlab::Ci::Model
include Gitlab::SQL::Pattern
+ include IgnorableColumn
include RedisCacheable
include ChronicDurationAttribute
@@ -11,6 +14,8 @@ module Ci
AVAILABLE_SCOPES = %w[specific shared active paused online].freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze
+ ignore_column :is_shared
+
has_many :builds
has_many :runner_projects, inverse_of: :runner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :projects, through: :runner_projects
@@ -21,13 +26,16 @@ module Ci
before_validation :set_default_values
- scope :specific, -> { where(is_shared: false) }
- scope :shared, -> { where(is_shared: true) }
scope :active, -> { where(active: true) }
scope :paused, -> { where(active: false) }
scope :online, -> { where('contacted_at > ?', contact_time_deadline) }
scope :ordered, -> { order(id: :desc) }
+ # BACKWARD COMPATIBILITY: There are needed to maintain compatibility with `AVAILABLE_SCOPES` used by `lib/api/runners.rb`
+ scope :deprecated_shared, -> { instance_type }
+ # this should get replaced with `project_type.or(group_type)` once using Rails5
+ scope :deprecated_specific, -> { where(runner_type: [runner_types[:project_type], runner_types[:group_type]]) }
+
scope :belonging_to_project, -> (project_id) {
joins(:runner_projects).where(ci_runner_projects: { project_id: project_id })
}
@@ -39,9 +47,9 @@ module Ci
joins(:groups).where(namespaces: { id: hierarchy_groups })
}
- scope :owned_or_shared, -> (project_id) do
+ scope :owned_or_instance_wide, -> (project_id) do
union = Gitlab::SQL::Union.new(
- [belonging_to_project(project_id), belonging_to_parent_group_of_project(project_id), shared],
+ [belonging_to_project(project_id), belonging_to_parent_group_of_project(project_id), instance_type],
remove_duplicates: false
)
from("(#{union.to_sql}) ci_runners")
@@ -63,7 +71,6 @@ module Ci
validate :no_groups, unless: :group_type?
validate :any_project, if: :project_type?
validate :exactly_one_group, if: :group_type?
- validate :validate_is_shared
acts_as_taggable
@@ -113,8 +120,7 @@ module Ci
end
def assign_to(project, current_user = nil)
- if shared?
- self.is_shared = false if shared?
+ if instance_type?
self.runner_type = :project_type
elsif group_type?
raise ArgumentError, 'Transitioning a group runner to a project runner is not supported'
@@ -137,10 +143,6 @@ module Ci
description
end
- def shared?
- is_shared
- end
-
def online?
contacted_at && contacted_at > self.class.contact_time_deadline
end
@@ -159,10 +161,6 @@ module Ci
runner_projects.count == 1
end
- def specific?
- !shared?
- end
-
def assigned_to_group?
runner_namespaces.any?
end
@@ -260,7 +258,7 @@ module Ci
end
def assignable_for?(project_id)
- self.class.owned_or_shared(project_id).where(id: self.id).any?
+ self.class.owned_or_instance_wide(project_id).where(id: self.id).any?
end
def no_projects
@@ -287,12 +285,6 @@ module Ci
end
end
- def validate_is_shared
- unless is_shared? == instance_type?
- errors.add(:is_shared, 'is not equal to instance_type?')
- end
- end
-
def accepting_tags?(build)
(run_untagged? || build.has_tags?) && (build.tag_list - tag_list).empty?
end
diff --git a/app/models/ci/runner_namespace.rb b/app/models/ci/runner_namespace.rb
index 29508fdd326..22b80b98551 100644
--- a/app/models/ci/runner_namespace.rb
+++ b/app/models/ci/runner_namespace.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class RunnerNamespace < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb
index 52437047300..1a718d24141 100644
--- a/app/models/ci/runner_project.rb
+++ b/app/models/ci/runner_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class RunnerProject < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index ea07f37e6c1..511ded55dc3 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class Stage < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index aa065e33739..55db42162ca 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class Trigger < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index 215b1cf6753..913936a0bcb 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class TriggerRequest < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 452cb910bca..524d79014f8 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class Variable < ActiveRecord::Base
extend Gitlab::Ci::Model
diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb
index 58de3448577..55bbf7cae7e 100644
--- a/app/models/clusters/applications/helm.rb
+++ b/app/models/clusters/applications/helm.rb
@@ -1,13 +1,28 @@
+# frozen_string_literal: true
+
+require 'openssl'
+
module Clusters
module Applications
class Helm < ActiveRecord::Base
self.table_name = 'clusters_applications_helm'
+ attr_encrypted :ca_key,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_truncated,
+ algorithm: 'aes-256-cbc'
+
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
default_value_for :version, Gitlab::Kubernetes::Helm::HELM_VERSION
+ before_create :create_keys_and_certs
+
+ def issue_client_cert
+ ca_cert_obj.issue
+ end
+
def set_initial_status
return unless not_installable?
@@ -15,7 +30,41 @@ module Clusters
end
def install_command
- Gitlab::Kubernetes::Helm::InitCommand.new(name)
+ Gitlab::Kubernetes::Helm::InitCommand.new(
+ name: name,
+ files: files
+ )
+ end
+
+ def has_ssl?
+ ca_key.present? && ca_cert.present?
+ end
+
+ private
+
+ def files
+ {
+ 'ca.pem': ca_cert,
+ 'cert.pem': tiller_cert.cert_string,
+ 'key.pem': tiller_cert.key_string
+ }
+ end
+
+ def create_keys_and_certs
+ ca_cert = Gitlab::Kubernetes::Helm::Certificate.generate_root
+ self.ca_key = ca_cert.key_string
+ self.ca_cert = ca_cert.cert_string
+ end
+
+ def tiller_cert
+ @tiller_cert ||= ca_cert_obj.issue(expires_in: Gitlab::Kubernetes::Helm::Certificate::INFINITE_EXPIRY)
+ end
+
+ def ca_cert_obj
+ return unless has_ssl?
+
+ Gitlab::Kubernetes::Helm::Certificate
+ .from_strings(ca_key, ca_cert)
end
end
end
diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb
index 27fc3b85465..93f654e0638 100644
--- a/app/models/clusters/applications/ingress.rb
+++ b/app/models/clusters/applications/ingress.rb
@@ -1,15 +1,20 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class Ingress < ActiveRecord::Base
+ VERSION = '0.23.0'.freeze
+
self.table_name = 'clusters_applications_ingress'
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
+ include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData
include AfterCommitQueue
default_value_for :ingress_type, :nginx
- default_value_for :version, :nginx
+ default_value_for :version, VERSION
enum ingress_type: {
nginx: 1
@@ -32,9 +37,10 @@ module Clusters
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
- name,
+ name: name,
+ version: VERSION,
chart: chart,
- values: values
+ files: files
)
end
diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb
index 975d434e1a4..ef1c76c03bd 100644
--- a/app/models/clusters/applications/jupyter.rb
+++ b/app/models/clusters/applications/jupyter.rb
@@ -1,12 +1,15 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class Jupyter < ActiveRecord::Base
- VERSION = '0.0.1'.freeze
+ VERSION = 'v0.6'.freeze
self.table_name = 'clusters_applications_jupyter'
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
+ include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData
belongs_to :oauth_application, class_name: 'Doorkeeper::Application'
@@ -35,9 +38,10 @@ module Clusters
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
- name,
+ name: name,
+ version: VERSION,
chart: chart,
- values: values,
+ files: files,
repository: repository
)
end
diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb
index 48137c2ed68..88399dbbb95 100644
--- a/app/models/clusters/applications/prometheus.rb
+++ b/app/models/clusters/applications/prometheus.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class Prometheus < ActiveRecord::Base
@@ -9,6 +11,7 @@ module Clusters
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
+ include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData
default_value_for :version, VERSION
@@ -21,6 +24,14 @@ module Clusters
end
end
+ def ready_status
+ [:installed]
+ end
+
+ def ready?
+ ready_status.include?(status_name)
+ end
+
def chart
'stable/prometheus'
end
@@ -35,10 +46,10 @@ module Clusters
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
- name,
+ name: name,
+ version: VERSION,
chart: chart,
- version: version,
- values: values
+ files: files
)
end
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index e6f795f3e0b..bde255723c8 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -1,12 +1,15 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class Runner < ActiveRecord::Base
- VERSION = '0.1.13'.freeze
+ VERSION = '0.1.31'.freeze
self.table_name = 'clusters_applications_runners'
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
+ include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData
belongs_to :runner, class_name: 'Ci::Runner', foreign_key: :runner_id
@@ -28,9 +31,10 @@ module Clusters
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
- name,
+ name: name,
+ version: VERSION,
chart: chart,
- values: values,
+ files: files,
repository: repository
)
end
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index b426b1bf8a1..7cf75403ab6 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
class Cluster < ActiveRecord::Base
include Presentable
diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb
index 623b836c0ed..e3deedfb036 100644
--- a/app/models/clusters/concerns/application_core.rb
+++ b/app/models/clusters/concerns/application_core.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Concerns
module ApplicationCore
diff --git a/app/models/clusters/concerns/application_data.rb b/app/models/clusters/concerns/application_data.rb
index 96ac757e99e..52498f123ff 100644
--- a/app/models/clusters/concerns/application_data.rb
+++ b/app/models/clusters/concerns/application_data.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Concerns
module ApplicationData
@@ -12,8 +14,34 @@ module Clusters
File.read(chart_values_file)
end
+ def files
+ @files ||= begin
+ files = { 'values.yaml': values }
+
+ files.merge!(certificate_files) if cluster.application_helm.has_ssl?
+
+ files
+ end
+ end
+
private
+ def certificate_files
+ {
+ 'ca.pem': ca_cert,
+ 'cert.pem': helm_cert.cert_string,
+ 'key.pem': helm_cert.key_string
+ }
+ end
+
+ def ca_cert
+ cluster.application_helm.ca_cert
+ end
+
+ def helm_cert
+ @helm_cert ||= cluster.application_helm.issue_client_cert
+ end
+
def chart_values_file
"#{Rails.root}/vendor/#{name}/values.yaml"
end
diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb
index 8f3eb75bfa9..d4d3859dfd5 100644
--- a/app/models/clusters/concerns/application_status.rb
+++ b/app/models/clusters/concerns/application_status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Concerns
module ApplicationStatus
diff --git a/app/models/clusters/concerns/application_version.rb b/app/models/clusters/concerns/application_version.rb
new file mode 100644
index 00000000000..ccad74dc35a
--- /dev/null
+++ b/app/models/clusters/concerns/application_version.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Concerns
+ module ApplicationVersion
+ extend ActiveSupport::Concern
+
+ included do
+ state_machine :status do
+ after_transition any => [:installing] do |application|
+ application.update(version: application.class.const_get(:VERSION))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index 36631d57ad1..e6ddca0d5d0 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Platforms
class Kubernetes < ActiveRecord::Base
diff --git a/app/models/clusters/project.rb b/app/models/clusters/project.rb
index eeb734b20b8..839ce796081 100644
--- a/app/models/clusters/project.rb
+++ b/app/models/clusters/project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
class Project < ActiveRecord::Base
self.table_name = 'cluster_projects'
diff --git a/app/models/clusters/providers/gcp.rb b/app/models/clusters/providers/gcp.rb
index 4db1bb35c12..16b59cd9d14 100644
--- a/app/models/clusters/providers/gcp.rb
+++ b/app/models/clusters/providers/gcp.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Providers
class Gcp < ActiveRecord::Base
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 56d4c86774e..8b9f4490ffa 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -1,4 +1,6 @@
# coding: utf-8
+# frozen_string_literal: true
+
class Commit
extend ActiveModel::Naming
extend Gitlab::Cache::RequestCache
@@ -339,21 +341,21 @@ class Commit
end
def cherry_pick_description(user)
- message_body = "(cherry picked from commit #{sha})"
+ message_body = ["(cherry picked from commit #{sha})"]
if merged_merge_request?(user)
commits_in_merge_request = merged_merge_request(user).commits
if commits_in_merge_request.present?
- message_body << "\n"
+ message_body << ""
commits_in_merge_request.reverse.each do |commit_in_merge|
- message_body << "\n#{commit_in_merge.short_id} #{commit_in_merge.title}"
+ message_body << "#{commit_in_merge.short_id} #{commit_in_merge.title}"
end
end
end
- message_body
+ message_body.join("\n")
end
def cherry_pick_message(user)
diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb
index b93c111dabc..094747ee48d 100644
--- a/app/models/commit_range.rb
+++ b/app/models/commit_range.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# CommitRange makes it easier to work with commit ranges
#
# Examples:
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 97516079b66..b65d7672973 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CommitStatus < ActiveRecord::Base
include HasStatus
include Importable
@@ -46,7 +48,8 @@ class CommitStatus < ActiveRecord::Base
api_failure: 2,
stuck_or_timeout_failure: 3,
runner_system_failure: 4,
- missing_dependency_failure: 5
+ missing_dependency_failure: 5,
+ runner_unsupported: 6
}
##
diff --git a/app/models/compare.rb b/app/models/compare.rb
index feb4b89c781..b2d46ada831 100644
--- a/app/models/compare.rb
+++ b/app/models/compare.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Compare
include Gitlab::Utils::StrongMemoize
diff --git a/app/models/concerns/access_requestable.rb b/app/models/concerns/access_requestable.rb
index d502e7e54c6..31ee3a4da73 100644
--- a/app/models/concerns/access_requestable.rb
+++ b/app/models/concerns/access_requestable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == AccessRequestable concern
#
# Contains functionality related to objects that can receive request for access.
diff --git a/app/models/concerns/artifact_migratable.rb b/app/models/concerns/artifact_migratable.rb
index ff52ca64459..cbd63ba8876 100644
--- a/app/models/concerns/artifact_migratable.rb
+++ b/app/models/concerns/artifact_migratable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Adapter class to unify the interface between mounted uploaders and the
# Ci::Artifact model
# Meant to be prepended so the interface can stay the same
diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb
index 164c704260e..7f6d48d972c 100644
--- a/app/models/concerns/atomic_internal_id.rb
+++ b/app/models/concerns/atomic_internal_id.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Include atomic internal id generation scheme for a model
#
# This allows us to atomically generate internal ids that are
@@ -26,21 +28,30 @@ module AtomicInternalId
module ClassMethods
def has_internal_id(column, scope:, init:, presence: true) # rubocop:disable Naming/PredicateName
+ # We require init here to retain the ability to recalculate in the absence of a
+ # InternaLId record (we may delete records in `internal_ids` for example).
+ raise "has_internal_id requires a init block, none given." unless init
+
before_validation :"ensure_#{scope}_#{column}!", on: :create
validates column, presence: presence
define_method("ensure_#{scope}_#{column}!") do
scope_value = association(scope).reader
+ value = read_attribute(column)
+
+ return value unless scope_value
- if read_attribute(column).blank? && scope_value
- scope_attrs = { scope_value.class.table_name.singularize.to_sym => scope_value }
- usage = self.class.table_name.to_sym
+ scope_attrs = { scope_value.class.table_name.singularize.to_sym => scope_value }
+ usage = self.class.table_name.to_sym
- new_iid = InternalId.generate_next(self, scope_attrs, usage, init)
- write_attribute(column, new_iid)
+ if value.present?
+ InternalId.track_greatest(self, scope_attrs, usage, value, init)
+ else
+ value = InternalId.generate_next(self, scope_attrs, usage, init)
+ write_attribute(column, value)
end
- read_attribute(column)
+ value
end
end
end
diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb
index 095897b08e3..c0233661a9b 100644
--- a/app/models/concerns/avatarable.rb
+++ b/app/models/concerns/avatarable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Avatarable
extend ActiveSupport::Concern
@@ -19,7 +21,7 @@ module Avatarable
# We use avatar_path instead of overriding avatar_url because of carrierwave.
# See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11001/diffs#note_28659864
- avatar_path(only_path: args.fetch(:only_path, true)) || super
+ avatar_path(only_path: args.fetch(:only_path, true), size: args[:size]) || super
end
def retrieve_upload(identifier, paths)
@@ -40,12 +42,13 @@ module Avatarable
end
end
- def avatar_path(only_path: true)
+ def avatar_path(only_path: true, size: nil)
return unless self[:avatar].present?
asset_host = ActionController::Base.asset_host
use_asset_host = asset_host.present?
use_authentication = respond_to?(:public?) && !public?
+ query_params = size&.nonzero? ? "?width=#{size}" : ""
# Avatars for private and internal groups and projects require authentication to be viewed,
# which means they can only be served by Rails, on the regular GitLab host.
@@ -56,7 +59,8 @@ module Avatarable
only_path = false
end
- url_base = ""
+ url_base = []
+
if use_asset_host
url_base << asset_host unless only_path
else
@@ -64,7 +68,7 @@ module Avatarable
url_base << gitlab_config.relative_url_root
end
- url_base + avatar.local_url
+ url_base.join + avatar.local_url + query_params
end
# Path that is persisted in the tracking Upload model. Used to fetch the
diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb
index fce37e7f78e..dd07f389fa5 100644
--- a/app/models/concerns/awardable.rb
+++ b/app/models/concerns/awardable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Awardable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/batch_destroy_dependent_associations.rb b/app/models/concerns/batch_destroy_dependent_associations.rb
index 353ee2e73d0..45fbc88fbba 100644
--- a/app/models/concerns/batch_destroy_dependent_associations.rb
+++ b/app/models/concerns/batch_destroy_dependent_associations.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Provides a way to work around Rails issue where dependent objects are all
# loaded into memory before destroyed: https://github.com/rails/rails/issues/22510.
#
diff --git a/app/models/concerns/blob_like.rb b/app/models/concerns/blob_like.rb
index adb81561000..e96fefe81c4 100644
--- a/app/models/concerns/blob_like.rb
+++ b/app/models/concerns/blob_like.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BlobLike
extend ActiveSupport::Concern
include Linguist::BlobHelper
diff --git a/app/models/concerns/blocks_json_serialization.rb b/app/models/concerns/blocks_json_serialization.rb
index 8019e6adc1c..d346da1ba4b 100644
--- a/app/models/concerns/blocks_json_serialization.rb
+++ b/app/models/concerns/blocks_json_serialization.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Overrides `as_json` and `to_json` to raise an exception when called in order
# to prevent accidentally exposing attributes
#
diff --git a/app/models/concerns/bulk_member_access_load.rb b/app/models/concerns/bulk_member_access_load.rb
index 984c4f53bf7..c4346d5dd17 100644
--- a/app/models/concerns/bulk_member_access_load.rb
+++ b/app/models/concerns/bulk_member_access_load.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Returns and caches in thread max member access for a resource
#
module BulkMemberAccessLoad
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 9f6358cecbe..6e2adc76ec6 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This module takes care of updating cache columns for Markdown-containing
# fields. Use like this in the body of your class:
#
@@ -40,6 +42,18 @@ module CacheMarkdownField
end
end
+ class MarkdownEngine
+ def self.from_version(version = nil)
+ return :common_mark if version.nil? || version == 0
+
+ if version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
+ :redcarpet
+ else
+ :common_mark
+ end
+ end
+ end
+
def skip_project_check?
false
end
@@ -57,7 +71,7 @@ module CacheMarkdownField
# Banzai is less strict about authors, so don't always have an author key
context[:author] = self.author if self.respond_to?(:author)
- context[:markdown_engine] = markdown_engine
+ context[:markdown_engine] = MarkdownEngine.from_version(latest_cached_markdown_version)
context
end
@@ -123,14 +137,6 @@ module CacheMarkdownField
end
end
- def markdown_engine
- if latest_cached_markdown_version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
- :redcarpet
- else
- :common_mark
- end
- end
-
included do
cattr_reader :cached_markdown_fields do
FieldData.new
diff --git a/app/models/concerns/cacheable_attributes.rb b/app/models/concerns/cacheable_attributes.rb
index d58d7165969..62b78c3611c 100644
--- a/app/models/concerns/cacheable_attributes.rb
+++ b/app/models/concerns/cacheable_attributes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CacheableAttributes
extend ActiveSupport::Concern
@@ -7,7 +9,7 @@ module CacheableAttributes
class_methods do
def cache_key
- "#{name}:#{Gitlab::VERSION}:#{Gitlab.migrations_hash}:#{Rails.version}".freeze
+ "#{name}:#{Gitlab::VERSION}:#{Rails.version}".freeze
end
# Can be overriden
@@ -69,6 +71,6 @@ module CacheableAttributes
end
def cache!
- Rails.cache.write(self.class.cache_key, self)
+ Rails.cache.write(self.class.cache_key, self, expires_in: 1.minute)
end
end
diff --git a/app/models/concerns/case_sensitivity.rb b/app/models/concerns/case_sensitivity.rb
index 034e9f40ff0..0ba542b75ab 100644
--- a/app/models/concerns/case_sensitivity.rb
+++ b/app/models/concerns/case_sensitivity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Concern for querying columns with specific case sensitivity handling.
module CaseSensitivity
extend ActiveSupport::Concern
diff --git a/app/models/concerns/chronic_duration_attribute.rb b/app/models/concerns/chronic_duration_attribute.rb
index 593a9b3d71d..edf6ac96730 100644
--- a/app/models/concerns/chronic_duration_attribute.rb
+++ b/app/models/concerns/chronic_duration_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChronicDurationAttribute
extend ActiveSupport::Concern
diff --git a/app/models/concerns/created_at_filterable.rb b/app/models/concerns/created_at_filterable.rb
index e8a3e41203d..a1f46478b6f 100644
--- a/app/models/concerns/created_at_filterable.rb
+++ b/app/models/concerns/created_at_filterable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CreatedAtFilterable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index 52851b3d0b2..91052013592 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeploymentPlatform
# EE would override this and utilize environment argument
# rubocop:disable Gitlab/ModuleWithInstanceVariables
diff --git a/app/models/concerns/diff_file.rb b/app/models/concerns/diff_file.rb
index 72332072012..47ea14163dc 100644
--- a/app/models/concerns/diff_file.rb
+++ b/app/models/concerns/diff_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffFile
extend ActiveSupport::Concern
diff --git a/app/models/concerns/discussion_on_diff.rb b/app/models/concerns/discussion_on_diff.rb
index 8b3c55387b3..c180d7b7c9a 100644
--- a/app/models/concerns/discussion_on_diff.rb
+++ b/app/models/concerns/discussion_on_diff.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Contains functionality shared between `DiffDiscussion` and `LegacyDiffDiscussion`.
module DiscussionOnDiff
extend ActiveSupport::Concern
diff --git a/app/models/concerns/each_batch.rb b/app/models/concerns/each_batch.rb
index 6ddbb8da1a9..a9e14cb55eb 100644
--- a/app/models/concerns/each_batch.rb
+++ b/app/models/concerns/each_batch.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EachBatch
extend ActiveSupport::Concern
diff --git a/app/models/concerns/editable.rb b/app/models/concerns/editable.rb
index c0a3099f676..2e49e720ac9 100644
--- a/app/models/concerns/editable.rb
+++ b/app/models/concerns/editable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Editable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/enum_with_nil.rb b/app/models/concerns/enum_with_nil.rb
index 6b37903da20..23acfe9a55f 100644
--- a/app/models/concerns/enum_with_nil.rb
+++ b/app/models/concerns/enum_with_nil.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EnumWithNil
extend ActiveSupport::Concern
diff --git a/app/models/concerns/expirable.rb b/app/models/concerns/expirable.rb
index b66ba08dc59..1f274487935 100644
--- a/app/models/concerns/expirable.rb
+++ b/app/models/concerns/expirable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Expirable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/fast_destroy_all.rb b/app/models/concerns/fast_destroy_all.rb
index 7ea042c6742..65ed46ea202 100644
--- a/app/models/concerns/fast_destroy_all.rb
+++ b/app/models/concerns/fast_destroy_all.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# This module is for replacing `dependent: :destroy` and `before_destroy` hooks.
#
diff --git a/app/models/concerns/faster_cache_keys.rb b/app/models/concerns/faster_cache_keys.rb
index 5b14723fa2d..312a9aa9305 100644
--- a/app/models/concerns/faster_cache_keys.rb
+++ b/app/models/concerns/faster_cache_keys.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module FasterCacheKeys
# A faster version of Rails' "cache_key" method.
#
diff --git a/app/models/concerns/feature_gate.rb b/app/models/concerns/feature_gate.rb
index 5db64fe82c4..3f84de54ad5 100644
--- a/app/models/concerns/feature_gate.rb
+++ b/app/models/concerns/feature_gate.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module FeatureGate
def flipper_id
return nil if new_record?
diff --git a/app/models/concerns/ghost_user.rb b/app/models/concerns/ghost_user.rb
index da696127a80..15278c431fb 100644
--- a/app/models/concerns/ghost_user.rb
+++ b/app/models/concerns/ghost_user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GhostUser
extend ActiveSupport::Concern
diff --git a/app/models/concerns/group_descendant.rb b/app/models/concerns/group_descendant.rb
index 261ace57a17..05cd4265133 100644
--- a/app/models/concerns/group_descendant.rb
+++ b/app/models/concerns/group_descendant.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GroupDescendant
# Returns the hierarchy of a project or group in the from of a hash upto a
# given top.
@@ -44,8 +46,8 @@ module GroupDescendant
This error is not user facing, but causes a +1 query.
MSG
extras = {
- parent: parent,
- child: child,
+ parent: parent.inspect,
+ child: child.inspect,
preloaded: preloaded.map(&:full_path)
}
issue_url = 'https://gitlab.com/gitlab-org/gitlab-ce/issues/40785'
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index 72c236a0fc7..b3960cbad1a 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module HasStatus
extend ActiveSupport::Concern
diff --git a/app/models/concerns/has_variable.rb b/app/models/concerns/has_variable.rb
index c8e20c0ab81..dfbe413a878 100644
--- a/app/models/concerns/has_variable.rb
+++ b/app/models/concerns/has_variable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module HasVariable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/ignorable_column.rb b/app/models/concerns/ignorable_column.rb
index 03793e8bcbb..2b074c1921c 100644
--- a/app/models/concerns/ignorable_column.rb
+++ b/app/models/concerns/ignorable_column.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Module that can be included into a model to make it easier to ignore database
# columns.
#
diff --git a/app/models/concerns/iid_routes.rb b/app/models/concerns/iid_routes.rb
index 246748cf52c..b7f99e845ca 100644
--- a/app/models/concerns/iid_routes.rb
+++ b/app/models/concerns/iid_routes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module IidRoutes
##
# This automagically enforces all related routes to use `iid` instead of `id`
diff --git a/app/models/concerns/importable.rb b/app/models/concerns/importable.rb
index c9331eaf4cc..4d2707b08ab 100644
--- a/app/models/concerns/importable.rb
+++ b/app/models/concerns/importable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Importable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index b93c1145f82..e8072145551 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == Issuable concern
#
# Contains common functionality shared between Issues and MergeRequests
@@ -154,7 +156,7 @@ module Issuable
end
# Break ties with the ID column for pagination
- sorted.order(id: :desc)
+ sorted.with_order_id_desc
end
def order_due_date_and_labels_priority(excluded_labels: [])
@@ -243,6 +245,12 @@ module Issuable
opened?
end
+ def overdue?
+ return false unless respond_to?(:due_date)
+
+ due_date.try(:past?) || false
+ end
+
def user_notes_count
if notes.loaded?
# Use the in-memory association to select and count to avoid hitting the db
diff --git a/app/models/concerns/label_eventable.rb b/app/models/concerns/label_eventable.rb
new file mode 100644
index 00000000000..d22d93448e4
--- /dev/null
+++ b/app/models/concerns/label_eventable.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# == LabelEventable concern
+#
+# Contains functionality related to objects that support adding/removing labels.
+#
+# This concern is not used yet, it will be used for:
+# https://gitlab.com/gitlab-org/gitlab-ce/issues/48483
+
+module LabelEventable
+ extend ActiveSupport::Concern
+
+ included do
+ has_many :resource_label_events
+ end
+end
diff --git a/app/models/concerns/loaded_in_group_list.rb b/app/models/concerns/loaded_in_group_list.rb
index 935e9d10133..a2233eb2997 100644
--- a/app/models/concerns/loaded_in_group_list.rb
+++ b/app/models/concerns/loaded_in_group_list.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module LoadedInGroupList
extend ActiveSupport::Concern
diff --git a/app/models/concerns/manual_inverse_association.rb b/app/models/concerns/manual_inverse_association.rb
index 0fca8feaf89..d0d781dc15f 100644
--- a/app/models/concerns/manual_inverse_association.rb
+++ b/app/models/concerns/manual_inverse_association.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ManualInverseAssociation
extend ActiveSupport::Concern
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index c013e5a708f..7e7eccb1c27 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == Mentionable concern
#
# Contains functionality related to objects that can mention Users, Issues, MergeRequests, Commits or Snippets by
diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb
index 2d86a70c395..f6fd28bac33 100644
--- a/app/models/concerns/mentionable/reference_regexes.rb
+++ b/app/models/concerns/mentionable/reference_regexes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mentionable
module ReferenceRegexes
def self.reference_pattern(link_patterns, issue_pattern)
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index 967fd9c5eea..e44a069b730 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestoneish
def closed_items_count(user)
memoize_per_user(user, :closed_items_count) do
diff --git a/app/models/concerns/note_on_diff.rb b/app/models/concerns/note_on_diff.rb
index 510b8868462..aad19329be1 100644
--- a/app/models/concerns/note_on_diff.rb
+++ b/app/models/concerns/note_on_diff.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Contains functionality shared between `DiffNote` and `LegacyDiffNote`.
module NoteOnDiff
extend ActiveSupport::Concern
diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb
index 86f28f30032..ce778eae271 100644
--- a/app/models/concerns/noteable.rb
+++ b/app/models/concerns/noteable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Noteable
# Names of all implementers of `Noteable` that support resolvable notes.
RESOLVABLE_TYPES = %w(MergeRequest).freeze
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 01b1ef9f82c..1f6c42f3b3a 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == Participable concern
#
# Contains functionality related to objects that can have participants, such as
diff --git a/app/models/concerns/presentable.rb b/app/models/concerns/presentable.rb
index bc4fbd19a02..06c300c2e41 100644
--- a/app/models/concerns/presentable.rb
+++ b/app/models/concerns/presentable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Presentable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index 1f7d78a2efe..f268a842db4 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Makes api V4 compatible with old project features permissions methods
#
# After migrating issues_enabled merge_requests_enabled builds_enabled snippets_enabled and wiki_enabled
diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb
index 18cbbd871a1..a29e80fe0c1 100644
--- a/app/models/concerns/prometheus_adapter.rb
+++ b/app/models/concerns/prometheus_adapter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module PrometheusAdapter
extend ActiveSupport::Concern
@@ -24,11 +26,10 @@ module PrometheusAdapter
def query(query_name, *args)
return unless can_query?
- query_class = Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query")
-
- args.map!(&:id)
+ query_class = query_klass_for(query_name)
+ query_args = build_query_args(*args)
- with_reactive_cache(query_class.name, *args, &query_class.method(:transform_reactive_result))
+ with_reactive_cache(query_class.name, *query_args, &query_class.method(:transform_reactive_result))
end
# Cache metrics for specific environment
@@ -44,5 +45,13 @@ module PrometheusAdapter
rescue Gitlab::PrometheusClient::Error => err
{ success: false, result: err.message }
end
+
+ def query_klass_for(query_name)
+ Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query")
+ end
+
+ def build_query_args(*args)
+ args.map(&:id)
+ end
end
end
diff --git a/app/models/concerns/protected_branch_access.rb b/app/models/concerns/protected_branch_access.rb
index e62f42e8e70..744f7f48dc8 100644
--- a/app/models/concerns/protected_branch_access.rb
+++ b/app/models/concerns/protected_branch_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedBranchAccess
extend ActiveSupport::Concern
diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb
index 94eef4ff7cd..e62e680af6e 100644
--- a/app/models/concerns/protected_ref.rb
+++ b/app/models/concerns/protected_ref.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedRef
extend ActiveSupport::Concern
@@ -23,7 +25,7 @@ module ProtectedRef
# If we don't `protected_branch` or `protected_tag` would be empty and
# `project` cannot be delegated to it, which in turn would cause validations
# to fail.
- has_many :"#{type}_access_levels", inverse_of: self.model_name.singular # rubocop:disable Cop/ActiveRecordDependent
+ 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}." }
diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb
index e3a7f2d5498..efa666fb3f2 100644
--- a/app/models/concerns/protected_ref_access.rb
+++ b/app/models/concerns/protected_ref_access.rb
@@ -1,21 +1,29 @@
+# frozen_string_literal: true
+
module ProtectedRefAccess
extend ActiveSupport::Concern
ALLOWED_ACCESS_LEVELS = [
- Gitlab::Access::MASTER,
+ Gitlab::Access::MAINTAINER,
Gitlab::Access::DEVELOPER,
Gitlab::Access::NO_ACCESS
].freeze
HUMAN_ACCESS_LEVELS = {
- Gitlab::Access::MASTER => "Maintainers".freeze,
+ Gitlab::Access::MAINTAINER => "Maintainers".freeze,
Gitlab::Access::DEVELOPER => "Developers + Maintainers".freeze,
Gitlab::Access::NO_ACCESS => "No one".freeze
}.freeze
included do
- scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
+ scope :master, -> { maintainer } # @deprecated
+ scope :maintainer, -> { where(access_level: Gitlab::Access::MAINTAINER) }
scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
+ scope :by_user, -> (user) { where(user_id: user ) }
+ scope :by_group, -> (group) { where(group_id: group ) }
+ scope :for_role, -> { where(user_id: nil, group_id: nil) }
+ scope :for_user, -> { where.not(user_id: nil) }
+ scope :for_group, -> { where.not(group_id: nil) }
validates :access_level, presence: true, if: :role?, inclusion: {
in: ALLOWED_ACCESS_LEVELS
diff --git a/app/models/concerns/protected_tag_access.rb b/app/models/concerns/protected_tag_access.rb
index ee65de24dd8..04bd54d6b1c 100644
--- a/app/models/concerns/protected_tag_access.rb
+++ b/app/models/concerns/protected_tag_access.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedTagAccess
extend ActiveSupport::Concern
diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb
index be0a5b49012..d3572875fb3 100644
--- a/app/models/concerns/reactive_caching.rb
+++ b/app/models/concerns/reactive_caching.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# The ReactiveCaching concern is used to fetch some data in the background and
# store 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+,
@@ -42,6 +44,8 @@
module ReactiveCaching
extend ActiveSupport::Concern
+ InvalidateReactiveCache = Class.new(StandardError)
+
included do
class_attribute :reactive_cache_lease_timeout
@@ -59,16 +63,23 @@ module ReactiveCaching
raise NotImplementedError
end
+ def reactive_cache_updated(*args)
+ end
+
def with_reactive_cache(*args, &blk)
- bootstrap = !within_reactive_cache_lifetime?(*args)
- Rails.cache.write(alive_reactive_cache_key(*args), true, expires_in: self.class.reactive_cache_lifetime)
+ unless within_reactive_cache_lifetime?(*args)
+ refresh_reactive_cache!(*args)
+ return nil
+ end
- if bootstrap
- ReactiveCachingWorker.perform_async(self.class, id, *args)
- nil
- else
+ keep_alive_reactive_cache!(*args)
+
+ begin
data = Rails.cache.read(full_reactive_cache_key(*args))
yield data if data.present?
+ rescue InvalidateReactiveCache
+ refresh_reactive_cache!(*args)
+ nil
end
end
@@ -81,8 +92,11 @@ module ReactiveCaching
locking_reactive_cache(*args) do
if within_reactive_cache_lifetime?(*args)
enqueuing_update(*args) do
- value = calculate_reactive_cache(*args)
- Rails.cache.write(full_reactive_cache_key(*args), value)
+ key = full_reactive_cache_key(*args)
+ new_value = calculate_reactive_cache(*args)
+ old_value = Rails.cache.read(key)
+ Rails.cache.write(key, new_value)
+ reactive_cache_updated(*args) if new_value != old_value
end
end
end
@@ -90,6 +104,16 @@ module ReactiveCaching
private
+ def refresh_reactive_cache!(*args)
+ clear_reactive_cache!(*args)
+ keep_alive_reactive_cache!(*args)
+ ReactiveCachingWorker.perform_async(self.class, id, *args)
+ end
+
+ def keep_alive_reactive_cache!(*args)
+ Rails.cache.write(alive_reactive_cache_key(*args), true, expires_in: self.class.reactive_cache_lifetime)
+ end
+
def full_reactive_cache_key(*qualifiers)
prefix = self.class.reactive_cache_key
prefix = prefix.call(self) if prefix.respond_to?(:call)
diff --git a/app/models/concerns/reactive_service.rb b/app/models/concerns/reactive_service.rb
index 713246039c1..af69da24994 100644
--- a/app/models/concerns/reactive_service.rb
+++ b/app/models/concerns/reactive_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ReactiveService
extend ActiveSupport::Concern
diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb
index 3bdc1330d23..69554f18ea2 100644
--- a/app/models/concerns/redis_cacheable.rb
+++ b/app/models/concerns/redis_cacheable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RedisCacheable
extend ActiveSupport::Concern
include Gitlab::Utils::StrongMemoize
diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb
index b782e85717e..468eaf68883 100644
--- a/app/models/concerns/referable.rb
+++ b/app/models/concerns/referable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == Referable concern
#
# Contains functionality related to making a model referable in Markdown, such
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb
index afacdb8cb12..85229cded5d 100644
--- a/app/models/concerns/relative_positioning.rb
+++ b/app/models/concerns/relative_positioning.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RelativePositioning
extend ActiveSupport::Concern
diff --git a/app/models/concerns/resolvable_discussion.rb b/app/models/concerns/resolvable_discussion.rb
index 7c236369793..c0490af2453 100644
--- a/app/models/concerns/resolvable_discussion.rb
+++ b/app/models/concerns/resolvable_discussion.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ResolvableDiscussion
extend ActiveSupport::Concern
include ::Gitlab::Utils::StrongMemoize
diff --git a/app/models/concerns/resolvable_note.rb b/app/models/concerns/resolvable_note.rb
index 4a0f8b92b3a..f47e20229f1 100644
--- a/app/models/concerns/resolvable_note.rb
+++ b/app/models/concerns/resolvable_note.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ResolvableNote
extend ActiveSupport::Concern
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 0176a12a131..b9ffc64e4a9 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Store object full path in separate table for easy lookup and uniq validation
# Object must have name and path db fields and respond to parent and parent_changed? methods.
module Routable
@@ -90,34 +92,17 @@ module Routable
end
def full_name
- if route && route.name.present?
- @full_name ||= route.name # rubocop:disable Gitlab/ModuleWithInstanceVariables
- else
- update_route if persisted?
-
- build_full_name
- end
+ route&.name || build_full_name
end
- # Every time `project.namespace.becomes(Namespace)` is called for polymorphic_path,
- # a new instance is instantiated, and we end up duplicating the same query to retrieve
- # the route. Caching this per request ensures that even if we have multiple instances,
- # we will not have to duplicate work, avoiding N+1 queries in some cases.
def full_path
- return uncached_full_path unless RequestStore.active? && persisted?
-
- RequestStore[full_path_key] ||= uncached_full_path
+ route&.path || build_full_path
end
def full_path_components
full_path.split('/')
end
- def expires_full_path_cache
- RequestStore.delete(full_path_key) if RequestStore.active?
- @full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
- end
-
def build_full_path
if parent && path
parent.full_path + '/' + path
@@ -138,16 +123,6 @@ module Routable
self.errors[:path].concat(route_path_errors) if route_path_errors
end
- def uncached_full_path
- if route && route.path.present?
- @full_path ||= route.path # rubocop:disable Gitlab/ModuleWithInstanceVariables
- else
- update_route if persisted?
-
- build_full_path
- end
- end
-
def full_name_changed?
name_changed? || parent_changed?
end
@@ -156,10 +131,6 @@ module Routable
path_changed? || parent_changed?
end
- def full_path_key
- @full_path_key ||= "routable/full_path/#{self.class.name}/#{self.id}"
- end
-
def build_full_name
if parent && name
parent.human_name + ' / ' + name
@@ -168,18 +139,9 @@ module Routable
end
end
- def update_route
- return if Gitlab::Database.read_only?
-
- prepare_route
- route.save
- end
-
def prepare_route
route || build_route(source: self)
route.path = build_full_path
route.name = build_full_name
- @full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
- @full_name = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
diff --git a/app/models/concerns/select_for_project_authorization.rb b/app/models/concerns/select_for_project_authorization.rb
index 58194b0ea13..39306179eb8 100644
--- a/app/models/concerns/select_for_project_authorization.rb
+++ b/app/models/concerns/select_for_project_authorization.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SelectForProjectAuthorization
extend ActiveSupport::Concern
@@ -6,8 +8,11 @@ module SelectForProjectAuthorization
select("projects.id AS project_id, members.access_level")
end
- def select_as_master_for_project_authorization
- select(["projects.id AS project_id", "#{Gitlab::Access::MASTER} AS access_level"])
+ def select_as_maintainer_for_project_authorization
+ select(["projects.id AS project_id", "#{Gitlab::Access::MAINTAINER} AS access_level"])
end
+
+ # @deprecated
+ alias_method :select_as_master_for_project_authorization, :select_as_maintainer_for_project_authorization
end
end
diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb
index 3796737427a..c322c356db2 100644
--- a/app/models/concerns/sha_attribute.rb
+++ b/app/models/concerns/sha_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ShaAttribute
extend ActiveSupport::Concern
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index cb76ae971d4..501bd1bb83c 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == Sortable concern
#
# Set default scope for ordering objects
@@ -6,6 +8,7 @@ module Sortable
extend ActiveSupport::Concern
included do
+ scope :with_order_id_desc, -> { order(id: :desc) }
scope :order_id_desc, -> { reorder(id: :desc) }
scope :order_id_asc, -> { reorder(id: :asc) }
scope :order_created_desc, -> { reorder(created_at: :desc) }
diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb
index 5e4274619c4..c6e3dc385fe 100644
--- a/app/models/concerns/spammable.rb
+++ b/app/models/concerns/spammable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Spammable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
index f66bdd529f1..3b745657a9e 100644
--- a/app/models/concerns/storage/legacy_namespace.rb
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Storage
module LegacyNamespace
extend ActiveSupport::Concern
@@ -11,8 +13,6 @@ module Storage
Namespace.find(parent_id_was) # raise NotFound early if needed
end
- expires_full_path_cache
-
move_repositories
if parent_changed?
@@ -34,13 +34,12 @@ module Storage
begin
send_update_instructions
write_projects_repository_config
-
- true
- rescue
- # Returning false does not rollback after_* transaction but gives
- # us information about failing some of tasks
- false
+ rescue => e
+ # Raise if development/test environment, else just notify Sentry
+ Gitlab::Sentry.track_exception(e, extra: { full_path_was: full_path_was, full_path: full_path, action: 'move_dir' })
end
+
+ true # false would cancel later callbacks but not rollback
end
# Hooks
diff --git a/app/models/concerns/storage/legacy_project_wiki.rb b/app/models/concerns/storage/legacy_project_wiki.rb
index ff82cb0ffa9..a377fa1e5de 100644
--- a/app/models/concerns/storage/legacy_project_wiki.rb
+++ b/app/models/concerns/storage/legacy_project_wiki.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Storage
module LegacyProjectWiki
extend ActiveSupport::Concern
diff --git a/app/models/concerns/storage/legacy_repository.rb b/app/models/concerns/storage/legacy_repository.rb
index 593749bf019..eb93d0fc7f1 100644
--- a/app/models/concerns/storage/legacy_repository.rb
+++ b/app/models/concerns/storage/legacy_repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Storage
module LegacyRepository
extend ActiveSupport::Concern
diff --git a/app/models/concerns/strip_attribute.rb b/app/models/concerns/strip_attribute.rb
index 8806ebe897a..344f677a3f3 100644
--- a/app/models/concerns/strip_attribute.rb
+++ b/app/models/concerns/strip_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == Strip Attribute module
#
# Contains functionality to clean attributes before validation
diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb
index f478c8ede18..1d0a61364b0 100644
--- a/app/models/concerns/subscribable.rb
+++ b/app/models/concerns/subscribable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == Subscribable concern
#
# Users can subscribe to these models.
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index ccd6f0e0a7d..603d4d62578 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'task_list'
require 'task_list/filter'
diff --git a/app/models/concerns/throttled_touch.rb b/app/models/concerns/throttled_touch.rb
index ad0ff0f20d4..797c46f6cc5 100644
--- a/app/models/concerns/throttled_touch.rb
+++ b/app/models/concerns/throttled_touch.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# ThrottledTouch can be used to throttle the number of updates triggered by
# calling "touch" on an ActiveRecord model.
module ThrottledTouch
diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb
index 0fc321c52bc..f61a0bbc65b 100644
--- a/app/models/concerns/time_trackable.rb
+++ b/app/models/concerns/time_trackable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# == TimeTrackable concern
#
# Contains functionality related to objects that support time tracking.
diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb
index ec3543f7053..522b65e4205 100644
--- a/app/models/concerns/token_authenticatable.rb
+++ b/app/models/concerns/token_authenticatable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TokenAuthenticatable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/triggerable_hooks.rb b/app/models/concerns/triggerable_hooks.rb
index ec0ed3b795a..f55ab2fcaf3 100644
--- a/app/models/concerns/triggerable_hooks.rb
+++ b/app/models/concerns/triggerable_hooks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TriggerableHooks
AVAILABLE_TRIGGERS = {
repository_update_hooks: :repository_update_events,
diff --git a/app/models/concerns/uniquify.rb b/app/models/concerns/uniquify.rb
index 549a76da20e..382e826ec58 100644
--- a/app/models/concerns/uniquify.rb
+++ b/app/models/concerns/uniquify.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Uniquify
#
# Return a version of the given 'base' string that is unique
diff --git a/app/models/concerns/updated_at_filterable.rb b/app/models/concerns/updated_at_filterable.rb
index edb423b7828..1ab5ee9fbb9 100644
--- a/app/models/concerns/updated_at_filterable.rb
+++ b/app/models/concerns/updated_at_filterable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module UpdatedAtFilterable
extend ActiveSupport::Concern
diff --git a/app/models/concerns/valid_attribute.rb b/app/models/concerns/valid_attribute.rb
index 8c35cea8d58..251db9ce30b 100644
--- a/app/models/concerns/valid_attribute.rb
+++ b/app/models/concerns/valid_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ValidAttribute
extend ActiveSupport::Concern
diff --git a/app/models/concerns/with_uploads.rb b/app/models/concerns/with_uploads.rb
index 4245d083a49..e231af5368d 100644
--- a/app/models/concerns/with_uploads.rb
+++ b/app/models/concerns/with_uploads.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Mounted uploaders are destroyed by carrierwave's after_commit
# hook. This hook fetches upload location (local vs remote) from
# Upload model. So it's neccessary to make sure that during that
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
index d0c94d3b694..41413854d5c 100644
--- a/app/models/container_repository.rb
+++ b/app/models/container_repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContainerRepository < ActiveRecord::Base
belongs_to :project
diff --git a/app/models/conversational_development_index/card.rb b/app/models/conversational_development_index/card.rb
index e8f09dc9161..f9180bdd97b 100644
--- a/app/models/conversational_development_index/card.rb
+++ b/app/models/conversational_development_index/card.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ConversationalDevelopmentIndex
class Card
attr_accessor :metric, :title, :description, :feature, :blog, :docs
diff --git a/app/models/conversational_development_index/idea_to_production_step.rb b/app/models/conversational_development_index/idea_to_production_step.rb
index 6e1753c9f30..e78a734693c 100644
--- a/app/models/conversational_development_index/idea_to_production_step.rb
+++ b/app/models/conversational_development_index/idea_to_production_step.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ConversationalDevelopmentIndex
class IdeaToProductionStep
attr_accessor :metric, :title, :features
diff --git a/app/models/conversational_development_index/metric.rb b/app/models/conversational_development_index/metric.rb
index 0bee62f954f..c54537572d6 100644
--- a/app/models/conversational_development_index/metric.rb
+++ b/app/models/conversational_development_index/metric.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ConversationalDevelopmentIndex
class Metric < ActiveRecord::Base
include Presentable
diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb
index b34d1382d43..d0f5b6970b1 100644
--- a/app/models/cycle_analytics.rb
+++ b/app/models/cycle_analytics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CycleAnalytics
STAGES = %i[issue plan code test review staging production].freeze
diff --git a/app/models/dashboard_group_milestone.rb b/app/models/dashboard_group_milestone.rb
new file mode 100644
index 00000000000..13807d43265
--- /dev/null
+++ b/app/models/dashboard_group_milestone.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+# Dashboard Group Milestones are milestones that allow us to pull more info out for the UI that the Milestone object doesn't allow for
+class DashboardGroupMilestone < GlobalMilestone
+ extend ::Gitlab::Utils::Override
+
+ attr_reader :group_name
+
+ override :initialize
+ def initialize(milestone)
+ super(milestone.title, Array(milestone))
+
+ @group_name = milestone.group.full_name
+ end
+
+ def self.build_collection(groups)
+ MilestonesFinder.new(group_ids: groups.pluck(:id)).execute.map { |m| new(m) }
+ end
+
+ override :group_milestone?
+ def group_milestone?
+ @first_milestone.group_milestone?
+ end
+
+ override :milestoneish_ids
+ def milestoneish_ids
+ milestones.map(&:id)
+ end
+
+ def group
+ @first_milestone.group
+ end
+
+ def iid
+ @first_milestone.iid
+ end
+end
diff --git a/app/models/dashboard_milestone.rb b/app/models/dashboard_milestone.rb
index 86eb4ec76fc..96bc8090b81 100644
--- a/app/models/dashboard_milestone.rb
+++ b/app/models/dashboard_milestone.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DashboardMilestone < GlobalMilestone
def issues_finder_params
{ authorized_only: true }
diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb
index 89a74b7dcb1..fd5d7726fb6 100644
--- a/app/models/deploy_key.rb
+++ b/app/models/deploy_key.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeployKey < Key
include IgnorableColumn
diff --git a/app/models/deploy_keys_project.rb b/app/models/deploy_keys_project.rb
index 6eef12c4373..71fd02fac86 100644
--- a/app/models/deploy_keys_project.rb
+++ b/app/models/deploy_keys_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeployKeysProject < ActiveRecord::Base
belongs_to :project
belongs_to :deploy_key, inverse_of: :deploy_keys_projects
diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index 5082dc45368..0b2eedf3631 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -1,6 +1,9 @@
+# frozen_string_literal: true
+
class DeployToken < ActiveRecord::Base
include Expirable
include TokenAuthenticatable
+ include PolicyActor
add_authentication_token_field :token
AVAILABLE_SCOPES = %i(read_repository read_registry).freeze
@@ -27,7 +30,7 @@ class DeployToken < ActiveRecord::Base
end
def active?
- !revoked
+ !revoked && !expired?
end
def scopes
@@ -60,6 +63,12 @@ class DeployToken < ActiveRecord::Base
private
+ def expired?
+ return false unless expires_at
+
+ expires_at < Date.today
+ end
+
def ensure_at_least_one_scope
errors.add(:base, "Scopes can't be blank") unless read_repository || read_registry
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index ac86e9e8de0..6962b54441b 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Deployment < ActiveRecord::Base
include AtomicInternalId
include IidRoutes
@@ -92,10 +94,6 @@ class Deployment < ActiveRecord::Base
@stop_action ||= manual_actions.find_by(name: on_stop)
end
- def stop_action?
- stop_action.present?
- end
-
def formatted_deployment_time
created_at.to_time.in_time_zone.to_s(:medium)
end
diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb
index bd6af622bfb..93e3ebf7896 100644
--- a/app/models/diff_discussion.rb
+++ b/app/models/diff_discussion.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A discussion on merge request or commit diffs consisting of `DiffNote` notes.
#
# A discussion of this type can be resolvable.
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index d752d5bcdee..58d949315e0 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A note on merge request or commit diffs
#
# A note of this type can be resolvable.
diff --git a/app/models/diff_viewer/added.rb b/app/models/diff_viewer/added.rb
index 1909e6ef9d8..70d13e3478c 100644
--- a/app/models/diff_viewer/added.rb
+++ b/app/models/diff_viewer/added.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
class Added < Base
include Simple
diff --git a/app/models/diff_viewer/base.rb b/app/models/diff_viewer/base.rb
index 0cbe714288d..1176861a827 100644
--- a/app/models/diff_viewer/base.rb
+++ b/app/models/diff_viewer/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
class Base
PARTIAL_PATH_PREFIX = 'projects/diffs/viewers'.freeze
diff --git a/app/models/diff_viewer/client_side.rb b/app/models/diff_viewer/client_side.rb
index cf41d07f8eb..cc049e1ca49 100644
--- a/app/models/diff_viewer/client_side.rb
+++ b/app/models/diff_viewer/client_side.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
module ClientSide
extend ActiveSupport::Concern
diff --git a/app/models/diff_viewer/deleted.rb b/app/models/diff_viewer/deleted.rb
index 9c129bac694..78671084eeb 100644
--- a/app/models/diff_viewer/deleted.rb
+++ b/app/models/diff_viewer/deleted.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
class Deleted < Base
include Simple
diff --git a/app/models/diff_viewer/image.rb b/app/models/diff_viewer/image.rb
index 759d9a36ebb..c356c2ca50e 100644
--- a/app/models/diff_viewer/image.rb
+++ b/app/models/diff_viewer/image.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
class Image < Base
include Rich
diff --git a/app/models/diff_viewer/mode_changed.rb b/app/models/diff_viewer/mode_changed.rb
index d487d996f8d..bd07d1e21b9 100644
--- a/app/models/diff_viewer/mode_changed.rb
+++ b/app/models/diff_viewer/mode_changed.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
class ModeChanged < Base
include Simple
diff --git a/app/models/diff_viewer/no_preview.rb b/app/models/diff_viewer/no_preview.rb
index 5455fee4490..b0dd5fd151d 100644
--- a/app/models/diff_viewer/no_preview.rb
+++ b/app/models/diff_viewer/no_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
class NoPreview < Base
include Simple
diff --git a/app/models/diff_viewer/not_diffable.rb b/app/models/diff_viewer/not_diffable.rb
index 4f9638626ea..dc86599e722 100644
--- a/app/models/diff_viewer/not_diffable.rb
+++ b/app/models/diff_viewer/not_diffable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
class NotDiffable < Base
include Simple
diff --git a/app/models/diff_viewer/renamed.rb b/app/models/diff_viewer/renamed.rb
index f1fbfd8c6d5..70ab9d4ca2c 100644
--- a/app/models/diff_viewer/renamed.rb
+++ b/app/models/diff_viewer/renamed.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
class Renamed < Base
include Simple
diff --git a/app/models/diff_viewer/rich.rb b/app/models/diff_viewer/rich.rb
index 3b0ca6e4cff..2faa1be6567 100644
--- a/app/models/diff_viewer/rich.rb
+++ b/app/models/diff_viewer/rich.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
module Rich
extend ActiveSupport::Concern
diff --git a/app/models/diff_viewer/server_side.rb b/app/models/diff_viewer/server_side.rb
index aed1a0791b1..977204e6c97 100644
--- a/app/models/diff_viewer/server_side.rb
+++ b/app/models/diff_viewer/server_side.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
module ServerSide
extend ActiveSupport::Concern
diff --git a/app/models/diff_viewer/simple.rb b/app/models/diff_viewer/simple.rb
index 65750996ee4..8d28ca5239a 100644
--- a/app/models/diff_viewer/simple.rb
+++ b/app/models/diff_viewer/simple.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
module Simple
extend ActiveSupport::Concern
diff --git a/app/models/diff_viewer/static.rb b/app/models/diff_viewer/static.rb
index d761328b3f6..1278c22185c 100644
--- a/app/models/diff_viewer/static.rb
+++ b/app/models/diff_viewer/static.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
module Static
extend ActiveSupport::Concern
diff --git a/app/models/diff_viewer/text.rb b/app/models/diff_viewer/text.rb
index 98f4b2aea2a..d3d5055c402 100644
--- a/app/models/diff_viewer/text.rb
+++ b/app/models/diff_viewer/text.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DiffViewer
class Text < Base
include Simple
diff --git a/app/models/directly_addressed_user.rb b/app/models/directly_addressed_user.rb
index 0d519c6ac22..06df2d6c012 100644
--- a/app/models/directly_addressed_user.rb
+++ b/app/models/directly_addressed_user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DirectlyAddressedUser
class << self
def reference_pattern
diff --git a/app/models/discussion.rb b/app/models/discussion.rb
index 35a0ef00856..dbc7b6e67be 100644
--- a/app/models/discussion.rb
+++ b/app/models/discussion.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A non-diff discussion on an issue, merge request, commit, or snippet, consisting of `DiscussionNote` notes.
#
# A discussion of this type can be resolvable.
diff --git a/app/models/discussion_note.rb b/app/models/discussion_note.rb
index e660b024083..89d86aaed66 100644
--- a/app/models/discussion_note.rb
+++ b/app/models/discussion_note.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A note in a non-diff discussion on an issue, merge request, commit, or snippet.
#
# A note of this type can be resolvable.
diff --git a/app/models/email.rb b/app/models/email.rb
index d6516761f0a..b6a977dfa22 100644
--- a/app/models/email.rb
+++ b/app/models/email.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Email < ActiveRecord::Base
include Sortable
include Gitlab::SQL::Pattern
@@ -25,6 +27,10 @@ class Email < ActiveRecord::Base
self.errors.add(:email, 'has already been taken') if User.exists?(email: self.email)
end
+ def accept_pending_invitations!
+ user.accept_pending_invitations!
+ end
+
# once email is confirmed, update the gpg signatures
def update_invalid_gpg_signatures
user.update_invalid_gpg_signatures if confirmed?
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 8d523dae324..c8d1d378ae0 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Environment < ActiveRecord::Base
# Used to generate random suffixes for the slug
LETTERS = 'a'..'z'
@@ -117,7 +119,7 @@ class Environment < ActiveRecord::Base
external_url.gsub(%r{\A.*?://}, '')
end
- def stop_action?
+ def stop_action_available?
available? && stop_action.present?
end
@@ -173,7 +175,7 @@ class Environment < ActiveRecord::Base
# * cannot end with `-`
def generate_slug
# Lowercase letters and numbers only
- slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-')
+ slugified = +name.to_s.downcase.gsub(/[^a-z0-9]/, '-')
# Must start with a letter
slugified = 'env-' + slugified unless LETTERS.cover?(slugified[0])
diff --git a/app/models/epic.rb b/app/models/epic.rb
index 286b855de3f..f027993376c 100644
--- a/app/models/epic.rb
+++ b/app/models/epic.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Placeholder class for model that is implemented in EE
# It reserves '&' as a reference prefix, but the table does not exists in CE
class Epic < ActiveRecord::Base
diff --git a/app/models/event.rb b/app/models/event.rb
index ac0b1c7b27c..ba28866e8e6 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Event < ActiveRecord::Base
include Sortable
include IgnorableColumn
diff --git a/app/models/event_collection.rb b/app/models/event_collection.rb
index 8b8244314af..a4c69b11781 100644
--- a/app/models/event_collection.rb
+++ b/app/models/event_collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A collection of events to display in an event list.
#
# An EventCollection is meant to be used for displaying events to a user (e.g.
diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb
index 282fd7edcb7..4f73beaafc5 100644
--- a/app/models/external_issue.rb
+++ b/app/models/external_issue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ExternalIssue
include Referable
diff --git a/app/models/fork_network.rb b/app/models/fork_network.rb
index 7f1728e8c77..1b9bf93cbbc 100644
--- a/app/models/fork_network.rb
+++ b/app/models/fork_network.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ForkNetwork < ActiveRecord::Base
belongs_to :root_project, class_name: 'Project'
has_many :fork_network_members
diff --git a/app/models/fork_network_member.rb b/app/models/fork_network_member.rb
index eb9417dc34f..36c66f21b0b 100644
--- a/app/models/fork_network_member.rb
+++ b/app/models/fork_network_member.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ForkNetworkMember < ActiveRecord::Base
belongs_to :fork_network
belongs_to :project
diff --git a/app/models/forked_project_link.rb b/app/models/forked_project_link.rb
index 8d35864eff6..0f7067238cd 100644
--- a/app/models/forked_project_link.rb
+++ b/app/models/forked_project_link.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ForkedProjectLink < ActiveRecord::Base
belongs_to :forked_to_project, -> { where.not(pending_delete: true) }, class_name: 'Project'
belongs_to :forked_from_project, -> { where.not(pending_delete: true) }, class_name: 'Project'
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
index 5ac8bde44cd..3028bf21301 100644
--- a/app/models/generic_commit_status.rb
+++ b/app/models/generic_commit_status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GenericCommitStatus < CommitStatus
before_validation :set_default_values
diff --git a/app/models/global_label.rb b/app/models/global_label.rb
index 2a1b7564962..c5b2492bbf6 100644
--- a/app/models/global_label.rb
+++ b/app/models/global_label.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GlobalLabel
attr_accessor :title, :labels
alias_attribute :name, :title
diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb
index dc2f6817190..6e23e811b0e 100644
--- a/app/models/global_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+# Global Milestones are milestones that can be shared across multiple projects
class GlobalMilestone
include Milestoneish
diff --git a/app/models/gpg_key.rb b/app/models/gpg_key.rb
index 44eda741679..077afffd358 100644
--- a/app/models/gpg_key.rb
+++ b/app/models/gpg_key.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GpgKey < ActiveRecord::Base
KEY_PREFIX = '-----BEGIN PGP PUBLIC KEY BLOCK-----'.freeze
KEY_SUFFIX = '-----END PGP PUBLIC KEY BLOCK-----'.freeze
diff --git a/app/models/gpg_key_subkey.rb b/app/models/gpg_key_subkey.rb
index b57922aba30..440b588bc78 100644
--- a/app/models/gpg_key_subkey.rb
+++ b/app/models/gpg_key_subkey.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GpgKeySubkey < ActiveRecord::Base
include ShaAttribute
diff --git a/app/models/gpg_signature.rb b/app/models/gpg_signature.rb
index bf88d75246f..0816778deae 100644
--- a/app/models/gpg_signature.rb
+++ b/app/models/gpg_signature.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GpgSignature < ActiveRecord::Base
include ShaAttribute
diff --git a/app/models/group.rb b/app/models/group.rb
index 9c171de7fc3..106a1f4a94c 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'carrierwave/orm/activerecord'
class Group < Namespace
@@ -39,6 +41,8 @@ class Group < Namespace
has_many :boards
has_many :badges, class_name: 'GroupBadge'
+ has_many :todos
+
accepts_nested_attributes_for :variables, allow_destroy: true
validate :visibility_level_allowed_by_projects
@@ -178,10 +182,13 @@ class Group < Namespace
add_user(user, :developer, current_user: current_user)
end
- def add_master(user, current_user = nil)
- add_user(user, :master, current_user: current_user)
+ def add_maintainer(user, current_user = nil)
+ add_user(user, :maintainer, current_user: current_user)
end
+ # @deprecated
+ alias_method :add_master, :add_maintainer
+
def add_owner(user, current_user = nil)
add_user(user, :owner, current_user: current_user)
end
@@ -198,12 +205,15 @@ class Group < Namespace
members_with_parents.owners.where(user_id: user).any?
end
- def has_master?(user)
+ def has_maintainer?(user)
return false unless user
- members_with_parents.masters.where(user_id: user).any?
+ members_with_parents.maintainers.where(user_id: user).any?
end
+ # @deprecated
+ alias_method :has_master?, :has_maintainer?
+
# Check if user is a last owner of the group.
# Parent owners are ignored for nested groups.
def last_owner?(user)
diff --git a/app/models/group_custom_attribute.rb b/app/models/group_custom_attribute.rb
index 8157d602d67..22f14885657 100644
--- a/app/models/group_custom_attribute.rb
+++ b/app/models/group_custom_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupCustomAttribute < ActiveRecord::Base
belongs_to :group
diff --git a/app/models/group_label.rb b/app/models/group_label.rb
index 92c83b54861..ff14529c6e6 100644
--- a/app/models/group_label.rb
+++ b/app/models/group_label.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupLabel < Label
belongs_to :group
diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb
index 98135ee3c8b..9dfaebacc83 100644
--- a/app/models/group_milestone.rb
+++ b/app/models/group_milestone.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+# Group Milestones are milestones that can be shared among many projects within the same group
class GroupMilestone < GlobalMilestone
attr_accessor :group
diff --git a/app/models/guest.rb b/app/models/guest.rb
index df287c277a7..9c8097e1ac8 100644
--- a/app/models/guest.rb
+++ b/app/models/guest.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Guest
class << self
def can?(action, subject = :global)
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index ec072882cc9..18c387f0d34 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectHook < WebHook
include TriggerableHooks
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index aef11514945..bda82a116a1 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ServiceHook < WebHook
belongs_to :service
validates :service, presence: true
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index 6bef00f26ea..90b4588a325 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SystemHook < WebHook
include TriggerableHooks
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index e353abdda9c..f18aadefa5c 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WebHook < ActiveRecord::Base
include Sortable
diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb
index e72c125fb69..2d9f7594e8c 100644
--- a/app/models/hooks/web_hook_log.rb
+++ b/app/models/hooks/web_hook_log.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WebHookLog < ActiveRecord::Base
belongs_to :web_hook
@@ -7,6 +9,11 @@ class WebHookLog < ActiveRecord::Base
validates :web_hook, presence: true
+ def self.recent
+ where('created_at >= ?', 2.days.ago.beginning_of_day)
+ .order(created_at: :desc)
+ end
+
def success?
response_status =~ /^2/
end
diff --git a/app/models/identity.rb b/app/models/identity.rb
index 3fd0c5e751d..f5a13dbd6f2 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Identity < ActiveRecord::Base
def self.uniqueness_scope
:provider
diff --git a/app/models/import_export_upload.rb b/app/models/import_export_upload.rb
new file mode 100644
index 00000000000..f0cc5aafcd4
--- /dev/null
+++ b/app/models/import_export_upload.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class ImportExportUpload < ActiveRecord::Base
+ include WithUploads
+ include ObjectStorage::BackgroundMove
+
+ belongs_to :project
+
+ # These hold the project Import/Export archives (.tar.gz files)
+ mount_uploader :import_file, ImportExportUploader
+ mount_uploader :export_file, ImportExportUploader
+
+ def retrieve_upload(_identifier, paths)
+ Upload.find_by(model: self, path: paths)
+ end
+end
diff --git a/app/models/individual_note_discussion.rb b/app/models/individual_note_discussion.rb
index 6be8ca45739..07ee7470ea2 100644
--- a/app/models/individual_note_discussion.rb
+++ b/app/models/individual_note_discussion.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A discussion to wrap a single `Note` note on the root of an issue, merge request,
# commit, or snippet, that is not displayed as a discussion.
#
diff --git a/app/models/instance_configuration.rb b/app/models/instance_configuration.rb
index b30b707e5fe..7d8ce0bbd05 100644
--- a/app/models/instance_configuration.rb
+++ b/app/models/instance_configuration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'resolv'
class InstanceConfiguration
diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb
index f50f28deffe..4eb211eff61 100644
--- a/app/models/internal_id.rb
+++ b/app/models/internal_id.rb
@@ -1,6 +1,11 @@
+# frozen_string_literal: true
+
# An InternalId is a strictly monotone sequence of integers
# generated for a given scope and usage.
#
+# The monotone sequence may be broken if an ID is explicitly provided
+# to `.track_greatest_and_save!` or `#track_greatest`.
+#
# For example, issues use their project to scope internal ids:
# In that sense, scope is "project" and usage is "issues".
# Generated internal ids for an issue are unique per project.
@@ -25,13 +30,34 @@ class InternalId < ActiveRecord::Base
# The operation locks the record and gathers a `ROW SHARE` lock (in PostgreSQL).
# As such, the increment is atomic and safe to be called concurrently.
def increment_and_save!
+ update_and_save { self.last_value = (last_value || 0) + 1 }
+ end
+
+ # Increments #last_value with new_value if it is greater than the current,
+ # and saves the record
+ #
+ # The operation locks the record and gathers a `ROW SHARE` lock (in PostgreSQL).
+ # As such, the increment is atomic and safe to be called concurrently.
+ def track_greatest_and_save!(new_value)
+ update_and_save { self.last_value = [last_value || 0, new_value].max }
+ end
+
+ private
+
+ def update_and_save(&block)
lock!
- self.last_value = (last_value || 0) + 1
+ yield
save!
last_value
end
class << self
+ def track_greatest(subject, scope, usage, new_value, init)
+ return new_value unless available?
+
+ InternalIdGenerator.new(subject, scope, usage, init).track_greatest(new_value)
+ end
+
def generate_next(subject, scope, usage, init)
# Shortcut if `internal_ids` table is not available (yet)
# This can be the case in other (unrelated) migration specs
@@ -94,6 +120,16 @@ class InternalId < ActiveRecord::Base
end
end
+ # Create a record in internal_ids if one does not yet exist
+ # and set its new_value if it is higher than the current last_value
+ #
+ # Note this will acquire a ROW SHARE lock on the InternalId record
+ def track_greatest(new_value)
+ subject.transaction do
+ (lookup || create_record).track_greatest_and_save!(new_value)
+ end
+ end
+
private
# Retrieve InternalId record for (project, usage) combination, if it exists
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 4715d942c8d..94cf12f3c2b 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'carrierwave/orm/activerecord'
class Issue < ActiveRecord::Base
@@ -12,6 +14,7 @@ class Issue < ActiveRecord::Base
include TimeTrackable
include ThrottledTouch
include IgnorableColumn
+ include LabelEventable
ignore_column :assignee_id, :branch_name, :deleted_at
@@ -275,10 +278,6 @@ class Issue < ActiveRecord::Base
user ? readable_by?(user) : publicly_visible?
end
- def overdue?
- due_date.try(:past?) || false
- end
-
def check_for_spam?
project.public? && (title_changed? || description_changed?)
end
diff --git a/app/models/issue/metrics.rb b/app/models/issue/metrics.rb
index 012d545c440..0f5ee957ec9 100644
--- a/app/models/issue/metrics.rb
+++ b/app/models/issue/metrics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Issue::Metrics < ActiveRecord::Base
belongs_to :issue
diff --git a/app/models/issue_assignee.rb b/app/models/issue_assignee.rb
index 326b9eb7ad5..400c0256945 100644
--- a/app/models/issue_assignee.rb
+++ b/app/models/issue_assignee.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueAssignee < ActiveRecord::Base
belongs_to :issue
belongs_to :assignee, class_name: "User", foreign_key: :user_id
diff --git a/app/models/issue_collection.rb b/app/models/issue_collection.rb
index 49f011c113f..05607fc3a08 100644
--- a/app/models/issue_collection.rb
+++ b/app/models/issue_collection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# IssueCollection can be used to reduce a list of issues down to a subset.
#
# IssueCollection is not meant to be some sort of Enumerable, instead it's meant
diff --git a/app/models/key.rb b/app/models/key.rb
index ae5769c0627..3bb0d2f6f9c 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'digest/md5'
class Key < ActiveRecord::Base
diff --git a/app/models/label.rb b/app/models/label.rb
index 7bbcaa121ca..96c1515b41a 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
class Label < ActiveRecord::Base
include CacheMarkdownField
include Referable
include Subscribable
+ include Gitlab::SQL::Pattern
# Represents a "No Label" state used for filtering Issues and Merge
# Requests that have no label assigned.
@@ -103,6 +106,17 @@ class Label < ActiveRecord::Base
nil
end
+ # Searches for labels with a matching title or description.
+ #
+ # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
+ #
+ # query - The search query as a String.
+ #
+ # Returns an ActiveRecord::Relation.
+ def self.search(query)
+ fuzzy_search(query, [:title, :description])
+ end
+
def open_issues_count(user = nil)
issues_count(user, state: 'opened')
end
diff --git a/app/models/label_link.rb b/app/models/label_link.rb
index d68e1f54317..779657b25d5 100644
--- a/app/models/label_link.rb
+++ b/app/models/label_link.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LabelLink < ActiveRecord::Base
include Importable
diff --git a/app/models/label_priority.rb b/app/models/label_priority.rb
index 5b85e0b6533..8ed8bb7577f 100644
--- a/app/models/label_priority.rb
+++ b/app/models/label_priority.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LabelPriority < ActiveRecord::Base
belongs_to :project
belongs_to :label
diff --git a/app/models/legacy_diff_discussion.rb b/app/models/legacy_diff_discussion.rb
index 80fc6304fd4..7d78c580fa2 100644
--- a/app/models/legacy_diff_discussion.rb
+++ b/app/models/legacy_diff_discussion.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A discussion on merge request or commit diffs consisting of `LegacyDiffNote` notes.
#
# All new diff discussions are of the type `DiffDiscussion`, but any diff discussions created
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index d90cafd14b4..20f9b18e4ca 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A note on merge request or commit diffs, using the legacy implementation.
#
# All new diff notes are of the type `DiffNote`, but any diff notes created
diff --git a/app/models/lfs_file_lock.rb b/app/models/lfs_file_lock.rb
index 50bb6ca382d..431d37e12e9 100644
--- a/app/models/lfs_file_lock.rb
+++ b/app/models/lfs_file_lock.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LfsFileLock < ActiveRecord::Base
belongs_to :project
belongs_to :user
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index 84487031ee5..2a1a4ef48b7 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LfsObject < ActiveRecord::Base
include AfterCommitQueue
include ObjectStorage::BackgroundMove
diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb
index b0625c52b62..353602800d7 100644
--- a/app/models/lfs_objects_project.rb
+++ b/app/models/lfs_objects_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LfsObjectsProject < ActiveRecord::Base
belongs_to :project
belongs_to :lfs_object
diff --git a/app/models/list.rb b/app/models/list.rb
index 4edcfa78835..1a30acc83cf 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
class List < ActiveRecord::Base
belongs_to :board
belongs_to :label
- enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3 }
+ enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3, milestone: 4 }
validates :board, :list_type, presence: true
validates :label, :position, presence: true, if: :label?
@@ -25,11 +27,11 @@ class List < ActiveRecord::Base
end
def destroyable?
- label?
+ self.class.destroyable_types.include?(list_type&.to_sym)
end
def movable?
- label?
+ self.class.movable_types.include?(list_type&.to_sym)
end
def title
diff --git a/app/models/member.rb b/app/models/member.rb
index 68572f2e33a..05c0bc8cb97 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Member < ActiveRecord::Base
include AfterCommitQueue
include Sortable
@@ -69,9 +71,11 @@ class Member < ActiveRecord::Base
scope :guests, -> { active.where(access_level: GUEST) }
scope :reporters, -> { active.where(access_level: REPORTER) }
scope :developers, -> { active.where(access_level: DEVELOPER) }
- scope :masters, -> { active.where(access_level: MASTER) }
+ scope :maintainers, -> { active.where(access_level: MAINTAINER) }
+ scope :masters, -> { maintainers } # @deprecated
scope :owners, -> { active.where(access_level: OWNER) }
- scope :owners_and_masters, -> { active.where(access_level: [OWNER, MASTER]) }
+ scope :owners_and_maintainers, -> { active.where(access_level: [OWNER, MAINTAINER]) }
+ scope :owners_and_masters, -> { owners_and_maintainers } # @deprecated
scope :order_name_asc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'ASC')) }
scope :order_name_desc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'DESC')) }
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 5da739f9618..fc49ee7ac8c 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupMember < Member
SOURCE_TYPE = 'Namespace'.freeze
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 024106056b4..0154fe5aeba 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectMember < Member
SOURCE_TYPE = 'Project'.freeze
@@ -17,19 +19,19 @@ class ProjectMember < Member
# Add users to projects with passed access option
#
# access can be an integer representing a access code
- # or symbol like :master representing role
+ # or symbol like :maintainer representing role
#
# Ex.
# add_users_to_projects(
# project_ids,
# user_ids,
- # ProjectMember::MASTER
+ # ProjectMember::MAINTAINER
# )
#
# add_users_to_projects(
# project_ids,
# user_ids,
- # :master
+ # :maintainer
# )
#
def add_users_to_projects(project_ids, users, access_level, current_user: nil, expires_at: nil)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 6c96c8ca391..396647a14ae 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequest < ActiveRecord::Base
include AtomicInternalId
include IidRoutes
@@ -10,6 +12,12 @@ class MergeRequest < ActiveRecord::Base
include EachBatch
include ThrottledTouch
include Gitlab::Utils::StrongMemoize
+ include LabelEventable
+ include ReactiveCaching
+
+ self.reactive_cache_key = ->(model) { [model.project.id, model.iid] }
+ self.reactive_cache_refresh_interval = 10.minutes
+ self.reactive_cache_lifetime = 10.minutes
ignore_column :locked_at,
:ref_fetched,
@@ -52,6 +60,8 @@ class MergeRequest < ActiveRecord::Base
class_name: 'MergeRequestsClosingIssues',
dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
+ has_many :cached_closes_issues, through: :merge_requests_closing_issues, source: :issue
+
belongs_to :assignee, class_name: "User"
serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize
@@ -128,14 +138,9 @@ class MergeRequest < ActiveRecord::Base
end
after_transition unchecked: :cannot_be_merged do |merge_request, transition|
- begin
- if merge_request.notify_conflict?
- NotificationService.new.merge_request_unmergeable(merge_request)
- TodoService.new.merge_request_became_unmergeable(merge_request)
- end
- rescue Gitlab::Git::CommandError
- # Checking mergeability can trigger exception, e.g. non-utf8
- # We ignore this type of errors.
+ if merge_request.notify_conflict?
+ NotificationService.new.merge_request_unmergeable(merge_request)
+ TodoService.new.merge_request_became_unmergeable(merge_request)
end
end
@@ -707,7 +712,14 @@ class MergeRequest < ActiveRecord::Base
end
def notify_conflict?
- (opened? || locked?) && !project.repository.can_be_merged?(diff_head_sha, target_branch)
+ (opened? || locked?) &&
+ has_commits? &&
+ !branch_missing? &&
+ !project.repository.can_be_merged?(diff_head_sha, target_branch)
+ rescue Gitlab::Git::CommandError
+ # Checking mergeability can trigger exception, e.g. non-utf8
+ # We ignore this type of errors.
+ false
end
def related_notes
@@ -753,8 +765,9 @@ class MergeRequest < ActiveRecord::Base
# Calculating this information for a number of merge requests requires
# running `ReferenceExtractor` on each of them separately.
# This optimization does not apply to issues from external sources.
- def cache_merge_request_closes_issues!(current_user)
+ def cache_merge_request_closes_issues!(current_user = self.author)
return unless project.issues_enabled?
+ return if closed? || merged?
transaction do
self.merge_requests_closing_issues.delete_all
@@ -767,6 +780,18 @@ class MergeRequest < ActiveRecord::Base
end
end
+ def visible_closing_issues_for(current_user = self.author)
+ strong_memoize(:visible_closing_issues_for) do
+ if self.target_project.has_external_issue_tracker?
+ closes_issues(current_user)
+ else
+ cached_closes_issues.select do |issue|
+ Ability.allowed?(current_user, :read_issue, issue)
+ end
+ end
+ end
+ end
+
# Return the set of issues that will be closed if this merge request is accepted.
def closes_issues(current_user = self.author)
if target_branch == project.default_branch
@@ -786,7 +811,7 @@ class MergeRequest < ActiveRecord::Base
ext = Gitlab::ReferenceExtractor.new(project, current_user)
ext.analyze("#{title}\n#{description}")
- ext.issues - closes_issues(current_user)
+ ext.issues - visible_closing_issues_for(current_user)
end
def target_project_path
@@ -834,7 +859,7 @@ class MergeRequest < ActiveRecord::Base
end
def merge_commit_message(include_description: false)
- closes_issues_references = closes_issues.map do |issue|
+ closes_issues_references = visible_closing_issues_for.map do |issue|
issue.to_reference(target_project)
end
@@ -1007,6 +1032,35 @@ class MergeRequest < ActiveRecord::Base
.order(id: :desc)
end
+ def has_test_reports?
+ actual_head_pipeline&.has_test_reports?
+ end
+
+ def compare_test_reports
+ unless has_test_reports?
+ return { status: :error, status_reason: 'This merge request does not have test reports' }
+ end
+
+ with_reactive_cache(:compare_test_results) do |data|
+ unless Ci::CompareTestReportsService.new(project)
+ .latest?(base_pipeline, actual_head_pipeline, data)
+ raise InvalidateReactiveCache
+ end
+
+ data
+ end || { status: :parsing }
+ end
+
+ def calculate_reactive_cache(identifier, *args)
+ case identifier.to_sym
+ when :compare_test_results
+ Ci::CompareTestReportsService.new(project).execute(
+ base_pipeline, actual_head_pipeline)
+ else
+ raise NotImplementedError, "Unknown identifier: #{identifier}"
+ end
+ end
+
def all_commits
# MySQL doesn't support LIMIT in a subquery.
diffs_relation = if Gitlab::Database.postgresql?
@@ -1040,23 +1094,29 @@ class MergeRequest < ActiveRecord::Base
def can_be_reverted?(current_user)
return false unless merge_commit
+ return false unless merged_at
- merged_at = metrics&.merged_at
- notes_association = notes_with_associations
+ # It is not guaranteed that Note#created_at will be strictly later than
+ # MergeRequestMetric#merged_at. Nanoseconds on MySQL may break this
+ # comparison, as will a HA environment if clocks are not *precisely*
+ # synchronized. Add a minute's leeway to compensate for both possibilities
+ cutoff = merged_at - 1.minute
- if merged_at
- # It is not guaranteed that Note#created_at will be strictly later than
- # MergeRequestMetric#merged_at. Nanoseconds on MySQL may break this
- # comparison, as will a HA environment if clocks are not *precisely*
- # synchronized. Add a minute's leeway to compensate for both possibilities
- cutoff = merged_at - 1.minute
-
- notes_association = notes_association.where('created_at >= ?', cutoff)
- end
+ notes_association = notes_with_associations.where('created_at >= ?', cutoff)
!merge_commit.has_been_reverted?(current_user, notes_association)
end
+ def merged_at
+ strong_memoize(:merged_at) do
+ next unless merged?
+
+ metrics&.merged_at ||
+ merge_event&.created_at ||
+ notes.system.reorder(nil).find_by(note: 'merged')&.created_at
+ end
+ end
+
def can_be_cherry_picked?
merge_commit.present?
end
@@ -1119,6 +1179,12 @@ class MergeRequest < ActiveRecord::Base
true
end
+ def base_pipeline
+ @base_pipeline ||= project.pipelines
+ .order(id: :desc)
+ .find_by(sha: diff_base_sha)
+ end
+
def discussions_rendered_on_frontend?
true
end
diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb
index 9e660eccd86..65e94a97b0a 100644
--- a/app/models/merge_request/metrics.rb
+++ b/app/models/merge_request/metrics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequest::Metrics < ActiveRecord::Base
belongs_to :merge_request
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :pipeline_id
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 3d72c447b4b..d9393b4e545 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestDiff < ActiveRecord::Base
include Sortable
include Importable
@@ -182,7 +184,7 @@ class MergeRequestDiff < ActiveRecord::Base
end
def diffs(diff_options = nil)
- if without_files? && comparison = diff_refs.compare_in(project)
+ if without_files? && comparison = diff_refs&.compare_in(project)
# It should fetch the repository when diffs are cleaned by the system.
# We don't keep these for storage overload purposes.
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/37639
@@ -249,15 +251,13 @@ class MergeRequestDiff < ActiveRecord::Base
end
def load_diffs(options)
- raw = merge_request_diff_files.map(&:to_hash)
+ collection = merge_request_diff_files
if paths = options[:paths]
- raw = raw.select do |diff|
- paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
- end
+ collection = collection.where('old_path IN (?) OR new_path IN (?)', paths, paths)
end
- Gitlab::Git::DiffCollection.new(raw, options)
+ Gitlab::Git::DiffCollection.new(collection.map(&:to_hash), options)
end
def load_commits
diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb
index 1c2e57bb01f..4ad3690512d 100644
--- a/app/models/merge_request_diff_commit.rb
+++ b/app/models/merge_request_diff_commit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestDiffCommit < ActiveRecord::Base
include ShaAttribute
diff --git a/app/models/merge_request_diff_file.rb b/app/models/merge_request_diff_file.rb
index cd8ba6b904d..a9f110bec5c 100644
--- a/app/models/merge_request_diff_file.rb
+++ b/app/models/merge_request_diff_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestDiffFile < ActiveRecord::Base
include Gitlab::EncodingHelper
include DiffFile
diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb
index 7f7c114803d..242b65bedc0 100644
--- a/app/models/merge_requests_closing_issues.rb
+++ b/app/models/merge_requests_closing_issues.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestsClosingIssues < ActiveRecord::Base
belongs_to :merge_request
belongs_to :issue
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index d05dcfd083a..cb1def1b422 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Milestone < ActiveRecord::Base
# Represents a "No Milestone" state used for filtering Issues and Merge
# Requests that have no milestone assigned.
@@ -131,9 +133,10 @@ class Milestone < ActiveRecord::Base
rel.order(:project_id, :due_date).select('DISTINCT ON (project_id) id')
else
rel
- .group(:project_id)
+ .group(:project_id, :due_date, :id)
.having('due_date = MIN(due_date)')
.pluck(:id, :project_id, :due_date)
+ .uniq(&:second)
.map(&:first)
end
end
@@ -143,18 +146,25 @@ class Milestone < ActiveRecord::Base
end
def self.sort_by_attribute(method)
- case method.to_s
- when 'due_date_asc'
- reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC'))
- when 'due_date_desc'
- reorder(Gitlab::Database.nulls_last_order('due_date', 'DESC'))
- when 'start_date_asc'
- reorder(Gitlab::Database.nulls_last_order('start_date', 'ASC'))
- when 'start_date_desc'
- reorder(Gitlab::Database.nulls_last_order('start_date', 'DESC'))
- else
- order_by(method)
- end
+ sorted =
+ case method.to_s
+ when 'due_date_asc'
+ reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC'))
+ when 'due_date_desc'
+ reorder(Gitlab::Database.nulls_last_order('due_date', 'DESC'))
+ when 'name_asc'
+ reorder(Arel::Nodes::Ascending.new(arel_table[:title].lower))
+ when 'name_desc'
+ reorder(Arel::Nodes::Descending.new(arel_table[:title].lower))
+ when 'start_date_asc'
+ reorder(Gitlab::Database.nulls_last_order('start_date', 'ASC'))
+ when 'start_date_desc'
+ reorder(Gitlab::Database.nulls_last_order('start_date', 'DESC'))
+ else
+ order_by(method)
+ end
+
+ sorted.with_order_id_desc
end
##
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 7034c633268..b974309aeb6 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Namespace < ActiveRecord::Base
include CacheMarkdownField
include Sortable
@@ -122,6 +124,7 @@ class Namespace < ActiveRecord::Base
def to_param
full_path
end
+ alias_method :flipper_id, :to_param
def human_name
owner_name
@@ -304,7 +307,6 @@ class Namespace < ActiveRecord::Base
def write_projects_repository_config
all_projects.find_each do |project|
- project.expires_full_path_cache # we need to clear cache to validate renames correctly
project.write_repository_config
end
end
diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb
index 22d48c9e661..6c5a4c56377 100644
--- a/app/models/network/commit.rb
+++ b/app/models/network/commit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Network
class Commit
include ActionView::Helpers::TagHelper
@@ -11,8 +13,8 @@ module Network
@parent_spaces = []
end
- def method_missing(m, *args, &block)
- @commit.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
+ def method_missing(msg, *args, &block)
+ @commit.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
def space
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index 1e0d1f9edcb..1431dfefc55 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Network
class Graph
attr_reader :days, :commits, :map, :notes, :repo
diff --git a/app/models/note.rb b/app/models/note.rb
index abc40d9016e..2e343b8f9f8 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# A note on the root of an issue, merge request, commit, or snippet.
#
# A note of this type is never resolvable.
@@ -101,7 +103,7 @@ class Note < ActiveRecord::Base
scope :inc_author_project, -> { includes(:project, :author) }
scope :inc_author, -> { includes(:author) }
scope :inc_relations_for_view, -> do
- includes(:project, :author, :updated_by, :resolved_by, :award_emoji,
+ includes(:project, { author: :status }, :updated_by, :resolved_by, :award_emoji,
:system_note_metadata, :note_diff_file)
end
@@ -202,7 +204,7 @@ class Note < ActiveRecord::Base
end
def hook_attrs
- attributes
+ Gitlab::HookData::NoteBuilder.new(self).build
end
def for_commit?
@@ -229,6 +231,10 @@ class Note < ActiveRecord::Base
!for_personal_snippet?
end
+ def for_issuable?
+ for_issue? || for_merge_request?
+ end
+
def skip_project_check?
!for_project_noteable?
end
diff --git a/app/models/note_diff_file.rb b/app/models/note_diff_file.rb
index e688018a6d9..27aef7adc48 100644
--- a/app/models/note_diff_file.rb
+++ b/app/models/note_diff_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteDiffFile < ActiveRecord::Base
include DiffFile
diff --git a/app/models/notification_reason.rb b/app/models/notification_reason.rb
index c3965565022..0a13487574f 100644
--- a/app/models/notification_reason.rb
+++ b/app/models/notification_reason.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Holds reasons for a notification to have been sent as well as a priority list to select which reason to use
# above the rest
class NotificationReason
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index 1a03dd9df56..9f16eefe074 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NotificationRecipient
include Gitlab::Utils::StrongMemoize
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 9195408551f..1df3a51a7fc 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NotificationSetting < ActiveRecord::Base
include IgnorableColumn
@@ -32,6 +34,7 @@ class NotificationSetting < ActiveRecord::Base
:reopen_issue,
:close_issue,
:reassign_issue,
+ :issue_due,
:new_merge_request,
:push_to_merge_request,
:reopen_merge_request,
diff --git a/app/models/oauth_access_grant.rb b/app/models/oauth_access_grant.rb
index 3a997406565..d5a8a1a25b6 100644
--- a/app/models/oauth_access_grant.rb
+++ b/app/models/oauth_access_grant.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class OauthAccessGrant < Doorkeeper::AccessGrant
belongs_to :resource_owner, class_name: 'User'
belongs_to :application, class_name: 'Doorkeeper::Application'
diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb
index e8595b13d6d..0aa920fa828 100644
--- a/app/models/oauth_access_token.rb
+++ b/app/models/oauth_access_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class OauthAccessToken < Doorkeeper::AccessToken
belongs_to :resource_owner, class_name: 'User'
belongs_to :application, class_name: 'Doorkeeper::Application'
diff --git a/app/models/out_of_context_discussion.rb b/app/models/out_of_context_discussion.rb
index 4227c40b69a..4de717e2c51 100644
--- a/app/models/out_of_context_discussion.rb
+++ b/app/models/out_of_context_discussion.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# When notes on a commit are displayed in the context of a merge request that
# contains that commit, they are displayed as if they were a discussion.
#
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index bfea64c3759..7739a3894d3 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PagesDomain < ActiveRecord::Base
VERIFICATION_KEY = 'gitlab-pages-verification-code'.freeze
VERIFICATION_THRESHOLD = 3.days.freeze
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 063dc521324..207146479c0 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PersonalAccessToken < ActiveRecord::Base
include Expirable
include TokenAuthenticatable
diff --git a/app/models/personal_snippet.rb b/app/models/personal_snippet.rb
index 355624fd552..1b5be8698b1 100644
--- a/app/models/personal_snippet.rb
+++ b/app/models/personal_snippet.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PersonalSnippet < Snippet
include WithUploads
end
diff --git a/app/models/postgresql/replication_slot.rb b/app/models/postgresql/replication_slot.rb
new file mode 100644
index 00000000000..70c7432e6b5
--- /dev/null
+++ b/app/models/postgresql/replication_slot.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Postgresql
+ class ReplicationSlot < ActiveRecord::Base
+ self.table_name = 'pg_replication_slots'
+
+ # Returns true if the lag observed across all replication slots exceeds a
+ # given threshold.
+ #
+ # max - The maximum replication lag size, in bytes. Based on GitLab.com
+ # statistics it takes between 1 and 5 seconds to replicate around
+ # 100 MB of data.
+ def self.lag_too_great?(max = 100.megabytes)
+ lag_function = "#{Gitlab::Database.pg_wal_lsn_diff}" \
+ "(#{Gitlab::Database.pg_current_wal_insert_lsn}(), restart_lsn)::bigint"
+
+ # We force the use of a transaction here so the query always goes to the
+ # primary, even when using the EE DB load balancer.
+ sizes = transaction { pluck(lag_function) }
+ too_great = sizes.count { |size| size >= max }
+
+ # If too many replicas are falling behind too much, the availability of a
+ # GitLab instance might suffer. To prevent this from happening we require
+ # at least 1 replica to have data recent enough.
+ if sizes.any? && too_great.positive?
+ (sizes.length - too_great) <= 1
+ else
+ false
+ end
+ end
+ end
+end
diff --git a/app/models/programming_language.rb b/app/models/programming_language.rb
new file mode 100644
index 00000000000..0e667dac21e
--- /dev/null
+++ b/app/models/programming_language.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class ProgrammingLanguage < ActiveRecord::Base
+ validates :name, presence: true
+ validates :color, allow_blank: false, color: true
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index cf740de9405..7735f23cb9e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'carrierwave/orm/activerecord'
class Project < ActiveRecord::Base
@@ -31,6 +33,7 @@ class Project < ActiveRecord::Base
BoardLimitExceeded = Class.new(StandardError)
+ STATISTICS_ATTRIBUTE = 'repositories_count'.freeze
NUMBER_OF_PERMITTED_BOARDS = 1
UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze
# Hashed Storage versions handle rolling out new storage to project and dependents models:
@@ -79,6 +82,10 @@ class Project < ActiveRecord::Base
after_create :create_project_feature, unless: :project_feature
+ after_create -> { SiteStatistic.track(STATISTICS_ATTRIBUTE) }
+ before_destroy ->(project) { project.project_feature.untrack_statistics_for_deletion! }
+ after_destroy -> { SiteStatistic.untrack(STATISTICS_ATTRIBUTE) }
+
after_create :create_ci_cd_settings,
unless: :ci_cd_settings,
if: proc { ProjectCiCdSetting.available? }
@@ -154,6 +161,7 @@ class Project < ActiveRecord::Base
has_one :mock_monitoring_service
has_one :microsoft_teams_service
has_one :packagist_service
+ has_one :hangouts_chat_service
# TODO: replace these relations with the fork network versions
has_one :forked_project_link, foreign_key: "forked_to_project_id"
@@ -171,6 +179,7 @@ class Project < ActiveRecord::Base
has_one :fork_network, through: :fork_network_member
has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project
+ has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
# Merge Requests for target project should be removed with it
has_many :merge_requests, foreign_key: 'target_project_id'
@@ -185,6 +194,7 @@ class Project < ActiveRecord::Base
has_many :hooks, class_name: 'ProjectHook'
has_many :protected_branches
has_many :protected_tags
+ has_many :repository_languages, -> { order "share DESC" }
has_many :project_authorizations
has_many :authorized_users, through: :project_authorizations, source: :user, class_name: 'User'
@@ -268,7 +278,8 @@ class Project < ActiveRecord::Base
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true
delegate :add_user, :add_users, to: :team
- delegate :add_guest, :add_reporter, :add_developer, :add_master, :add_role, to: :team
+ delegate :add_guest, :add_reporter, :add_developer, :add_maintainer, :add_role, to: :team
+ delegate :add_master, to: :team # @deprecated
delegate :group_runners_enabled, :group_runners_enabled=, :group_runners_enabled?, to: :ci_cd_settings
# Validations
@@ -324,6 +335,7 @@ class Project < ActiveRecord::Base
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
+ scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) }
scope :archived, -> { where(archived: true) }
scope :non_archived, -> { where(archived: false) }
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
@@ -366,8 +378,10 @@ class Project < ActiveRecord::Base
chronic_duration_attr :build_timeout_human_readable, :build_timeout, default: 3600
validates :build_timeout, allow_nil: true,
- numericality: { greater_than_or_equal_to: 600,
- message: 'needs to be at least 10 minutes' }
+ numericality: { greater_than_or_equal_to: 10.minutes,
+ less_than: 1.month,
+ only_integer: true,
+ message: 'needs to be beetween 10 minutes and 1 month' }
# Returns a collection of projects that is either public or visible to the
# logged in user.
@@ -511,6 +525,10 @@ class Project < ActiveRecord::Base
end
end
+ def has_auto_devops_implicitly_enabled?
+ auto_devops&.enabled.nil? && Gitlab::CurrentSettings.auto_devops_enabled?
+ end
+
def has_auto_devops_implicitly_disabled?
auto_devops&.enabled.nil? && !Gitlab::CurrentSettings.auto_devops_enabled?
end
@@ -555,6 +573,10 @@ class Project < ActiveRecord::Base
repository.commit_by(oid: oid)
end
+ def commits_by(oids:)
+ repository.commits_by(oids: oids)
+ end
+
# ref can't be HEAD, can only be branch/tag name or SHA
def latest_successful_builds_for(ref = default_branch)
latest_pipeline = pipelines.latest_successful_for(ref)
@@ -654,6 +676,8 @@ class Project < ActiveRecord::Base
project_import_data.credentials ||= {}
project_import_data.credentials = project_import_data.credentials.merge(credentials)
end
+
+ project_import_data
end
def import?
@@ -1249,8 +1273,6 @@ class Project < ActiveRecord::Base
return true if skip_disk_validation
return false unless repository_storage
- expires_full_path_cache # we need to clear cache to validate renames correctly
-
# Check if repository with same path already exists on disk we can
# skip this for the hashed storage because the path does not change
if legacy_storage? && repository_with_same_path_already_exists?
@@ -1326,14 +1348,6 @@ class Project < ActiveRecord::Base
:visibility_level
end
- def archive!
- update_attribute(:archived, true)
- end
-
- def unarchive!
- update_attribute(:archived, false)
- end
-
def change_head(branch)
if repository.branch_exists?(branch)
repository.before_change_head
@@ -1444,7 +1458,7 @@ class Project < ActiveRecord::Base
end
def shared_runners
- @shared_runners ||= shared_runners_available? ? Ci::Runner.shared : Ci::Runner.none
+ @shared_runners ||= shared_runners_available? ? Ci::Runner.instance_type : Ci::Runner.none
end
def group_runners
@@ -1574,47 +1588,33 @@ class Project < ActiveRecord::Base
end
def rename_repo
- new_full_path = build_full_path
+ path_before = previous_changes['path'].first
+ full_path_before = full_path_was
+ full_path_after = build_full_path
- Rails.logger.error "Attempting to rename #{full_path_was} -> #{new_full_path}"
+ Gitlab::AppLogger.info("Attempting to rename #{full_path_was} -> #{full_path_after}")
if has_container_registry_tags?
- Rails.logger.error "Project #{full_path_was} cannot be renamed because container registry tags are present!"
+ Gitlab::AppLogger.info("Project #{full_path_was} cannot be renamed because container registry tags are present!")
- # we currently doesn't support renaming repository if it contains images in container registry
+ # we currently don't support renaming repository if it contains images in container registry
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end
- expire_caches_before_rename(full_path_was)
+ expire_caches_before_rename(full_path_before)
- if storage.rename_repo
- Gitlab::AppLogger.info "Project was renamed: #{full_path_was} -> #{new_full_path}"
- rename_repo_notify!
- after_rename_repo
+ if rename_or_migrate_repository!
+ Gitlab::AppLogger.info("Project was renamed: #{full_path_before} -> #{full_path_after}")
+ after_rename_repository(full_path_before, path_before)
else
- Rails.logger.error "Repository could not be renamed: #{full_path_was} -> #{new_full_path}"
+ Gitlab::AppLogger.info("Repository could not be renamed: #{full_path_before} -> #{full_path_after}")
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
- raise StandardError.new('repository cannot be renamed')
+ raise StandardError.new('Repository cannot be renamed')
end
end
- def after_rename_repo
- write_repository_config
-
- path_before_change = previous_changes['path'].first
-
- # We need to check if project had been rolled out to move resource to hashed storage or not and decide
- # if we need execute any take action or no-op.
-
- unless hashed_storage?(:attachments)
- Gitlab::UploadsTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
- end
-
- Gitlab::PagesTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
- end
-
def write_repository_config(gl_full_path: full_path)
# We'd need to keep track of project full path otherwise directory tree
# created with hashed storage enabled cannot be usefully imported using
@@ -1625,18 +1625,6 @@ class Project < ActiveRecord::Base
nil
end
- def rename_repo_notify!
- # When we import a project overwriting the original project, there
- # is a move operation. In that case we don't want to send the instructions.
- send_move_instructions(full_path_was) unless import_started?
- expires_full_path_cache
-
- self.old_path_with_namespace = full_path_was
- SystemHooksService.new.execute_hooks_for(self, :rename)
-
- reload_repository!
- end
-
def after_import
repository.after_import
wiki.repository.after_import
@@ -1668,10 +1656,10 @@ class Project < ActiveRecord::Base
params = {
name: default_branch,
push_access_levels_attributes: [{
- access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_PUSH ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
+ access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_PUSH ? Gitlab::Access::DEVELOPER : Gitlab::Access::MAINTAINER
}],
merge_access_levels_attributes: [{
- access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
+ access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE ? Gitlab::Access::DEVELOPER : Gitlab::Access::MAINTAINER
}]
}
@@ -1734,7 +1722,7 @@ class Project < ActiveRecord::Base
:started
elsif after_export_in_progress?
:after_export_action
- elsif export_project_path
+ elsif export_project_path || export_project_object_exists?
:finished
else
:none
@@ -1749,16 +1737,21 @@ class Project < ActiveRecord::Base
import_export_shared.after_export_in_progress?
end
- def remove_exports
- return nil unless export_path.present?
-
- FileUtils.rm_rf(export_path)
+ def remove_exports(path = export_path)
+ if path.present?
+ FileUtils.rm_rf(path)
+ elsif export_project_object_exists?
+ import_export_upload.remove_export_file!
+ import_export_upload.save
+ end
end
def remove_exported_project_file
- return unless export_project_path.present?
+ remove_exports(export_project_path)
+ end
- FileUtils.rm_f(export_project_path)
+ def export_project_object_exists?
+ Gitlab::ImportExport.object_storage? && import_export_upload&.export_file&.file
end
def full_path_slug
@@ -1796,6 +1789,15 @@ class Project < ActiveRecord::Base
end
end
+ def default_environment
+ production_first = "(CASE WHEN name = 'production' THEN 0 ELSE 1 END), id ASC"
+
+ environments
+ .with_state(:available)
+ .reorder(production_first)
+ .first
+ end
+
def secret_variables_for(ref:, environment: nil)
# EE would use the environment
if protected_for?(ref)
@@ -2047,6 +2049,39 @@ class Project < ActiveRecord::Base
private
+ def rename_or_migrate_repository!
+ if Gitlab::CurrentSettings.hashed_storage_enabled? && storage_version != LATEST_STORAGE_VERSION
+ ::Projects::HashedStorageMigrationService.new(self, full_path_was).execute
+ else
+ storage.rename_repo
+ end
+ end
+
+ def after_rename_repository(full_path_before, path_before)
+ execute_rename_repository_hooks!(full_path_before)
+
+ write_repository_config
+
+ # We need to check if project had been rolled out to move resource to hashed storage or not and decide
+ # if we need execute any take action or no-op.
+ unless hashed_storage?(:attachments)
+ Gitlab::UploadsTransfer.new.rename_project(path_before, self.path, namespace.full_path)
+ end
+
+ Gitlab::PagesTransfer.new.rename_project(path_before, self.path, namespace.full_path)
+ end
+
+ def execute_rename_repository_hooks!(full_path_before)
+ # When we import a project overwriting the original project, there
+ # is a move operation. In that case we don't want to send the instructions.
+ send_move_instructions(full_path_before) unless import_started?
+
+ self.old_path_with_namespace = full_path_before
+ SystemHooksService.new.execute_hooks_for(self, :rename)
+
+ reload_repository!
+ end
+
def storage
@storage ||=
if hashed_storage?(:repository)
@@ -2175,10 +2210,13 @@ class Project < ActiveRecord::Base
merge_requests = source_of_merge_requests.opened
.where(allow_collaboration: true)
- if branch_name
- merge_requests.find_by(source_branch: branch_name)&.can_be_merged_by?(user)
- else
- merge_requests.any? { |merge_request| merge_request.can_be_merged_by?(user) }
+ # Issue for N+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/49322
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ if branch_name
+ merge_requests.find_by(source_branch: branch_name)&.can_be_merged_by?(user)
+ else
+ merge_requests.any? { |merge_request| merge_request.can_be_merged_by?(user) }
+ end
end
end
diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb
index 73302207e6b..746bb4584c9 100644
--- a/app/models/project_authorization.rb
+++ b/app/models/project_authorization.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectAuthorization < ActiveRecord::Base
belongs_to :user
belongs_to :project
diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb
index faa831b1949..155400d1a43 100644
--- a/app/models/project_auto_devops.rb
+++ b/app/models/project_auto_devops.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectAutoDevops < ActiveRecord::Base
belongs_to :project
diff --git a/app/models/project_ci_cd_setting.rb b/app/models/project_ci_cd_setting.rb
index 588cced5781..1dad235cc2b 100644
--- a/app/models/project_ci_cd_setting.rb
+++ b/app/models/project_ci_cd_setting.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectCiCdSetting < ActiveRecord::Base
belongs_to :project, inverse_of: :ci_cd_settings
diff --git a/app/models/project_custom_attribute.rb b/app/models/project_custom_attribute.rb
index 3f1a7b86a82..4e767cb3b26 100644
--- a/app/models/project_custom_attribute.rb
+++ b/app/models/project_custom_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectCustomAttribute < ActiveRecord::Base
belongs_to :project
diff --git a/app/models/project_deploy_token.rb b/app/models/project_deploy_token.rb
index ab4482f0c0b..719c492a1ff 100644
--- a/app/models/project_deploy_token.rb
+++ b/app/models/project_deploy_token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectDeployToken < ActiveRecord::Base
belongs_to :project
belongs_to :deploy_token, inverse_of: :project_deploy_tokens
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index bfb8d703ec9..d74cb2506ba 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectFeature < ActiveRecord::Base
# == Project features permissions
#
@@ -19,6 +21,7 @@ class ProjectFeature < ActiveRecord::Base
ENABLED = 20
FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze
+ STATISTICS_ATTRIBUTE = 'wikis_count'.freeze
class << self
def access_level_attribute(feature)
@@ -52,6 +55,9 @@ class ProjectFeature < ActiveRecord::Base
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
+ after_create ->(model) { SiteStatistic.track(STATISTICS_ATTRIBUTE) if model.wiki_enabled? }
+ after_update :update_site_statistics
+
def feature_available?(feature, user)
get_permission(user, access_level(feature))
end
@@ -76,8 +82,30 @@ class ProjectFeature < ActiveRecord::Base
issues_access_level > DISABLED
end
+ # This is a workaround for the removal hooks not been triggered when removing a Project.
+ #
+ # ProjectFeature is removed using database cascade index rule.
+ # This method is called by Project model when deletion starts.
+ def untrack_statistics_for_deletion!
+ return unless wiki_enabled?
+
+ SiteStatistic.untrack(STATISTICS_ATTRIBUTE)
+ end
+
private
+ def update_site_statistics
+ return unless wiki_access_level_changed?
+
+ if self.wiki_access_level_was == DISABLED
+ # possible new states are PRIVATE / ENABLED, both should be tracked
+ SiteStatistic.track(STATISTICS_ATTRIBUTE)
+ elsif self.wiki_access_level == DISABLED
+ # old state was either PRIVATE / ENABLED, only untrack if new state is DISABLED
+ SiteStatistic.untrack(STATISTICS_ATTRIBUTE)
+ end
+ end
+
# Validates builds and merge requests access level
# which cannot be higher than repository access level
def repository_children_level
diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb
index ac1e9ab2b0b..bc3759142ae 100644
--- a/app/models/project_group_link.rb
+++ b/app/models/project_group_link.rb
@@ -1,10 +1,13 @@
+# frozen_string_literal: true
+
class ProjectGroupLink < ActiveRecord::Base
include Expirable
GUEST = 10
REPORTER = 20
DEVELOPER = 30
- MASTER = 40
+ MAINTAINER = 40
+ MASTER = MAINTAINER # @deprecated
belongs_to :project
belongs_to :group
diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb
index 1d7089ccfc7..2c3080c6d8d 100644
--- a/app/models/project_import_data.rb
+++ b/app/models/project_import_data.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'carrierwave/orm/activerecord'
class ProjectImportData < ActiveRecord::Base
diff --git a/app/models/project_import_state.rb b/app/models/project_import_state.rb
index 1605317ae14..89ed09af96a 100644
--- a/app/models/project_import_state.rb
+++ b/app/models/project_import_state.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectImportState < ActiveRecord::Base
include AfterCommitQueue
diff --git a/app/models/project_label.rb b/app/models/project_label.rb
index 313815e5869..d0b16cc98b4 100644
--- a/app/models/project_label.rb
+++ b/app/models/project_label.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectLabel < Label
MAX_NUMBER_OF_PRIORITIES = 1
diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb
index 4f289e6e215..35c19049c04 100644
--- a/app/models/project_services/asana_service.rb
+++ b/app/models/project_services/asana_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'asana'
class AsanaService < Service
diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb
index 4234b8044e5..60575e45a90 100644
--- a/app/models/project_services/assembla_service.rb
+++ b/app/models/project_services/assembla_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AssemblaService < Service
prop_accessor :token, :subdomain
validates :token, presence: true, if: :activated?
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index 7f4c47a6d14..d502423726c 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BambooService < CiService
include ReactiveService
@@ -67,11 +69,11 @@ class BambooService < CiService
def execute(data)
return unless supported_events.include?(data[:object_kind])
- get_path("updateAndBuild.action?buildKey=#{build_key}")
+ get_path("updateAndBuild.action", { buildKey: build_key })
end
def calculate_reactive_cache(sha, ref)
- response = get_path("rest/api/latest/result?label=#{sha}")
+ response = get_path("rest/api/latest/result/byChangeset/#{sha}")
{ build_page: read_build_page(response), commit_status: read_commit_status(response) }
end
@@ -113,18 +115,20 @@ class BambooService < CiService
URI.join("#{bamboo_url}/", path).to_s
end
- def get_path(path)
+ def get_path(path, query_params = {})
url = build_url(path)
if username.blank? && password.blank?
- Gitlab::HTTP.get(url, verify: false)
+ Gitlab::HTTP.get(url, verify: false, query: query_params)
else
- url << '&os_authType=basic'
- Gitlab::HTTP.get(url, verify: false,
- basic_auth: {
- username: username,
- password: password
- })
+ query_params[:os_authType] = 'basic'
+ Gitlab::HTTP.get(url,
+ verify: false,
+ query: query_params,
+ basic_auth: {
+ username: username,
+ password: password
+ })
end
end
end
diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb
index e4e3a80976b..1a2bb6a171b 100644
--- a/app/models/project_services/bugzilla_service.rb
+++ b/app/models/project_services/bugzilla_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BugzillaService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb
index 35884c4560c..43edfde851c 100644
--- a/app/models/project_services/buildkite_service.rb
+++ b/app/models/project_services/buildkite_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "addressable/uri"
class BuildkiteService < CiService
diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb
index 0c526b53d72..f2295a95b60 100644
--- a/app/models/project_services/builds_email_service.rb
+++ b/app/models/project_services/builds_email_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class is to be removed with 9.1
# We should also by then remove BuildsEmailService from database
class BuildsEmailService < Service
diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb
index cb4af73807b..1d7877a1fb5 100644
--- a/app/models/project_services/campfire_service.rb
+++ b/app/models/project_services/campfire_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CampfireService < Service
prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated?
@@ -82,7 +84,7 @@ class CampfireService < Service
before = push[:before]
after = push[:after]
- message = ""
+ message = []
message << "[#{project.full_name}] "
message << "#{push[:user_name]} "
@@ -95,6 +97,6 @@ class CampfireService < Service
message << "#{project.web_url}/compare/#{before}...#{after}"
end
- message
+ message.join
end
end
diff --git a/app/models/project_services/chat_message/base_message.rb b/app/models/project_services/chat_message/base_message.rb
index f710fa85b5d..8c68ddc40f2 100644
--- a/app/models/project_services/chat_message/base_message.rb
+++ b/app/models/project_services/chat_message/base_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'slack-notifier'
module ChatMessage
diff --git a/app/models/project_services/chat_message/issue_message.rb b/app/models/project_services/chat_message/issue_message.rb
index 3273f41dbd2..0cdcfcf0237 100644
--- a/app/models/project_services/chat_message/issue_message.rb
+++ b/app/models/project_services/chat_message/issue_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class IssueMessage < BaseMessage
attr_reader :title
diff --git a/app/models/project_services/chat_message/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb
index f412b6833d9..58631e09538 100644
--- a/app/models/project_services/chat_message/merge_message.rb
+++ b/app/models/project_services/chat_message/merge_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class MergeMessage < BaseMessage
attr_reader :merge_request_iid
diff --git a/app/models/project_services/chat_message/note_message.rb b/app/models/project_services/chat_message/note_message.rb
index 7f9486132e6..741474fb27b 100644
--- a/app/models/project_services/chat_message/note_message.rb
+++ b/app/models/project_services/chat_message/note_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class NoteMessage < BaseMessage
attr_reader :note
diff --git a/app/models/project_services/chat_message/pipeline_message.rb b/app/models/project_services/chat_message/pipeline_message.rb
index 96fd23aede3..62aec4351db 100644
--- a/app/models/project_services/chat_message/pipeline_message.rb
+++ b/app/models/project_services/chat_message/pipeline_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class PipelineMessage < BaseMessage
attr_reader :ref_type
diff --git a/app/models/project_services/chat_message/push_message.rb b/app/models/project_services/chat_message/push_message.rb
index 8d599c5f116..82be33a12a1 100644
--- a/app/models/project_services/chat_message/push_message.rb
+++ b/app/models/project_services/chat_message/push_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class PushMessage < BaseMessage
attr_reader :after
diff --git a/app/models/project_services/chat_message/wiki_page_message.rb b/app/models/project_services/chat_message/wiki_page_message.rb
index d84b80f2de2..b605d289278 100644
--- a/app/models/project_services/chat_message/wiki_page_message.rb
+++ b/app/models/project_services/chat_message/wiki_page_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class WikiPageMessage < BaseMessage
attr_reader :title
diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb
index a60b4c7fd0d..c10ee07ccf4 100644
--- a/app/models/project_services/chat_notification_service.rb
+++ b/app/models/project_services/chat_notification_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for Chat notifications services
# This class is not meant to be used directly, but only to inherit from.
class ChatNotificationService < Service
diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb
index 82979c8bd34..f0ef2d925ab 100644
--- a/app/models/project_services/ci_service.rb
+++ b/app/models/project_services/ci_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for CI services
# List methods you need to implement to get your CI service
# working with GitLab Merge Requests
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
index 456c7f5cee2..b8f8072869c 100644
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CustomIssueTrackerService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
diff --git a/app/models/project_services/deployment_service.rb b/app/models/project_services/deployment_service.rb
index 5b8320158fc..6dae4f3a4a6 100644
--- a/app/models/project_services/deployment_service.rb
+++ b/app/models/project_services/deployment_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for deployment services
#
# These services integrate with a deployment solution like Kubernetes/OpenShift,
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index ab4e46da89f..158ae0bf255 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DroneCiService < CiService
include ReactiveService
diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb
index b604d860a87..fb73d430fb1 100644
--- a/app/models/project_services/emails_on_push_service.rb
+++ b/app/models/project_services/emails_on_push_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EmailsOnPushService < Service
boolean_accessor :send_from_committer_email
boolean_accessor :disable_diffs
diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb
index a4b1ef09e93..d2835c6ac82 100644
--- a/app/models/project_services/external_wiki_service.rb
+++ b/app/models/project_services/external_wiki_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ExternalWikiService < Service
prop_accessor :external_wiki_url
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index da01ac1b7cf..2545df06f6b 100644
--- a/app/models/project_services/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "flowdock-git-hook"
# Flow dock depends on Grit to compute the number of commits between two given
diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
index 8a6b0ed1a5f..67a92c441b1 100644
--- a/app/models/project_services/gemnasium_service.rb
+++ b/app/models/project_services/gemnasium_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "gemnasium/gitlab_service"
class GemnasiumService < Service
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 16e32a4139e..fa9abf58e62 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitlabIssueTrackerService < IssueTrackerService
include Gitlab::Routing
diff --git a/app/models/project_services/hangouts_chat_service.rb b/app/models/project_services/hangouts_chat_service.rb
new file mode 100644
index 00000000000..272cd0f4e47
--- /dev/null
+++ b/app/models/project_services/hangouts_chat_service.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'hangouts_chat'
+
+class HangoutsChatService < ChatNotificationService
+ def title
+ 'Hangouts Chat'
+ end
+
+ def description
+ 'Receive event notifications in Google Hangouts Chat'
+ end
+
+ def self.to_param
+ 'hangouts_chat'
+ end
+
+ def help
+ 'This service sends notifications about projects events to Google Hangouts Chat room.<br />
+ To set up this service:
+ <ol>
+ <li><a href="https://developers.google.com/hangouts/chat/how-tos/webhooks">Set up an incoming webhook for your room</a>. All notifications will come to this room.</li>
+ <li>Paste the <strong>Webhook URL</strong> into the field below.</li>
+ <li>Select events below to enable notifications.</li>
+ </ol>'
+ end
+
+ def event_field(event)
+ end
+
+ def default_channel_placeholder
+ end
+
+ def webhook_placeholder
+ 'https://chat.googleapis.com/v1/spaces…'
+ end
+
+ def default_fields
+ [
+ { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
+ { type: 'checkbox', name: 'notify_only_broken_pipelines' },
+ { type: 'checkbox', name: 'notify_only_default_branch' }
+ ]
+ end
+
+ private
+
+ def notify(message, opts)
+ simple_text = parse_simple_text_message(message)
+ HangoutsChat::Sender.new(webhook).simple(simple_text)
+ end
+
+ def parse_simple_text_message(message)
+ header = message.pretext
+ return header if message.attachments.empty?
+
+ attachment = message.attachments.first
+ title = format_attachment_title(attachment)
+ body = attachment[:text]
+
+ [header, title, body].compact.join("\n")
+ end
+
+ def format_attachment_title(attachment)
+ return attachment[:title] unless attachment[:title_link]
+
+ "<#{attachment[:title_link]}|#{attachment[:title]}>"
+ end
+end
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index dce878e485f..66012f0da99 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class HipchatService < Service
include ActionView::Helpers::SanitizeHelper
@@ -108,7 +110,7 @@ class HipchatService < Service
before = push[:before]
after = push[:after]
- message = ""
+ message = []
message << "#{push[:user_name]} "
if Gitlab::Git.blank_ref?(before)
@@ -132,7 +134,7 @@ class HipchatService < Service
end
end
- message
+ message.join
end
def markdown(text, options = {})
@@ -165,11 +167,11 @@ class HipchatService < Service
description = obj_attr[:description]
issue_link = "<a href=\"#{issue_url}\">issue ##{issue_iid}</a>"
- message = "#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"
+ message = ["#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"]
message << "<pre>#{markdown(description)}</pre>"
- message
+ message.join
end
def create_merge_request_message(data)
@@ -184,12 +186,11 @@ class HipchatService < Service
merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_id}</a>"
- message = "#{user_name} #{state} #{merge_request_link} in " \
- "#{project_link}: <b>#{title}</b>"
+ message = ["#{user_name} #{state} #{merge_request_link} in " \
+ "#{project_link}: <b>#{title}</b>"]
message << "<pre>#{markdown(description)}</pre>"
-
- message
+ message.join
end
def format_title(title)
@@ -235,12 +236,11 @@ class HipchatService < Service
end
subject_html = "<a href=\"#{note_url}\">#{subject_type} #{subject_desc}</a>"
- message = "#{user_name} commented on #{subject_html} in #{project_link}: "
+ message = ["#{user_name} commented on #{subject_html} in #{project_link}: "]
message << title
message << "<pre>#{markdown(note, ref: commit_id)}</pre>"
-
- message
+ message.join
end
def create_pipeline_message(data)
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index 27bdf708c80..a783a314071 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
class IrkerService < Service
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index df6dcd90985..c7520d766a8 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueTrackerService < Service
validate :one_issue_tracker, if: :activated?, on: :manual_change
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 412d62388f0..cc98b3f5a41 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class JiraService < IssueTrackerService
include Gitlab::Routing
include ApplicationHelper
@@ -8,6 +10,10 @@ class JiraService < IssueTrackerService
validates :username, presence: true, if: :activated?
validates :password, presence: true, if: :activated?
+ validates :jira_issue_transition_id,
+ format: { with: Gitlab::Regex.jira_transition_id_regex, message: "transition ids can have only numbers which can be split with , or ;" },
+ allow_blank: true
+
prop_accessor :username, :password, :url, :api_url, :jira_issue_transition_id, :title, :description
before_update :reset_password
@@ -91,7 +97,7 @@ class JiraService < IssueTrackerService
{ type: 'text', name: 'api_url', title: 'JIRA API URL', placeholder: 'If different from Web URL' },
{ type: 'text', name: 'username', placeholder: '', required: true },
{ type: 'password', name: 'password', placeholder: '', required: true },
- { type: 'text', name: 'jira_issue_transition_id', title: 'Transition ID', placeholder: '' }
+ { type: 'text', name: 'jira_issue_transition_id', title: 'Transition ID(s)', placeholder: 'Use , or ; to separate multiple transition IDs' }
]
end
@@ -191,8 +197,18 @@ class JiraService < IssueTrackerService
end
end
+ # jira_issue_transition_id can have multiple values split by , or ;
+ # the issue is transitioned at the order given by the user
+ # if any transition fails it will log the error message and stop the transition sequence
def transition_issue(issue)
- issue.transitions.build.save(transition: { id: jira_issue_transition_id })
+ jira_issue_transition_id.scan(Gitlab::Regex.jira_transition_id_regex).each do |transition_id|
+ begin
+ issue.transitions.build.save!(transition: { id: transition_id })
+ rescue => error
+ Rails.logger.info "#{self.class.name} Issue Transition failed message ERROR: #{client_url} - #{error.message}"
+ return false
+ end
+ end
end
def add_issue_solved_comment(issue, commit_id, commit_url)
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
index ddd4026019b..bda1f67b8ff 100644
--- a/app/models/project_services/kubernetes_service.rb
+++ b/app/models/project_services/kubernetes_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# NOTE:
# We'll move this class to Clusters::Platforms::Kubernetes, which contains exactly the same logic.
@@ -240,7 +242,7 @@ class KubernetesService < DeploymentService
end
def deprecation_validation
- return if active_changed?(from: true, to: false)
+ return if active_changed?(from: true, to: false) || (new_record? && !active?)
if deprecated?
errors[:base] << deprecation_message
diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb
index 0362ed172c7..b8bc83b870e 100644
--- a/app/models/project_services/mattermost_service.rb
+++ b/app/models/project_services/mattermost_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MattermostService < ChatNotificationService
def title
'Mattermost notifications'
diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb
index 227d430083d..ca324f68d2d 100644
--- a/app/models/project_services/mattermost_slash_commands_service.rb
+++ b/app/models/project_services/mattermost_slash_commands_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MattermostSlashCommandsService < SlashCommandsService
include TriggersHelper
diff --git a/app/models/project_services/microsoft_teams_service.rb b/app/models/project_services/microsoft_teams_service.rb
index 99500caec0e..5b0e5fed092 100644
--- a/app/models/project_services/microsoft_teams_service.rb
+++ b/app/models/project_services/microsoft_teams_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MicrosoftTeamsService < ChatNotificationService
def title
'Microsoft Teams Notification'
diff --git a/app/models/project_services/mock_ci_service.rb b/app/models/project_services/mock_ci_service.rb
index b89dc07a73e..6883976f0c8 100644
--- a/app/models/project_services/mock_ci_service.rb
+++ b/app/models/project_services/mock_ci_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# For an example companion mocking service, see https://gitlab.com/gitlab-org/gitlab-mock-ci-service
class MockCiService < CiService
ALLOWED_STATES = %w[failed canceled running pending success success_with_warnings skipped not_found].freeze
diff --git a/app/models/project_services/mock_deployment_service.rb b/app/models/project_services/mock_deployment_service.rb
index 59a3811ce5d..7ab1687f8ba 100644
--- a/app/models/project_services/mock_deployment_service.rb
+++ b/app/models/project_services/mock_deployment_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MockDeploymentService < DeploymentService
def title
'Mock deployment'
diff --git a/app/models/project_services/mock_monitoring_service.rb b/app/models/project_services/mock_monitoring_service.rb
index ed0318c6b27..bcf8f1df5da 100644
--- a/app/models/project_services/mock_monitoring_service.rb
+++ b/app/models/project_services/mock_monitoring_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MockMonitoringService < MonitoringService
def title
'Mock monitoring'
diff --git a/app/models/project_services/monitoring_service.rb b/app/models/project_services/monitoring_service.rb
index 9af68b4e821..1b530a8247b 100644
--- a/app/models/project_services/monitoring_service.rb
+++ b/app/models/project_services/monitoring_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for monitoring services
#
# These services integrate with a deployment solution like Prometheus
diff --git a/app/models/project_services/packagist_service.rb b/app/models/project_services/packagist_service.rb
index ba62a5b7ac0..003884bb7ac 100644
--- a/app/models/project_services/packagist_service.rb
+++ b/app/models/project_services/packagist_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PackagistService < Service
prop_accessor :username, :token, :server
diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb
index 4cf149ac044..6f39a5e6e83 100644
--- a/app/models/project_services/pipelines_email_service.rb
+++ b/app/models/project_services/pipelines_email_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelinesEmailService < Service
prop_accessor :recipients
boolean_accessor :notify_only_broken_pipelines
diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb
index 3476e7d2283..617e502b639 100644
--- a/app/models/project_services/pivotaltracker_service.rb
+++ b/app/models/project_services/pivotaltracker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PivotaltrackerService < Service
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index df4254e0523..509e5b6089b 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PrometheusService < MonitoringService
include PrometheusAdapter
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
index 8777a44b72f..4e48c348b45 100644
--- a/app/models/project_services/pushover_service.rb
+++ b/app/models/project_services/pushover_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PushoverService < Service
BASE_URI = 'https://api.pushover.net/1'.freeze
@@ -79,7 +81,7 @@ class PushoverService < Service
end
if data[:total_commits_count] > 0
- message << "\nTotal commits count: #{data[:total_commits_count]}"
+ message = [message, "Total commits count: #{data[:total_commits_count]}"].join("\n")
end
pushover_data = {
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
index 3721093a6d1..a80be4b06da 100644
--- a/app/models/project_services/redmine_service.rb
+++ b/app/models/project_services/redmine_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RedmineService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index 71da0af75f6..482808255f9 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SlackService < ChatNotificationService
def title
'Slack notifications'
diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb
index 1c3892a3f75..6c82e088231 100644
--- a/app/models/project_services/slack_slash_commands_service.rb
+++ b/app/models/project_services/slack_slash_commands_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SlackSlashCommandsService < SlashCommandsService
include TriggersHelper
diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
index 37ea45109ae..e3ab60adefd 100644
--- a/app/models/project_services/slash_commands_service.rb
+++ b/app/models/project_services/slash_commands_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for Chat services
# This class is not meant to be used directly, but only to inherrit from.
class SlashCommandsService < Service
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index 802678147cf..eeeff5e802a 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TeamcityService < CiService
include ReactiveService
diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb
index 25b5d777641..b3585c4cf4c 100644
--- a/app/models/project_snippet.rb
+++ b/app/models/project_snippet.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectSnippet < Snippet
belongs_to :project
belongs_to :author, class_name: "User"
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 5d4e3c34b39..781a197d56f 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectStatistics < ActiveRecord::Base
belongs_to :project
belongs_to :namespace
@@ -5,7 +7,7 @@ class ProjectStatistics < ActiveRecord::Base
before_save :update_storage_size
COLUMNS_TO_REFRESH = [:repository_size, :lfs_objects_size, :commit_count].freeze
- INCREMENTABLE_COLUMNS = [:build_artifacts_size].freeze
+ INCREMENTABLE_COLUMNS = { build_artifacts_size: %i[storage_size] }.freeze
def total_repository_size
repository_size + lfs_objects_size
@@ -38,11 +40,28 @@ class ProjectStatistics < ActiveRecord::Base
self.storage_size = repository_size + lfs_objects_size + build_artifacts_size
end
+ # Since this incremental update method does not call update_storage_size above,
+ # we have to update the storage_size here as additional column.
+ # Additional columns are updated depending on key => [columns], which allows
+ # to update statistics which are and also those which aren't included in storage_size
+ # or any other additional summary column in the future.
def self.increment_statistic(project_id, key, amount)
- raise ArgumentError, "Cannot increment attribute: #{key}" unless key.in?(INCREMENTABLE_COLUMNS)
+ raise ArgumentError, "Cannot increment attribute: #{key}" unless INCREMENTABLE_COLUMNS.key?(key)
return if amount == 0
where(project_id: project_id)
- .update_all(["#{key} = COALESCE(#{key}, 0) + (?)", amount])
+ .columns_to_increment(key, amount)
+ end
+
+ def self.columns_to_increment(key, amount)
+ updates = ["#{key} = COALESCE(#{key}, 0) + (#{amount})"]
+
+ if (additional = INCREMENTABLE_COLUMNS[key])
+ additional.each do |column|
+ updates << "#{column} = COALESCE(#{column}, 0) + (#{amount})"
+ end
+ end
+
+ update_all(updates.join(', '))
end
end
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 9a38806baab..33bc6a561f9 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectTeam
include BulkMemberAccessLoad
@@ -19,10 +21,13 @@ class ProjectTeam
add_user(user, :developer, current_user: current_user)
end
- def add_master(user, current_user: nil)
- add_user(user, :master, current_user: current_user)
+ def add_maintainer(user, current_user: nil)
+ add_user(user, :maintainer, current_user: current_user)
end
+ # @deprecated
+ alias_method :add_master, :add_maintainer
+
def add_role(user, role, current_user: nil)
public_send(:"add_#{role}", user, current_user: current_user) # rubocop:disable GitlabSecurity/PublicSend
end
@@ -81,10 +86,13 @@ class ProjectTeam
@developers ||= fetch_members(Gitlab::Access::DEVELOPER)
end
- def masters
- @masters ||= fetch_members(Gitlab::Access::MASTER)
+ def maintainers
+ @maintainers ||= fetch_members(Gitlab::Access::MAINTAINER)
end
+ # @deprecated
+ alias_method :masters, :maintainers
+
def owners
@owners ||=
if group
@@ -136,10 +144,13 @@ class ProjectTeam
max_member_access(user.id) == Gitlab::Access::DEVELOPER
end
- def master?(user)
- max_member_access(user.id) == Gitlab::Access::MASTER
+ def maintainer?(user)
+ max_member_access(user.id) == Gitlab::Access::MAINTAINER
end
+ # @deprecated
+ alias_method :master?, :maintainer?
+
# Checks if `user` is authorized for this project, with at least the
# `min_access_level` (if given).
def member?(user, min_access_level = Gitlab::Access::GUEST)
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index a6f94b3e3b0..f4b3421f04b 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectWiki
include Gitlab::ShellAdapter
include Storage::LegacyProjectWiki
@@ -9,6 +11,7 @@ class ProjectWiki
}.freeze unless defined?(MARKUPS)
CouldNotCreateWikiError = Class.new(StandardError)
+ SIDEBAR = '_sidebar'
# Returns a string describing what went wrong after
# an operation fails.
@@ -20,7 +23,6 @@ class ProjectWiki
@user = user
end
- delegate :empty?, to: :pages
delegate :repository_storage, :hashed_storage?, to: :project
def path
@@ -74,9 +76,13 @@ class ProjectWiki
!!find_page('home')
end
+ def empty?
+ pages(limit: 1).empty?
+ end
+
# Returns an Array of Gitlab WikiPage instances or an
# empty Array if this Wiki has no pages.
- def pages(limit: nil)
+ def pages(limit: 0)
wiki.pages(limit: limit).map { |page| WikiPage.new(self, page, true) }
end
@@ -95,6 +101,10 @@ class ProjectWiki
end
end
+ def find_sidebar(version = nil)
+ find_page(SIDEBAR, version)
+ end
+
def find_file(name, version = nil)
wiki.file(name, version)
end
@@ -107,7 +117,7 @@ class ProjectWiki
update_project_activity
rescue Gitlab::Git::Wiki::DuplicatePageError => e
@error_message = "Duplicate page: #{e.message}"
- return false
+ false
end
def update_page(page, content:, title: nil, format: :markdown, message: nil)
diff --git a/app/models/protectable_dropdown.rb b/app/models/protectable_dropdown.rb
index c96edc5a259..25e70ab406c 100644
--- a/app/models/protectable_dropdown.rb
+++ b/app/models/protectable_dropdown.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectableDropdown
REF_TYPES = %i[branches tags].freeze
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index dff99cfca35..6c1073265a1 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedBranch < ActiveRecord::Base
include Gitlab::ShellAdapter
include ProtectedRef
diff --git a/app/models/protected_branch/merge_access_level.rb b/app/models/protected_branch/merge_access_level.rb
index e8d35ac326f..b0d5c64e931 100644
--- a/app/models/protected_branch/merge_access_level.rb
+++ b/app/models/protected_branch/merge_access_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
end
diff --git a/app/models/protected_branch/push_access_level.rb b/app/models/protected_branch/push_access_level.rb
index 7a2e9e5ec5d..b2a88229853 100644
--- a/app/models/protected_branch/push_access_level.rb
+++ b/app/models/protected_branch/push_access_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
end
diff --git a/app/models/protected_ref_matcher.rb b/app/models/protected_ref_matcher.rb
index d970f2b01fc..bfa9180ac93 100644
--- a/app/models/protected_ref_matcher.rb
+++ b/app/models/protected_ref_matcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedRefMatcher
def initialize(protected_ref)
@protected_ref = protected_ref
diff --git a/app/models/protected_tag.rb b/app/models/protected_tag.rb
index 42a9bcf7723..a36f0d36262 100644
--- a/app/models/protected_tag.rb
+++ b/app/models/protected_tag.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedTag < ActiveRecord::Base
include Gitlab::ShellAdapter
include ProtectedRef
diff --git a/app/models/protected_tag/create_access_level.rb b/app/models/protected_tag/create_access_level.rb
index 6b6ab3d8279..b06e55fb5dd 100644
--- a/app/models/protected_tag/create_access_level.rb
+++ b/app/models/protected_tag/create_access_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedTag::CreateAccessLevel < ActiveRecord::Base
include ProtectedTagAccess
diff --git a/app/models/push_event.rb b/app/models/push_event.rb
index 90c085c888e..9c0267c3140 100644
--- a/app/models/push_event.rb
+++ b/app/models/push_event.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PushEvent < Event
# This validation exists so we can't accidentally use PushEvent with a
# different "action" value.
diff --git a/app/models/push_event_payload.rb b/app/models/push_event_payload.rb
index 6cdb1cd4fe9..c7769edf055 100644
--- a/app/models/push_event_payload.rb
+++ b/app/models/push_event_payload.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PushEventPayload < ActiveRecord::Base
include ShaAttribute
diff --git a/app/models/readme_blob.rb b/app/models/readme_blob.rb
index 1863a08f1de..7b49fa632f6 100644
--- a/app/models/readme_blob.rb
+++ b/app/models/readme_blob.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ReadmeBlob < SimpleDelegator
attr_reader :repository
diff --git a/app/models/redirect_route.rb b/app/models/redirect_route.rb
index 31de204d824..c6bd4bb6dfa 100644
--- a/app/models/redirect_route.rb
+++ b/app/models/redirect_route.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RedirectRoute < ActiveRecord::Base
belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
diff --git a/app/models/release.rb b/app/models/release.rb
index c936899799e..cba80ad30ca 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Release < ActiveRecord::Base
include CacheMarkdownField
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index c4b5dd2dc96..833faf3bc82 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RemoteMirror < ActiveRecord::Base
include AfterCommitQueue
@@ -48,22 +50,22 @@ class RemoteMirror < ActiveRecord::Base
state :failed
after_transition any => :started do |remote_mirror, _|
- Gitlab::Metrics.add_event(:remote_mirrors_running, path: remote_mirror.project.full_path)
+ Gitlab::Metrics.add_event(:remote_mirrors_running)
remote_mirror.update(last_update_started_at: Time.now)
end
after_transition started: :finished do |remote_mirror, _|
- Gitlab::Metrics.add_event(:remote_mirrors_finished, path: remote_mirror.project.full_path)
+ Gitlab::Metrics.add_event(:remote_mirrors_finished)
timestamp = Time.now
- remote_mirror.update_attributes!(
+ remote_mirror.update!(
last_update_at: timestamp, last_successful_update_at: timestamp, last_error: nil
)
end
after_transition started: :failed do |remote_mirror, _|
- Gitlab::Metrics.add_event(:remote_mirrors_failed, path: remote_mirror.project.full_path)
+ Gitlab::Metrics.add_event(:remote_mirrors_failed)
remote_mirror.update(last_update_at: Time.now)
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 3056c20516a..69f375dc6f3 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'securerandom'
class Repository
@@ -83,7 +85,7 @@ class Repository
@raw_repository&.cleanup
end
- # Return absolute path to repository
+ # Don't use this! It's going away. Use Gitaly to read or write from repos.
def path_to_repo
@path_to_repo ||=
begin
@@ -99,11 +101,11 @@ class Repository
"#<#{self.class.name}:#{@disk_path}>"
end
- def commit(ref = 'HEAD')
+ def commit(ref = nil)
return nil unless exists?
return ref if ref.is_a?(::Commit)
- find_commit(ref)
+ find_commit(ref || root_ref)
end
# Finding a commit by the passed SHA
@@ -154,12 +156,9 @@ class Repository
# Returns a list of commits that are not present in any reference
def new_commits(newrev)
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1233
- refs = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- ::Gitlab::Git::RevList.new(raw, newrev: newrev).new_refs
- end
+ commits = raw.new_commits(newrev)
- refs.map { |sha| commit(sha.strip) }
+ ::Commit.decorate(commits, project)
end
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/384
@@ -174,8 +173,8 @@ class Repository
CommitCollection.new(project, commits, ref)
end
- def find_branch(name, fresh_repo: true)
- raw_repository.find_branch(name, fresh_repo)
+ def find_branch(name)
+ raw_repository.find_branch(name)
end
def find_tag(name)
@@ -238,6 +237,12 @@ class Repository
false
end
+ def languages
+ return [] if empty?
+
+ raw_repository.languages(root_ref)
+ end
+
# Makes sure a commit is kept around when Git garbage collection runs.
# Git GC will delete commits from the repository that are no longer in any
# branches or tags, but we want to keep some of these commits around, for
@@ -250,7 +255,7 @@ class Repository
# This will still fail if the file is corrupted (e.g. 0 bytes)
raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
rescue Gitlab::Git::CommandError => ex
- Rails.logger.error "Unable to create keep-around reference for repository #{path}: #{ex}"
+ Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}"
end
def kept_around?(sha)
@@ -283,6 +288,10 @@ class Repository
)
end
+ def cached_methods
+ CACHED_METHODS
+ end
+
def expire_tags_cache
expire_method_caches(%i(tag_names tag_count))
@tags = nil
@@ -311,6 +320,8 @@ class Repository
# types - An Array of file types (e.g. `:readme`) used to refresh extra
# caches.
def refresh_method_caches(types)
+ return if types.empty?
+
to_refresh = []
types.each do |type|
@@ -423,12 +434,14 @@ class Repository
# Runs code after the HEAD of a repository is changed.
def after_change_head
- expire_method_caches(METHOD_CACHES_FOR_FILE_TYPES.keys)
+ expire_all_method_caches
end
# Runs code after a repository has been forked/imported.
def after_import
expire_content_cache
+
+ DetectRepositoryLanguagesWorker.perform_async(project.id, project.owner.id)
end
# Runs code after a new commit has been pushed.
@@ -458,12 +471,12 @@ class Repository
expire_branches_cache
end
- def method_missing(m, *args, &block)
- if m == :lookup && !block_given?
- lookup_cache[m] ||= {}
- lookup_cache[m][args.join(":")] ||= raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
+ def method_missing(msg, *args, &block)
+ if msg == :lookup && !block_given?
+ lookup_cache[msg] ||= {}
+ lookup_cache[msg][args.join(":")] ||= raw_repository.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
else
- raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
+ raw_repository.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
end
@@ -560,7 +573,7 @@ class Repository
end
def rendered_readme
- MarkupHelper.markup_unsafe(readme.name, readme.data, project: project) if readme
+ MarkupHelper.markup_unsafe(readme.name, readme.data, project: project, markdown_engine: :redcarpet) if readme
end
cache_method :rendered_readme
@@ -1028,7 +1041,7 @@ class Repository
end
def repository_event(event, tags = {})
- Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags))
+ Gitlab::Metrics.add_event(event, tags)
end
def initialize_raw_repository
diff --git a/app/models/repository_language.rb b/app/models/repository_language.rb
new file mode 100644
index 00000000000..b18142a2ac4
--- /dev/null
+++ b/app/models/repository_language.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class RepositoryLanguage < ActiveRecord::Base
+ belongs_to :project
+ belongs_to :programming_language
+
+ default_scope { includes(:programming_language) }
+
+ validates :project, presence: true
+ validates :share, inclusion: { in: 0..100, message: "The share of a lanuage is between 0 and 100" }
+ validates :programming_language, uniqueness: { scope: :project_id }
+
+ delegate :name, :color, to: :programming_language
+end
diff --git a/app/models/resource_label_event.rb b/app/models/resource_label_event.rb
new file mode 100644
index 00000000000..42c255fcd1e
--- /dev/null
+++ b/app/models/resource_label_event.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+# This model is not used yet, it will be used for:
+# https://gitlab.com/gitlab-org/gitlab-ce/issues/48483
+class ResourceLabelEvent < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :issue
+ belongs_to :merge_request
+ belongs_to :label
+
+ validates :user, presence: true, on: :create
+ validates :label, presence: true, on: :create
+ validate :exactly_one_issuable
+
+ enum action: {
+ add: 1,
+ remove: 2
+ }
+
+ def self.issuable_columns
+ %i(issue_id merge_request_id).freeze
+ end
+
+ def issuable
+ issue || merge_request
+ end
+
+ private
+
+ def exactly_one_issuable
+ if self.class.issuable_columns.count { |attr| self[attr] } != 1
+ errors.add(:base, "Exactly one of #{self.class.issuable_columns.join(', ')} is required")
+ end
+ end
+end
diff --git a/app/models/route.rb b/app/models/route.rb
index 2d609920051..4b23dfa5778 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Route < ActiveRecord::Base
include CaseSensitivity
diff --git a/app/models/security_event.rb b/app/models/security_event.rb
index d131c11cb6c..3fe4cc99c9b 100644
--- a/app/models/security_event.rb
+++ b/app/models/security_event.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class SecurityEvent < AuditEvent
end
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index 3da7c301d28..e65b3df0fb6 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SentNotification < ActiveRecord::Base
serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
diff --git a/app/models/service.rb b/app/models/service.rb
index 1d259bcfec7..140058771ee 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# To add new service you should build a class inherited from Service
# and implement a set of methods
class Service < ActiveRecord::Base
@@ -254,6 +256,7 @@ class Service < ActiveRecord::Base
emails_on_push
external_wiki
flowdock
+ hangouts_chat
hipchat
irker
jira
@@ -281,9 +284,9 @@ class Service < ActiveRecord::Base
def self.build_from_template(project_id, template)
service = template.dup
- service.active = false unless service.valid?
service.template = false
service.project_id = project_id
+ service.active = false if service.active? && !service.valid?
service
end
diff --git a/app/models/site_statistic.rb b/app/models/site_statistic.rb
new file mode 100644
index 00000000000..daac1c57db9
--- /dev/null
+++ b/app/models/site_statistic.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+class SiteStatistic < ActiveRecord::Base
+ # prevents the creation of multiple rows
+ default_value_for :id, 1
+
+ COUNTER_ATTRIBUTES = %w(repositories_count wikis_count).freeze
+ REQUIRED_SCHEMA_VERSION = 20180629153018
+
+ # Tracks specific attribute
+ #
+ # @param [String] raw_attribute must be one of the values listed in COUNTER_ATTRIBUTES
+ def self.track(raw_attribute)
+ with_statistics_available(raw_attribute) do |attribute|
+ SiteStatistic.update_all(["#{attribute} = #{attribute}+1"])
+ end
+ end
+
+ # Untracks specific attribute
+ #
+ # @param [String] raw_attribute must be one of the values listed in COUNTER_ATTRIBUTES
+ def self.untrack(raw_attribute)
+ with_statistics_available(raw_attribute) do |attribute|
+ SiteStatistic.update_all(["#{attribute} = #{attribute}-1 WHERE #{attribute} > 0"])
+ end
+ end
+
+ # Wrapper for track/untrack operations with basic validations and enforced requirements
+ #
+ # @param [String] raw_attribute must be one of the values listed in COUNTER_ATTRIBUTES
+ # @yield [String] attribute quoted to be used inside SQL / Arel query
+ def self.with_statistics_available(raw_attribute)
+ unless raw_attribute.in?(COUNTER_ATTRIBUTES)
+ raise ArgumentError, "Invalid attribute: '#{raw_attribute}' to '#{caller_locations(1, 1)[0].label}' method. " \
+ "Valid attributes are: #{COUNTER_ATTRIBUTES.join(', ')}"
+ end
+
+ return unless available?
+
+ self.fetch # make sure record exists
+
+ attribute = self.connection.quote_column_name(raw_attribute)
+
+ # will be running on its own transaction context
+ yield(attribute)
+ end
+
+ # Returns a site statistic record with tracked information
+ #
+ # @return [SiteStatistic] record with tracked information
+ def self.fetch
+ SiteStatistic.transaction(requires_new: true) do
+ SiteStatistic.first_or_create!
+ end
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+
+ # Return whether required schema change is available
+ #
+ # This is needed in order to degrade gracefully when testing schema migrations
+ #
+ # @return [Boolean] whether schema is available
+ def self.available?
+ @available_flag ||= ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION
+ end
+
+ # Resets cached column information
+ #
+ # This is called during schema migration specs, in order to reset internal cache state
+ def self.reset_column_information
+ @available_flag = nil
+
+ super
+ end
+end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 644120453cf..5b394e3fa79 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Snippet < ActiveRecord::Base
include Gitlab::VisibilityLevel
include CacheMarkdownField
@@ -49,6 +51,7 @@ class Snippet < ActiveRecord::Base
scope :are_public, -> { where(visibility_level: Snippet::PUBLIC) }
scope :public_and_internal, -> { where(visibility_level: [Snippet::PUBLIC, Snippet::INTERNAL]) }
scope :fresh, -> { order("created_at DESC") }
+ scope :inc_relations_for_view, -> { includes(author: :status) }
participant :author
participant :notes_with_associations
diff --git a/app/models/snippet_blob.rb b/app/models/snippet_blob.rb
index fa5fa151607..cf1ab089829 100644
--- a/app/models/snippet_blob.rb
+++ b/app/models/snippet_blob.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SnippetBlob
include BlobLike
diff --git a/app/models/spam_log.rb b/app/models/spam_log.rb
index 56a115d1db4..ef3f974b959 100644
--- a/app/models/spam_log.rb
+++ b/app/models/spam_log.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SpamLog < ActiveRecord::Base
belongs_to :user
diff --git a/app/models/storage/hashed_project.rb b/app/models/storage/hashed_project.rb
index 26b4b78ac64..90710f73fd3 100644
--- a/app/models/storage/hashed_project.rb
+++ b/app/models/storage/hashed_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Storage
class HashedProject
attr_accessor :project
diff --git a/app/models/storage/legacy_project.rb b/app/models/storage/legacy_project.rb
index 27cb388c702..9f6f19acb41 100644
--- a/app/models/storage/legacy_project.rb
+++ b/app/models/storage/legacy_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Storage
class LegacyProject
attr_accessor :project
diff --git a/app/models/subscription.rb b/app/models/subscription.rb
index 2f0c9640744..0f6ee0ddf7e 100644
--- a/app/models/subscription.rb
+++ b/app/models/subscription.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :project
diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb
index 1c2161accc4..c5c77bc8333 100644
--- a/app/models/system_note_metadata.rb
+++ b/app/models/system_note_metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SystemNoteMetadata < ActiveRecord::Base
# These notes's action text might contain a reference that is external.
# We should always force a deep validation upon references that are found
diff --git a/app/models/term_agreement.rb b/app/models/term_agreement.rb
index c317bd0c90b..9b3c8ac68bd 100644
--- a/app/models/term_agreement.rb
+++ b/app/models/term_agreement.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TermAgreement < ActiveRecord::Base
belongs_to :term, class_name: 'ApplicationSetting::Term'
belongs_to :user
diff --git a/app/models/timelog.rb b/app/models/timelog.rb
index 659146f43e4..e04c644a53a 100644
--- a/app/models/timelog.rb
+++ b/app/models/timelog.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Timelog < ActiveRecord::Base
validates :time_spent, :user, presence: true
validate :issuable_id_is_present
diff --git a/app/models/todo.rb b/app/models/todo.rb
index a2ab405fdbe..48d92ad04b3 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Todo < ActiveRecord::Base
include Sortable
@@ -22,15 +24,18 @@ class Todo < ActiveRecord::Base
belongs_to :author, class_name: "User"
belongs_to :note
belongs_to :project
+ belongs_to :group
belongs_to :target, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations
belongs_to :user
delegate :name, :email, to: :author, prefix: true, allow_nil: true
- validates :action, :project, :target_type, :user, presence: true
+ validates :action, :target_type, :user, presence: true
validates :author, presence: true
validates :target_id, presence: true, unless: :for_commit?
validates :commit_id, presence: true, if: :for_commit?
+ validates :project, presence: true, unless: :group_id
+ validates :group, presence: true, unless: :project_id
scope :pending, -> { with_state(:pending) }
scope :done, -> { with_state(:done) }
@@ -44,7 +49,7 @@ class Todo < ActiveRecord::Base
state :done
end
- after_save :keep_around_commit
+ after_save :keep_around_commit, if: :commit_id
class << self
# Priority sorting isn't displayed in the dropdown, because we don't show
@@ -79,6 +84,10 @@ class Todo < ActiveRecord::Base
end
end
+ def parent
+ project
+ end
+
def unmergeable?
action == UNMERGEABLE
end
diff --git a/app/models/tree.rb b/app/models/tree.rb
index 4c1856b67a8..3641c33254c 100644
--- a/app/models/tree.rb
+++ b/app/models/tree.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Tree
include Gitlab::MarkupHelper
diff --git a/app/models/trending_project.rb b/app/models/trending_project.rb
index 27e3732da17..7b22e8cb760 100644
--- a/app/models/trending_project.rb
+++ b/app/models/trending_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TrendingProject < ActiveRecord::Base
belongs_to :project
diff --git a/app/models/u2f_registration.rb b/app/models/u2f_registration.rb
index 808acec098f..37598173fd1 100644
--- a/app/models/u2f_registration.rb
+++ b/app/models/u2f_registration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Registration information for U2F (universal 2nd factor) devices, like Yubikeys
class U2fRegistration < ActiveRecord::Base
diff --git a/app/models/upload.rb b/app/models/upload.rb
index cf71a7b76fc..23bc9ca42fc 100644
--- a/app/models/upload.rb
+++ b/app/models/upload.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Upload < ActiveRecord::Base
# Upper limit for foreground checksum processing
CHECKSUM_THRESHOLD = 100.megabytes
diff --git a/app/models/user.rb b/app/models/user.rb
index 8e0dc91b2a7..37f2e8b680e 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'carrierwave/orm/activerecord'
class User < ActiveRecord::Base
@@ -14,7 +16,6 @@ class User < ActiveRecord::Base
include IgnorableColumn
include FeatureGate
include CreatedAtFilterable
- include IgnorableColumn
include BulkMemberAccessLoad
include BlocksJsonSerialization
include WithUploads
@@ -99,7 +100,8 @@ class User < ActiveRecord::Base
has_many :group_members, -> { where(requested_at: nil) }, source: 'GroupMember'
has_many :groups, through: :group_members
has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
- has_many :masters_groups, -> { where(members: { access_level: Gitlab::Access::MASTER }) }, through: :group_members, source: :group
+ has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group
+ alias_attribute :masters_groups, :maintainers_groups
# Projects
has_many :groups_projects, through: :groups, source: :projects
@@ -128,7 +130,7 @@ class User < ActiveRecord::Base
has_many :builds, dependent: :nullify, class_name: 'Ci::Build' # rubocop:disable Cop/ActiveRecordDependent
has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline' # rubocop:disable Cop/ActiveRecordDependent
has_many :todos
- has_many :notification_settings, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :notification_settings
has_many :award_emoji, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id # rubocop:disable Cop/ActiveRecordDependent
@@ -141,6 +143,8 @@ class User < ActiveRecord::Base
has_many :term_agreements
belongs_to :accepted_term, class_name: 'ApplicationSetting::Term'
+ has_one :status, class_name: 'UserStatus'
+
#
# Validations
#
@@ -244,10 +248,11 @@ class User < ActiveRecord::Base
scope :blocked, -> { with_states(:blocked, :ldap_blocked) }
scope :external, -> { where(external: true) }
scope :active, -> { with_state(:active).non_internal }
- scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members WHERE user_id IS NOT NULL AND requested_at IS NULL)') }
+ scope :without_projects, -> { joins('LEFT JOIN project_authorizations ON users.id = project_authorizations.user_id').where(project_authorizations: { user_id: nil }) }
scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) }
scope :order_recent_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'DESC')) }
scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'ASC')) }
+ scope :confirmed, -> { where.not(confirmed_at: nil) }
def self.with_two_factor_indistinct
joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id")
@@ -293,14 +298,17 @@ class User < ActiveRecord::Base
end
# Find a User by their primary email or any associated secondary email
- def find_by_any_email(email)
- by_any_email(email).take
+ def find_by_any_email(email, confirmed: false)
+ by_any_email(email, confirmed: confirmed).take
end
# Returns a relation containing all the users for the given Email address
- def by_any_email(email)
+ def by_any_email(email, confirmed: false)
users = where(email: email)
+ users = users.confirmed if confirmed
+
emails = joins(:emails).where(emails: { email: email })
+ emails = emails.confirmed if confirmed
union = Gitlab::SQL::Union.new([users, emails])
from("(#{union.to_sql}) #{table_name}")
@@ -496,7 +504,7 @@ class User < ActiveRecord::Base
def disable_two_factor!
transaction do
- update_attributes(
+ update(
otp_required_for_login: false,
encrypted_otp_secret: nil,
encrypted_otp_secret_iv: nil,
@@ -728,7 +736,7 @@ class User < ActiveRecord::Base
end
def several_namespaces?
- owned_groups.any? || masters_groups.any?
+ owned_groups.any? || maintainers_groups.any?
end
def namespace_id
@@ -974,15 +982,15 @@ class User < ActiveRecord::Base
end
def manageable_groups
- union_sql = Gitlab::SQL::Union.new([owned_groups.select(:id), masters_groups.select(:id)]).to_sql
+ union_sql = Gitlab::SQL::Union.new([owned_groups.select(:id), maintainers_groups.select(:id)]).to_sql
# Update this line to not use raw SQL when migrated to Rails 5.2.
# Either ActiveRecord or Arel constructions are fine.
# This was replaced with the raw SQL construction because of bugs in the arel gem.
# Bugs were fixed in arel 9.0.0 (Rails 5.2).
- owned_and_master_groups = Group.where("namespaces.id IN (#{union_sql})") # rubocop:disable GitlabSecurity/SqlInjection
+ owned_and_maintainer_groups = Group.where("namespaces.id IN (#{union_sql})") # rubocop:disable GitlabSecurity/SqlInjection
- Gitlab::GroupHierarchy.new(owned_and_master_groups).base_and_descendants
+ Gitlab::GroupHierarchy.new(owned_and_maintainer_groups).base_and_descendants
end
def namespaces
@@ -1023,16 +1031,16 @@ class User < ActiveRecord::Base
def ci_owned_runners
@ci_owned_runners ||= begin
project_runner_ids = Ci::RunnerProject
- .where(project: authorized_projects(Gitlab::Access::MASTER))
+ .where(project: authorized_projects(Gitlab::Access::MAINTAINER))
.select(:runner_id)
group_runner_ids = Ci::RunnerNamespace
- .where(namespace_id: owned_or_masters_groups.select(:id))
+ .where(namespace_id: owned_or_maintainers_groups.select(:id))
.select(:runner_id)
union = Gitlab::SQL::Union.new([project_runner_ids, group_runner_ids])
- Ci::Runner.specific.where("ci_runners.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
+ Ci::Runner.where("ci_runners.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
end
end
@@ -1053,7 +1061,7 @@ class User < ActiveRecord::Base
return @global_notification_setting if defined?(@global_notification_setting)
@global_notification_setting = notification_settings.find_or_initialize_by(source: nil)
- @global_notification_setting.update_attributes(level: NotificationSetting.levels[DEFAULT_NOTIFICATION_LEVEL]) unless @global_notification_setting.persisted?
+ @global_notification_setting.update(level: NotificationSetting.levels[DEFAULT_NOTIFICATION_LEVEL]) unless @global_notification_setting.persisted?
@global_notification_setting
end
@@ -1236,11 +1244,14 @@ class User < ActiveRecord::Base
!terms_accepted?
end
- def owned_or_masters_groups
- union = Gitlab::SQL::Union.new([owned_groups, masters_groups])
+ def owned_or_maintainers_groups
+ union = Gitlab::SQL::Union.new([owned_groups, maintainers_groups])
Group.from("(#{union.to_sql}) namespaces")
end
+ # @deprecated
+ alias_method :owned_or_masters_groups, :owned_or_maintainers_groups
+
protected
# override, from Devise::Validatable
@@ -1333,8 +1344,8 @@ class User < ActiveRecord::Base
end
end
- def self.unique_internal(scope, username, email_pattern, &b)
- scope.first || create_unique_internal(scope, username, email_pattern, &b)
+ def self.unique_internal(scope, username, email_pattern, &block)
+ scope.first || create_unique_internal(scope, username, email_pattern, &block)
end
def self.create_unique_internal(scope, username, email_pattern, &creation_block)
diff --git a/app/models/user_agent_detail.rb b/app/models/user_agent_detail.rb
index 2d05fdd3e54..e2b2e7f1df9 100644
--- a/app/models/user_agent_detail.rb
+++ b/app/models/user_agent_detail.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserAgentDetail < ActiveRecord::Base
belongs_to :subject, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb
index 9d461c6750a..97e955ace36 100644
--- a/app/models/user_callout.rb
+++ b/app/models/user_callout.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserCallout < ActiveRecord::Base
belongs_to :user
diff --git a/app/models/user_custom_attribute.rb b/app/models/user_custom_attribute.rb
index eff25b31f9b..e0ffe8ebbfd 100644
--- a/app/models/user_custom_attribute.rb
+++ b/app/models/user_custom_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserCustomAttribute < ActiveRecord::Base
belongs_to :user
diff --git a/app/models/user_interacted_project.rb b/app/models/user_interacted_project.rb
index dd55a6acb79..ae6778e49be 100644
--- a/app/models/user_interacted_project.rb
+++ b/app/models/user_interacted_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserInteractedProject < ActiveRecord::Base
belongs_to :user
belongs_to :project
diff --git a/app/models/user_status.rb b/app/models/user_status.rb
new file mode 100644
index 00000000000..2bbb0c59ac1
--- /dev/null
+++ b/app/models/user_status.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class UserStatus < ActiveRecord::Base
+ include CacheMarkdownField
+
+ self.primary_key = :user_id
+
+ DEFAULT_EMOJI = 'speech_balloon'.freeze
+
+ belongs_to :user
+
+ validates :user, presence: true
+ validates :emoji, inclusion: { in: Gitlab::Emoji.emojis_names }
+ validates :message, length: { maximum: 100 }, allow_blank: true
+
+ cache_markdown_field :message, pipeline: :emoji
+end
diff --git a/app/models/user_synced_attributes_metadata.rb b/app/models/user_synced_attributes_metadata.rb
index 688432a9d67..7115262942d 100644
--- a/app/models/user_synced_attributes_metadata.rb
+++ b/app/models/user_synced_attributes_metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserSyncedAttributesMetadata < ActiveRecord::Base
belongs_to :user
diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb
index 0dfe597317e..bdaf58ae1c1 100644
--- a/app/models/users_star_project.rb
+++ b/app/models/users_star_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UsersStarProject < ActiveRecord::Base
belongs_to :project, counter_cache: :star_count, touch: true
belongs_to :user
diff --git a/app/models/wiki_directory.rb b/app/models/wiki_directory.rb
index 9340fc2dbbe..712ba79bbd2 100644
--- a/app/models/wiki_directory.rb
+++ b/app/models/wiki_directory.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WikiDirectory
include ActiveModel::Validations
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index cde79b95062..33790afc35e 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -1,3 +1,6 @@
+# frozen_string_literal: true
+
+# rubocop:disable Rails/ActiveRecordAliases
class WikiPage
PageChangedError = Class.new(StandardError)
PageRenameError = Class.new(StandardError)
@@ -59,7 +62,7 @@ class WikiPage
attr_accessor :attributes
def hook_attrs
- attributes
+ Gitlab::HookData::WikiPageBuilder.new(self).build
end
def initialize(wiki, page = nil, persisted = false)
diff --git a/app/policies/application_setting/term_policy.rb b/app/policies/application_setting/term_policy.rb
index f03bf748c76..17f00f33d35 100644
--- a/app/policies/application_setting/term_policy.rb
+++ b/app/policies/application_setting/term_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ApplicationSetting
class TermPolicy < BasePolicy
include Gitlab::Utils::StrongMemoize
diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb
index 603218aa6df..0d0f1c28bad 100644
--- a/app/policies/base_policy.rb
+++ b/app/policies/base_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency 'declarative_policy'
class BasePolicy < DeclarativePolicy::Base
diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb
index 1c0cc7425ec..3858b29c82c 100644
--- a/app/policies/ci/build_policy.rb
+++ b/app/policies/ci/build_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class BuildPolicy < CommitStatusPolicy
condition(:protected_ref) do
@@ -18,6 +20,10 @@ module Ci
@subject.project.branch_allows_collaboration?(@user, @subject.ref)
end
+ condition(:terminal, scope: :subject) do
+ @subject.has_terminal?
+ end
+
rule { protected_ref }.policy do
prevent :update_build
prevent :erase_build
@@ -29,5 +35,7 @@ module Ci
enable :update_build
enable :update_commit_status
end
+
+ rule { can?(:update_build) & terminal }.enable :create_build_terminal
end
end
diff --git a/app/policies/ci/pipeline_policy.rb b/app/policies/ci/pipeline_policy.rb
index b81329d0625..f9623587957 100644
--- a/app/policies/ci/pipeline_policy.rb
+++ b/app/policies/ci/pipeline_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PipelinePolicy < BasePolicy
delegate { @subject.project }
diff --git a/app/policies/ci/pipeline_schedule_policy.rb b/app/policies/ci/pipeline_schedule_policy.rb
index ecba0488d3c..cf3f784f851 100644
--- a/app/policies/ci/pipeline_schedule_policy.rb
+++ b/app/policies/ci/pipeline_schedule_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PipelineSchedulePolicy < PipelinePolicy
alias_method :pipeline_schedule, :subject
diff --git a/app/policies/ci/runner_policy.rb b/app/policies/ci/runner_policy.rb
index 895abe87d86..c44f22b6ad3 100644
--- a/app/policies/ci/runner_policy.rb
+++ b/app/policies/ci/runner_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class RunnerPolicy < BasePolicy
with_options scope: :subject, score: 0
diff --git a/app/policies/ci/trigger_policy.rb b/app/policies/ci/trigger_policy.rb
index 5592ac30812..209db44539c 100644
--- a/app/policies/ci/trigger_policy.rb
+++ b/app/policies/ci/trigger_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class TriggerPolicy < BasePolicy
delegate { @subject.project }
diff --git a/app/policies/clusters/cluster_policy.rb b/app/policies/clusters/cluster_policy.rb
index 1f7c13072b9..147943a3d6c 100644
--- a/app/policies/clusters/cluster_policy.rb
+++ b/app/policies/clusters/cluster_policy.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
module Clusters
class ClusterPolicy < BasePolicy
alias_method :cluster, :subject
delegate { cluster.project }
- rule { can?(:master_access) }.policy do
+ rule { can?(:maintainer_access) }.policy do
enable :update_cluster
enable :admin_cluster
end
diff --git a/app/policies/commit_status_policy.rb b/app/policies/commit_status_policy.rb
index 24b2a4cc7fd..eea2a24fb2d 100644
--- a/app/policies/commit_status_policy.rb
+++ b/app/policies/commit_status_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CommitStatusPolicy < BasePolicy
delegate { @subject.project }
diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb
new file mode 100644
index 00000000000..069d065280e
--- /dev/null
+++ b/app/policies/concerns/policy_actor.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+# Include this module if we want to pass something else than the user to
+# check policies. This defines several methods which the policy checker
+# would call and check.
+module PolicyActor
+ extend ActiveSupport::Concern
+
+ def blocked?
+ false
+ end
+
+ def admin?
+ false
+ end
+
+ def external?
+ false
+ end
+
+ def internal?
+ false
+ end
+
+ def access_locked?
+ false
+ end
+
+ def required_terms_not_accepted?
+ false
+ end
+
+ def can_create_group
+ false
+ end
+end
diff --git a/app/policies/deploy_key_policy.rb b/app/policies/deploy_key_policy.rb
index 62a22a59be6..204c54a5b20 100644
--- a/app/policies/deploy_key_policy.rb
+++ b/app/policies/deploy_key_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeployKeyPolicy < BasePolicy
with_options scope: :subject, score: 0
condition(:private_deploy_key) { @subject.private? }
diff --git a/app/policies/deploy_token_policy.rb b/app/policies/deploy_token_policy.rb
index 7aa9106e8b1..e648df3edfc 100644
--- a/app/policies/deploy_token_policy.rb
+++ b/app/policies/deploy_token_policy.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
class DeployTokenPolicy < BasePolicy
with_options scope: :subject, score: 0
- condition(:master) { @subject.project.team.master?(@user) }
+ condition(:maintainer) { @subject.project.team.maintainer?(@user) }
rule { anonymous }.prevent_all
- rule { master }.policy do
+ rule { maintainer }.policy do
enable :create_deploy_token
enable :update_deploy_token
end
diff --git a/app/policies/deployment_policy.rb b/app/policies/deployment_policy.rb
index 62b63b9f87b..56ac898b6ab 100644
--- a/app/policies/deployment_policy.rb
+++ b/app/policies/deployment_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeploymentPolicy < BasePolicy
delegate { @subject.project }
end
diff --git a/app/policies/environment_policy.rb b/app/policies/environment_policy.rb
index 375a5535359..d1243491f5a 100644
--- a/app/policies/environment_policy.rb
+++ b/app/policies/environment_policy.rb
@@ -1,9 +1,16 @@
+# frozen_string_literal: true
+
class EnvironmentPolicy < BasePolicy
delegate { @subject.project }
- condition(:stop_action_allowed) do
- @subject.stop_action? && can?(:update_build, @subject.stop_action)
+ condition(:stop_with_deployment_allowed) do
+ @subject.stop_action_available? &&
+ can?(:create_deployment) && can?(:update_build, @subject.stop_action)
+ end
+
+ condition(:stop_with_update_allowed) do
+ !@subject.stop_action_available? && can?(:update_environment, @subject)
end
- rule { can?(:create_deployment) & stop_action_allowed }.enable :stop_environment
+ rule { stop_with_deployment_allowed | stop_with_update_allowed }.enable :stop_environment
end
diff --git a/app/policies/external_issue_policy.rb b/app/policies/external_issue_policy.rb
index e031b38078c..1106536e075 100644
--- a/app/policies/external_issue_policy.rb
+++ b/app/policies/external_issue_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ExternalIssuePolicy < BasePolicy
delegate { @subject.project }
end
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index 1cf5515d9d7..16c58730878 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GlobalPolicy < BasePolicy
desc "User is blocked"
with_options scope: :user, score: 0
@@ -17,6 +19,11 @@ class GlobalPolicy < BasePolicy
@user&.required_terms_not_accepted?
end
+ condition(:private_instance_statistics, score: 0) { Gitlab::CurrentSettings.instance_statistics_visibility_private? }
+
+ rule { admin | (~private_instance_statistics & ~anonymous) }
+ .enable :read_instance_statistics
+
rule { anonymous }.policy do
prevent :log_in
prevent :receive_notifications
diff --git a/app/policies/group_label_policy.rb b/app/policies/group_label_policy.rb
index e3dd3296699..9f3acd44b23 100644
--- a/app/policies/group_label_policy.rb
+++ b/app/policies/group_label_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupLabelPolicy < BasePolicy
delegate { @subject.group }
end
diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb
index 23dd0d7cd23..6f1afb87c85 100644
--- a/app/policies/group_member_policy.rb
+++ b/app/policies/group_member_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupMemberPolicy < BasePolicy
delegate :group
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 520710b757d..a8d7a05f509 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupPolicy < BasePolicy
desc "Group is public"
with_options scope: :subject, score: 0
@@ -11,7 +13,7 @@ class GroupPolicy < BasePolicy
condition(:guest) { access_level >= GroupMember::GUEST }
condition(:developer) { access_level >= GroupMember::DEVELOPER }
condition(:owner) { access_level >= GroupMember::OWNER }
- condition(:master) { access_level >= GroupMember::MASTER }
+ condition(:maintainer) { access_level >= GroupMember::MAINTAINER }
condition(:reporter) { access_level >= GroupMember::REPORTER }
condition(:nested_groups_supported, scope: :global) { Group.supports_nested_groups? }
@@ -59,7 +61,7 @@ class GroupPolicy < BasePolicy
enable :admin_issue
end
- rule { master }.policy do
+ rule { maintainer }.policy do
enable :create_projects
enable :admin_pipeline
enable :admin_build
@@ -72,6 +74,19 @@ class GroupPolicy < BasePolicy
enable :change_visibility_level
end
+ rule { can?(:read_nested_project_resources) }.policy do
+ enable :read_group_activity
+ enable :read_group_issues
+ enable :read_group_boards
+ enable :read_group_labels
+ enable :read_group_milestones
+ enable :read_group_merge_requests
+ end
+
+ rule { can?(:read_cross_project) & can?(:read_group) }.policy do
+ enable :read_nested_project_resources
+ end
+
rule { owner & nested_groups_supported }.enable :create_subgroup
rule { public_group | logged_in_viewable }.enable :view_globally
diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb
index b431d376e3d..198bb168d85 100644
--- a/app/policies/issuable_policy.rb
+++ b/app/policies/issuable_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssuablePolicy < BasePolicy
delegate { @subject.project }
diff --git a/app/policies/issue_policy.rb b/app/policies/issue_policy.rb
index 263c6e3039c..94b5f37c682 100644
--- a/app/policies/issue_policy.rb
+++ b/app/policies/issue_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssuePolicy < IssuablePolicy
# This class duplicates the same check of Issue#readable_by? for performance reasons
# Make sure to sync this class checks with issue.rb to avoid security problems.
diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb
index c3fe857f8a2..a2950951d03 100644
--- a/app/policies/merge_request_policy.rb
+++ b/app/policies/merge_request_policy.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class MergeRequestPolicy < IssuablePolicy
end
diff --git a/app/policies/namespace_policy.rb b/app/policies/namespace_policy.rb
index eb01218eb0a..2babcb0a2d9 100644
--- a/app/policies/namespace_policy.rb
+++ b/app/policies/namespace_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NamespacePolicy < BasePolicy
rule { anonymous }.prevent_all
diff --git a/app/policies/nil_policy.rb b/app/policies/nil_policy.rb
index 13f46ba60f0..fc969f8cd05 100644
--- a/app/policies/nil_policy.rb
+++ b/app/policies/nil_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NilPolicy < BasePolicy
rule { default }.prevent_all
end
diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb
index 077a6761ee6..bbc2b48b856 100644
--- a/app/policies/note_policy.rb
+++ b/app/policies/note_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NotePolicy < BasePolicy
delegate { @subject.project }
delegate { @subject.noteable if DeclarativePolicy.has_policy?(@subject.noteable) }
diff --git a/app/policies/personal_snippet_policy.rb b/app/policies/personal_snippet_policy.rb
index c1a84727cfa..777f933cdcd 100644
--- a/app/policies/personal_snippet_policy.rb
+++ b/app/policies/personal_snippet_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PersonalSnippetPolicy < BasePolicy
condition(:public_snippet, scope: :subject) { @subject.public? }
condition(:is_author) { @user && @subject.author == @user }
diff --git a/app/policies/project_label_policy.rb b/app/policies/project_label_policy.rb
index 2d0f021118b..5ce896ecaf2 100644
--- a/app/policies/project_label_policy.rb
+++ b/app/policies/project_label_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectLabelPolicy < BasePolicy
delegate { @subject.project }
end
diff --git a/app/policies/project_member_policy.rb b/app/policies/project_member_policy.rb
index 9aedb620be9..f2f18406bd3 100644
--- a/app/policies/project_member_policy.rb
+++ b/app/policies/project_member_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectMemberPolicy < BasePolicy
delegate { @subject.project }
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 199bcf92b21..f52a3bad77d 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectPolicy < BasePolicy
extend ClassMethods
@@ -46,7 +48,7 @@ class ProjectPolicy < BasePolicy
condition(:developer) { team_access_level >= Gitlab::Access::DEVELOPER }
desc "User has maintainer access"
- condition(:master) { team_access_level >= Gitlab::Access::MASTER }
+ condition(:maintainer) { team_access_level >= Gitlab::Access::MAINTAINER }
desc "Project is public"
condition(:public_project, scope: :subject, score: 0) { project.public? }
@@ -123,14 +125,14 @@ class ProjectPolicy < BasePolicy
rule { guest }.enable :guest_access
rule { reporter }.enable :reporter_access
rule { developer }.enable :developer_access
- rule { master }.enable :master_access
+ rule { maintainer }.enable :maintainer_access
rule { owner | admin }.enable :owner_access
rule { can?(:owner_access) }.policy do
enable :guest_access
enable :reporter_access
enable :developer_access
- enable :master_access
+ enable :maintainer_access
enable :change_namespace
enable :change_visibility_level
@@ -228,7 +230,7 @@ class ProjectPolicy < BasePolicy
enable :create_deployment
end
- rule { can?(:master_access) }.policy do
+ rule { can?(:maintainer_access) }.policy do
enable :push_to_delete_protected_branch
enable :update_project_snippet
enable :update_environment
diff --git a/app/policies/project_policy/class_methods.rb b/app/policies/project_policy/class_methods.rb
index 60e5aba00ba..42d993406a9 100644
--- a/app/policies/project_policy/class_methods.rb
+++ b/app/policies/project_policy/class_methods.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectPolicy
module ClassMethods
def create_read_update_admin_destroy(name)
diff --git a/app/policies/project_snippet_policy.rb b/app/policies/project_snippet_policy.rb
index dd270643bbf..288bf070cfc 100644
--- a/app/policies/project_snippet_policy.rb
+++ b/app/policies/project_snippet_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectSnippetPolicy < BasePolicy
delegate :project
diff --git a/app/policies/protected_branch_policy.rb b/app/policies/protected_branch_policy.rb
index 1a7faa4db40..0e83d2e5834 100644
--- a/app/policies/protected_branch_policy.rb
+++ b/app/policies/protected_branch_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedBranchPolicy < BasePolicy
delegate { @subject.project }
diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb
index ee219f0a0d0..e1efd84e510 100644
--- a/app/policies/user_policy.rb
+++ b/app/policies/user_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserPolicy < BasePolicy
desc "The current user is the user in question"
condition(:user_is_self, score: 0) { @subject == @user }
@@ -5,11 +7,18 @@ class UserPolicy < BasePolicy
desc "This is the ghost user"
condition(:subject_ghost, scope: :subject, score: 0) { @subject.ghost? }
+ desc "The profile is private"
+ condition(:private_profile, scope: :subject, score: 0) { @subject.private_profile? }
+
rule { ~restricted_public_level }.enable :read_user
rule { ~anonymous }.enable :read_user
rule { ~subject_ghost & (user_is_self | admin) }.policy do
enable :destroy_user
enable :update_user
+ enable :update_user_status
end
+
+ rule { default }.enable :read_user_profile
+ rule { private_profile & ~(user_is_self | admin) }.prevent :read_user_profile
end
diff --git a/app/presenters/ci/build_metadata_presenter.rb b/app/presenters/ci/build_metadata_presenter.rb
index 5048f967ea8..015b1f67db7 100644
--- a/app/presenters/ci/build_metadata_presenter.rb
+++ b/app/presenters/ci/build_metadata_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class BuildMetadataPresenter < Gitlab::View::Presenter::Delegated
TIMEOUT_SOURCES = {
diff --git a/app/presenters/ci/build_presenter.rb b/app/presenters/ci/build_presenter.rb
index e0aaa5cb736..5331cdf632b 100644
--- a/app/presenters/ci/build_presenter.rb
+++ b/app/presenters/ci/build_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class BuildPresenter < CommitStatusPresenter
def erased_by_user?
diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb
new file mode 100644
index 00000000000..02f6c5bdf81
--- /dev/null
+++ b/app/presenters/ci/build_runner_presenter.rb
@@ -0,0 +1,43 @@
+module Ci
+ class BuildRunnerPresenter < SimpleDelegator
+ def artifacts
+ return unless options[:artifacts]
+
+ list = []
+ list << create_archive(options[:artifacts])
+ list << create_reports(options[:artifacts][:reports], expire_in: options[:artifacts][:expire_in])
+ list.flatten.compact
+ end
+
+ private
+
+ def create_archive(artifacts)
+ return unless artifacts[:untracked] || artifacts[:paths]
+
+ {
+ artifact_type: :archive,
+ artifact_format: :zip,
+ name: artifacts[:name],
+ untracked: artifacts[:untracked],
+ paths: artifacts[:paths],
+ when: artifacts[:when],
+ expire_in: artifacts[:expire_in]
+ }
+ end
+
+ def create_reports(reports, expire_in:)
+ return unless reports&.any?
+
+ reports.map do |k, v|
+ {
+ artifact_type: k.to_sym,
+ artifact_format: :gzip,
+ name: ::Ci::JobArtifact::DEFAULT_FILE_NAMES[k.to_sym],
+ paths: v,
+ when: 'always',
+ expire_in: expire_in
+ }
+ end
+ end
+ end
+end
diff --git a/app/presenters/ci/group_variable_presenter.rb b/app/presenters/ci/group_variable_presenter.rb
index 98d68bc7a83..99011150c84 100644
--- a/app/presenters/ci/group_variable_presenter.rb
+++ b/app/presenters/ci/group_variable_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class GroupVariablePresenter < Gitlab::View::Presenter::Delegated
presents :variable
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index cc2bce9862d..93a38f92073 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PipelinePresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
diff --git a/app/presenters/ci/variable_presenter.rb b/app/presenters/ci/variable_presenter.rb
index 96159f88c59..f027f3aa560 100644
--- a/app/presenters/ci/variable_presenter.rb
+++ b/app/presenters/ci/variable_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class VariablePresenter < Gitlab::View::Presenter::Delegated
presents :variable
diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb
index a424da5ab24..dfdd8e82f97 100644
--- a/app/presenters/clusters/cluster_presenter.rb
+++ b/app/presenters/clusters/cluster_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
class ClusterPresenter < Gitlab::View::Presenter::Delegated
presents :cluster
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index 9a7aaf4ef32..a08f34e2335 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -1,16 +1,20 @@
+# frozen_string_literal: true
+
class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
CALLOUT_FAILURE_MESSAGES = {
- unknown_failure: 'There is an unknown failure, please try again',
- api_failure: 'There has been an API failure, please try again',
- stuck_or_timeout_failure: 'There has been a timeout failure or the job got stuck. Check your timeout limits or try again',
- runner_system_failure: 'There has been a runner system failure, please try again',
- missing_dependency_failure: 'There has been a missing dependency failure'
+ unknown_failure: 'There is an unknown failure, please try again',
+ script_failure: nil,
+ api_failure: 'There has been an API failure, please try again',
+ stuck_or_timeout_failure: 'There has been a timeout failure or the job got stuck. Check your timeout limits or try again',
+ runner_system_failure: 'There has been a runner system failure, please try again',
+ missing_dependency_failure: 'There has been a missing dependency failure',
+ runner_unsupported: 'Your runner is outdated, please upgrade your runner'
}.freeze
presents :build
def callout_failure_message
- CALLOUT_FAILURE_MESSAGES[failure_reason.to_sym]
+ CALLOUT_FAILURE_MESSAGES.fetch(failure_reason.to_sym)
end
def recoverable?
diff --git a/app/presenters/conversational_development_index/metric_presenter.rb b/app/presenters/conversational_development_index/metric_presenter.rb
index bb65ba2646b..e0312c6f431 100644
--- a/app/presenters/conversational_development_index/metric_presenter.rb
+++ b/app/presenters/conversational_development_index/metric_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ConversationalDevelopmentIndex
class MetricPresenter < Gitlab::View::Presenter::Simple
def cards
diff --git a/app/presenters/generic_commit_status_presenter.rb b/app/presenters/generic_commit_status_presenter.rb
index da09df29a37..a1dc72d5b98 100644
--- a/app/presenters/generic_commit_status_presenter.rb
+++ b/app/presenters/generic_commit_status_presenter.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class GenericCommitStatusPresenter < CommitStatusPresenter
end
diff --git a/app/presenters/group_member_presenter.rb b/app/presenters/group_member_presenter.rb
index 8f53dfa105e..c4dcc9e60f9 100644
--- a/app/presenters/group_member_presenter.rb
+++ b/app/presenters/group_member_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupMemberPresenter < MemberPresenter
private
diff --git a/app/presenters/member_presenter.rb b/app/presenters/member_presenter.rb
index 7d2f9303b8f..2497bea4aff 100644
--- a/app/presenters/member_presenter.rb
+++ b/app/presenters/member_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MemberPresenter < Gitlab::View::Presenter::Delegated
presents :member
diff --git a/app/presenters/members_presenter.rb b/app/presenters/members_presenter.rb
index e4aba37b69e..03ebea36d49 100644
--- a/app/presenters/members_presenter.rb
+++ b/app/presenters/members_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MembersPresenter < Gitlab::View::Presenter::Delegated
include Enumerable
diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb
index f77b3541644..8c4eac3c31d 100644
--- a/app/presenters/merge_request_presenter.rb
+++ b/app/presenters/merge_request_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
include GitlabRoutingHelper
@@ -204,7 +206,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end
def closing_issues
- @closing_issues ||= closes_issues(current_user)
+ @closing_issues ||= visible_closing_issues_for(current_user)
end
def pipeline
diff --git a/app/presenters/project_member_presenter.rb b/app/presenters/project_member_presenter.rb
index 7f42d2b70df..e4731074e86 100644
--- a/app/presenters/project_member_presenter.rb
+++ b/app/presenters/project_member_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectMemberPresenter < MemberPresenter
private
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index ad655a7b3f4..4c2f33213d6 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectPresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::NumberHelper
include ActionView::Helpers::UrlHelper
@@ -27,6 +29,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def statistics_buttons(show_auto_devops_callout:)
[
+ readme_anchor_data,
changelog_anchor_data,
license_anchor_data,
contribution_guide_anchor_data,
@@ -212,11 +215,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def readme_anchor_data
- if current_user && can_current_user_push_to_default_branch? && repository.readme.blank?
+ if current_user && can_current_user_push_to_default_branch? && repository.readme.nil?
OpenStruct.new(enabled: false,
label: _('Add Readme'),
link: add_readme_path)
- elsif repository.readme.present?
+ elsif repository.readme
OpenStruct.new(enabled: true,
label: _('Readme'),
link: default_view != 'readme' ? readme_path : '#readme')
diff --git a/app/presenters/projects/settings/deploy_keys_presenter.rb b/app/presenters/projects/settings/deploy_keys_presenter.rb
index c226586fba5..28eaef00a12 100644
--- a/app/presenters/projects/settings/deploy_keys_presenter.rb
+++ b/app/presenters/projects/settings/deploy_keys_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module Settings
class DeployKeysPresenter < Gitlab::View::Presenter::Simple
diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb
index bdc22d71202..99663c8d5eb 100644
--- a/app/serializers/analytics_build_entity.rb
+++ b/app/serializers/analytics_build_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsBuildEntity < Grape::Entity
include RequestAwareEntity
include EntityDateHelper
diff --git a/app/serializers/analytics_build_serializer.rb b/app/serializers/analytics_build_serializer.rb
index f172d67d356..9c9f76a1c28 100644
--- a/app/serializers/analytics_build_serializer.rb
+++ b/app/serializers/analytics_build_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsBuildSerializer < BaseSerializer
entity AnalyticsBuildEntity
end
diff --git a/app/serializers/analytics_commit_entity.rb b/app/serializers/analytics_commit_entity.rb
index 402cecbfd08..b25c603c9f0 100644
--- a/app/serializers/analytics_commit_entity.rb
+++ b/app/serializers/analytics_commit_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsCommitEntity < CommitEntity
include EntityDateHelper
diff --git a/app/serializers/analytics_commit_serializer.rb b/app/serializers/analytics_commit_serializer.rb
index cdbfecf2b70..0f65687a3c0 100644
--- a/app/serializers/analytics_commit_serializer.rb
+++ b/app/serializers/analytics_commit_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsCommitSerializer < BaseSerializer
entity AnalyticsCommitEntity
end
diff --git a/app/serializers/analytics_generic_serializer.rb b/app/serializers/analytics_generic_serializer.rb
index 9f4859e8410..10391c13034 100644
--- a/app/serializers/analytics_generic_serializer.rb
+++ b/app/serializers/analytics_generic_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsGenericSerializer < BaseSerializer
def represent(resource, opts = {})
resource.symbolize_keys!
diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb
index b7d95ea020f..ab15bd0ac7a 100644
--- a/app/serializers/analytics_issue_entity.rb
+++ b/app/serializers/analytics_issue_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsIssueEntity < Grape::Entity
include RequestAwareEntity
include EntityDateHelper
diff --git a/app/serializers/analytics_issue_serializer.rb b/app/serializers/analytics_issue_serializer.rb
index 4fb3e8f1bb4..4a1777276a4 100644
--- a/app/serializers/analytics_issue_serializer.rb
+++ b/app/serializers/analytics_issue_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsIssueSerializer < AnalyticsGenericSerializer
entity AnalyticsIssueEntity
end
diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb
index 888265eaa38..b7134da9461 100644
--- a/app/serializers/analytics_merge_request_entity.rb
+++ b/app/serializers/analytics_merge_request_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsMergeRequestEntity < AnalyticsIssueEntity
expose :state
diff --git a/app/serializers/analytics_merge_request_serializer.rb b/app/serializers/analytics_merge_request_serializer.rb
index 4622a1dd855..f0b9115d02c 100644
--- a/app/serializers/analytics_merge_request_serializer.rb
+++ b/app/serializers/analytics_merge_request_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsMergeRequestSerializer < AnalyticsGenericSerializer
entity AnalyticsMergeRequestEntity
end
diff --git a/app/serializers/analytics_stage_entity.rb b/app/serializers/analytics_stage_entity.rb
index 3e355a13e06..ae7c20c3bba 100644
--- a/app/serializers/analytics_stage_entity.rb
+++ b/app/serializers/analytics_stage_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsStageEntity < Grape::Entity
include EntityDateHelper
diff --git a/app/serializers/analytics_stage_serializer.rb b/app/serializers/analytics_stage_serializer.rb
index 613cf6874d8..86786273240 100644
--- a/app/serializers/analytics_stage_serializer.rb
+++ b/app/serializers/analytics_stage_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsStageSerializer < BaseSerializer
entity AnalyticsStageEntity
end
diff --git a/app/serializers/analytics_summary_entity.rb b/app/serializers/analytics_summary_entity.rb
index 9c37afd53e1..39c6b4b06b2 100644
--- a/app/serializers/analytics_summary_entity.rb
+++ b/app/serializers/analytics_summary_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsSummaryEntity < Grape::Entity
expose :value, safe: true
expose :title
diff --git a/app/serializers/analytics_summary_serializer.rb b/app/serializers/analytics_summary_serializer.rb
index c87a24aa47c..b22bd737f03 100644
--- a/app/serializers/analytics_summary_serializer.rb
+++ b/app/serializers/analytics_summary_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsSummarySerializer < BaseSerializer
entity AnalyticsSummaryEntity
end
diff --git a/app/serializers/award_emoji_entity.rb b/app/serializers/award_emoji_entity.rb
index 6e03cd02392..212931a2fa9 100644
--- a/app/serializers/award_emoji_entity.rb
+++ b/app/serializers/award_emoji_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AwardEmojiEntity < Grape::Entity
expose :name
expose :user, using: API::Entities::UserSafe
diff --git a/app/serializers/base_serializer.rb b/app/serializers/base_serializer.rb
index 8cade280b0c..7b65bd22f54 100644
--- a/app/serializers/base_serializer.rb
+++ b/app/serializers/base_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseSerializer
attr_reader :params
diff --git a/app/serializers/blob_entity.rb b/app/serializers/blob_entity.rb
index b501fd5e964..3ac61481dea 100644
--- a/app/serializers/blob_entity.rb
+++ b/app/serializers/blob_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BlobEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/build_action_entity.rb b/app/serializers/build_action_entity.rb
index f2d76a8ad81..f9da3f63911 100644
--- a/app/serializers/build_action_entity.rb
+++ b/app/serializers/build_action_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildActionEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/build_artifact_entity.rb b/app/serializers/build_artifact_entity.rb
index 6e0e33bc09b..414f436e76e 100644
--- a/app/serializers/build_artifact_entity.rb
+++ b/app/serializers/build_artifact_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildArtifactEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index ca4480fe2b1..b887b99d31c 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildDetailsEntity < JobEntity
expose :coverage, :erased_at, :duration
expose :tag_list, as: :tags
@@ -35,7 +37,7 @@ class BuildDetailsEntity < JobEntity
def build_failed_issue_options
{ title: "Job Failed ##{build.id}",
- description: "Job [##{build.id}](#{project_job_path(project, build)}) failed for #{build.sha}:\n" }
+ description: "Job [##{build.id}](#{project_job_url(project, build)}) failed for #{build.sha}:\n" }
end
def current_user
diff --git a/app/serializers/build_metadata_entity.rb b/app/serializers/build_metadata_entity.rb
index f16f3badffa..6242ee8957d 100644
--- a/app/serializers/build_metadata_entity.rb
+++ b/app/serializers/build_metadata_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildMetadataEntity < Grape::Entity
expose :timeout_human_readable
expose :timeout_source do |metadata|
diff --git a/app/serializers/build_serializer.rb b/app/serializers/build_serializer.rb
index bae9932847f..0649fdad6a8 100644
--- a/app/serializers/build_serializer.rb
+++ b/app/serializers/build_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildSerializer < BaseSerializer
entity JobEntity
diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb
index 77fc3336521..2bd17e58086 100644
--- a/app/serializers/cluster_application_entity.rb
+++ b/app/serializers/cluster_application_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterApplicationEntity < Grape::Entity
expose :name
expose :status_name, as: :status
diff --git a/app/serializers/cluster_entity.rb b/app/serializers/cluster_entity.rb
index 7e5b0997878..c59f68bbc49 100644
--- a/app/serializers/cluster_entity.rb
+++ b/app/serializers/cluster_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/cluster_serializer.rb b/app/serializers/cluster_serializer.rb
index 2e13c1501e7..4bb4d4880d4 100644
--- a/app/serializers/cluster_serializer.rb
+++ b/app/serializers/cluster_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterSerializer < BaseSerializer
entity ClusterEntity
diff --git a/app/serializers/cohort_activity_month_entity.rb b/app/serializers/cohort_activity_month_entity.rb
index e6788a8b596..cdbc89a2dcd 100644
--- a/app/serializers/cohort_activity_month_entity.rb
+++ b/app/serializers/cohort_activity_month_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortActivityMonthEntity < Grape::Entity
include ActionView::Helpers::NumberHelper
diff --git a/app/serializers/cohort_entity.rb b/app/serializers/cohort_entity.rb
index 7cdba5b0484..3d0213e1038 100644
--- a/app/serializers/cohort_entity.rb
+++ b/app/serializers/cohort_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortEntity < Grape::Entity
include ActionView::Helpers::NumberHelper
diff --git a/app/serializers/cohorts_entity.rb b/app/serializers/cohorts_entity.rb
index 98f5995ba6f..a84d568bf30 100644
--- a/app/serializers/cohorts_entity.rb
+++ b/app/serializers/cohorts_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortsEntity < Grape::Entity
expose :months_included
expose :cohorts, using: CohortEntity
diff --git a/app/serializers/cohorts_serializer.rb b/app/serializers/cohorts_serializer.rb
index fe9367b13d8..ceca5e1e5a8 100644
--- a/app/serializers/cohorts_serializer.rb
+++ b/app/serializers/cohorts_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortsSerializer < AnalyticsGenericSerializer
entity CohortsEntity
end
diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb
index c8dd98cc04d..b3287c66554 100644
--- a/app/serializers/commit_entity.rb
+++ b/app/serializers/commit_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CommitEntity < API::Entities::Commit
include RequestAwareEntity
diff --git a/app/serializers/concerns/user_status_tooltip.rb b/app/serializers/concerns/user_status_tooltip.rb
new file mode 100644
index 00000000000..aa6e67e3351
--- /dev/null
+++ b/app/serializers/concerns/user_status_tooltip.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module UserStatusTooltip
+ extend ActiveSupport::Concern
+ include ActionView::Helpers::TagHelper
+ include ActionView::Context
+ include EmojiHelper
+ include UsersHelper
+
+ included do
+ expose :user_status_if_loaded, as: :status_tooltip_html
+
+ def user_status_if_loaded
+ return nil unless object.association(:status).loaded?
+
+ user_status(object)
+ end
+ end
+end
diff --git a/app/serializers/concerns/with_pagination.rb b/app/serializers/concerns/with_pagination.rb
index 89631b73fcf..c8ffae355e8 100644
--- a/app/serializers/concerns/with_pagination.rb
+++ b/app/serializers/concerns/with_pagination.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WithPagination
attr_accessor :paginator
diff --git a/app/serializers/container_repositories_serializer.rb b/app/serializers/container_repositories_serializer.rb
index 56dc70b5687..e1ce3c7b3ae 100644
--- a/app/serializers/container_repositories_serializer.rb
+++ b/app/serializers/container_repositories_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContainerRepositoriesSerializer < BaseSerializer
entity ContainerRepositoryEntity
end
diff --git a/app/serializers/container_repository_entity.rb b/app/serializers/container_repository_entity.rb
index 1103cf30a07..59bf35f5aff 100644
--- a/app/serializers/container_repository_entity.rb
+++ b/app/serializers/container_repository_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContainerRepositoryEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/container_tag_entity.rb b/app/serializers/container_tag_entity.rb
index 8f1488e6cbb..637294877f8 100644
--- a/app/serializers/container_tag_entity.rb
+++ b/app/serializers/container_tag_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContainerTagEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/container_tags_serializer.rb b/app/serializers/container_tags_serializer.rb
index 6ff3adff135..982ce33f6e3 100644
--- a/app/serializers/container_tags_serializer.rb
+++ b/app/serializers/container_tags_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContainerTagsSerializer < BaseSerializer
entity ContainerTagEntity
diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb
index 2678f99510c..54bf030aba1 100644
--- a/app/serializers/deploy_key_entity.rb
+++ b/app/serializers/deploy_key_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeployKeyEntity < Grape::Entity
expose :id
expose :user_id
diff --git a/app/serializers/deploy_key_serializer.rb b/app/serializers/deploy_key_serializer.rb
index 8f849eb88b7..a1cd98b631b 100644
--- a/app/serializers/deploy_key_serializer.rb
+++ b/app/serializers/deploy_key_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeployKeySerializer < BaseSerializer
entity DeployKeyEntity
end
diff --git a/app/serializers/deploy_keys_project_entity.rb b/app/serializers/deploy_keys_project_entity.rb
index 568ef5ab75e..5775ad72d0d 100644
--- a/app/serializers/deploy_keys_project_entity.rb
+++ b/app/serializers/deploy_keys_project_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeployKeysProjectEntity < Grape::Entity
expose :can_push
expose :project, using: ProjectEntity
diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb
index 241c689bccd..344148a1fb7 100644
--- a/app/serializers/deployment_entity.rb
+++ b/app/serializers/deployment_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeploymentEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/deployment_serializer.rb b/app/serializers/deployment_serializer.rb
index cba5c3f311f..04db6b88489 100644
--- a/app/serializers/deployment_serializer.rb
+++ b/app/serializers/deployment_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeploymentSerializer < BaseSerializer
entity DeploymentEntity
diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb
index aa289a96975..79844c9210a 100644
--- a/app/serializers/diff_file_entity.rb
+++ b/app/serializers/diff_file_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DiffFileEntity < Grape::Entity
include RequestAwareEntity
include BlobHelper
@@ -25,6 +27,8 @@ class DiffFileEntity < Grape::Entity
expose :can_modify_blob do |diff_file|
merge_request = options[:merge_request]
+ next unless diff_file.blob
+
if merge_request&.source_project && current_user
can_modify_blob?(diff_file.blob, merge_request.source_project, merge_request.source_branch)
else
@@ -108,6 +112,7 @@ class DiffFileEntity < Grape::Entity
project = merge_request.target_project
next unless project
+ next unless diff_file.content_sha
project_blob_path(project, tree_join(diff_file.content_sha, diff_file.new_path))
end
@@ -125,6 +130,8 @@ class DiffFileEntity < Grape::Entity
end
expose :context_lines_path, if: -> (diff_file, _) { diff_file.text? } do |diff_file|
+ next unless diff_file.content_sha
+
project_blob_diff_path(diff_file.repository.project, tree_join(diff_file.content_sha, diff_file.file_path))
end
diff --git a/app/serializers/diffs_entity.rb b/app/serializers/diffs_entity.rb
index bb804e5347a..f75ace14d9c 100644
--- a/app/serializers/diffs_entity.rb
+++ b/app/serializers/diffs_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DiffsEntity < Grape::Entity
include DiffHelper
include RequestAwareEntity
diff --git a/app/serializers/diffs_serializer.rb b/app/serializers/diffs_serializer.rb
index 6771e10c5ac..9e5eb1699d4 100644
--- a/app/serializers/diffs_serializer.rb
+++ b/app/serializers/diffs_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DiffsSerializer < BaseSerializer
entity DiffsEntity
end
diff --git a/app/serializers/discussion_entity.rb b/app/serializers/discussion_entity.rb
index 63f28133a64..b8321037fa5 100644
--- a/app/serializers/discussion_entity.rb
+++ b/app/serializers/discussion_entity.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
class DiscussionEntity < Grape::Entity
include RequestAwareEntity
include NotesHelper
expose :id, :reply_id
- expose :position, if: -> (d, _) { d.diff_discussion? }
+ expose :position, if: -> (d, _) { d.diff_discussion? && !d.legacy_diff_discussion? }
expose :line_code, if: -> (d, _) { d.diff_discussion? }
expose :expanded?, as: :expanded
expose :active?, as: :active, if: -> (d, _) { d.diff_discussion? }
diff --git a/app/serializers/discussion_serializer.rb b/app/serializers/discussion_serializer.rb
index ed5e1224bb2..5be40e74175 100644
--- a/app/serializers/discussion_serializer.rb
+++ b/app/serializers/discussion_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DiscussionSerializer < BaseSerializer
entity DiscussionEntity
end
diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb
index 464217123b4..cc0c2abf863 100644
--- a/app/serializers/entity_date_helper.rb
+++ b/app/serializers/entity_date_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EntityDateHelper
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TagHelper
@@ -50,15 +52,20 @@ module EntityDateHelper
elsif entity.due_date
is_upcoming = (entity.due_date - Date.today).to_i > 0
time_ago = time_ago_in_words(entity.due_date)
- content = time_ago.gsub(/\d+/) { |match| "<strong>#{match}</strong>" }
- content.slice!("about ")
- content << " " + (is_upcoming ? _("remaining") : _("ago"))
- content.html_safe
+
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/49440
+ #
+ # Need to improve the i18n here and do a full translation
+ # of the string instead of piecewise translations.
+ content = time_ago
+ .gsub(/\d+/) { |match| "<strong>#{match}</strong>" }
+ .remove("about ")
+ remaining_or_ago = is_upcoming ? _("remaining") : _("ago")
+
+ "#{content} #{remaining_or_ago}".html_safe
elsif entity.start_date && entity.start_date.past?
- days = entity.elapsed_days
- content = content_tag(:strong, days)
- content << " #{'day'.pluralize(days)} elapsed"
- content.html_safe
+ days = entity.elapsed_days
+ "#{content_tag(:strong, days)} #{'day'.pluralize(days)} elapsed".html_safe
end
end
end
diff --git a/app/serializers/entity_request.rb b/app/serializers/entity_request.rb
index 456ba1174c0..49e026e8c2a 100644
--- a/app/serializers/entity_request.rb
+++ b/app/serializers/entity_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EntityRequest
# We use EntityRequest object to collect parameters and variables
# from the controller. Because options that are being passed to the entity
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
index ba0ae6ba8a0..b18e9706db6 100644
--- a/app/serializers/environment_entity.rb
+++ b/app/serializers/environment_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EnvironmentEntity < Grape::Entity
include RequestAwareEntity
@@ -7,7 +9,7 @@ class EnvironmentEntity < Grape::Entity
expose :external_url
expose :environment_type
expose :last_deployment, using: DeploymentEntity
- expose :stop_action?
+ expose :stop_action_available?, as: :has_stop_action
expose :metrics_path, if: -> (environment, _) { environment.has_metrics? } do |environment|
metrics_project_environment_path(environment.project, environment)
@@ -31,4 +33,14 @@ class EnvironmentEntity < Grape::Entity
end
expose :created_at, :updated_at
+
+ expose :can_stop do |environment|
+ environment.available? && can?(current_user, :stop_environment, environment)
+ end
+
+ private
+
+ def current_user
+ request.current_user
+ end
end
diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb
index 84722f33f59..dc1686c30c4 100644
--- a/app/serializers/environment_serializer.rb
+++ b/app/serializers/environment_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EnvironmentSerializer < BaseSerializer
include WithPagination
diff --git a/app/serializers/group_child_entity.rb b/app/serializers/group_child_entity.rb
index ee150eefd9e..f6804fe7f6a 100644
--- a/app/serializers/group_child_entity.rb
+++ b/app/serializers/group_child_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupChildEntity < Grape::Entity
include ActionView::Helpers::NumberHelper
include RequestAwareEntity
diff --git a/app/serializers/group_child_serializer.rb b/app/serializers/group_child_serializer.rb
index 2baef0a5703..789707c2c9b 100644
--- a/app/serializers/group_child_serializer.rb
+++ b/app/serializers/group_child_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupChildSerializer < BaseSerializer
include WithPagination
diff --git a/app/serializers/group_entity.rb b/app/serializers/group_entity.rb
index 6d8466da902..c46c342ee5d 100644
--- a/app/serializers/group_entity.rb
+++ b/app/serializers/group_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupEntity < Grape::Entity
include ActionView::Helpers::NumberHelper
include RequestAwareEntity
diff --git a/app/serializers/group_serializer.rb b/app/serializers/group_serializer.rb
index 8cf7eb63bcf..38c5757a6c1 100644
--- a/app/serializers/group_serializer.rb
+++ b/app/serializers/group_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupSerializer < BaseSerializer
include WithPagination
diff --git a/app/serializers/group_variable_entity.rb b/app/serializers/group_variable_entity.rb
index 62cf0b21e1e..0edab4a3092 100644
--- a/app/serializers/group_variable_entity.rb
+++ b/app/serializers/group_variable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupVariableEntity < Grape::Entity
expose :id
expose :key
diff --git a/app/serializers/group_variable_serializer.rb b/app/serializers/group_variable_serializer.rb
index 8f8205924aa..ed20b240cce 100644
--- a/app/serializers/group_variable_serializer.rb
+++ b/app/serializers/group_variable_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupVariableSerializer < BaseSerializer
entity GroupVariableEntity
end
diff --git a/app/serializers/issuable_entity.rb b/app/serializers/issuable_entity.rb
index 6f31fbd6b7c..e71bd3313fb 100644
--- a/app/serializers/issuable_entity.rb
+++ b/app/serializers/issuable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssuableEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/issuable_sidebar_entity.rb b/app/serializers/issuable_sidebar_entity.rb
index 29138c803df..773d78d324c 100644
--- a/app/serializers/issuable_sidebar_entity.rb
+++ b/app/serializers/issuable_sidebar_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssuableSidebarEntity < Grape::Entity
include TimeTrackableEntity
include RequestAwareEntity
diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb
index 840fdbcbf14..16a477c92fa 100644
--- a/app/serializers/issue_entity.rb
+++ b/app/serializers/issue_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueEntity < IssuableEntity
include TimeTrackableEntity
diff --git a/app/serializers/issue_serializer.rb b/app/serializers/issue_serializer.rb
index 2555595379b..37cf5e28396 100644
--- a/app/serializers/issue_serializer.rb
+++ b/app/serializers/issue_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueSerializer < BaseSerializer
# This overrided method takes care of which entity should be used
# to serialize the `issue` based on `basic` key in `opts` param.
diff --git a/app/serializers/issue_sidebar_entity.rb b/app/serializers/issue_sidebar_entity.rb
index 6c823dbfe95..349ad9d1fef 100644
--- a/app/serializers/issue_sidebar_entity.rb
+++ b/app/serializers/issue_sidebar_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueSidebarEntity < IssuableSidebarEntity
expose :assignees, using: API::Entities::UserBasic
end
diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb
index 960e7291ae6..7bc1d87dea5 100644
--- a/app/serializers/job_entity.rb
+++ b/app/serializers/job_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class JobEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/job_group_entity.rb b/app/serializers/job_group_entity.rb
index 8554de55517..0941a9d36be 100644
--- a/app/serializers/job_group_entity.rb
+++ b/app/serializers/job_group_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class JobGroupEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/label_entity.rb b/app/serializers/label_entity.rb
index 4452161051e..98743d62b50 100644
--- a/app/serializers/label_entity.rb
+++ b/app/serializers/label_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LabelEntity < Grape::Entity
expose :id, if: ->(label, _) { !label.is_a?(GlobalLabel) }
diff --git a/app/serializers/label_serializer.rb b/app/serializers/label_serializer.rb
index ad6ba8c46c9..25b9f7de243 100644
--- a/app/serializers/label_serializer.rb
+++ b/app/serializers/label_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LabelSerializer < BaseSerializer
entity LabelEntity
diff --git a/app/serializers/lfs_file_lock_entity.rb b/app/serializers/lfs_file_lock_entity.rb
index 264a77adc3f..7961c4e666b 100644
--- a/app/serializers/lfs_file_lock_entity.rb
+++ b/app/serializers/lfs_file_lock_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LfsFileLockEntity < Grape::Entity
root 'locks', 'lock'
diff --git a/app/serializers/lfs_file_lock_serializer.rb b/app/serializers/lfs_file_lock_serializer.rb
index ba8fb1a461d..0367097e376 100644
--- a/app/serializers/lfs_file_lock_serializer.rb
+++ b/app/serializers/lfs_file_lock_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LfsFileLockSerializer < BaseSerializer
entity LfsFileLockEntity
end
diff --git a/app/serializers/merge_request_basic_entity.rb b/app/serializers/merge_request_basic_entity.rb
index 1c06691026d..f7eb74cf392 100644
--- a/app/serializers/merge_request_basic_entity.rb
+++ b/app/serializers/merge_request_basic_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestBasicEntity < IssuableSidebarEntity
expose :assignee_id
expose :merge_status
diff --git a/app/serializers/merge_request_basic_serializer.rb b/app/serializers/merge_request_basic_serializer.rb
index cc5c664c8fa..a68b48b00db 100644
--- a/app/serializers/merge_request_basic_serializer.rb
+++ b/app/serializers/merge_request_basic_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestBasicSerializer < BaseSerializer
entity MergeRequestBasicEntity
end
diff --git a/app/serializers/merge_request_create_entity.rb b/app/serializers/merge_request_create_entity.rb
index 11234313293..e7a93004dda 100644
--- a/app/serializers/merge_request_create_entity.rb
+++ b/app/serializers/merge_request_create_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestCreateEntity < Grape::Entity
expose :iid
diff --git a/app/serializers/merge_request_create_serializer.rb b/app/serializers/merge_request_create_serializer.rb
index 08daf473319..b6df9ee13fc 100644
--- a/app/serializers/merge_request_create_serializer.rb
+++ b/app/serializers/merge_request_create_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestCreateSerializer < BaseSerializer
entity MergeRequestCreateEntity
end
diff --git a/app/serializers/merge_request_diff_entity.rb b/app/serializers/merge_request_diff_entity.rb
index 32c761b45ac..433bfe60474 100644
--- a/app/serializers/merge_request_diff_entity.rb
+++ b/app/serializers/merge_request_diff_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestDiffEntity < Grape::Entity
include Gitlab::Routing
include GitHelper
diff --git a/app/serializers/merge_request_metrics_entity.rb b/app/serializers/merge_request_metrics_entity.rb
index 3548107ac16..1c9db08d103 100644
--- a/app/serializers/merge_request_metrics_entity.rb
+++ b/app/serializers/merge_request_metrics_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestMetricsEntity < Grape::Entity
expose :latest_closed_at, as: :closed_at
expose :merged_at
diff --git a/app/serializers/merge_request_serializer.rb b/app/serializers/merge_request_serializer.rb
index caf193bdae3..1f8c830e1aa 100644
--- a/app/serializers/merge_request_serializer.rb
+++ b/app/serializers/merge_request_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestSerializer < BaseSerializer
# This overrided method takes care of which entity should be used
# to serialize the `merge_request` based on `serializer` key in `opts` param.
diff --git a/app/serializers/merge_request_user_entity.rb b/app/serializers/merge_request_user_entity.rb
index 33fc7b724d5..fd2d2897113 100644
--- a/app/serializers/merge_request_user_entity.rb
+++ b/app/serializers/merge_request_user_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestUserEntity < UserEntity
include RequestAwareEntity
include BlobHelper
diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb
index 5d72ebdd7fd..f55d448235a 100644
--- a/app/serializers/merge_request_widget_entity.rb
+++ b/app/serializers/merge_request_widget_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestWidgetEntity < IssuableEntity
expose :state
expose :in_progress_merge_commit_sha
@@ -10,9 +12,15 @@ class MergeRequestWidgetEntity < IssuableEntity
expose :merge_when_pipeline_succeeds
expose :source_branch
expose :source_project_id
+ expose :source_project_full_path do |merge_request|
+ merge_request.source_project&.full_path
+ end
expose :squash
expose :target_branch
expose :target_project_id
+ expose :target_project_full_path do |merge_request|
+ merge_request.project&.full_path
+ end
expose :allow_collaboration
expose :should_be_rebased?, as: :should_be_rebased
@@ -124,6 +132,10 @@ class MergeRequestWidgetEntity < IssuableEntity
can?(request.current_user, :create_note, merge_request)
end
+ expose :can_create_issue do |merge_request|
+ can?(current_user, :create_issue, merge_request.project)
+ end
+
expose :can_update do |merge_request|
can?(request.current_user, :update_merge_request, merge_request)
end
@@ -219,6 +231,12 @@ class MergeRequestWidgetEntity < IssuableEntity
end
end
+ expose :test_reports_path do |merge_request|
+ if merge_request.has_test_reports?
+ test_reports_project_merge_request_path(merge_request.project, merge_request, format: :json)
+ end
+ end
+
private
delegate :current_user, to: :request
diff --git a/app/serializers/note_attachment_entity.rb b/app/serializers/note_attachment_entity.rb
index 1ad50568ab9..dc801e2bf4e 100644
--- a/app/serializers/note_attachment_entity.rb
+++ b/app/serializers/note_attachment_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteAttachmentEntity < Grape::Entity
expose :url
expose :filename
diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb
index ce0c31b5806..daa5c24d0f5 100644
--- a/app/serializers/note_entity.rb
+++ b/app/serializers/note_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteEntity < API::Entities::Note
include RequestAwareEntity
include NotesHelper
@@ -62,6 +64,8 @@ class NoteEntity < API::Entities::Note
expose :attachment, using: NoteAttachmentEntity, if: -> (note, _) { note.attachment? }
+ expose :cached_markdown_version
+
private
def current_user
diff --git a/app/serializers/note_user_entity.rb b/app/serializers/note_user_entity.rb
index 7289f3a0222..b00dfa7d353 100644
--- a/app/serializers/note_user_entity.rb
+++ b/app/serializers/note_user_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteUserEntity < UserEntity
unexpose :web_url
end
diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb
index 8ba9cac53c4..3b56767f774 100644
--- a/app/serializers/pipeline_details_entity.rb
+++ b/app/serializers/pipeline_details_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineDetailsEntity < PipelineEntity
expose :details do
expose :ordered_stages, as: :stages, using: StageEntity
diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb
index f782b411b84..6cf1925adda 100644
--- a/app/serializers/pipeline_entity.rb
+++ b/app/serializers/pipeline_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index 17a022539bb..3205578b83e 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineSerializer < BaseSerializer
include WithPagination
entity PipelineDetailsEntity
@@ -9,10 +11,15 @@ class PipelineSerializer < BaseSerializer
:retryable_builds,
:cancelable_statuses,
:trigger_requests,
- :project,
:manual_actions,
:artifacts,
- { pending_builds: :project }
+ {
+ pending_builds: :project,
+ project: [:route, { namespace: :route }],
+ artifacts: {
+ project: [:route, { namespace: :route }]
+ }
+ }
])
end
diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb
index b3e5fd21e97..60c4ba135d6 100644
--- a/app/serializers/project_entity.rb
+++ b/app/serializers/project_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/project_mirror_entity.rb b/app/serializers/project_mirror_entity.rb
index a9c08ac021a..8aba244cd11 100644
--- a/app/serializers/project_mirror_entity.rb
+++ b/app/serializers/project_mirror_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectMirrorEntity < Grape::Entity
expose :id
diff --git a/app/serializers/project_note_entity.rb b/app/serializers/project_note_entity.rb
index e541bfbee8d..d7c4d0aacc6 100644
--- a/app/serializers/project_note_entity.rb
+++ b/app/serializers/project_note_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectNoteEntity < NoteEntity
expose :human_access do |note|
note.project.team.human_max_access(note.author_id)
diff --git a/app/serializers/project_note_serializer.rb b/app/serializers/project_note_serializer.rb
index 763ad0bdb3f..2182904e815 100644
--- a/app/serializers/project_note_serializer.rb
+++ b/app/serializers/project_note_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectNoteSerializer < BaseSerializer
entity ProjectNoteEntity
end
diff --git a/app/serializers/project_serializer.rb b/app/serializers/project_serializer.rb
index 74de1e79a8f..23b96c2fc9e 100644
--- a/app/serializers/project_serializer.rb
+++ b/app/serializers/project_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectSerializer < BaseSerializer
entity ProjectEntity
end
diff --git a/app/serializers/request_aware_entity.rb b/app/serializers/request_aware_entity.rb
index d53fcfb8c1b..1524c1291d8 100644
--- a/app/serializers/request_aware_entity.rb
+++ b/app/serializers/request_aware_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RequestAwareEntity
extend ActiveSupport::Concern
diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb
index e9999a36d8a..04ec80e0efa 100644
--- a/app/serializers/runner_entity.rb
+++ b/app/serializers/runner_entity.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
class RunnerEntity < Grape::Entity
include RequestAwareEntity
expose :id, :description
expose :edit_path,
- if: -> (*) { can?(request.current_user, :admin_build, project) && runner.specific? } do |runner|
+ if: -> (*) { can?(request.current_user, :admin_build, project) && runner.project_type? } do |runner|
edit_project_runner_path(project, runner)
end
diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb
index 2516df70ad9..00e6d32ee3a 100644
--- a/app/serializers/stage_entity.rb
+++ b/app/serializers/stage_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StageEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/stage_serializer.rb b/app/serializers/stage_serializer.rb
index 091d8e91e43..11fb0d3f852 100644
--- a/app/serializers/stage_serializer.rb
+++ b/app/serializers/stage_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StageSerializer < BaseSerializer
include WithPagination
diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb
index 47df7f9dcf9..306c30f0323 100644
--- a/app/serializers/status_entity.rb
+++ b/app/serializers/status_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StatusEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/submodule_entity.rb b/app/serializers/submodule_entity.rb
index ed1f1ae0ef0..e475a4f301f 100644
--- a/app/serializers/submodule_entity.rb
+++ b/app/serializers/submodule_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SubmoduleEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/test_case_entity.rb b/app/serializers/test_case_entity.rb
new file mode 100644
index 00000000000..5c1cbf37182
--- /dev/null
+++ b/app/serializers/test_case_entity.rb
@@ -0,0 +1,7 @@
+class TestCaseEntity < Grape::Entity
+ expose :status
+ expose :name
+ expose :execution_time
+ expose :system_output
+ expose :stack_trace
+end
diff --git a/app/serializers/test_reports_comparer_entity.rb b/app/serializers/test_reports_comparer_entity.rb
new file mode 100644
index 00000000000..b95d820d093
--- /dev/null
+++ b/app/serializers/test_reports_comparer_entity.rb
@@ -0,0 +1,11 @@
+class TestReportsComparerEntity < Grape::Entity
+ expose :total_status, as: :status
+
+ expose :summary do
+ expose :total_count, as: :total
+ expose :resolved_count, as: :resolved
+ expose :failed_count, as: :failed
+ end
+
+ expose :suite_comparers, as: :suites, using: TestSuiteComparerEntity
+end
diff --git a/app/serializers/test_reports_comparer_serializer.rb b/app/serializers/test_reports_comparer_serializer.rb
new file mode 100644
index 00000000000..a739858efb2
--- /dev/null
+++ b/app/serializers/test_reports_comparer_serializer.rb
@@ -0,0 +1,3 @@
+class TestReportsComparerSerializer < BaseSerializer
+ entity TestReportsComparerEntity
+end
diff --git a/app/serializers/test_suite_comparer_entity.rb b/app/serializers/test_suite_comparer_entity.rb
new file mode 100644
index 00000000000..a3965ba3930
--- /dev/null
+++ b/app/serializers/test_suite_comparer_entity.rb
@@ -0,0 +1,14 @@
+class TestSuiteComparerEntity < Grape::Entity
+ expose :name
+ expose :total_status, as: :status
+
+ expose :summary do
+ expose :total_count, as: :total
+ expose :resolved_count, as: :resolved
+ expose :failed_count, as: :failed
+ end
+
+ expose :new_failures, using: TestCaseEntity
+ expose :resolved_failures, using: TestCaseEntity
+ expose :existing_failures, using: TestCaseEntity
+end
diff --git a/app/serializers/time_trackable_entity.rb b/app/serializers/time_trackable_entity.rb
index e81cd7bec72..613d19703a4 100644
--- a/app/serializers/time_trackable_entity.rb
+++ b/app/serializers/time_trackable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TimeTrackableEntity
extend ActiveSupport::Concern
extend Grape
diff --git a/app/serializers/tree_entity.rb b/app/serializers/tree_entity.rb
index 9f1b485347f..9b7dc80e1d9 100644
--- a/app/serializers/tree_entity.rb
+++ b/app/serializers/tree_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TreeEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/tree_root_entity.rb b/app/serializers/tree_root_entity.rb
index 496f070ddbd..f1cfcd943d8 100644
--- a/app/serializers/tree_root_entity.rb
+++ b/app/serializers/tree_root_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# TODO: Inherit from TreeEntity, when `Tree` implements `id` and `name` like `Gitlab::Git::Tree`.
class TreeRootEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/tree_serializer.rb b/app/serializers/tree_serializer.rb
index 713ade23bc9..536b8ab1ae2 100644
--- a/app/serializers/tree_serializer.rb
+++ b/app/serializers/tree_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TreeSerializer < BaseSerializer
entity TreeRootEntity
end
diff --git a/app/serializers/user_entity.rb b/app/serializers/user_entity.rb
index 876512b12dc..656900bb8af 100644
--- a/app/serializers/user_entity.rb
+++ b/app/serializers/user_entity.rb
@@ -1,5 +1,8 @@
+# frozen_string_literal: true
+
class UserEntity < API::Entities::UserBasic
include RequestAwareEntity
+ include UserStatusTooltip
expose :path do |user|
user_path(user)
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index 49a71ebac61..2111e1b5667 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserSerializer < BaseSerializer
entity UserEntity
end
diff --git a/app/serializers/variable_entity.rb b/app/serializers/variable_entity.rb
index d576745c073..85cf367fe51 100644
--- a/app/serializers/variable_entity.rb
+++ b/app/serializers/variable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class VariableEntity < Grape::Entity
expose :id
expose :key
diff --git a/app/serializers/variable_serializer.rb b/app/serializers/variable_serializer.rb
index 32ae82ab51c..586666cad8e 100644
--- a/app/serializers/variable_serializer.rb
+++ b/app/serializers/variable_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class VariableSerializer < BaseSerializer
entity VariableEntity
end
diff --git a/app/services/access_token_validation_service.rb b/app/services/access_token_validation_service.rb
index 46e19230328..2a337918d21 100644
--- a/app/services/access_token_validation_service.rb
+++ b/app/services/access_token_validation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AccessTokenValidationService
# Results:
VALID = :valid
diff --git a/app/services/after_branch_delete_service.rb b/app/services/after_branch_delete_service.rb
index 227e9ea9c6d..e7eb74d3e7d 100644
--- a/app/services/after_branch_delete_service.rb
+++ b/app/services/after_branch_delete_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# Branch can be deleted either by DeleteBranchService
# or by GitPushService.
diff --git a/app/services/akismet_service.rb b/app/services/akismet_service.rb
index 0521393dd27..82ae66ab0f5 100644
--- a/app/services/akismet_service.rb
+++ b/app/services/akismet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AkismetService
attr_accessor :owner, :text, :options
diff --git a/app/services/application_settings/base_service.rb b/app/services/application_settings/base_service.rb
index 2bcc7d7c08b..ebe067536ca 100644
--- a/app/services/application_settings/base_service.rb
+++ b/app/services/application_settings/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApplicationSettings
class BaseService < ::BaseService
def initialize(application_setting, user, params = {})
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
index 7bcb8f49d0d..19cf34e2ac4 100644
--- a/app/services/application_settings/update_service.rb
+++ b/app/services/application_settings/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApplicationSettings
class UpdateService < ApplicationSettings::BaseService
attr_reader :params, :application_setting
diff --git a/app/services/applications/create_service.rb b/app/services/applications/create_service.rb
index 94a434b95dd..7db90c0b3c6 100644
--- a/app/services/applications/create_service.rb
+++ b/app/services/applications/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Applications
class CreateService
def initialize(current_user, params)
diff --git a/app/services/audit_event_service.rb b/app/services/audit_event_service.rb
index 5ad9a50687c..4c5e22bdd7e 100644
--- a/app/services/audit_event_service.rb
+++ b/app/services/audit_event_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AuditEventService
def initialize(author, entity, details = {})
@author, @entity, @details = author, entity, details
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index f28cddb2af3..893b37b831a 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Auth
class ContainerRegistryAuthenticationService < BaseService
AUDIENCE = 'container_registry'.freeze
@@ -7,11 +9,11 @@ module Auth
return error('UNAVAILABLE', status: 404, message: 'registry not enabled') unless registry.enabled
- unless scope || current_user || project
+ unless scopes.any? || current_user || project
return error('DENIED', status: 403, message: 'access forbidden')
end
- { token: authorized_token(scope).encoded }
+ { token: authorized_token(*scopes).encoded }
end
def self.full_access_token(*names)
@@ -45,10 +47,12 @@ module Auth
end
end
- def scope
- return unless params[:scope]
+ def scopes
+ return [] unless params[:scopes]
- @scope ||= process_scope(params[:scope])
+ @scopes ||= params[:scopes].map do |scope|
+ process_scope(scope)
+ end.compact
end
def process_scope(scope)
diff --git a/app/services/badges/base_service.rb b/app/services/badges/base_service.rb
index 4f87426bd38..45fc9ac4373 100644
--- a/app/services/badges/base_service.rb
+++ b/app/services/badges/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class BaseService
protected
diff --git a/app/services/badges/build_service.rb b/app/services/badges/build_service.rb
index 6267e571838..e5ede1586b6 100644
--- a/app/services/badges/build_service.rb
+++ b/app/services/badges/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class BuildService < Badges::BaseService
# returns the created badge
diff --git a/app/services/badges/create_service.rb b/app/services/badges/create_service.rb
index aafb87f7dcd..4a55a00daeb 100644
--- a/app/services/badges/create_service.rb
+++ b/app/services/badges/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class CreateService < Badges::BaseService
# returns the created badge
diff --git a/app/services/badges/update_service.rb b/app/services/badges/update_service.rb
index 7ca84b5df31..a653b7903dd 100644
--- a/app/services/badges/update_service.rb
+++ b/app/services/badges/update_service.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
module Badges
class UpdateService < Badges::BaseService
# returns the updated badge
def execute(badge)
if params.present?
- badge.update_attributes(params)
+ badge.update(params)
end
badge
diff --git a/app/services/base_count_service.rb b/app/services/base_count_service.rb
index 975e288301c..ad1647842b8 100644
--- a/app/services/base_count_service.rb
+++ b/app/services/base_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for services that count a single resource such as the number of
# issues for a project.
class BaseCountService
diff --git a/app/services/base_renderer.rb b/app/services/base_renderer.rb
index d6e30bd7008..30a6e8c62dd 100644
--- a/app/services/base_renderer.rb
+++ b/app/services/base_renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseRenderer
attr_reader :current_user
diff --git a/app/services/base_service.rb b/app/services/base_service.rb
index 3519b7c5e7d..3e968c8f707 100644
--- a/app/services/base_service.rb
+++ b/app/services/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseService
include Gitlab::Allowable
diff --git a/app/services/boards/base_service.rb b/app/services/boards/base_service.rb
index 72822ffffa1..205db47888e 100644
--- a/app/services/boards/base_service.rb
+++ b/app/services/boards/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class BaseService < ::BaseService
# Parent can either a group or a project
diff --git a/app/services/boards/create_service.rb b/app/services/boards/create_service.rb
index bd0bb387662..4caf5ffa3cb 100644
--- a/app/services/boards/create_service.rb
+++ b/app/services/boards/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class CreateService < Boards::BaseService
def execute
diff --git a/app/services/boards/issues/create_service.rb b/app/services/boards/issues/create_service.rb
index 3025029755c..bd045e18b8d 100644
--- a/app/services/boards/issues/create_service.rb
+++ b/app/services/boards/issues/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Issues
class CreateService < Boards::BaseService
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index b1dbe73cdf7..0db1418b37a 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -1,14 +1,37 @@
+# frozen_string_literal: true
+
module Boards
module Issues
class ListService < Boards::BaseService
+ include Gitlab::Utils::StrongMemoize
+
def execute
- issues = IssuesFinder.new(current_user, filter_params).execute
- issues = filter(issues)
- issues.order_by_position_and_priority
+ fetch_issues.order_by_position_and_priority
+ end
+
+ def metadata
+ keys = metadata_fields.keys
+ columns = metadata_fields.values_at(*keys).join(', ')
+ results = Issue.where(id: fetch_issues.select('issues.id')).pluck(columns)
+
+ Hash[keys.zip(results.flatten)]
end
private
+ def metadata_fields
+ { size: 'COUNT(*)' }
+ end
+
+ # We memoize the query here since the finder methods we use are quite complex. This does not memoize the result of the query.
+ def fetch_issues
+ strong_memoize(:fetch_issues) do
+ issues = IssuesFinder.new(current_user, filter_params).execute
+
+ filter(issues).reorder(nil)
+ end
+ end
+
def filter(issues)
issues = without_board_labels(issues) unless list&.movable? || list&.closed?
issues = with_list_label(issues) if list&.label?
diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb
index ee3112c7571..6fd8a23b2a1 100644
--- a/app/services/boards/issues/move_service.rb
+++ b/app/services/boards/issues/move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Issues
class MoveService < Boards::BaseService
diff --git a/app/services/boards/list_service.rb b/app/services/boards/list_service.rb
index 9269b8d2620..edd1cc7c2e1 100644
--- a/app/services/boards/list_service.rb
+++ b/app/services/boards/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class ListService < Boards::BaseService
def execute
diff --git a/app/services/boards/lists/create_service.rb b/app/services/boards/lists/create_service.rb
index 6fd9885d4f3..48d2d5abaec 100644
--- a/app/services/boards/lists/create_service.rb
+++ b/app/services/boards/lists/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class CreateService < Boards::BaseService
diff --git a/app/services/boards/lists/destroy_service.rb b/app/services/boards/lists/destroy_service.rb
index d75c5fd3dc6..e12d4f46e19 100644
--- a/app/services/boards/lists/destroy_service.rb
+++ b/app/services/boards/lists/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class DestroyService < Boards::BaseService
diff --git a/app/services/boards/lists/generate_service.rb b/app/services/boards/lists/generate_service.rb
index 05d4ab5dbcc..4fbf1026019 100644
--- a/app/services/boards/lists/generate_service.rb
+++ b/app/services/boards/lists/generate_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class GenerateService < Boards::BaseService
diff --git a/app/services/boards/lists/list_service.rb b/app/services/boards/lists/list_service.rb
index e57c95294af..e10eb52e041 100644
--- a/app/services/boards/lists/list_service.rb
+++ b/app/services/boards/lists/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class ListService < Boards::BaseService
diff --git a/app/services/boards/lists/move_service.rb b/app/services/boards/lists/move_service.rb
index 7d0730e8332..27a36051662 100644
--- a/app/services/boards/lists/move_service.rb
+++ b/app/services/boards/lists/move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class MoveService < Boards::BaseService
diff --git a/app/services/chat_names/authorize_user_service.rb b/app/services/chat_names/authorize_user_service.rb
index 7256466c9e8..78b53cb3637 100644
--- a/app/services/chat_names/authorize_user_service.rb
+++ b/app/services/chat_names/authorize_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatNames
class AuthorizeUserService
include Gitlab::Routing
diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb
index d458b814183..854b191c45c 100644
--- a/app/services/chat_names/find_user_service.rb
+++ b/app/services/chat_names/find_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatNames
class FindUserService
def initialize(service, params)
diff --git a/app/services/ci/compare_test_reports_service.rb b/app/services/ci/compare_test_reports_service.rb
new file mode 100644
index 00000000000..ec25e934a27
--- /dev/null
+++ b/app/services/ci/compare_test_reports_service.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Ci
+ class CompareTestReportsService < ::BaseService
+ def execute(base_pipeline, head_pipeline)
+ comparer = Gitlab::Ci::Reports::TestReportsComparer
+ .new(base_pipeline&.test_reports, head_pipeline.test_reports)
+
+ {
+ status: :parsed,
+ key: key(base_pipeline, head_pipeline),
+ data: TestReportsComparerSerializer
+ .new(project: project)
+ .represent(comparer).as_json
+ }
+ rescue => e
+ {
+ status: :error,
+ key: key(base_pipeline, head_pipeline),
+ status_reason: e.message
+ }
+ end
+
+ def latest?(base_pipeline, head_pipeline, data)
+ data&.fetch(:key, nil) == key(base_pipeline, head_pipeline)
+ end
+
+ private
+
+ def key(base_pipeline, head_pipeline)
+ [
+ base_pipeline&.id, base_pipeline&.updated_at,
+ head_pipeline&.id, head_pipeline&.updated_at
+ ]
+ end
+ end
+end
diff --git a/app/services/ci/create_pipeline_schedule_service.rb b/app/services/ci/create_pipeline_schedule_service.rb
index cd40deb6187..0d5f50c26a1 100644
--- a/app/services/ci/create_pipeline_schedule_service.rb
+++ b/app/services/ci/create_pipeline_schedule_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class CreatePipelineScheduleService < BaseService
def execute
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 17a53b6a8fd..85df8bcff8c 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class CreatePipelineService < BaseService
attr_reader :pipeline
diff --git a/app/services/ci/ensure_stage_service.rb b/app/services/ci/ensure_stage_service.rb
index b8c7be2d350..3d0e39d1b9f 100644
--- a/app/services/ci/ensure_stage_service.rb
+++ b/app/services/ci/ensure_stage_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
##
# We call this service everytime we persist a CI/CD job.
diff --git a/app/services/ci/extract_sections_from_build_trace_service.rb b/app/services/ci/extract_sections_from_build_trace_service.rb
index 75f9e0f897d..693f6d55be3 100644
--- a/app/services/ci/extract_sections_from_build_trace_service.rb
+++ b/app/services/ci/extract_sections_from_build_trace_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class ExtractSectionsFromBuildTraceService < BaseService
def execute(build)
diff --git a/app/services/ci/fetch_kubernetes_token_service.rb b/app/services/ci/fetch_kubernetes_token_service.rb
index bca883ec0a0..15eda56cac6 100644
--- a/app/services/ci/fetch_kubernetes_token_service.rb
+++ b/app/services/ci/fetch_kubernetes_token_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# TODO:
# Almost components in this class were copied from app/models/project_services/kubernetes_service.rb
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
index 85533a1cbdb..f54574b026b 100644
--- a/app/services/ci/pipeline_trigger_service.rb
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PipelineTriggerService < BaseService
include Gitlab::Utils::StrongMemoize
diff --git a/app/services/ci/play_build_service.rb b/app/services/ci/play_build_service.rb
index e24f48c2d16..eb0b070657d 100644
--- a/app/services/ci/play_build_service.rb
+++ b/app/services/ci/play_build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PlayBuildService < ::BaseService
def execute(build)
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index 55af193d717..cda9bbff3b4 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class ProcessPipelineService < BaseService
attr_reader :pipeline
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index 9bdbb2c0d99..11f85627faf 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
# This class responsible for assigning
# proper pending build to runner on runner API request
@@ -13,9 +15,9 @@ module Ci
@runner = runner
end
- def execute
+ def execute(params = {})
builds =
- if runner.shared?
+ if runner.instance_type?
builds_for_shared_runner
elsif runner.group_type?
builds_for_group_runner
@@ -39,14 +41,10 @@ module Ci
begin
# In case when 2 runners try to assign the same build, second runner will be declined
# with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
- begin
- build.runner_id = runner.id
- build.run!
+ if assign_runner!(build, params)
register_success(build)
return Result.new(build, true) # rubocop:disable Cop/AvoidReturnFromBlocks
- rescue Ci::Build::MissingDependenciesError
- build.drop!(:missing_dependency_failure)
end
rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
# We are looping to find another build that is not conflicting
@@ -68,6 +66,24 @@ module Ci
private
+ def assign_runner!(build, params)
+ build.runner_id = runner.id
+ build.runner_session_attributes = params[:session] if params[:session].present?
+
+ unless build.has_valid_build_dependencies?
+ build.drop!(:missing_dependency_failure)
+ return false
+ end
+
+ unless build.supported_runner?(params.dig(:info, :features))
+ build.drop!(:runner_unsupported)
+ return false
+ end
+
+ build.run!
+ true
+ end
+
def builds_for_shared_runner
new_builds.
# don't run projects which have not enabled shared runners and builds
@@ -99,7 +115,7 @@ module Ci
end
def running_builds_for_shared_runners
- Ci::Build.running.where(runner: Ci::Runner.shared)
+ Ci::Build.running.where(runner: Ci::Runner.instance_type)
.group(:project_id).select(:project_id, 'count(*) AS running_builds')
end
@@ -115,7 +131,7 @@ module Ci
end
def register_success(job)
- labels = { shared_runner: runner.shared?,
+ labels = { shared_runner: runner.instance_type?,
jobs_running_for_project: jobs_running_for_project(job) }
job_queue_duration_seconds.observe(labels, Time.now - job.queued_at) unless job.queued_at.nil?
@@ -123,10 +139,10 @@ module Ci
end
def jobs_running_for_project(job)
- return '+Inf' unless runner.shared?
+ return '+Inf' unless runner.instance_type?
# excluding currently started job
- running_jobs_count = job.project.builds.running.where(runner: Ci::Runner.shared)
+ running_jobs_count = job.project.builds.running.where(runner: Ci::Runner.instance_type)
.limit(JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET + 1).count - 1
running_jobs_count < JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET ? running_jobs_count : "#{JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET}+"
end
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index 6128b2a8fbb..6ceb59e4780 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class RetryBuildService < ::BaseService
CLONE_ACCESSORS = %i[pipeline project ref tag options commands name
diff --git a/app/services/ci/retry_pipeline_service.rb b/app/services/ci/retry_pipeline_service.rb
index c5a43869990..42a13367a99 100644
--- a/app/services/ci/retry_pipeline_service.rb
+++ b/app/services/ci/retry_pipeline_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class RetryPipelineService < ::BaseService
include Gitlab::OptimisticLocking
diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb
index 43c9a065fcf..973ae5ce5aa 100644
--- a/app/services/ci/stop_environments_service.rb
+++ b/app/services/ci/stop_environments_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class StopEnvironmentsService < BaseService
attr_reader :ref
@@ -8,7 +10,7 @@ module Ci
return unless @ref.present?
environments.each do |environment|
- next unless environment.stop_action?
+ next unless environment.stop_action_available?
next unless can?(current_user, :stop_environment, environment)
environment.stop_with_action!(current_user)
diff --git a/app/services/ci/update_build_queue_service.rb b/app/services/ci/update_build_queue_service.rb
index 41b1c144c3e..9c589d910eb 100644
--- a/app/services/ci/update_build_queue_service.rb
+++ b/app/services/ci/update_build_queue_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class UpdateBuildQueueService
def execute(build)
diff --git a/app/services/ci/update_runner_service.rb b/app/services/ci/update_runner_service.rb
index 450ee7da1c9..e4117a51fe6 100644
--- a/app/services/ci/update_runner_service.rb
+++ b/app/services/ci/update_runner_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class UpdateRunnerService
attr_reader :runner
diff --git a/app/services/clusters/applications/base_helm_service.rb b/app/services/clusters/applications/base_helm_service.rb
index cba1b920f7c..270a8eb24f4 100644
--- a/app/services/clusters/applications/base_helm_service.rb
+++ b/app/services/clusters/applications/base_helm_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class BaseHelmService
diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb
index e572b1e5d99..f32e73e8b1c 100644
--- a/app/services/clusters/applications/check_ingress_ip_address_service.rb
+++ b/app/services/clusters/applications/check_ingress_ip_address_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class CheckIngressIpAddressService < BaseHelmService
diff --git a/app/services/clusters/applications/check_installation_progress_service.rb b/app/services/clusters/applications/check_installation_progress_service.rb
index 90393e951a4..35f5cff0e0c 100644
--- a/app/services/clusters/applications/check_installation_progress_service.rb
+++ b/app/services/clusters/applications/check_installation_progress_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class CheckInstallationProgressService < BaseHelmService
@@ -33,7 +35,7 @@ module Clusters
def check_timeout
if timeouted?
begin
- app.make_errored!('Installation timeouted')
+ app.make_errored!('Installation timed out')
ensure
remove_installation_pod
end
@@ -48,17 +50,17 @@ module Clusters
end
def remove_installation_pod
- helm_api.delete_installation_pod!(install_command.pod_name)
+ helm_api.delete_pod!(install_command.pod_name)
rescue
# no-op
end
def installation_phase
- helm_api.installation_status(install_command.pod_name)
+ helm_api.status(install_command.pod_name)
end
def installation_errors
- helm_api.installation_log(install_command.pod_name)
+ helm_api.log(install_command.pod_name)
end
end
end
diff --git a/app/services/clusters/applications/install_service.rb b/app/services/clusters/applications/install_service.rb
index 7ec3a9baa6e..7e3c0e77a83 100644
--- a/app/services/clusters/applications/install_service.rb
+++ b/app/services/clusters/applications/install_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class InstallService < BaseHelmService
diff --git a/app/services/clusters/applications/schedule_installation_service.rb b/app/services/clusters/applications/schedule_installation_service.rb
index 9c5461e85e1..4ead4f619c8 100644
--- a/app/services/clusters/applications/schedule_installation_service.rb
+++ b/app/services/clusters/applications/schedule_installation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class ScheduleInstallationService < ::BaseService
diff --git a/app/services/clusters/create_service.rb b/app/services/clusters/create_service.rb
index 418888e3293..e3e0cfa462c 100644
--- a/app/services/clusters/create_service.rb
+++ b/app/services/clusters/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
class CreateService < BaseService
attr_reader :access_token
diff --git a/app/services/clusters/gcp/fetch_operation_service.rb b/app/services/clusters/gcp/fetch_operation_service.rb
index a4cd3ca5c11..02c96a1e286 100644
--- a/app/services/clusters/gcp/fetch_operation_service.rb
+++ b/app/services/clusters/gcp/fetch_operation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class FetchOperationService
diff --git a/app/services/clusters/gcp/finalize_creation_service.rb b/app/services/clusters/gcp/finalize_creation_service.rb
index 84944e95542..264419501dc 100644
--- a/app/services/clusters/gcp/finalize_creation_service.rb
+++ b/app/services/clusters/gcp/finalize_creation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class FinalizeCreationService
diff --git a/app/services/clusters/gcp/provision_service.rb b/app/services/clusters/gcp/provision_service.rb
index 8beea5a8cfb..ab1bf9c64f6 100644
--- a/app/services/clusters/gcp/provision_service.rb
+++ b/app/services/clusters/gcp/provision_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class ProvisionService
diff --git a/app/services/clusters/gcp/verify_provision_status_service.rb b/app/services/clusters/gcp/verify_provision_status_service.rb
index 7cc4324677e..b24246f5c4b 100644
--- a/app/services/clusters/gcp/verify_provision_status_service.rb
+++ b/app/services/clusters/gcp/verify_provision_status_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class VerifyProvisionStatusService
diff --git a/app/services/clusters/update_service.rb b/app/services/clusters/update_service.rb
index 989218e32a2..98fdeec4fb1 100644
--- a/app/services/clusters/update_service.rb
+++ b/app/services/clusters/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
class UpdateService < BaseService
def execute(cluster)
diff --git a/app/services/cohorts_service.rb b/app/services/cohorts_service.rb
index 6781533af28..7a14e97f749 100644
--- a/app/services/cohorts_service.rb
+++ b/app/services/cohorts_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortsService
MONTHS_INCLUDED = 12
diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb
index b9d0173a2d0..2fbd442fc2e 100644
--- a/app/services/commits/change_service.rb
+++ b/app/services/commits/change_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class ChangeService < Commits::CreateService
def initialize(*args)
@@ -13,8 +15,6 @@ module Commits
# rubocop:disable GitlabSecurity/PublicSend
message = @commit.public_send(:"#{action}_message", current_user)
-
- # rubocop:disable GitlabSecurity/PublicSend
repository.public_send(
action,
current_user,
diff --git a/app/services/commits/cherry_pick_service.rb b/app/services/commits/cherry_pick_service.rb
index 320e229560d..4c5b15b2f95 100644
--- a/app/services/commits/cherry_pick_service.rb
+++ b/app/services/commits/cherry_pick_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class CherryPickService < ChangeService
def create_commit!
diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb
index 4d0578becbe..3ce9acc833c 100644
--- a/app/services/commits/create_service.rb
+++ b/app/services/commits/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class CreateService < ::BaseService
ValidationError = Class.new(StandardError)
diff --git a/app/services/commits/revert_service.rb b/app/services/commits/revert_service.rb
index dc27399e047..dddb8b24eac 100644
--- a/app/services/commits/revert_service.rb
+++ b/app/services/commits/revert_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class RevertService < ChangeService
def create_commit!
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index 2a69a205629..3adf8a0c1a1 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'securerandom'
# Compare 2 refs for one repo or between repositories
diff --git a/app/services/concerns/exclusive_lease_guard.rb b/app/services/concerns/exclusive_lease_guard.rb
index f45436370c1..f102e00d150 100644
--- a/app/services/concerns/exclusive_lease_guard.rb
+++ b/app/services/concerns/exclusive_lease_guard.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#
# Concern that helps with getting an exclusive lease for running a block
# of code.
diff --git a/app/services/concerns/issues/resolve_discussions.rb b/app/services/concerns/issues/resolve_discussions.rb
index 455f761ca9b..1563ed965df 100644
--- a/app/services/concerns/issues/resolve_discussions.rb
+++ b/app/services/concerns/issues/resolve_discussions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
module ResolveDiscussions
include Gitlab::Utils::StrongMemoize
diff --git a/app/services/concerns/update_visibility_level.rb b/app/services/concerns/update_visibility_level.rb
index 536fcc6acce..b7a161f5089 100644
--- a/app/services/concerns/update_visibility_level.rb
+++ b/app/services/concerns/update_visibility_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module UpdateVisibilityLevel
def valid_visibility_level_change?(target, new_visibility)
# check that user is allowed to set specified visibility_level
diff --git a/app/services/concerns/users/new_user_notifier.rb b/app/services/concerns/users/new_user_notifier.rb
index 231693ce7a9..11547e4a5b6 100644
--- a/app/services/concerns/users/new_user_notifier.rb
+++ b/app/services/concerns/users/new_user_notifier.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
module NewUserNotifier
def notify_new_user(user, reset_token)
diff --git a/app/services/concerns/users/participable_service.rb b/app/services/concerns/users/participable_service.rb
index bf60b96938d..5b408bd96c7 100644
--- a/app/services/concerns/users/participable_service.rb
+++ b/app/services/concerns/users/participable_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
module ParticipableService
extend ActiveSupport::Concern
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index 9b1a4d960e2..65208b07e27 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateBranchService < BaseService
def execute(branch_name, ref)
create_master_branch if project.empty_repo?
diff --git a/app/services/create_deployment_service.rb b/app/services/create_deployment_service.rb
index 7e5a77fb056..bb3f605da28 100644
--- a/app/services/create_deployment_service.rb
+++ b/app/services/create_deployment_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateDeploymentService
attr_reader :job
diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb
index 54ff1f74126..09c68390007 100644
--- a/app/services/create_release_service.rb
+++ b/app/services/create_release_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
diff --git a/app/services/create_snippet_service.rb b/app/services/create_snippet_service.rb
index 40286dbf3bf..6f1fce4989e 100644
--- a/app/services/create_snippet_service.rb
+++ b/app/services/create_snippet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateSnippetService < BaseService
include SpamCheckService
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index e1499dcee64..44252f7b0a6 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteBranchService < BaseService
def execute(branch_name)
repository = project.repository
diff --git a/app/services/delete_merged_branches_service.rb b/app/services/delete_merged_branches_service.rb
index c98d1e3c540..ff3e4783fe3 100644
--- a/app/services/delete_merged_branches_service.rb
+++ b/app/services/delete_merged_branches_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteMergedBranchesService < BaseService
def async_execute
DeleteMergedBranchesWorker.perform_async(project.id, current_user.id)
diff --git a/app/services/deploy_keys/create_service.rb b/app/services/deploy_keys/create_service.rb
index 16de3d08df2..a25e73666f8 100644
--- a/app/services/deploy_keys/create_service.rb
+++ b/app/services/deploy_keys/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeployKeys
class CreateService < Keys::BaseService
def execute
diff --git a/app/services/deploy_tokens/create_service.rb b/app/services/deploy_tokens/create_service.rb
index 52f545947af..dc0122002e9 100644
--- a/app/services/deploy_tokens/create_service.rb
+++ b/app/services/deploy_tokens/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeployTokens
class CreateService < BaseService
def execute
diff --git a/app/services/discussions/base_service.rb b/app/services/discussions/base_service.rb
index e4dfe6e71bb..86b8310f0a6 100644
--- a/app/services/discussions/base_service.rb
+++ b/app/services/discussions/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Discussions
class BaseService < ::BaseService
end
diff --git a/app/services/discussions/resolve_service.rb b/app/services/discussions/resolve_service.rb
index 0437195f588..816cd45b07a 100644
--- a/app/services/discussions/resolve_service.rb
+++ b/app/services/discussions/resolve_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Discussions
class ResolveService < Discussions::BaseService
def execute(one_or_more_discussions)
diff --git a/app/services/discussions/update_diff_position_service.rb b/app/services/discussions/update_diff_position_service.rb
index 746f209e20f..c61437fb2e3 100644
--- a/app/services/discussions/update_diff_position_service.rb
+++ b/app/services/discussions/update_diff_position_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Discussions
class UpdateDiffPositionService < BaseService
def execute(discussion)
diff --git a/app/services/emails/base_service.rb b/app/services/emails/base_service.rb
index 5bbceeb3b3f..ba7b689a9af 100644
--- a/app/services/emails/base_service.rb
+++ b/app/services/emails/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class BaseService
def initialize(current_user, params = {})
diff --git a/app/services/emails/confirm_service.rb b/app/services/emails/confirm_service.rb
index b5301bf2b82..38204e011dd 100644
--- a/app/services/emails/confirm_service.rb
+++ b/app/services/emails/confirm_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class ConfirmService < ::Emails::BaseService
def execute(email)
diff --git a/app/services/emails/create_service.rb b/app/services/emails/create_service.rb
index 94a841af7c3..acf575e24e5 100644
--- a/app/services/emails/create_service.rb
+++ b/app/services/emails/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class CreateService < ::Emails::BaseService
def execute(extra_params = {})
diff --git a/app/services/emails/destroy_service.rb b/app/services/emails/destroy_service.rb
index 1ed131fe326..9ca1a03e172 100644
--- a/app/services/emails/destroy_service.rb
+++ b/app/services/emails/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class DestroyService < ::Emails::BaseService
def execute(email)
diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb
index 44dc90b3462..e7464fd9d5f 100644
--- a/app/services/event_create_service.rb
+++ b/app/services/event_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# EventCreateService class
#
# Used for creating events feed on dashboard after certain user action
diff --git a/app/services/events/render_service.rb b/app/services/events/render_service.rb
index bb72d7685dd..50429683902 100644
--- a/app/services/events/render_service.rb
+++ b/app/services/events/render_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Events
class RenderService < BaseRenderer
def execute(events, atom_request: false)
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index 8d4b9f14780..025f093a428 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class BaseService < Commits::CreateService
FileChangedError = Class.new(StandardError)
diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb
index 8ecac6115bd..362b80071ba 100644
--- a/app/services/files/create_dir_service.rb
+++ b/app/services/files/create_dir_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class CreateDirService < Files::BaseService
def create_commit!
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index a954564946b..fd5442a6c28 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class CreateService < Files::BaseService
def create_commit!
diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb
index 32a57484d4e..0ec1f79d396 100644
--- a/app/services/files/delete_service.rb
+++ b/app/services/files/delete_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class DeleteService < Files::BaseService
def create_commit!
diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb
index 13a1dee4173..08088f8c592 100644
--- a/app/services/files/multi_service.rb
+++ b/app/services/files/multi_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class MultiService < Files::BaseService
UPDATE_FILE_ACTIONS = %w(update move delete).freeze
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index 1902d1cea72..2b3e96e6c53 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class UpdateService < Files::BaseService
def create_commit!
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index f3bfc53dcd3..637c1df4ad9 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -1,6 +1,9 @@
+# frozen_string_literal: true
+
class GitPushService < BaseService
attr_accessor :push_data, :push_commits
include Gitlab::Access
+ include Gitlab::Utils::StrongMemoize
# The N most recent commits to process in a single push payload.
PROCESS_COMMIT_LIMIT = 100
@@ -19,14 +22,14 @@ class GitPushService < BaseService
# 6. Checks if the project's main language has changed
#
def execute
- @project.repository.after_create if @project.empty_repo?
- @project.repository.after_push_commit(branch_name)
+ project.repository.after_create if project.empty_repo?
+ project.repository.after_push_commit(branch_name)
if push_remove_branch?
- @project.repository.after_remove_branch
+ project.repository.after_remove_branch
@push_commits = []
elsif push_to_new_branch?
- @project.repository.after_create_branch
+ project.repository.after_create_branch
# Re-find the pushed commits.
if default_branch?
@@ -36,14 +39,14 @@ class GitPushService < BaseService
# Use the pushed commits that aren't reachable by the default branch
# as a heuristic. This may include more commits than are actually pushed, but
# that shouldn't matter because we check for existing cross-references later.
- @push_commits = @project.repository.commits_between(@project.default_branch, params[:newrev])
+ @push_commits = project.repository.commits_between(project.default_branch, params[:newrev])
# don't process commits for the initial push to the default branch
process_commit_messages
end
elsif push_to_existing_branch?
# Collect data for this git push
- @push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev])
+ @push_commits = project.repository.commits_between(params[:oldrev], params[:newrev])
process_commit_messages
@@ -62,7 +65,7 @@ class GitPushService < BaseService
end
def update_gitattributes
- @project.repository.copy_gitattributes(params[:ref])
+ project.repository.copy_gitattributes(params[:ref])
end
def update_caches
@@ -74,7 +77,7 @@ class GitPushService < BaseService
else
paths = Set.new
- @push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
+ last_pushed_commits.each do |commit|
commit.raw_deltas.each do |diff|
paths << diff.new_path
end
@@ -82,15 +85,17 @@ class GitPushService < BaseService
types = Gitlab::FileDetector.types_in_paths(paths.to_a)
end
+
+ DetectRepositoryLanguagesWorker.perform_async(@project.id, current_user.id)
else
types = []
end
- ProjectCacheWorker.perform_async(@project.id, types, [:commit_count, :repository_size])
+ ProjectCacheWorker.perform_async(project.id, types, [:commit_count, :repository_size])
end
def update_signatures
- commit_shas = @push_commits.last(PROCESS_COMMIT_LIMIT).map(&:sha)
+ commit_shas = last_pushed_commits.map(&:sha)
return if commit_shas.empty?
@@ -101,16 +106,14 @@ class GitPushService < BaseService
commit_shas = Gitlab::Git::Commit.shas_with_signatures(project.repository, commit_shas)
- commit_shas.each do |sha|
- CreateGpgSignatureWorker.perform_async(sha, project.id)
- end
+ CreateGpgSignatureWorker.perform_async(commit_shas, project.id)
end
# Schedules processing of commit messages.
def process_commit_messages
default = default_branch?
- @push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
+ last_pushed_commits.each do |commit|
if commit.matches_cross_reference_regex?
ProcessCommitWorker
.perform_async(project.id, current_user.id, commit.to_hash, default)
@@ -121,10 +124,10 @@ class GitPushService < BaseService
protected
def update_remote_mirrors
- return unless @project.has_remote_mirror?
+ return unless project.has_remote_mirror?
- @project.mark_stuck_remote_mirrors_as_failed!
- @project.update_remote_mirrors
+ project.mark_stuck_remote_mirrors_as_failed!
+ project.update_remote_mirrors
end
def execute_related_hooks
@@ -132,14 +135,14 @@ class GitPushService < BaseService
# could cause the last commit of a merge request to change.
#
UpdateMergeRequestsWorker
- .perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref])
+ .perform_async(project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref])
- EventCreateService.new.push(@project, current_user, build_push_data)
- Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute(:push)
+ EventCreateService.new.push(project, current_user, build_push_data)
+ Ci::CreatePipelineService.new(project, current_user, build_push_data).execute(:push)
SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks)
- @project.execute_hooks(build_push_data.dup, :push_hooks)
- @project.execute_services(build_push_data.dup, :push_hooks)
+ project.execute_hooks(build_push_data.dup, :push_hooks)
+ project.execute_services(build_push_data.dup, :push_hooks)
if push_remove_branch?
AfterBranchDeleteService
@@ -149,52 +152,50 @@ class GitPushService < BaseService
end
def perform_housekeeping
- housekeeping = Projects::HousekeepingService.new(@project)
+ housekeeping = Projects::HousekeepingService.new(project)
housekeeping.increment!
housekeeping.execute if housekeeping.needed?
rescue Projects::HousekeepingService::LeaseTaken
end
def process_default_branch
- @push_commits_count = project.repository.commit_count_for_ref(params[:ref])
-
- offset = [@push_commits_count - PROCESS_COMMIT_LIMIT, 0].max
+ offset = [push_commits_count - PROCESS_COMMIT_LIMIT, 0].max
@push_commits = project.repository.commits(params[:newrev], offset: offset, limit: PROCESS_COMMIT_LIMIT)
- @project.after_create_default_branch
+ project.after_create_default_branch
end
def build_push_data
@push_data ||= Gitlab::DataBuilder::Push.build(
- @project,
+ project,
current_user,
params[:oldrev],
params[:newrev],
params[:ref],
@push_commits,
- commits_count: @push_commits_count)
+ commits_count: push_commits_count)
end
def push_to_existing_branch?
# Return if this is not a push to a branch (e.g. new commits)
- Gitlab::Git.branch_ref?(params[:ref]) && !Gitlab::Git.blank_ref?(params[:oldrev])
+ branch_ref? && !Gitlab::Git.blank_ref?(params[:oldrev])
end
def push_to_new_branch?
- Gitlab::Git.branch_ref?(params[:ref]) && Gitlab::Git.blank_ref?(params[:oldrev])
+ strong_memoize(:push_to_new_branch) do
+ branch_ref? && Gitlab::Git.blank_ref?(params[:oldrev])
+ end
end
def push_remove_branch?
- Gitlab::Git.branch_ref?(params[:ref]) && Gitlab::Git.blank_ref?(params[:newrev])
- end
-
- def push_to_branch?
- Gitlab::Git.branch_ref?(params[:ref])
+ strong_memoize(:push_remove_branch) do
+ branch_ref? && Gitlab::Git.blank_ref?(params[:newrev])
+ end
end
def default_branch?
- Gitlab::Git.branch_ref?(params[:ref]) &&
- (Gitlab::Git.ref_name(params[:ref]) == project.default_branch || project.default_branch.nil?)
+ branch_ref? &&
+ (branch_name == project.default_branch || project.default_branch.nil?)
end
def commit_user(commit)
@@ -202,6 +203,24 @@ class GitPushService < BaseService
end
def branch_name
- @branch_name ||= Gitlab::Git.ref_name(params[:ref])
+ strong_memoize(:branch_name) do
+ Gitlab::Git.ref_name(params[:ref])
+ end
+ end
+
+ def branch_ref?
+ strong_memoize(:branch_ref) do
+ Gitlab::Git.branch_ref?(params[:ref])
+ end
+ end
+
+ def push_commits_count
+ strong_memoize(:push_commits_count) do
+ project.repository.commit_count_for_ref(params[:ref])
+ end
+ end
+
+ def last_pushed_commits
+ @last_pushed_commits ||= @push_commits.last(PROCESS_COMMIT_LIMIT)
end
end
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index 9917a39b795..dbadafc0f52 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitTagPushService < BaseService
attr_accessor :push_data
@@ -7,12 +9,12 @@ class GitTagPushService < BaseService
@push_data = build_push_data
- EventCreateService.new.push(project, current_user, @push_data)
- Ci::CreatePipelineService.new(project, current_user, @push_data).execute(:push)
+ EventCreateService.new.push(project, current_user, push_data)
+ Ci::CreatePipelineService.new(project, current_user, push_data).execute(:push)
- SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks)
- project.execute_hooks(@push_data.dup, :tag_push_hooks)
- project.execute_services(@push_data.dup, :tag_push_hooks)
+ SystemHooksService.new.execute_hooks(build_system_push_data, :tag_push_hooks)
+ project.execute_hooks(push_data.dup, :tag_push_hooks)
+ project.execute_services(push_data.dup, :tag_push_hooks)
ProjectCacheWorker.perform_async(project.id, [], [:commit_count, :repository_size])
diff --git a/app/services/gpg_keys/create_service.rb b/app/services/gpg_keys/create_service.rb
index e822a89c4d3..e41444b2a82 100644
--- a/app/services/gpg_keys/create_service.rb
+++ b/app/services/gpg_keys/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GpgKeys
class CreateService < Keys::BaseService
def execute
diff --git a/app/services/gravatar_service.rb b/app/services/gravatar_service.rb
index c6e52c3bb91..2a7a5dae291 100644
--- a/app/services/gravatar_service.rb
+++ b/app/services/gravatar_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GravatarService
def execute(email, size = nil, scale = 2, username: nil)
return unless Gitlab::CurrentSettings.gravatar_enabled?
diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb
index a8fa098246a..8c8acce5ca5 100644
--- a/app/services/groups/base_service.rb
+++ b/app/services/groups/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class BaseService < ::BaseService
attr_accessor :group, :current_user, :params
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index 70e50aa0f12..24d8400c625 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class CreateService < Groups::BaseService
def initialize(user, params = {})
diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb
index 58e88688dfa..c4554ce45fb 100644
--- a/app/services/groups/destroy_service.rb
+++ b/app/services/groups/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class DestroyService < Groups::BaseService
def async_execute
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
index 5c337a9faa5..50d34d8cb91 100644
--- a/app/services/groups/nested_create_service.rb
+++ b/app/services/groups/nested_create_service.rb
@@ -1,11 +1,14 @@
+# frozen_string_literal: true
+
module Groups
class NestedCreateService < Groups::BaseService
- attr_reader :group_path
+ attr_reader :group_path, :visibility_level
def initialize(user, params)
@current_user, @params = user, params.dup
-
@group_path = @params.delete(:group_path)
+ @visibility_level = @params.delete(:visibility_level) ||
+ Gitlab::CurrentSettings.current_application_settings.default_group_visibility
end
def execute
@@ -36,11 +39,12 @@ module Groups
new_params = params.reverse_merge(
path: subgroup_name,
name: subgroup_name,
- parent: last_group
+ parent: last_group,
+ visibility_level: visibility_level
)
- new_params[:visibility_level] ||= Gitlab::CurrentSettings.current_application_settings.default_group_visibility
- last_group = namespace_or_group(partial_path) || Groups::CreateService.new(current_user, new_params).execute
+ last_group = namespace_or_group(partial_path) ||
+ Groups::CreateService.new(current_user, new_params).execute
end
last_group
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index e591c820cff..ea7576077f3 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class TransferService < Groups::BaseService
ERROR_MESSAGES = {
diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb
index 08e3efb96e3..fe47aa2f140 100644
--- a/app/services/groups/update_service.rb
+++ b/app/services/groups/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class UpdateService < Groups::BaseService
include UpdateVisibilityLevel
@@ -12,7 +14,9 @@ module Groups
group.assign_attributes(params)
begin
- group.save
+ after_update if group.save
+
+ true
rescue Gitlab::UpdatePathError => e
group.errors.add(:base, e.message)
@@ -22,6 +26,13 @@ module Groups
private
+ def after_update
+ if group.previous_changes.include?(:visibility_level) && group.private?
+ # don't enqueue immediately to prevent todos removal in case of a mistake
+ TodosDestroyer::GroupPrivateWorker.perform_in(1.hour, group.id)
+ end
+ end
+
def reject_parent_id!
params.except!(:parent_id)
end
diff --git a/app/services/ham_service.rb b/app/services/ham_service.rb
index b0e1799b489..794eb34d9ca 100644
--- a/app/services/ham_service.rb
+++ b/app/services/ham_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class HamService
attr_accessor :spam_log
diff --git a/app/services/import_export_clean_up_service.rb b/app/services/import_export_clean_up_service.rb
index 74088b970c9..e75a951944e 100644
--- a/app/services/import_export_clean_up_service.rb
+++ b/app/services/import_export_clean_up_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ImportExportCleanUpService
LAST_MODIFIED_TIME_IN_MINUTES = 1440
@@ -10,7 +12,9 @@ class ImportExportCleanUpService
def execute
Gitlab::Metrics.measure(:import_export_clean_up) do
- next unless File.directory?(path)
+ clean_up_export_object_files
+
+ break unless File.directory?(path)
clean_up_export_files
end
@@ -21,4 +25,11 @@ class ImportExportCleanUpService
def clean_up_export_files
Gitlab::Popen.popen(%W(find #{path} -not -path #{path} -mmin +#{mmin} -delete))
end
+
+ def clean_up_export_object_files
+ ImportExportUpload.where('updated_at < ?', mmin.minutes.ago).each do |upload|
+ upload.remove_export_file!
+ upload.save!
+ end
+ end
end
diff --git a/app/services/issuable/bulk_update_service.rb b/app/services/issuable/bulk_update_service.rb
index 5d42a89fced..051d5ba881d 100644
--- a/app/services/issuable/bulk_update_service.rb
+++ b/app/services/issuable/bulk_update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issuable
class BulkUpdateService < IssuableBaseService
def execute(type)
diff --git a/app/services/issuable/common_system_notes_service.rb b/app/services/issuable/common_system_notes_service.rb
index 3da21bd8b8f..028b350ca07 100644
--- a/app/services/issuable/common_system_notes_service.rb
+++ b/app/services/issuable/common_system_notes_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issuable
class CommonSystemNotesService < ::BaseService
attr_reader :issuable
diff --git a/app/services/issuable/destroy_service.rb b/app/services/issuable/destroy_service.rb
index 0b1a33518c6..4c64655a622 100644
--- a/app/services/issuable/destroy_service.rb
+++ b/app/services/issuable/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issuable
class DestroyService < IssuableBaseService
def execute(issuable)
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 683f64e82ad..7d60c65bb79 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssuableBaseService < BaseService
private
@@ -130,7 +132,7 @@ class IssuableBaseService < BaseService
def create_issuable(issuable, attributes, label_ids:)
issuable.with_transaction_returning_status do
if issuable.save
- issuable.update_attributes(label_ids: label_ids)
+ issuable.update(label_ids: label_ids)
end
end
end
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 9f6cfc0f6d3..25389a946bb 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class BaseService < ::IssuableBaseService
def hook_data(issue, action, old_associations: {})
@@ -32,8 +34,9 @@ module Issues
def filter_assignee(issuable)
return if params[:assignee_ids].blank?
- # The number of assignees is limited by one for GitLab CE
- params[:assignee_ids] = params[:assignee_ids][0, 1]
+ unless issuable.allows_multiple_assignees?
+ params[:assignee_ids] = params[:assignee_ids].take(1)
+ end
assignee_ids = params[:assignee_ids].select { |assignee_id| assignee_can_read?(issuable, assignee_id) }
diff --git a/app/services/issues/build_service.rb b/app/services/issues/build_service.rb
index 3a4f7b159f1..52b45f1b2ce 100644
--- a/app/services/issues/build_service.rb
+++ b/app/services/issues/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class BuildService < Issues::BaseService
include ResolveDiscussions
@@ -44,14 +46,14 @@ module Issues
other_note_count = discussion.notes.size - 1
- discussion_info = "- [ ] #{first_note_to_resolve.author.to_reference} #{action} a [discussion](#{note_url}): "
- discussion_info << " (+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0
+ discussion_info = ["- [ ] #{first_note_to_resolve.author.to_reference} #{action} a [discussion](#{note_url}): "]
+ discussion_info << "(+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0
note_without_block_quotes = Banzai::Filter::BlockquoteFenceFilter.new(first_note_to_resolve.note).call
spaces = ' ' * 4
quote = note_without_block_quotes.lines.map { |line| "#{spaces}> #{line}" }.join
- [discussion_info, quote].join("\n\n")
+ [discussion_info.join(' '), quote].join("\n\n")
end
def issue_params
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index 4a99367c575..e5cc12e6082 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class CloseService < Issues::BaseService
# Closes the supplied issue if the current user is able to do so.
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 0307634c0b6..5793a15e1bc 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class CreateService < Issues::BaseService
include SpamCheckService
diff --git a/app/services/issues/duplicate_service.rb b/app/services/issues/duplicate_service.rb
index 5c0854e664d..9b22f5e7914 100644
--- a/app/services/issues/duplicate_service.rb
+++ b/app/services/issues/duplicate_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class DuplicateService < Issues::BaseService
def execute(duplicate_issue, canonical_issue)
diff --git a/app/services/issues/fetch_referenced_merge_requests_service.rb b/app/services/issues/fetch_referenced_merge_requests_service.rb
index 39c8ded9df4..5e84f3c81c9 100644
--- a/app/services/issues/fetch_referenced_merge_requests_service.rb
+++ b/app/services/issues/fetch_referenced_merge_requests_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class FetchReferencedMergeRequestsService < Issues::BaseService
def execute(issue)
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index 6e5c29a5c40..841bce9949e 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class MoveService < Issues::BaseService
MoveError = Class.new(StandardError)
diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb
index 02224f3357a..3bd53f9ccdc 100644
--- a/app/services/issues/reopen_service.rb
+++ b/app/services/issues/reopen_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class ReopenService < Issues::BaseService
def execute(issue)
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 1000e1842b6..faa4c8a5a4f 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class UpdateService < Issues::BaseService
include SpamCheckService
@@ -35,6 +37,8 @@ module Issues
end
if issue.previous_changes.include?('confidential')
+ # don't enqueue immediately to prevent todos removal in case of a mistake
+ TodosDestroyer::ConfidentialIssueWorker.perform_in(1.hour, issue.id) if issue.confidential?
create_confidentiality_note(issue)
end
diff --git a/app/services/keys/base_service.rb b/app/services/keys/base_service.rb
index df8e82f5f60..113e22b01ce 100644
--- a/app/services/keys/base_service.rb
+++ b/app/services/keys/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class BaseService
attr_accessor :user, :params
diff --git a/app/services/keys/create_service.rb b/app/services/keys/create_service.rb
index e2e5a6c46c5..d9fa69a88d7 100644
--- a/app/services/keys/create_service.rb
+++ b/app/services/keys/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class CreateService < ::Keys::BaseService
def execute
diff --git a/app/services/keys/destroy_service.rb b/app/services/keys/destroy_service.rb
index 785cfa3a1d8..e2ae4047941 100644
--- a/app/services/keys/destroy_service.rb
+++ b/app/services/keys/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class DestroyService < ::Keys::BaseService
def execute(key)
diff --git a/app/services/keys/last_used_service.rb b/app/services/keys/last_used_service.rb
index dbd79f7da55..daef544bac0 100644
--- a/app/services/keys/last_used_service.rb
+++ b/app/services/keys/last_used_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class LastUsedService
TIMEOUT = 1.day.to_i
diff --git a/app/services/labels/base_service.rb b/app/services/labels/base_service.rb
index 91d72a57b4e..ead7f2ea607 100644
--- a/app/services/labels/base_service.rb
+++ b/app/services/labels/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class BaseService < ::BaseService
COLOR_NAME_TO_HEX = {
diff --git a/app/services/labels/create_service.rb b/app/services/labels/create_service.rb
index 6c399c92377..fe34be41ac1 100644
--- a/app/services/labels/create_service.rb
+++ b/app/services/labels/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class CreateService < Labels::BaseService
def initialize(params = {})
diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb
index 079f611b3f3..e4486764a4d 100644
--- a/app/services/labels/find_or_create_service.rb
+++ b/app/services/labels/find_or_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class FindOrCreateService
def initialize(current_user, parent, params = {})
@@ -20,6 +22,7 @@ module Labels
@available_labels ||= LabelsFinder.new(
current_user,
"#{parent_type}_id".to_sym => parent.id,
+ include_ancestor_groups: include_ancestor_groups?,
only_group_labels: parent_is_group?
).execute(skip_authorization: skip_authorization)
end
@@ -30,7 +33,8 @@ module Labels
new_label = available_labels.find_by(title: title)
if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, parent))
- new_label = Labels::CreateService.new(params).execute(parent_type.to_sym => parent)
+ create_params = params.except(:include_ancestor_groups)
+ new_label = Labels::CreateService.new(create_params).execute(parent_type.to_sym => parent)
end
new_label
@@ -47,5 +51,9 @@ module Labels
def parent_is_group?
parent_type == "group"
end
+
+ def include_ancestor_groups?
+ params[:include_ancestor_groups] == true
+ end
end
end
diff --git a/app/services/labels/promote_service.rb b/app/services/labels/promote_service.rb
index 74a85e5c9f0..c0463052821 100644
--- a/app/services/labels/promote_service.rb
+++ b/app/services/labels/promote_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class PromoteService < BaseService
BATCH_SIZE = 1000
diff --git a/app/services/labels/transfer_service.rb b/app/services/labels/transfer_service.rb
index 9b7486cf53b..1bd8d9fc325 100644
--- a/app/services/labels/transfer_service.rb
+++ b/app/services/labels/transfer_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Labels::TransferService class
#
# User for recreate the missing group labels at project level
diff --git a/app/services/labels/update_service.rb b/app/services/labels/update_service.rb
index 28dcabf9541..c3a720a1c66 100644
--- a/app/services/labels/update_service.rb
+++ b/app/services/labels/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class UpdateService < Labels::BaseService
def initialize(params = {})
diff --git a/app/services/lfs/file_transformer.rb b/app/services/lfs/file_transformer.rb
index 69281ee3137..c8eccb8e6cd 100644
--- a/app/services/lfs/file_transformer.rb
+++ b/app/services/lfs/file_transformer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Lfs
# Usage: Calling `new_file` check to see if a file should be in LFS and
# return a transformed result with `content` and `encoding` to commit.
diff --git a/app/services/lfs/lock_file_service.rb b/app/services/lfs/lock_file_service.rb
index bbe10f84ef4..78434909d68 100644
--- a/app/services/lfs/lock_file_service.rb
+++ b/app/services/lfs/lock_file_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Lfs
class LockFileService < BaseService
def execute
diff --git a/app/services/lfs/locks_finder_service.rb b/app/services/lfs/locks_finder_service.rb
index 13c6cc6f81c..d52cf0e3cc4 100644
--- a/app/services/lfs/locks_finder_service.rb
+++ b/app/services/lfs/locks_finder_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Lfs
class LocksFinderService < BaseService
def execute
diff --git a/app/services/lfs/unlock_file_service.rb b/app/services/lfs/unlock_file_service.rb
index 7e3edf21d54..4d1443bf772 100644
--- a/app/services/lfs/unlock_file_service.rb
+++ b/app/services/lfs/unlock_file_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Lfs
class UnlockFileService < BaseService
def execute
diff --git a/app/services/mattermost/create_team_service.rb b/app/services/mattermost/create_team_service.rb
index e3206810f3a..afcd6439a14 100644
--- a/app/services/mattermost/create_team_service.rb
+++ b/app/services/mattermost/create_team_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
class CreateTeamService < ::BaseService
def initialize(group, current_user)
diff --git a/app/services/members/approve_access_request_service.rb b/app/services/members/approve_access_request_service.rb
index 6be08b590bc..52b890d1821 100644
--- a/app/services/members/approve_access_request_service.rb
+++ b/app/services/members/approve_access_request_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class ApproveAccessRequestService < Members::BaseService
def execute(access_requester, skip_authorization: false, skip_log_audit_event: false)
diff --git a/app/services/members/base_service.rb b/app/services/members/base_service.rb
index 74556fb20cf..8248f1441d7 100644
--- a/app/services/members/base_service.rb
+++ b/app/services/members/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class BaseService < ::BaseService
# current_user - The user that performs the action
diff --git a/app/services/members/create_service.rb b/app/services/members/create_service.rb
index bc6a9405aac..714b8586737 100644
--- a/app/services/members/create_service.rb
+++ b/app/services/members/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class CreateService < Members::BaseService
DEFAULT_LIMIT = 100
diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb
index 5b51e1982f1..c186a5971dc 100644
--- a/app/services/members/destroy_service.rb
+++ b/app/services/members/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class DestroyService < Members::BaseService
def execute(member, skip_authorization: false)
@@ -13,6 +15,8 @@ module Members
notification_service.decline_access_request(member)
end
+ enqeue_delete_todos(member)
+
after_execute(member: member)
member
@@ -20,6 +24,12 @@ module Members
private
+ def enqeue_delete_todos(member)
+ type = member.is_a?(GroupMember) ? 'Group' : 'Project'
+ # don't enqueue immediately to prevent todos removal in case of a mistake
+ TodosDestroyer::EntityLeaveWorker.perform_in(1.hour, member.user_id, member.source_id, type)
+ end
+
def can_destroy_member?(member)
can?(current_user, destroy_member_permission(member), member)
end
diff --git a/app/services/members/request_access_service.rb b/app/services/members/request_access_service.rb
index 24293b30005..b9b0550e290 100644
--- a/app/services/members/request_access_service.rb
+++ b/app/services/members/request_access_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class RequestAccessService < Members::BaseService
def execute(source)
diff --git a/app/services/members/update_service.rb b/app/services/members/update_service.rb
index 48b3d59f7bd..1f5618dae53 100644
--- a/app/services/members/update_service.rb
+++ b/app/services/members/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class UpdateService < Members::BaseService
# returns the updated member
@@ -6,7 +8,7 @@ module Members
old_access_level = member.human_access
- if member.update_attributes(params)
+ if member.update(params)
after_execute(action: permission, old_access_level: old_access_level, member: member)
end
diff --git a/app/services/merge_request_metrics_service.rb b/app/services/merge_request_metrics_service.rb
index 9248de14a53..4e88b77c855 100644
--- a/app/services/merge_request_metrics_service.rb
+++ b/app/services/merge_request_metrics_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestMetricsService
delegate :update!, to: :@merge_request_metrics
diff --git a/app/services/merge_requests/add_todo_when_build_fails_service.rb b/app/services/merge_requests/add_todo_when_build_fails_service.rb
index 6805b2f7d1c..79c43b8e7d5 100644
--- a/app/services/merge_requests/add_todo_when_build_fails_service.rb
+++ b/app/services/merge_requests/add_todo_when_build_fails_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class AddTodoWhenBuildFailsService < MergeRequests::BaseService
# Adds a todo to the parent merge_request when a CI build fails
diff --git a/app/services/merge_requests/assign_issues_service.rb b/app/services/merge_requests/assign_issues_service.rb
index 8c6c4841020..e9107b9998e 100644
--- a/app/services/merge_requests/assign_issues_service.rb
+++ b/app/services/merge_requests/assign_issues_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class AssignIssuesService < BaseService
def assignable_issues
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 4c420b38258..e6dd0e12a3a 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class BaseService < ::IssuableBaseService
def create_note(merge_request, state = merge_request.state)
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index a98bbdf74dd..bc988eb2a26 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class BuildService < MergeRequests::BaseService
include Gitlab::Utils::StrongMemoize
@@ -140,7 +142,8 @@ module MergeRequests
closes_issue = "Closes #{issue.to_reference}"
if description.present?
- merge_request.description += closes_issue.prepend("\n\n")
+ descr_parts = [merge_request.description, closes_issue]
+ merge_request.description = descr_parts.join("\n\n")
else
merge_request.description = closes_issue
end
@@ -164,9 +167,11 @@ module MergeRequests
return if merge_request.title.present?
if issue_iid.present?
- merge_request.title = "Resolve #{issue.to_reference}"
+ title_parts = ["Resolve #{issue.to_reference}"]
branch_title = source_branch.downcase.remove(issue_iid.downcase).titleize.humanize
- merge_request.title += " \"#{branch_title}\"" if branch_title.present?
+
+ title_parts << "\"#{branch_title}\"" if branch_title.present?
+ merge_request.title = title_parts.join(' ')
end
end
diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb
index db701c1145d..04527bb9713 100644
--- a/app/services/merge_requests/close_service.rb
+++ b/app/services/merge_requests/close_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class CloseService < MergeRequests::BaseService
def execute(merge_request, commit = nil)
diff --git a/app/services/merge_requests/conflicts/base_service.rb b/app/services/merge_requests/conflicts/base_service.rb
index b50875347d9..402f6c4e4c0 100644
--- a/app/services/merge_requests/conflicts/base_service.rb
+++ b/app/services/merge_requests/conflicts/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
module Conflicts
class BaseService
diff --git a/app/services/merge_requests/conflicts/list_service.rb b/app/services/merge_requests/conflicts/list_service.rb
index 72cbc49adb2..c6b3a6a1a69 100644
--- a/app/services/merge_requests/conflicts/list_service.rb
+++ b/app/services/merge_requests/conflicts/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
module Conflicts
class ListService < MergeRequests::Conflicts::BaseService
diff --git a/app/services/merge_requests/conflicts/resolve_service.rb b/app/services/merge_requests/conflicts/resolve_service.rb
index 27cafd2d7d9..b9f734310be 100644
--- a/app/services/merge_requests/conflicts/resolve_service.rb
+++ b/app/services/merge_requests/conflicts/resolve_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
module Conflicts
class ResolveService < MergeRequests::Conflicts::BaseService
diff --git a/app/services/merge_requests/create_from_issue_service.rb b/app/services/merge_requests/create_from_issue_service.rb
index 3407b312700..fd91dc4acd0 100644
--- a/app/services/merge_requests/create_from_issue_service.rb
+++ b/app/services/merge_requests/create_from_issue_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class CreateFromIssueService < MergeRequests::CreateService
def initialize(project, user, params)
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index fe1ac70781e..c36a2ecbfe3 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class CreateService < MergeRequests::BaseService
def execute
diff --git a/app/services/merge_requests/delete_non_latest_diffs_service.rb b/app/services/merge_requests/delete_non_latest_diffs_service.rb
index 40079b21189..2a8ea316921 100644
--- a/app/services/merge_requests/delete_non_latest_diffs_service.rb
+++ b/app/services/merge_requests/delete_non_latest_diffs_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class DeleteNonLatestDiffsService
BATCH_SIZE = 10
diff --git a/app/services/merge_requests/ff_merge_service.rb b/app/services/merge_requests/ff_merge_service.rb
index bffc09c34f0..479e0fe6699 100644
--- a/app/services/merge_requests/ff_merge_service.rb
+++ b/app/services/merge_requests/ff_merge_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
# MergeService class
#
diff --git a/app/services/merge_requests/get_urls_service.rb b/app/services/merge_requests/get_urls_service.rb
index 668a1741736..7c88c9abb41 100644
--- a/app/services/merge_requests/get_urls_service.rb
+++ b/app/services/merge_requests/get_urls_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class GetUrlsService < BaseService
attr_reader :project
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 3d587f97906..fb44f809c41 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
# MergeService class
#
diff --git a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
index 9a4e6eb2e88..973e5b64e88 100644
--- a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
+++ b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class MergeWhenPipelineSucceedsService < MergeRequests::BaseService
# Marks the passed `merge_request` to be merged when the pipeline succeeds or
diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb
index 7606d68ff29..f26e3bee06f 100644
--- a/app/services/merge_requests/post_merge_service.rb
+++ b/app/services/merge_requests/post_merge_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
# PostMergeService class
#
@@ -23,7 +25,7 @@ module MergeRequests
def close_issues(merge_request)
return unless merge_request.target_branch == project.default_branch
- closed_issues = merge_request.closes_issues(current_user)
+ closed_issues = merge_request.visible_closing_issues_for(current_user)
closed_issues.each do |issue|
if can?(current_user, :update_issue, issue)
diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb
index 5b4bc86b9ba..31b3ebf311e 100644
--- a/app/services/merge_requests/rebase_service.rb
+++ b/app/services/merge_requests/rebase_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class RebaseService < MergeRequests::WorkingCopyBaseService
REBASE_ERROR = 'Rebase failed. Please rebase locally'.freeze
@@ -26,7 +28,7 @@ module MergeRequests
Gitlab::GitLogger.info("#{log_prefix} rebased to #{rebase_sha}")
- merge_request.update_attributes(rebase_commit_sha: rebase_sha)
+ merge_request.update(rebase_commit_sha: rebase_sha)
Gitlab::GitLogger.info("#{log_prefix} rebase SHA saved: #{rebase_sha}")
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 0127d781686..48da796505f 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class RefreshService < MergeRequests::BaseService
def execute(oldrev, newrev, ref)
diff --git a/app/services/merge_requests/reload_diffs_service.rb b/app/services/merge_requests/reload_diffs_service.rb
index 2ec7b403903..8d85dc9eb5f 100644
--- a/app/services/merge_requests/reload_diffs_service.rb
+++ b/app/services/merge_requests/reload_diffs_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class ReloadDiffsService
def initialize(merge_request, current_user)
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index 8f1c95ac1b7..f6cbe769ef4 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class ReopenService < MergeRequests::BaseService
def execute(merge_request)
@@ -12,6 +14,7 @@ module MergeRequests
merge_request.mark_as_unchecked
invalidate_cache_counts(merge_request, users: merge_request.assignees)
merge_request.update_project_counter_caches
+ merge_request.cache_merge_request_closes_issues!(current_user)
end
merge_request
diff --git a/app/services/merge_requests/resolved_discussion_notification_service.rb b/app/services/merge_requests/resolved_discussion_notification_service.rb
index 66a0cbc81d4..03ded1512f9 100644
--- a/app/services/merge_requests/resolved_discussion_notification_service.rb
+++ b/app/services/merge_requests/resolved_discussion_notification_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class ResolvedDiscussionNotificationService < MergeRequests::BaseService
def execute(merge_request)
diff --git a/app/services/merge_requests/squash_service.rb b/app/services/merge_requests/squash_service.rb
index a40fb2786bd..a439a380255 100644
--- a/app/services/merge_requests/squash_service.rb
+++ b/app/services/merge_requests/squash_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class SquashService < MergeRequests::WorkingCopyBaseService
def execute(merge_request)
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 7350725e223..b112edbce7f 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class UpdateService < MergeRequests::BaseService
def execute(merge_request)
diff --git a/app/services/merge_requests/working_copy_base_service.rb b/app/services/merge_requests/working_copy_base_service.rb
index 186e05bf966..2d2be1f4c25 100644
--- a/app/services/merge_requests/working_copy_base_service.rb
+++ b/app/services/merge_requests/working_copy_base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class WorkingCopyBaseService < MergeRequests::BaseService
attr_reader :merge_request
diff --git a/app/services/metrics_service.rb b/app/services/metrics_service.rb
index 51ff9eff5e4..222a5c8c79c 100644
--- a/app/services/metrics_service.rb
+++ b/app/services/metrics_service.rb
@@ -1,35 +1,18 @@
+# frozen_string_literal: true
+
require 'prometheus/client/formats/text'
class MetricsService
- CHECKS = [
- Gitlab::HealthChecks::DbCheck,
- Gitlab::HealthChecks::Redis::RedisCheck,
- Gitlab::HealthChecks::Redis::CacheCheck,
- Gitlab::HealthChecks::Redis::QueuesCheck,
- Gitlab::HealthChecks::Redis::SharedStateCheck,
- Gitlab::HealthChecks::GitalyCheck
- ].freeze
-
def prometheus_metrics_text
Prometheus::Client::Formats::Text.marshal_multiprocess(multiprocess_metrics_path)
end
- def health_metrics_text
- metrics = CHECKS.flat_map(&:metrics)
-
- formatter.marshal(metrics)
- end
-
def metrics_text
- prometheus_metrics_text.concat(health_metrics_text)
+ prometheus_metrics_text
end
private
- def formatter
- @formatter ||= Gitlab::HealthChecks::PrometheusTextFormat.new
- end
-
def multiprocess_metrics_path
::Prometheus::Client.configuration.multiprocess_files_dir
end
diff --git a/app/services/milestones/base_service.rb b/app/services/milestones/base_service.rb
index cce0863d611..f30194c0bfe 100644
--- a/app/services/milestones/base_service.rb
+++ b/app/services/milestones/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class BaseService < ::BaseService
# Parent can either a group or a project
diff --git a/app/services/milestones/close_service.rb b/app/services/milestones/close_service.rb
index 5b06c4b601d..a252f5c144e 100644
--- a/app/services/milestones/close_service.rb
+++ b/app/services/milestones/close_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class CloseService < Milestones::BaseService
def execute(milestone)
diff --git a/app/services/milestones/create_service.rb b/app/services/milestones/create_service.rb
index ed2e833d833..6c3edd2e147 100644
--- a/app/services/milestones/create_service.rb
+++ b/app/services/milestones/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class CreateService < Milestones::BaseService
def execute
diff --git a/app/services/milestones/destroy_service.rb b/app/services/milestones/destroy_service.rb
index b18651476a8..15c04525075 100644
--- a/app/services/milestones/destroy_service.rb
+++ b/app/services/milestones/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class DestroyService < Milestones::BaseService
def execute(milestone)
diff --git a/app/services/milestones/promote_service.rb b/app/services/milestones/promote_service.rb
index 2187f26d1ed..37aa6d3a9bc 100644
--- a/app/services/milestones/promote_service.rb
+++ b/app/services/milestones/promote_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class PromoteService < Milestones::BaseService
PromoteMilestoneError = Class.new(StandardError)
diff --git a/app/services/milestones/reopen_service.rb b/app/services/milestones/reopen_service.rb
index 3efb33157c5..125a3ec1367 100644
--- a/app/services/milestones/reopen_service.rb
+++ b/app/services/milestones/reopen_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class ReopenService < Milestones::BaseService
def execute(milestone)
diff --git a/app/services/milestones/update_service.rb b/app/services/milestones/update_service.rb
index 31b441ed476..81b20943bab 100644
--- a/app/services/milestones/update_service.rb
+++ b/app/services/milestones/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class UpdateService < Milestones::BaseService
def execute(milestone)
@@ -11,7 +13,7 @@ module Milestones
end
if params.present?
- milestone.update_attributes(params.except(:state_event))
+ milestone.update(params.except(:state_event))
end
milestone
diff --git a/app/services/note_summary.rb b/app/services/note_summary.rb
index a6f6320d573..81f6f92f75c 100644
--- a/app/services/note_summary.rb
+++ b/app/services/note_summary.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteSummary
attr_reader :note
attr_reader :metadata
diff --git a/app/services/notes/build_service.rb b/app/services/notes/build_service.rb
index 77e7b8a5ea7..df5fe65de3c 100644
--- a/app/services/notes/build_service.rb
+++ b/app/services/notes/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class BuildService < ::BaseService
def execute
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 9ea28733f5f..049e6c5a871 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class CreateService < ::BaseService
def execute
diff --git a/app/services/notes/destroy_service.rb b/app/services/notes/destroy_service.rb
index fb78420d324..64e9accd97f 100644
--- a/app/services/notes/destroy_service.rb
+++ b/app/services/notes/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class DestroyService < BaseService
def execute(note)
diff --git a/app/services/notes/post_process_service.rb b/app/services/notes/post_process_service.rb
index 199b8028dbc..48722cc2a79 100644
--- a/app/services/notes/post_process_service.rb
+++ b/app/services/notes/post_process_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class PostProcessService
attr_accessor :note
diff --git a/app/services/notes/quick_actions_service.rb b/app/services/notes/quick_actions_service.rb
index 0a33d5f3f3d..7280449bb1c 100644
--- a/app/services/notes/quick_actions_service.rb
+++ b/app/services/notes/quick_actions_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class QuickActionsService < BaseService
UPDATE_SERVICES = {
diff --git a/app/services/notes/render_service.rb b/app/services/notes/render_service.rb
index efc9d6da2aa..0e1a55ae2ff 100644
--- a/app/services/notes/render_service.rb
+++ b/app/services/notes/render_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class RenderService < BaseRenderer
# Renders a collection of Note instances.
diff --git a/app/services/notes/resolve_service.rb b/app/services/notes/resolve_service.rb
index 0db8ee809a9..cf24795f050 100644
--- a/app/services/notes/resolve_service.rb
+++ b/app/services/notes/resolve_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class ResolveService < ::BaseService
def execute(note)
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
index 75fd08ea0a9..35db409eb27 100644
--- a/app/services/notes/update_service.rb
+++ b/app/services/notes/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class UpdateService < BaseService
def execute(note)
@@ -5,7 +7,7 @@ module Notes
old_mentioned_users = note.mentioned_users.to_a
- note.update_attributes(params.merge(updated_by: current_user))
+ note.update(params.merge(updated_by: current_user))
note.create_new_cross_references!(current_user)
if note.previous_changes.include?('note')
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 4fa38665abc..4389fd89538 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#
# Used by NotificationService to determine who should receive notification
#
@@ -10,16 +12,16 @@ module NotificationRecipientService
NotificationRecipient.new(user, *args).notifiable?
end
- def self.build_recipients(*a)
- Builder::Default.new(*a).notification_recipients
+ def self.build_recipients(*args)
+ Builder::Default.new(*args).notification_recipients
end
- def self.build_new_note_recipients(*a)
- Builder::NewNote.new(*a).notification_recipients
+ def self.build_new_note_recipients(*args)
+ Builder::NewNote.new(*args).notification_recipients
end
- def self.build_merge_request_unmergeable_recipients(*a)
- Builder::MergeRequestUnmergeable.new(*a).notification_recipients
+ def self.build_merge_request_unmergeable_recipients(*args)
+ Builder::MergeRequestUnmergeable.new(*args).notification_recipients
end
module Builder
@@ -44,7 +46,6 @@ module NotificationRecipientService
raise 'abstract'
end
- # rubocop:disable Rails/Delegate
def project
target.project
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 636cfbf5b45..4511c500fca 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# rubocop:disable GitlabSecurity/PublicSend
# NotificationService class
@@ -135,6 +137,8 @@ class NotificationService
# * watchers of the mr's labels
# * users with custom level checked with "new merge request"
#
+ # In EE, approvers of the merge request are also included
+ #
def new_merge_request(merge_request, current_user)
new_resource_email(merge_request, :new_merge_request_email)
end
@@ -256,6 +260,10 @@ class NotificationService
# ignore gitlab service messages
return true if note.cross_reference? && note.system?
+ send_new_note_notifications(note)
+ end
+
+ def send_new_note_notifications(note)
notify_method = "note_#{note.to_ability_name}_email".to_sym
recipients = NotificationRecipientService.build_new_note_recipients(note)
@@ -268,9 +276,9 @@ class NotificationService
def new_access_request(member)
return true unless member.notifiable?(:subscription)
- recipients = member.source.members.active_without_invites_and_requests.owners_and_masters
- if fallback_to_group_owners_masters?(recipients, member)
- recipients = member.source.group.members.active_without_invites_and_requests.owners_and_masters
+ recipients = member.source.members.active_without_invites_and_requests.owners_and_maintainers
+ if fallback_to_group_owners_maintainers?(recipients, member)
+ recipients = member.source.group.members.active_without_invites_and_requests.owners_and_maintainers
end
recipients.each { |recipient| deliver_access_request_email(recipient, member) }
@@ -513,7 +521,7 @@ class NotificationService
return [] unless project
- notifiable_users(project.team.masters, :watch, target: project)
+ notifiable_users(project.team.maintainers, :watch, target: project)
end
def notifiable?(*args)
@@ -528,7 +536,7 @@ class NotificationService
mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later
end
- def fallback_to_group_owners_masters?(recipients, member)
+ def fallback_to_group_owners_maintainers?(recipients, member)
return false if recipients.present?
member.source.respond_to?(:group) && member.source.group
diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb
index 4ee2c1796bd..a15ee4911ef 100644
--- a/app/services/preview_markdown_service.rb
+++ b/app/services/preview_markdown_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PreviewMarkdownService < BaseService
def execute
text, commands = explain_quick_actions(params[:text])
@@ -6,7 +8,8 @@ class PreviewMarkdownService < BaseService
success(
text: text,
users: users,
- commands: commands.join(' ')
+ commands: commands.join(' '),
+ markdown_engine: markdown_engine
)
end
@@ -42,4 +45,8 @@ class PreviewMarkdownService < BaseService
def commands_target_id
params[:quick_actions_target_id]
end
+
+ def markdown_engine
+ CacheMarkdownField::MarkdownEngine.from_version(params[:markdown_version].to_i)
+ end
end
diff --git a/app/services/projects/after_import_service.rb b/app/services/projects/after_import_service.rb
index 3047268b2d1..bbdde4408d2 100644
--- a/app/services/projects/after_import_service.rb
+++ b/app/services/projects/after_import_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class AfterImportService
RESERVED_REF_PREFIXES = Repository::RESERVED_REFS_NAMES.map { |n| File.join('refs', n, '/') }
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index aa60661f7f2..10eb2cea4a2 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class AutocompleteService < BaseService
def issues
@@ -20,24 +22,28 @@ module Projects
MergeRequestsFinder.new(current_user, project_id: project.id, state: 'opened').execute.select([:iid, :title])
end
- def labels(target = nil)
- labels = LabelsFinder.new(current_user, project_id: project.id, include_ancestor_groups: true)
- .execute.select([:color, :title])
-
- return labels unless target&.respond_to?(:labels)
-
- issuable_label_titles = target.labels.pluck(:title)
-
- if issuable_label_titles
- labels = labels.as_json(only: [:title, :color])
-
- issuable_label_titles.each do |issuable_label_title|
- found_label = labels.find { |label| label['title'] == issuable_label_title }
- found_label[:set] = true if found_label
+ def labels_as_hash(target = nil)
+ available_labels = LabelsFinder.new(
+ current_user,
+ project_id: project.id,
+ include_ancestor_groups: true
+ ).execute
+
+ label_hashes = available_labels.as_json(only: [:title, :color])
+
+ if target&.respond_to?(:labels)
+ already_set_labels = available_labels & target.labels
+ if already_set_labels.present?
+ titles = already_set_labels.map(&:title)
+ label_hashes.each do |hash|
+ if titles.include?(hash['title'])
+ hash[:set] = true
+ end
+ end
end
end
- labels
+ label_hashes
end
def commands(noteable, type)
diff --git a/app/services/projects/base_move_relations_service.rb b/app/services/projects/base_move_relations_service.rb
index e8fd3ef57e5..78cc2869b72 100644
--- a/app/services/projects/base_move_relations_service.rb
+++ b/app/services/projects/base_move_relations_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class BaseMoveRelationsService < BaseService
attr_reader :source_project
diff --git a/app/services/projects/batch_count_service.rb b/app/services/projects/batch_count_service.rb
index 178ebc5a143..aec3b32da89 100644
--- a/app/services/projects/batch_count_service.rb
+++ b/app/services/projects/batch_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Service class for getting and caching the number of elements of several projects
# Warning: do not user this service with a really large set of projects
# because the service use maps to retrieve the project ids.
diff --git a/app/services/projects/batch_forks_count_service.rb b/app/services/projects/batch_forks_count_service.rb
index e61fe6c86b2..9bf369df999 100644
--- a/app/services/projects/batch_forks_count_service.rb
+++ b/app/services/projects/batch_forks_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Service class for getting and caching the number of forks of several projects
# Warning: do not user this service with a really large set of projects
# because the service use maps to retrieve the project ids
diff --git a/app/services/projects/batch_open_issues_count_service.rb b/app/services/projects/batch_open_issues_count_service.rb
index 3b0ade2419b..d375fcf9dbd 100644
--- a/app/services/projects/batch_open_issues_count_service.rb
+++ b/app/services/projects/batch_open_issues_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Service class for getting and caching the number of issues of several projects
# Warning: do not user this service with a really large set of projects
# because the service use maps to retrieve the project ids
diff --git a/app/services/projects/count_service.rb b/app/services/projects/count_service.rb
index 4c8e000928f..3cee80c7bbc 100644
--- a/app/services/projects/count_service.rb
+++ b/app/services/projects/count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
# Base class for the various service classes that count project data (e.g.
# issues or forks).
diff --git a/app/services/projects/create_from_template_service.rb b/app/services/projects/create_from_template_service.rb
index 29b133cc466..8306d43ca7c 100644
--- a/app/services/projects/create_from_template_service.rb
+++ b/app/services/projects/create_from_template_service.rb
@@ -1,20 +1,28 @@
+# frozen_string_literal: true
+
module Projects
class CreateFromTemplateService < BaseService
+ include Gitlab::Utils::StrongMemoize
+
def initialize(user, params)
@current_user, @params = user, params.dup
end
def execute
- template_name = params.delete(:template_name)
- file = Gitlab::ProjectTemplate.find(template_name).file
+ file = Gitlab::ProjectTemplate.find(template_name)&.file
override_params = params.dup
params[:file] = file
GitlabProjectsImportService.new(current_user, params, override_params).execute
-
ensure
file&.close
end
+
+ def template_name
+ strong_memoize(:template_name) do
+ params.delete(:template_name).presence
+ end
+ end
end
end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index a02a9052fb2..02a3a3eb096 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
module Projects
class CreateService < BaseService
def initialize(user, params)
@current_user, @params = user, params.dup
+ @skip_wiki = @params.delete(:skip_wiki)
+ @initialize_with_readme = Gitlab::Utils.to_boolean(@params.delete(:initialize_with_readme))
end
def execute
@@ -11,7 +15,6 @@ module Projects
forked_from_project_id = params.delete(:forked_from_project_id)
import_data = params.delete(:import_data)
- @skip_wiki = params.delete(:skip_wiki)
@project = Project.new(params)
@@ -102,6 +105,8 @@ module Projects
setup_authorizations
current_user.invalidate_personal_projects_count
+
+ create_readme if @initialize_with_readme
end
# Refresh the current user's authorizations inline (so they can access the
@@ -112,10 +117,21 @@ module Projects
@project.group.refresh_members_authorized_projects(blocking: false)
current_user.refresh_authorized_projects
else
- @project.add_master(@project.namespace.owner, current_user: current_user)
+ @project.add_maintainer(@project.namespace.owner, current_user: current_user)
end
end
+ def create_readme
+ commit_attrs = {
+ branch_name: 'master',
+ commit_message: 'Initial commit',
+ file_path: 'README.md',
+ file_content: "# #{@project.name}\n\n#{@project.description}"
+ }
+
+ Files::CreateService.new(@project, current_user, commit_attrs).execute
+ end
+
def skip_wiki?
!@project.feature_available?(:wiki, current_user) || @skip_wiki
end
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 02769e72229..46a8a5e4d98 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class DestroyService < BaseService
include Gitlab::ShellAdapter
@@ -124,7 +126,7 @@ module Projects
# It's possible that the project was destroyed, but some after_commit
# hook failed and caused us to end up here. A destroyed model will be a frozen hash,
# which cannot be altered.
- project.update_attributes(delete_error: message, pending_delete: false) unless project.destroyed?
+ project.update(delete_error: message, pending_delete: false) unless project.destroyed?
log_error("Deletion failed on #{project.full_path} with the following message: #{message}")
end
diff --git a/app/services/projects/detect_repository_languages_service.rb b/app/services/projects/detect_repository_languages_service.rb
new file mode 100644
index 00000000000..4b4108de231
--- /dev/null
+++ b/app/services/projects/detect_repository_languages_service.rb
@@ -0,0 +1,52 @@
+module Projects
+ class DetectRepositoryLanguagesService < BaseService
+ attr_reader :detected_repository_languages, :programming_languages
+
+ def execute
+ repository_languages = project.repository_languages
+ detection = Gitlab::LanguageDetection.new(repository, repository_languages)
+
+ matching_programming_languages = ensure_programming_languages(detection)
+
+ RepositoryLanguage.transaction do
+ project.repository_languages.where(programming_language_id: detection.deletions).delete_all
+
+ detection.updates.each do |update|
+ RepositoryLanguage
+ .where(project_id: project.id)
+ .where(programming_language_id: update[:programming_language_id])
+ .update_all(share: update[:share])
+ end
+
+ Gitlab::Database.bulk_insert(
+ RepositoryLanguage.table_name,
+ detection.insertions(matching_programming_languages)
+ )
+ end
+
+ project.repository_languages.reload
+ end
+
+ private
+
+ def ensure_programming_languages(detection)
+ existing_languages = ProgrammingLanguage.where(name: detection.languages)
+ return existing_languages if detection.languages.size == existing_languages.size
+
+ missing_languages = detection.languages - existing_languages.map(&:name)
+ created_languages = missing_languages.map do |name|
+ create_language(name, detection.language_color(name))
+ end
+
+ existing_languages + created_languages
+ end
+
+ def create_language(name, color)
+ ProgrammingLanguage.transaction do
+ ProgrammingLanguage.where(name: name).first_or_create(color: color)
+ end
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+ end
+end
diff --git a/app/services/projects/download_service.rb b/app/services/projects/download_service.rb
index 604747e39d0..dd297c9ba43 100644
--- a/app/services/projects/download_service.rb
+++ b/app/services/projects/download_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class DownloadService < BaseService
WHITELIST = [
diff --git a/app/services/projects/enable_deploy_key_service.rb b/app/services/projects/enable_deploy_key_service.rb
index 121385afca3..b7c172028e9 100644
--- a/app/services/projects/enable_deploy_key_service.rb
+++ b/app/services/projects/enable_deploy_key_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class EnableDeployKeyService < BaseService
def execute
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 348eb0bf8d8..33ad2120a75 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class ForkService < BaseService
def execute(fork_to_project = nil)
@@ -37,7 +39,7 @@ module Projects
return new_project unless new_project.persisted?
builds_access_level = @project.project_feature.builds_access_level
- new_project.project_feature.update_attributes(builds_access_level: builds_access_level)
+ new_project.project_feature.update(builds_access_level: builds_access_level)
link_fork_network(new_project)
diff --git a/app/services/projects/forks_count_service.rb b/app/services/projects/forks_count_service.rb
index dc6eb19affd..b570c6d4754 100644
--- a/app/services/projects/forks_count_service.rb
+++ b/app/services/projects/forks_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
# Service class for getting and caching the number of forks of a project.
class ForksCountService < Projects::CountService
diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb
index a16268f4fd2..044afa1d5e1 100644
--- a/app/services/projects/gitlab_projects_import_service.rb
+++ b/app/services/projects/gitlab_projects_import_service.rb
@@ -1,8 +1,13 @@
+# frozen_string_literal: true
+
# This service is an adapter used to for the GitLab Import feature, and
# creating a project from a template.
# The latter will under the hood just import an archive supplied by GitLab.
module Projects
class GitlabProjectsImportService
+ include Gitlab::Utils::StrongMemoize
+ include Gitlab::TemplateHelper
+
attr_reader :current_user, :params
def initialize(user, import_params, override_params = nil)
@@ -10,39 +15,17 @@ module Projects
end
def execute
- FileUtils.mkdir_p(File.dirname(import_upload_path))
-
- file = params.delete(:file)
- FileUtils.copy_entry(file.path, import_upload_path)
-
- @overwrite = params.delete(:overwrite)
- data = {}
- data[:override_params] = @override_params if @override_params
-
- if overwrite_project?
- data[:original_path] = params[:path]
- params[:path] += "-#{tmp_filename}"
- end
+ prepare_template_environment(template_file)
- params[:import_type] = 'gitlab_project'
- params[:import_source] = import_upload_path
- params[:import_data] = { data: data } if data.present?
+ prepare_import_params
::Projects::CreateService.new(current_user, params).execute
end
private
- def import_upload_path
- @import_upload_path ||= Gitlab::ImportExport.import_upload_path(filename: tmp_filename)
- end
-
- def tmp_filename
- SecureRandom.hex
- end
-
def overwrite_project?
- @overwrite && project_with_same_full_path?
+ overwrite? && project_with_same_full_path?
end
def project_with_same_full_path?
@@ -50,7 +33,37 @@ module Projects
end
def current_namespace
- @current_namespace ||= Namespace.find_by(id: params[:namespace_id])
+ strong_memoize(:current_namespace) do
+ Namespace.find_by(id: params[:namespace_id])
+ end
+ end
+
+ def overwrite?
+ strong_memoize(:overwrite) do
+ params.delete(:overwrite)
+ end
+ end
+
+ def template_file
+ strong_memoize(:template_file) do
+ params.delete(:file)
+ end
+ end
+
+ def prepare_import_params
+ data = {}
+ data[:override_params] = @override_params if @override_params
+
+ if overwrite_project?
+ data[:original_path] = params[:path]
+ params[:path] += "-#{tmp_filename}"
+ end
+
+ if template_file
+ params[:import_type] = 'gitlab_project'
+ end
+
+ params[:import_data] = { data: data } if data.present?
end
end
end
diff --git a/app/services/projects/group_links/create_service.rb b/app/services/projects/group_links/create_service.rb
index 35624577024..1392775f805 100644
--- a/app/services/projects/group_links/create_service.rb
+++ b/app/services/projects/group_links/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module GroupLinks
class CreateService < BaseService
diff --git a/app/services/projects/group_links/destroy_service.rb b/app/services/projects/group_links/destroy_service.rb
index e3a20b4c1e4..8aefad048ce 100644
--- a/app/services/projects/group_links/destroy_service.rb
+++ b/app/services/projects/group_links/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module GroupLinks
class DestroyService < BaseService
diff --git a/app/services/projects/hashed_storage/migrate_attachments_service.rb b/app/services/projects/hashed_storage/migrate_attachments_service.rb
index bc897d891d5..a1f0302aeb7 100644
--- a/app/services/projects/hashed_storage/migrate_attachments_service.rb
+++ b/app/services/projects/hashed_storage/migrate_attachments_service.rb
@@ -1,20 +1,24 @@
+# frozen_string_literal: true
+
module Projects
module HashedStorage
AttachmentMigrationError = Class.new(StandardError)
class MigrateAttachmentsService < BaseService
- attr_reader :logger, :old_path, :new_path
+ attr_reader :logger, :old_disk_path, :new_disk_path
- def initialize(project, logger = nil)
+ def initialize(project, old_disk_path, logger: nil)
@project = project
@logger = logger || Rails.logger
+ @old_disk_path = old_disk_path
+ @new_disk_path = project.disk_path
end
def execute
- @old_path = project.full_path
- @new_path = project.disk_path
-
origin = FileUploader.absolute_base_dir(project)
+ # It's possible that old_disk_path does not match project.disk_path. For example, that happens when we rename a project
+ origin.sub!(/#{Regexp.escape(project.full_path)}\z/, old_disk_path)
+
project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:attachments]
target = FileUploader.absolute_base_dir(project)
@@ -30,22 +34,22 @@ module Projects
private
- def move_folder!(old_path, new_path)
- unless File.directory?(old_path)
- logger.info("Skipped attachments migration from '#{old_path}' to '#{new_path}', source path doesn't exist or is not a directory (PROJECT_ID=#{project.id})")
+ def move_folder!(old_disk_path, new_disk_path)
+ unless File.directory?(old_disk_path)
+ logger.info("Skipped attachments migration from '#{old_disk_path}' to '#{new_disk_path}', source path doesn't exist or is not a directory (PROJECT_ID=#{project.id})")
return
end
- if File.exist?(new_path)
- logger.error("Cannot migrate attachments from '#{old_path}' to '#{new_path}', target path already exist (PROJECT_ID=#{project.id})")
- raise AttachmentMigrationError, "Target path '#{new_path}' already exist"
+ if File.exist?(new_disk_path)
+ logger.error("Cannot migrate attachments from '#{old_disk_path}' to '#{new_disk_path}', target path already exist (PROJECT_ID=#{project.id})")
+ raise AttachmentMigrationError, "Target path '#{new_disk_path}' already exist"
end
# Create hashed storage base path folder
- FileUtils.mkdir_p(File.dirname(new_path))
+ FileUtils.mkdir_p(File.dirname(new_disk_path))
- FileUtils.mv(old_path, new_path)
- logger.info("Migrated project attachments from '#{old_path}' to '#{new_path}' (PROJECT_ID=#{project.id})")
+ FileUtils.mv(old_disk_path, new_disk_path)
+ logger.info("Migrated project attachments from '#{old_disk_path}' to '#{new_disk_path}' (PROJECT_ID=#{project.id})")
true
end
diff --git a/app/services/projects/hashed_storage/migrate_repository_service.rb b/app/services/projects/hashed_storage/migrate_repository_service.rb
index 68c1af2396b..641d46e6591 100644
--- a/app/services/projects/hashed_storage/migrate_repository_service.rb
+++ b/app/services/projects/hashed_storage/migrate_repository_service.rb
@@ -1,30 +1,31 @@
+# frozen_string_literal: true
+
module Projects
module HashedStorage
class MigrateRepositoryService < BaseService
include Gitlab::ShellAdapter
- attr_reader :old_disk_path, :new_disk_path, :old_wiki_disk_path, :old_storage_version, :logger
+ attr_reader :old_disk_path, :new_disk_path, :old_wiki_disk_path, :old_storage_version, :logger, :move_wiki
- def initialize(project, logger = nil)
+ def initialize(project, old_disk_path, logger: nil)
@project = project
@logger = logger || Rails.logger
+ @old_disk_path = old_disk_path
+ @old_wiki_disk_path = "#{old_disk_path}.wiki"
+ @move_wiki = has_wiki?
end
def execute
- @old_disk_path = project.disk_path
- has_wiki = project.wiki.repository_exists?
-
@old_storage_version = project.storage_version
project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
project.ensure_storage_path_exists
@new_disk_path = project.disk_path
- result = move_repository(@old_disk_path, @new_disk_path)
+ result = move_repository(old_disk_path, new_disk_path)
- if has_wiki
- @old_wiki_disk_path = "#{@old_disk_path}.wiki"
- result &&= move_repository("#{@old_wiki_disk_path}", "#{@new_disk_path}.wiki")
+ if move_wiki
+ result &&= move_repository("#{old_wiki_disk_path}", "#{new_disk_path}.wiki")
end
if result
@@ -46,6 +47,10 @@ module Projects
private
+ def has_wiki?
+ gitlab_shell.exists?(project.repository_storage, "#{old_wiki_disk_path}.git")
+ end
+
def move_repository(from_name, to_name)
from_exists = gitlab_shell.exists?(project.repository_storage, "#{from_name}.git")
to_exists = gitlab_shell.exists?(project.repository_storage, "#{to_name}.git")
@@ -64,8 +69,8 @@ module Projects
end
def rollback_folder_move
- move_repository(@new_disk_path, @old_disk_path)
- move_repository("#{@new_disk_path}.wiki", "#{@old_disk_path}.wiki")
+ move_repository(new_disk_path, old_disk_path)
+ move_repository("#{new_disk_path}.wiki", old_wiki_disk_path)
end
end
end
diff --git a/app/services/projects/hashed_storage_migration_service.rb b/app/services/projects/hashed_storage_migration_service.rb
index 662702c1db5..a0e734005f8 100644
--- a/app/services/projects/hashed_storage_migration_service.rb
+++ b/app/services/projects/hashed_storage_migration_service.rb
@@ -1,22 +1,27 @@
+# frozen_string_literal: true
+
module Projects
class HashedStorageMigrationService < BaseService
- attr_reader :logger
+ attr_reader :logger, :old_disk_path
- def initialize(project, logger = nil)
+ def initialize(project, old_disk_path, logger: nil)
@project = project
+ @old_disk_path = old_disk_path
@logger = logger || Rails.logger
end
def execute
# Migrate repository from Legacy to Hashed Storage
unless project.hashed_storage?(:repository)
- return unless HashedStorage::MigrateRepositoryService.new(project, logger).execute
+ return unless HashedStorage::MigrateRepositoryService.new(project, old_disk_path, logger: logger).execute
end
# Migrate attachments from Legacy to Hashed Storage
unless project.hashed_storage?(:attachments)
- HashedStorage::MigrateAttachmentsService.new(project, logger).execute
+ HashedStorage::MigrateAttachmentsService.new(project, old_disk_path, logger: logger).execute
end
+
+ true
end
end
end
diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb
index 120d57a188d..2f6dc4207dd 100644
--- a/app/services/projects/housekeeping_service.rb
+++ b/app/services/projects/housekeeping_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Projects::HousekeepingService class
#
# Used for git housekeeping
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 7bf0b90b491..e3491282a8a 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module ImportExport
class ExportService < BaseService
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index 1781a01cbd4..0c426faa22d 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class ImportService < BaseService
include Gitlab::ShellAdapter
@@ -23,7 +25,7 @@ module Projects
success
rescue => e
- error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}")
+ error("Error importing repository #{project.safe_import_url} into #{project.full_path} - #{e.message}")
end
private
@@ -65,7 +67,7 @@ module Projects
else
gitlab_shell.import_repository(project.repository_storage, project.disk_path, project.import_url)
end
- rescue Gitlab::Shell::Error, Gitlab::Git::RepositoryMirroring::RemoteError => e
+ rescue Gitlab::Shell::Error => e
# Expire cache to prevent scenarios such as:
# 1. First import failed, but the repo was imported successfully, so +exists?+ returns true
# 2. Retried import, repo is broken or not imported but +exists?+ still returns true
diff --git a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
index d9fb74b090e..a837ea82e38 100644
--- a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This service lists the download link from a remote source based on the
# oids provided
module Projects
diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb
index 6ea43561d61..7d4fa4e08df 100644
--- a/app/services/projects/lfs_pointers/lfs_download_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This service downloads and links lfs objects from a remote URL
module Projects
module LfsPointers
@@ -22,7 +24,7 @@ module Projects
private
def download_and_save_file(file, sanitized_uri)
- IO.copy_stream(open(sanitized_uri.sanitized_url, headers(sanitized_uri)), file)
+ IO.copy_stream(open(sanitized_uri.sanitized_url, headers(sanitized_uri)), file) # rubocop:disable Security/Open
end
def headers(sanitized_uri)
diff --git a/app/services/projects/lfs_pointers/lfs_import_service.rb b/app/services/projects/lfs_pointers/lfs_import_service.rb
index b6b0dec142f..97ce681a911 100644
--- a/app/services/projects/lfs_pointers/lfs_import_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_import_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This service manages the whole worflow of discovering the Lfs files in a
# repository, linking them to the project and downloading (and linking) the non
# existent ones.
diff --git a/app/services/projects/lfs_pointers/lfs_link_service.rb b/app/services/projects/lfs_pointers/lfs_link_service.rb
index d20bdf86c58..a2eba8e124e 100644
--- a/app/services/projects/lfs_pointers/lfs_link_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_link_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Given a list of oids, this services links the existent Lfs Objects to the project
module Projects
module LfsPointers
diff --git a/app/services/projects/lfs_pointers/lfs_list_service.rb b/app/services/projects/lfs_pointers/lfs_list_service.rb
index b770982cbc0..22160017f4f 100644
--- a/app/services/projects/lfs_pointers/lfs_list_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This service list all existent Lfs objects in a repository
module Projects
module LfsPointers
diff --git a/app/services/projects/move_access_service.rb b/app/services/projects/move_access_service.rb
index 3af3a22d486..8e2c3ad2f69 100644
--- a/app/services/projects/move_access_service.rb
+++ b/app/services/projects/move_access_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveAccessService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_deploy_keys_projects_service.rb b/app/services/projects/move_deploy_keys_projects_service.rb
index dde420655b0..40a22837eaf 100644
--- a/app/services/projects/move_deploy_keys_projects_service.rb
+++ b/app/services/projects/move_deploy_keys_projects_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveDeployKeysProjectsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_forks_service.rb b/app/services/projects/move_forks_service.rb
index d2901ea1457..076a7a50aa9 100644
--- a/app/services/projects/move_forks_service.rb
+++ b/app/services/projects/move_forks_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveForksService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_lfs_objects_projects_service.rb b/app/services/projects/move_lfs_objects_projects_service.rb
index 298da5f1a82..a5099519594 100644
--- a/app/services/projects/move_lfs_objects_projects_service.rb
+++ b/app/services/projects/move_lfs_objects_projects_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveLfsObjectsProjectsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_notification_settings_service.rb b/app/services/projects/move_notification_settings_service.rb
index f7be461a5da..746605d56f1 100644
--- a/app/services/projects/move_notification_settings_service.rb
+++ b/app/services/projects/move_notification_settings_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveNotificationSettingsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_project_authorizations_service.rb b/app/services/projects/move_project_authorizations_service.rb
index 5ef12fc49e5..60f2af88e99 100644
--- a/app/services/projects/move_project_authorizations_service.rb
+++ b/app/services/projects/move_project_authorizations_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
diff --git a/app/services/projects/move_project_group_links_service.rb b/app/services/projects/move_project_group_links_service.rb
index dbeffd7dae9..d9038030f7e 100644
--- a/app/services/projects/move_project_group_links_service.rb
+++ b/app/services/projects/move_project_group_links_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
diff --git a/app/services/projects/move_project_members_service.rb b/app/services/projects/move_project_members_service.rb
index 22a5f0a3fe6..bb0c0d10242 100644
--- a/app/services/projects/move_project_members_service.rb
+++ b/app/services/projects/move_project_members_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
diff --git a/app/services/projects/move_users_star_projects_service.rb b/app/services/projects/move_users_star_projects_service.rb
index 079fd5b9685..20121d429e2 100644
--- a/app/services/projects/move_users_star_projects_service.rb
+++ b/app/services/projects/move_users_star_projects_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveUsersStarProjectsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb
index 78b1477186a..5d6620c3c54 100644
--- a/app/services/projects/open_issues_count_service.rb
+++ b/app/services/projects/open_issues_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
# Service class for counting and caching the number of open issues of a
# project.
diff --git a/app/services/projects/open_merge_requests_count_service.rb b/app/services/projects/open_merge_requests_count_service.rb
index 77e6448fd5e..76ec13952ab 100644
--- a/app/services/projects/open_merge_requests_count_service.rb
+++ b/app/services/projects/open_merge_requests_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
# Service class for counting and caching the number of open merge requests of
# a project.
diff --git a/app/services/projects/overwrite_project_service.rb b/app/services/projects/overwrite_project_service.rb
index ce94f147aa9..696e1b665b2 100644
--- a/app/services/projects/overwrite_project_service.rb
+++ b/app/services/projects/overwrite_project_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class OverwriteProjectService < BaseService
def execute(source_project)
diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb
index 21741913385..7080f388e53 100644
--- a/app/services/projects/participants_service.rb
+++ b/app/services/projects/participants_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class ParticipantsService < BaseService
include Users::ParticipableService
diff --git a/app/services/projects/propagate_service_template.rb b/app/services/projects/propagate_service_template.rb
index a8ef2108492..fdfa91801ab 100644
--- a/app/services/projects/propagate_service_template.rb
+++ b/app/services/projects/propagate_service_template.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class PropagateServiceTemplate
BATCH_SIZE = 100
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 61acdd58021..c2a0c5fa7f3 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Projects::TransferService class
#
# Used for transfer project to another namespace
@@ -75,7 +77,6 @@ module Projects
Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
project.old_path_with_namespace = @old_path
- project.expires_full_path_cache
write_repository_config(@new_path)
diff --git a/app/services/projects/unlink_fork_service.rb b/app/services/projects/unlink_fork_service.rb
index 842fe4e09c4..2c0d91fe34f 100644
--- a/app/services/projects/unlink_fork_service.rb
+++ b/app/services/projects/unlink_fork_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class UnlinkForkService < BaseService
def execute
diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb
index 25017c5cbe3..efbd4c7b323 100644
--- a/app/services/projects/update_pages_configuration_service.rb
+++ b/app/services/projects/update_pages_configuration_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class UpdatePagesConfigurationService < BaseService
attr_reader :project
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index 1d8caec9c6f..eb2478be3cf 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class UpdatePagesService < BaseService
InvalidStateError = Class.new(StandardError)
diff --git a/app/services/projects/update_remote_mirror_service.rb b/app/services/projects/update_remote_mirror_service.rb
index 8183a2f26d7..4651f7c4f8f 100644
--- a/app/services/projects/update_remote_mirror_service.rb
+++ b/app/services/projects/update_remote_mirror_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class UpdateRemoteMirrorService < BaseService
attr_reader :errors
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index d8250cd8102..97f181ccea8 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -1,40 +1,30 @@
+# frozen_string_literal: true
+
module Projects
class UpdateService < BaseService
include UpdateVisibilityLevel
- def execute
- unless valid_visibility_level_change?(project, params[:visibility_level])
- return error('New visibility level not allowed!')
- end
-
- if renaming_project_with_container_registry_tags?
- return error('Cannot rename project because it contains container registry tags!')
- end
+ ValidationError = Class.new(StandardError)
- if changing_default_branch?
- return error("Could not set the default branch") unless project.change_head(params[:default_branch])
- end
+ def execute
+ validate!
ensure_wiki_exists if enabling_wiki?
yield if block_given?
# If the block added errors, don't try to save the project
- return validation_failed! if project.errors.any?
-
- if project.update_attributes(params.except(:default_branch))
- if project.previous_changes.include?('path')
- project.rename_repo
- else
- system_hook_service.execute_hooks_for(project, :update)
- end
+ return update_failed! if project.errors.any?
- update_pages_config if changing_pages_https_only?
+ if project.update(params.except(:default_branch))
+ after_update
success
else
- validation_failed!
+ update_failed!
end
+ rescue ValidationError => e
+ error(e.message)
end
def run_auto_devops_pipeline?
@@ -45,7 +35,45 @@ module Projects
private
- def validation_failed!
+ def validate!
+ unless valid_visibility_level_change?(project, params[:visibility_level])
+ raise ValidationError.new('New visibility level not allowed!')
+ end
+
+ if renaming_project_with_container_registry_tags?
+ raise ValidationError.new('Cannot rename project because it contains container registry tags!')
+ end
+
+ if changing_default_branch?
+ raise ValidationError.new("Could not set the default branch") unless project.change_head(params[:default_branch])
+ end
+ end
+
+ def after_update
+ todos_features_changes = %w(
+ issues_access_level
+ merge_requests_access_level
+ repository_access_level
+ )
+ project_changed_feature_keys = project.project_feature.previous_changes.keys
+
+ if project.previous_changes.include?(:visibility_level) && project.private?
+ # don't enqueue immediately to prevent todos removal in case of a mistake
+ TodosDestroyer::ProjectPrivateWorker.perform_in(1.hour, project.id)
+ elsif (project_changed_feature_keys & todos_features_changes).present?
+ TodosDestroyer::PrivateFeaturesWorker.perform_in(1.hour, project.id)
+ end
+
+ if project.previous_changes.include?('path')
+ project.rename_repo
+ else
+ system_hook_service.execute_hooks_for(project, :update)
+ end
+
+ update_pages_config if changing_pages_https_only?
+ end
+
+ def update_failed!
model_errors = project.errors.full_messages.to_sentence
error_message = model_errors.presence || 'Project could not be updated!'
@@ -67,7 +95,7 @@ module Projects
end
def enabling_wiki?
- return false if @project.wiki_enabled?
+ return false if project.wiki_enabled?
params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED
end
diff --git a/app/services/prometheus/adapter_service.rb b/app/services/prometheus/adapter_service.rb
index 4504d2ccfe6..a791845ba20 100644
--- a/app/services/prometheus/adapter_service.rb
+++ b/app/services/prometheus/adapter_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Prometheus
class AdapterService
def initialize(project, deployment_platform = nil)
@@ -28,7 +30,7 @@ module Prometheus
return unless deployment_platform.respond_to?(:cluster)
cluster = deployment_platform.cluster
- return unless cluster.application_prometheus&.installed?
+ return unless cluster.application_prometheus&.ready?
cluster.application_prometheus
end
diff --git a/app/services/protected_branches/access_level_params.rb b/app/services/protected_branches/access_level_params.rb
index 253ae8b0124..a7ef573ff0b 100644
--- a/app/services/protected_branches/access_level_params.rb
+++ b/app/services/protected_branches/access_level_params.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedBranches
class AccessLevelParams
attr_reader :type, :params
@@ -14,7 +16,7 @@ module ProtectedBranches
private
def params_with_default(params)
- params[:"#{type}_access_level"] ||= Gitlab::Access::MASTER if use_default_access_level?(params)
+ params[:"#{type}_access_level"] ||= Gitlab::Access::MAINTAINER if use_default_access_level?(params)
params
end
diff --git a/app/services/protected_branches/api_service.rb b/app/services/protected_branches/api_service.rb
index 4b40200644b..4340d3e8260 100644
--- a/app/services/protected_branches/api_service.rb
+++ b/app/services/protected_branches/api_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedBranches
class ApiService < BaseService
def create
diff --git a/app/services/protected_branches/create_service.rb b/app/services/protected_branches/create_service.rb
index 9d947f73af1..87aaf4672a4 100644
--- a/app/services/protected_branches/create_service.rb
+++ b/app/services/protected_branches/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedBranches
class CreateService < BaseService
def execute(skip_authorization: false)
diff --git a/app/services/protected_branches/destroy_service.rb b/app/services/protected_branches/destroy_service.rb
index 8172c896e76..7190bc2001b 100644
--- a/app/services/protected_branches/destroy_service.rb
+++ b/app/services/protected_branches/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedBranches
class DestroyService < BaseService
def execute(protected_branch)
diff --git a/app/services/protected_branches/legacy_api_create_service.rb b/app/services/protected_branches/legacy_api_create_service.rb
index e358fd0374e..aef99a860a0 100644
--- a/app/services/protected_branches/legacy_api_create_service.rb
+++ b/app/services/protected_branches/legacy_api_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# The branches#protect API still uses the `developers_can_push` and `developers_can_merge`
# flags for backward compatibility, and so performs translation between that format and the
# internal data model (separate access levels). The translation code is non-trivial, and so
@@ -9,14 +11,14 @@ module ProtectedBranches
if params.delete(:developers_can_push)
Gitlab::Access::DEVELOPER
else
- Gitlab::Access::MASTER
+ Gitlab::Access::MAINTAINER
end
merge_access_level =
if params.delete(:developers_can_merge)
Gitlab::Access::DEVELOPER
else
- Gitlab::Access::MASTER
+ Gitlab::Access::MAINTAINER
end
@params.merge!(push_access_levels_attributes: [{ access_level: push_access_level }],
diff --git a/app/services/protected_branches/legacy_api_update_service.rb b/app/services/protected_branches/legacy_api_update_service.rb
index 33176253ca2..1f6bbe72f85 100644
--- a/app/services/protected_branches/legacy_api_update_service.rb
+++ b/app/services/protected_branches/legacy_api_update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# The branches#protect API still uses the `developers_can_push` and `developers_can_merge`
# flags for backward compatibility, and so performs translation between that format and the
# internal data model (separate access levels). The translation code is non-trivial, and so
@@ -17,14 +19,14 @@ module ProtectedBranches
when true
params[:push_access_levels_attributes] = [{ access_level: Gitlab::Access::DEVELOPER }]
when false
- params[:push_access_levels_attributes] = [{ access_level: Gitlab::Access::MASTER }]
+ params[:push_access_levels_attributes] = [{ access_level: Gitlab::Access::MAINTAINER }]
end
case @developers_can_merge
when true
params[:merge_access_levels_attributes] = [{ access_level: Gitlab::Access::DEVELOPER }]
when false
- params[:merge_access_levels_attributes] = [{ access_level: Gitlab::Access::MASTER }]
+ params[:merge_access_levels_attributes] = [{ access_level: Gitlab::Access::MAINTAINER }]
end
service = ProtectedBranches::UpdateService.new(@project, @current_user, @params)
diff --git a/app/services/protected_branches/update_service.rb b/app/services/protected_branches/update_service.rb
index 95e46645374..4d7d498b8ca 100644
--- a/app/services/protected_branches/update_service.rb
+++ b/app/services/protected_branches/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedBranches
class UpdateService < BaseService
def execute(protected_branch)
diff --git a/app/services/protected_tags/create_service.rb b/app/services/protected_tags/create_service.rb
index faba7865a17..9aff55986b2 100644
--- a/app/services/protected_tags/create_service.rb
+++ b/app/services/protected_tags/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedTags
class CreateService < BaseService
attr_reader :protected_tag
diff --git a/app/services/protected_tags/destroy_service.rb b/app/services/protected_tags/destroy_service.rb
index c868d7ad8e6..dc4a1b39848 100644
--- a/app/services/protected_tags/destroy_service.rb
+++ b/app/services/protected_tags/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedTags
class DestroyService < BaseService
def execute(protected_tag)
diff --git a/app/services/protected_tags/update_service.rb b/app/services/protected_tags/update_service.rb
index aea6a48968d..3eb5f4955ee 100644
--- a/app/services/protected_tags/update_service.rb
+++ b/app/services/protected_tags/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProtectedTags
class UpdateService < BaseService
def execute(protected_tag)
diff --git a/app/services/push_event_payload_service.rb b/app/services/push_event_payload_service.rb
index b0a389c85f9..bb1259787af 100644
--- a/app/services/push_event_payload_service.rb
+++ b/app/services/push_event_payload_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Service class for creating push event payloads as stored in the
# "push_event_payloads" table.
#
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 9ac8fdb4cff..cdc8514c47c 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module QuickActions
class InterpretService < BaseService
include Gitlab::QuickActions::Dsl
diff --git a/app/services/repair_ldap_blocked_user_service.rb b/app/services/repair_ldap_blocked_user_service.rb
index 863cef7ff61..6ed42054ac3 100644
--- a/app/services/repair_ldap_blocked_user_service.rb
+++ b/app/services/repair_ldap_blocked_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepairLdapBlockedUserService
attr_accessor :user
diff --git a/app/services/repository_archive_clean_up_service.rb b/app/services/repository_archive_clean_up_service.rb
index ba7be4b3f89..99a9c834352 100644
--- a/app/services/repository_archive_clean_up_service.rb
+++ b/app/services/repository_archive_clean_up_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryArchiveCleanUpService
LAST_MODIFIED_TIME_IN_MINUTES = 120
diff --git a/app/services/reset_project_cache_service.rb b/app/services/reset_project_cache_service.rb
index a162a6eedb9..676d367a1c1 100644
--- a/app/services/reset_project_cache_service.rb
+++ b/app/services/reset_project_cache_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ResetProjectCacheService < BaseService
def execute
@project.increment!(:jobs_cache_index)
diff --git a/app/services/resource_events/change_labels_service.rb b/app/services/resource_events/change_labels_service.rb
new file mode 100644
index 00000000000..8edb0ddb3ed
--- /dev/null
+++ b/app/services/resource_events/change_labels_service.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+# This service is not used yet, it will be used for:
+# https://gitlab.com/gitlab-org/gitlab-ce/issues/48483
+module ResourceEvents
+ class ChangeLabelsService
+ attr_reader :resource, :user
+
+ def initialize(resource, user)
+ @resource, @user = resource, user
+ end
+
+ def execute(added_labels: [], removed_labels: [])
+ label_hash = {
+ resource_column(resource) => resource.id,
+ user_id: user.id,
+ created_at: Time.now
+ }
+
+ labels = added_labels.map do |label|
+ label_hash.merge(label_id: label.id, action: ResourceLabelEvent.actions['add'])
+ end
+ labels += removed_labels.map do |label|
+ label_hash.merge(label_id: label.id, action: ResourceLabelEvent.actions['remove'])
+ end
+
+ Gitlab::Database.bulk_insert(ResourceLabelEvent.table_name, labels)
+ end
+
+ private
+
+ def resource_column(resource)
+ case resource
+ when Issue
+ :issue_id
+ when MergeRequest
+ :merge_request_id
+ else
+ raise ArgumentError, "Unknown resource type #{resource.class.name}"
+ end
+ end
+ end
+end
diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb
index 92a32e703af..cb1bf0a03a5 100644
--- a/app/services/search/global_service.rb
+++ b/app/services/search/global_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Search
class GlobalService
attr_accessor :current_user, :params
diff --git a/app/services/search/group_service.rb b/app/services/search/group_service.rb
index b4efba68715..34803d005e3 100644
--- a/app/services/search/group_service.rb
+++ b/app/services/search/group_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Search
class GroupService < Search::GlobalService
attr_accessor :group
diff --git a/app/services/search/project_service.rb b/app/services/search/project_service.rb
index 9a22abae635..f223c8be103 100644
--- a/app/services/search/project_service.rb
+++ b/app/services/search/project_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Search
class ProjectService
attr_accessor :project, :current_user, :params
diff --git a/app/services/search/snippet_service.rb b/app/services/search/snippet_service.rb
index 85da0be6fff..e899a36f468 100644
--- a/app/services/search/snippet_service.rb
+++ b/app/services/search/snippet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Search
class SnippetService
attr_accessor :current_user, :params
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 1d4d03a8b7d..1b707d79b43 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SearchService
include Gitlab::Allowable
diff --git a/app/services/spam_check_service.rb b/app/services/spam_check_service.rb
index d4ade869777..895261925ba 100644
--- a/app/services/spam_check_service.rb
+++ b/app/services/spam_check_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# SpamCheckService
#
# Provide helper methods for checking if a given spammable object has
diff --git a/app/services/spam_service.rb b/app/services/spam_service.rb
index 73ea3018fbd..f2f133dae28 100644
--- a/app/services/spam_service.rb
+++ b/app/services/spam_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SpamService
attr_accessor :spammable, :request, :options
attr_reader :spam_log
diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb
index ac029fad7ea..93c2e222963 100644
--- a/app/services/submit_usage_ping_service.rb
+++ b/app/services/submit_usage_ping_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SubmitUsagePingService
URL = 'https://version.gitlab.com/usage_data'.freeze
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index ba7946fd23c..bd3907cdf8e 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SystemHooksService
def execute_hooks_for(model, event)
data = build_event_data(model, event)
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 00bf5434b7f..77494295f14 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# SystemNoteService
#
# Used for creating system notes (e.g., when a user references a merge request
@@ -21,9 +23,11 @@ module SystemNoteService
total_count = new_commits.length + existing_commits.length
commits_text = "#{total_count} commit".pluralize(total_count)
- body = "added #{commits_text}\n\n"
- body << commits_list(noteable, new_commits, existing_commits, oldrev)
- body << "\n\n[Compare with previous version](#{diff_comparison_url(noteable, project, oldrev)})"
+ text_parts = ["added #{commits_text}"]
+ text_parts << commits_list(noteable, new_commits, existing_commits, oldrev)
+ text_parts << "[Compare with previous version](#{diff_comparison_url(noteable, project, oldrev)})"
+
+ body = text_parts.join("\n\n")
create_note(NoteSummary.new(noteable, project, author, body, action: 'commit', commit_count: total_count))
end
@@ -103,18 +107,19 @@ module SystemNoteService
added_labels = added_labels.map(&references).join(' ')
removed_labels = removed_labels.map(&references).join(' ')
- body = ''
+ text_parts = []
if added_labels.present?
- body << "added #{added_labels}"
- body << ' and ' if removed_labels.present?
+ text_parts << "added #{added_labels}"
+ text_parts << 'and' if removed_labels.present?
end
if removed_labels.present?
- body << "removed #{removed_labels}"
+ text_parts << "removed #{removed_labels}"
end
- body << ' ' << 'label'.pluralize(labels_count)
+ text_parts << 'label'.pluralize(labels_count)
+ body = text_parts.join(' ')
create_note(NoteSummary.new(noteable, project, author, body, action: 'label'))
end
@@ -188,8 +193,10 @@ module SystemNoteService
spent_at = noteable.spent_at
parsed_time = Gitlab::TimeTrackingFormatter.output(time_spent.abs)
action = time_spent > 0 ? 'added' : 'subtracted'
- body = "#{action} #{parsed_time} of time spent"
- body << " at #{spent_at}" if spent_at
+
+ text_parts = ["#{action} #{parsed_time} of time spent"]
+ text_parts << "at #{spent_at}" if spent_at
+ body = text_parts.join(' ')
end
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
@@ -268,17 +275,19 @@ module SystemNoteService
diff_refs = change_position.diff_refs
version_index = merge_request.merge_request_diffs.viewable.count
- body = "changed this line in"
+ text_parts = ["changed this line in"]
if version_params = merge_request.version_params_for(diff_refs)
line_code = change_position.line_code(project.repository)
url = url_helpers.diffs_project_merge_request_url(project, merge_request, version_params.merge(anchor: line_code))
- body << " [version #{version_index} of the diff](#{url})"
+ text_parts << "[version #{version_index} of the diff](#{url})"
else
- body << " version #{version_index} of the diff"
+ text_parts << "version #{version_index} of the diff"
end
+ body = text_parts.join(' ')
note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
+
note = Note.create(note_attributes.merge(system: true))
note.system_note_metadata = SystemNoteMetadata.new(action: 'outdated')
diff --git a/app/services/tags/create_service.rb b/app/services/tags/create_service.rb
index 3cc88d77ba1..329722df747 100644
--- a/app/services/tags/create_service.rb
+++ b/app/services/tags/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Tags
class CreateService < BaseService
def execute(tag_name, target, message, release_description = nil)
diff --git a/app/services/tags/destroy_service.rb b/app/services/tags/destroy_service.rb
index b84b061e460..800268485a4 100644
--- a/app/services/tags/destroy_service.rb
+++ b/app/services/tags/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Tags
class DestroyService < BaseService
def execute(tag_name)
diff --git a/app/services/test_hooks/base_service.rb b/app/services/test_hooks/base_service.rb
index aadc1ea644b..8b5439c00bf 100644
--- a/app/services/test_hooks/base_service.rb
+++ b/app/services/test_hooks/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TestHooks
class BaseService
attr_accessor :hook, :current_user, :trigger
diff --git a/app/services/test_hooks/project_service.rb b/app/services/test_hooks/project_service.rb
index 65183e84cce..45e0e61e5c4 100644
--- a/app/services/test_hooks/project_service.rb
+++ b/app/services/test_hooks/project_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TestHooks
class ProjectService < TestHooks::BaseService
attr_writer :project
diff --git a/app/services/test_hooks/system_service.rb b/app/services/test_hooks/system_service.rb
index 9016c77b7f0..082830c5538 100644
--- a/app/services/test_hooks/system_service.rb
+++ b/app/services/test_hooks/system_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TestHooks
class SystemService < TestHooks::BaseService
private
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index f91cd03bf5c..0df61ad3bce 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# TodoService class
#
# Used for creating/updating todos after certain user actions
@@ -260,15 +262,15 @@ class TodoService
end
end
- def create_mention_todos(project, target, author, note = nil, skip_users = [])
+ def create_mention_todos(parent, target, author, note = nil, skip_users = [])
# Create Todos for directly addressed users
- directly_addressed_users = filter_directly_addressed_users(project, note || target, author, skip_users)
- attributes = attributes_for_todo(project, target, author, Todo::DIRECTLY_ADDRESSED, note)
+ directly_addressed_users = filter_directly_addressed_users(parent, note || target, author, skip_users)
+ attributes = attributes_for_todo(parent, target, author, Todo::DIRECTLY_ADDRESSED, note)
create_todos(directly_addressed_users, attributes)
# Create Todos for mentioned users
- mentioned_users = filter_mentioned_users(project, note || target, author, skip_users)
- attributes = attributes_for_todo(project, target, author, Todo::MENTIONED, note)
+ mentioned_users = filter_mentioned_users(parent, note || target, author, skip_users)
+ attributes = attributes_for_todo(parent, target, author, Todo::MENTIONED, note)
create_todos(mentioned_users, attributes)
end
@@ -299,36 +301,36 @@ class TodoService
def attributes_for_todo(project, target, author, action, note = nil)
attributes_for_target(target).merge!(
- project_id: project.id,
+ project_id: project&.id,
author_id: author.id,
action: action,
note: note
)
end
- def filter_todo_users(users, project, target)
- reject_users_without_access(users, project, target).uniq
+ def filter_todo_users(users, parent, target)
+ reject_users_without_access(users, parent, target).uniq
end
- def filter_mentioned_users(project, target, author, skip_users = [])
+ def filter_mentioned_users(parent, target, author, skip_users = [])
mentioned_users = target.mentioned_users(author) - skip_users
- filter_todo_users(mentioned_users, project, target)
+ filter_todo_users(mentioned_users, parent, target)
end
- def filter_directly_addressed_users(project, target, author, skip_users = [])
+ def filter_directly_addressed_users(parent, target, author, skip_users = [])
directly_addressed_users = target.directly_addressed_users(author) - skip_users
- filter_todo_users(directly_addressed_users, project, target)
+ filter_todo_users(directly_addressed_users, parent, target)
end
- def reject_users_without_access(users, project, target)
- if target.is_a?(Note) && (target.for_issue? || target.for_merge_request?)
+ def reject_users_without_access(users, parent, target)
+ if target.is_a?(Note) && target.for_issuable?
target = target.noteable
end
if target.is_a?(Issuable)
select_users(users, :"read_#{target.to_ability_name}", target)
else
- select_users(users, :read_project, project)
+ select_users(users, :read_project, parent)
end
end
diff --git a/app/services/todos/destroy/base_service.rb b/app/services/todos/destroy/base_service.rb
new file mode 100644
index 00000000000..dff5e1f30e5
--- /dev/null
+++ b/app/services/todos/destroy/base_service.rb
@@ -0,0 +1,33 @@
+module Todos
+ module Destroy
+ class BaseService
+ def execute
+ return unless todos_to_remove?
+
+ without_authorized(todos).delete_all
+ end
+
+ private
+
+ def without_authorized(items)
+ items.where('user_id NOT IN (?)', authorized_users)
+ end
+
+ def authorized_users
+ ProjectAuthorization.select(:user_id).where(project_id: project_ids)
+ end
+
+ def todos
+ raise NotImplementedError
+ end
+
+ def project_ids
+ raise NotImplementedError
+ end
+
+ def todos_to_remove?
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/app/services/todos/destroy/confidential_issue_service.rb b/app/services/todos/destroy/confidential_issue_service.rb
new file mode 100644
index 00000000000..c5b66df057a
--- /dev/null
+++ b/app/services/todos/destroy/confidential_issue_service.rb
@@ -0,0 +1,39 @@
+module Todos
+ module Destroy
+ class ConfidentialIssueService < ::Todos::Destroy::BaseService
+ extend ::Gitlab::Utils::Override
+
+ attr_reader :issue
+
+ def initialize(issue_id)
+ @issue = Issue.find_by(id: issue_id)
+ end
+
+ private
+
+ override :todos
+ def todos
+ Todo.where(target: issue)
+ .where('user_id != ?', issue.author_id)
+ .where('user_id NOT IN (?)', issue.assignees.select(:id))
+ end
+
+ override :todos_to_remove?
+ def todos_to_remove?
+ issue&.confidential?
+ end
+
+ override :project_ids
+ def project_ids
+ issue.project_id
+ end
+
+ override :authorized_users
+ def authorized_users
+ ProjectAuthorization.select(:user_id)
+ .where(project_id: project_ids)
+ .where('access_level >= ?', Gitlab::Access::REPORTER)
+ end
+ end
+ end
+end
diff --git a/app/services/todos/destroy/entity_leave_service.rb b/app/services/todos/destroy/entity_leave_service.rb
new file mode 100644
index 00000000000..045f5ecaae7
--- /dev/null
+++ b/app/services/todos/destroy/entity_leave_service.rb
@@ -0,0 +1,101 @@
+module Todos
+ module Destroy
+ class EntityLeaveService < ::Todos::Destroy::BaseService
+ extend ::Gitlab::Utils::Override
+
+ attr_reader :user, :entity
+
+ def initialize(user_id, entity_id, entity_type)
+ unless %w(Group Project).include?(entity_type)
+ raise ArgumentError.new("#{entity_type} is not an entity user can leave")
+ end
+
+ @user = User.find_by(id: user_id)
+ @entity = entity_type.constantize.find_by(id: entity_id)
+ end
+
+ def execute
+ return unless entity && user
+
+ # if at least reporter, all entities including confidential issues can be accessed
+ return if user_has_reporter_access?
+
+ remove_confidential_issue_todos
+
+ if entity.private?
+ remove_project_todos
+ remove_group_todos
+ else
+ enqueue_private_features_worker
+ end
+ end
+
+ private
+
+ def enqueue_private_features_worker
+ project_ids.each do |project_id|
+ TodosDestroyer::PrivateFeaturesWorker.perform_async(project_id, user.id)
+ end
+ end
+
+ def remove_confidential_issue_todos
+ Todo.where(
+ target_id: confidential_issues.select(:id), target_type: Issue, user_id: user.id
+ ).delete_all
+ end
+
+ def remove_project_todos
+ Todo.where(project_id: non_authorized_projects, user_id: user.id).delete_all
+ end
+
+ def remove_group_todos
+ Todo.where(group_id: non_authorized_groups, user_id: user.id).delete_all
+ end
+
+ override :project_ids
+ def project_ids
+ condition = case entity
+ when Project
+ { id: entity.id }
+ when Namespace
+ { namespace_id: non_member_groups }
+ end
+
+ Project.where(condition).select(:id)
+ end
+
+ def non_authorized_projects
+ project_ids.where('id NOT IN (?)', user.authorized_projects.select(:id))
+ end
+
+ def non_authorized_groups
+ return [] unless entity.is_a?(Namespace)
+
+ entity.self_and_descendants.select(:id)
+ .where('id NOT IN (?)', GroupsFinder.new(user).execute.select(:id))
+ end
+
+ def non_member_groups
+ entity.self_and_descendants.select(:id)
+ .where('id NOT IN (?)', user.membership_groups.select(:id))
+ end
+
+ def user_has_reporter_access?
+ return unless entity.is_a?(Namespace)
+
+ entity.member?(User.find(user.id), Gitlab::Access::REPORTER)
+ end
+
+ def confidential_issues
+ assigned_ids = IssueAssignee.select(:issue_id).where(user_id: user.id)
+ authorized_reporter_projects = user
+ .authorized_projects(Gitlab::Access::REPORTER).select(:id)
+
+ Issue.where(project_id: project_ids, confidential: true)
+ .where('project_id NOT IN(?)', authorized_reporter_projects)
+ .where('author_id != ?', user.id)
+ .where('id NOT IN (?)', assigned_ids)
+ end
+ end
+ end
+end
diff --git a/app/services/todos/destroy/group_private_service.rb b/app/services/todos/destroy/group_private_service.rb
new file mode 100644
index 00000000000..d13fa7a6516
--- /dev/null
+++ b/app/services/todos/destroy/group_private_service.rb
@@ -0,0 +1,30 @@
+module Todos
+ module Destroy
+ class GroupPrivateService < ::Todos::Destroy::BaseService
+ extend ::Gitlab::Utils::Override
+
+ attr_reader :group
+
+ def initialize(group_id)
+ @group = Group.find_by(id: group_id)
+ end
+
+ private
+
+ override :todos
+ def todos
+ Todo.where(group_id: group.id)
+ end
+
+ override :authorized_users
+ def authorized_users
+ group.direct_and_indirect_users.select(:id)
+ end
+
+ override :todos_to_remove?
+ def todos_to_remove?
+ group&.private?
+ end
+ end
+ end
+end
diff --git a/app/services/todos/destroy/private_features_service.rb b/app/services/todos/destroy/private_features_service.rb
new file mode 100644
index 00000000000..4d8e2877bfb
--- /dev/null
+++ b/app/services/todos/destroy/private_features_service.rb
@@ -0,0 +1,40 @@
+module Todos
+ module Destroy
+ class PrivateFeaturesService < ::Todos::Destroy::BaseService
+ attr_reader :project_ids, :user_id
+
+ def initialize(project_ids, user_id = nil)
+ @project_ids = project_ids
+ @user_id = user_id
+ end
+
+ def execute
+ ProjectFeature.where(project_id: project_ids).each do |project_features|
+ target_types = []
+ target_types << Issue if private?(project_features.issues_access_level)
+ target_types << MergeRequest if private?(project_features.merge_requests_access_level)
+ target_types << Commit if private?(project_features.repository_access_level)
+
+ next if target_types.empty?
+
+ remove_todos(project_features.project_id, target_types)
+ end
+ end
+
+ private
+
+ def private?(feature_level)
+ feature_level == ProjectFeature::PRIVATE
+ end
+
+ def remove_todos(project_id, target_types)
+ items = Todo.where(project_id: project_id)
+ items = items.where(user_id: user_id) if user_id
+
+ items.where('user_id NOT IN (?)', authorized_users)
+ .where(target_type: target_types)
+ .delete_all
+ end
+ end
+ end
+end
diff --git a/app/services/todos/destroy/project_private_service.rb b/app/services/todos/destroy/project_private_service.rb
new file mode 100644
index 00000000000..315a0c33398
--- /dev/null
+++ b/app/services/todos/destroy/project_private_service.rb
@@ -0,0 +1,30 @@
+module Todos
+ module Destroy
+ class ProjectPrivateService < ::Todos::Destroy::BaseService
+ extend ::Gitlab::Utils::Override
+
+ attr_reader :project
+
+ def initialize(project_id)
+ @project = Project.find_by(id: project_id)
+ end
+
+ private
+
+ override :todos
+ def todos
+ Todo.where(project_id: project.id)
+ end
+
+ override :project_ids
+ def project_ids
+ project.id
+ end
+
+ override :todos_to_remove?
+ def todos_to_remove?
+ project&.private?
+ end
+ end
+ end
+end
diff --git a/app/services/update_release_service.rb b/app/services/update_release_service.rb
index b7c36651968..422ba668e35 100644
--- a/app/services/update_release_service.rb
+++ b/app/services/update_release_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UpdateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
@@ -7,7 +9,7 @@ class UpdateReleaseService < BaseService
release = project.releases.find_by(tag: tag_name)
if release
- release.update_attributes(description: release_description)
+ release.update(description: release_description)
success(release)
else
diff --git a/app/services/update_snippet_service.rb b/app/services/update_snippet_service.rb
index 358bca73aec..15bc1046a4e 100644
--- a/app/services/update_snippet_service.rb
+++ b/app/services/update_snippet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UpdateSnippetService < BaseService
include SpamCheckService
diff --git a/app/services/upload_service.rb b/app/services/upload_service.rb
index d5a9b344905..39909ee4f82 100644
--- a/app/services/upload_service.rb
+++ b/app/services/upload_service.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
class UploadService
- def initialize(model, file, uploader_class = FileUploader)
- @model, @file, @uploader_class = model, file, uploader_class
+ def initialize(model, file, uploader_class = FileUploader, **uploader_context)
+ @model, @file, @uploader_class, @uploader_context = model, file, uploader_class, uploader_context
end
def execute
return nil unless @file && @file.size <= max_attachment_size
- uploader = @uploader_class.new(@model)
+ uploader = @uploader_class.new(@model, nil, @uploader_context)
uploader.store!(@file)
uploader.to_h
diff --git a/app/services/user_agent_detail_service.rb b/app/services/user_agent_detail_service.rb
index a1ee3df5fe1..5cb42e879a0 100644
--- a/app/services/user_agent_detail_service.rb
+++ b/app/services/user_agent_detail_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserAgentDetailService
attr_accessor :spammable, :request
diff --git a/app/services/user_project_access_changed_service.rb b/app/services/user_project_access_changed_service.rb
index 8630e572624..adca43660e8 100644
--- a/app/services/user_project_access_changed_service.rb
+++ b/app/services/user_project_access_changed_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserProjectAccessChangedService
def initialize(user_ids)
@user_ids = Array.wrap(user_ids)
diff --git a/app/services/users/activity_service.rb b/app/services/users/activity_service.rb
index 5803404c3c8..db03ba8756f 100644
--- a/app/services/users/activity_service.rb
+++ b/app/services/users/activity_service.rb
@@ -1,12 +1,22 @@
+# frozen_string_literal: true
+
module Users
class ActivityService
+ LEASE_TIMEOUT = 1.minute.to_i
+
def initialize(author, activity)
- @author = author.respond_to?(:user) ? author.user : author
+ @user = if author.respond_to?(:username)
+ author
+ elsif author.respond_to?(:user)
+ author.user
+ end
+
+ @user = nil unless @user.is_a?(User)
@activity = activity
end
def execute
- return unless @author && @author.is_a?(User)
+ return unless @user
record_activity
end
@@ -14,9 +24,14 @@ module Users
private
def record_activity
- Gitlab::UserActivities.record(@author.id) if Gitlab::Database.read_write?
+ return if Gitlab::Database.read_only?
+
+ lease = Gitlab::ExclusiveLease.new("acitvity_service:#{@user.id}",
+ timeout: LEASE_TIMEOUT)
+ return unless lease.try_obtain
- Rails.logger.debug("Recorded activity: #{@activity} for User ID: #{@author.id} (username: #{@author.username})")
+ @user.update_attribute(:last_activity_on, Date.today)
+ Rails.logger.debug("Recorded activity: #{@activity} for User ID: #{@user.id} (username: #{@user.username})")
end
end
end
diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb
index 4fb6d221909..acc2fa153ae 100644
--- a/app/services/users/build_service.rb
+++ b/app/services/users/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
class BuildService < BaseService
def initialize(current_user, params = {})
@@ -62,7 +64,8 @@ module Users
:theme_id,
:twitter,
:username,
- :website_url
+ :website_url,
+ :private_profile
]
end
diff --git a/app/services/users/create_service.rb b/app/services/users/create_service.rb
index c8a3c461d60..2ac6dfd90fa 100644
--- a/app/services/users/create_service.rb
+++ b/app/services/users/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
class CreateService < BaseService
include NewUserNotifier
diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb
index 06b604dad4d..4bc78b5b64e 100644
--- a/app/services/users/destroy_service.rb
+++ b/app/services/users/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
class DestroyService
attr_accessor :current_user
diff --git a/app/services/users/last_push_event_service.rb b/app/services/users/last_push_event_service.rb
index 57e446d7f30..a9c9497520b 100644
--- a/app/services/users/last_push_event_service.rb
+++ b/app/services/users/last_push_event_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
# Service class for caching and retrieving the last push event of a user.
class LastPushEventService
diff --git a/app/services/users/migrate_to_ghost_user_service.rb b/app/services/users/migrate_to_ghost_user_service.rb
index a2833b1e051..4d47078bf43 100644
--- a/app/services/users/migrate_to_ghost_user_service.rb
+++ b/app/services/users/migrate_to_ghost_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# When a user is destroyed, some of their associated records are
# moved to a "Ghost User", to prevent these associated records from
# being destroyed.
diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb
index f028f5eb0d4..23b63aaabdf 100644
--- a/app/services/users/refresh_authorized_projects_service.rb
+++ b/app/services/users/refresh_authorized_projects_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
# Service for refreshing the authorized projects of a user.
#
diff --git a/app/services/users/respond_to_terms_service.rb b/app/services/users/respond_to_terms_service.rb
index 06d660186cf..9efa3b285a8 100644
--- a/app/services/users/respond_to_terms_service.rb
+++ b/app/services/users/respond_to_terms_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
class RespondToTermsService
def initialize(user, term)
diff --git a/app/services/users/set_status_service.rb b/app/services/users/set_status_service.rb
new file mode 100644
index 00000000000..89008368d5f
--- /dev/null
+++ b/app/services/users/set_status_service.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Users
+ class SetStatusService
+ include Gitlab::Allowable
+
+ attr_reader :current_user, :target_user, :params
+
+ def initialize(current_user, params)
+ @current_user, @params = current_user, params.dup
+ @target_user = params.delete(:user) || current_user
+ end
+
+ def execute
+ return false unless can?(current_user, :update_user_status, target_user)
+
+ if params[:emoji].present? || params[:message].present?
+ set_status
+ else
+ remove_status
+ end
+ end
+
+ private
+
+ def set_status
+ params[:emoji] = UserStatus::DEFAULT_EMOJI if params[:emoji].blank?
+ user_status.update(params)
+ end
+
+ def remove_status
+ UserStatus.delete(target_user.id)
+ end
+
+ def user_status
+ target_user.status || target_user.build_status
+ end
+ end
+end
diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb
index 15ca1a55a5b..a897e4bd56a 100644
--- a/app/services/users/update_service.rb
+++ b/app/services/users/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
class UpdateService < BaseService
include NewUserNotifier
@@ -5,6 +7,7 @@ module Users
def initialize(current_user, params = {})
@current_user = current_user
@user = params.delete(:user)
+ @status_params = params.delete(:status)
@params = params.dup
end
@@ -15,10 +18,11 @@ module Users
assign_attributes(&block)
- if @user.save(validate: validate)
+ if @user.save(validate: validate) && update_status
notify_success(user_exists)
else
- error(@user.errors.full_messages.uniq.join('. '))
+ messages = @user.errors.full_messages + Array(@user.status&.errors&.full_messages)
+ error(messages.uniq.join('. '))
end
end
@@ -32,6 +36,12 @@ module Users
private
+ def update_status
+ return true unless @status_params
+
+ Users::SetStatusService.new(current_user, @status_params.merge(user: @user)).execute
+ end
+
def notify_success(user_exists)
notify_new_user(@user, nil) unless user_exists
diff --git a/app/services/validate_new_branch_service.rb b/app/services/validate_new_branch_service.rb
index 643f2ce1481..c19e2ec2043 100644
--- a/app/services/validate_new_branch_service.rb
+++ b/app/services/validate_new_branch_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'base_service'
class ValidateNewBranchService < BaseService
diff --git a/app/services/verify_pages_domain_service.rb b/app/services/verify_pages_domain_service.rb
index 13cb53dee01..07f7391f877 100644
--- a/app/services/verify_pages_domain_service.rb
+++ b/app/services/verify_pages_domain_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'resolv'
class VerifyPagesDomainService < BaseService
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 8a86e47f0ea..34724e0250d 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WebHookService
class InternalErrorResponse
attr_reader :body, :headers, :code
diff --git a/app/services/wiki_pages/base_service.rb b/app/services/wiki_pages/base_service.rb
index 260c04a8b94..e259f5bd1bc 100644
--- a/app/services/wiki_pages/base_service.rb
+++ b/app/services/wiki_pages/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WikiPages
class BaseService < ::BaseService
private
diff --git a/app/services/wiki_pages/create_service.rb b/app/services/wiki_pages/create_service.rb
index 24a817c06c9..2e2e0fd9033 100644
--- a/app/services/wiki_pages/create_service.rb
+++ b/app/services/wiki_pages/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WikiPages
class CreateService < WikiPages::BaseService
def execute
diff --git a/app/services/wiki_pages/destroy_service.rb b/app/services/wiki_pages/destroy_service.rb
index 6b93fb2f6d7..3f9343339cd 100644
--- a/app/services/wiki_pages/destroy_service.rb
+++ b/app/services/wiki_pages/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WikiPages
class DestroyService < WikiPages::BaseService
def execute(page)
diff --git a/app/services/wiki_pages/update_service.rb b/app/services/wiki_pages/update_service.rb
index 93cbd9a509f..2159dd91e9c 100644
--- a/app/services/wiki_pages/update_service.rb
+++ b/app/services/wiki_pages/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WikiPages
class UpdateService < WikiPages::BaseService
def execute(page)
diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb
index cd819dc9bff..0a166335b4e 100644
--- a/app/uploaders/attachment_uploader.rb
+++ b/app/uploaders/attachment_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AttachmentUploader < GitlabUploader
include RecordsUploads::Concern
include ObjectStorage::Concern
diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
index 5848e6c6994..b29ef57b071 100644
--- a/app/uploaders/avatar_uploader.rb
+++ b/app/uploaders/avatar_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AvatarUploader < GitlabUploader
include UploaderHelper
include RecordsUploads::Concern
diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb
index 3639375d474..a0b275b56a9 100644
--- a/app/uploaders/favicon_uploader.rb
+++ b/app/uploaders/favicon_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class FaviconUploader < AttachmentUploader
EXTENSION_WHITELIST = %w[png ico].freeze
diff --git a/app/uploaders/file_mover.rb b/app/uploaders/file_mover.rb
index bd7736ad74e..a7f8615e9ba 100644
--- a/app/uploaders/file_mover.rb
+++ b/app/uploaders/file_mover.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class FileMover
attr_reader :secret, :file_name, :model, :update_field
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 36bc0a4575a..b1365659834 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class breaks the actual CarrierWave concept.
# Every uploader should use a base_dir that is model agnostic so we can build
# back URLs from base_dir-relative paths saved in the `Upload` model.
@@ -13,7 +15,7 @@ class FileUploader < GitlabUploader
prepend ObjectStorage::Extension::RecordsUploads
MARKDOWN_PATTERN = %r{\!?\[.*?\]\(/uploads/(?<secret>[0-9a-f]{32})/(?<file>.*?)\)}
- DYNAMIC_PATH_PATTERN = %r{(?<secret>\h{32})/(?<identifier>.*)}
+ DYNAMIC_PATH_PATTERN = %r{.*(?<secret>\h{32})/(?<identifier>.*)}
after :remove, :prune_store_dir
@@ -65,6 +67,10 @@ class FileUploader < GitlabUploader
SecureRandom.hex
end
+ def self.extract_dynamic_path(path)
+ DYNAMIC_PATH_PATTERN.match(path)
+ end
+
def upload_paths(identifier)
[
File.join(secret, identifier),
@@ -81,6 +87,13 @@ class FileUploader < GitlabUploader
apply_context!(uploader_context)
end
+ def initialize_copy(from)
+ super
+
+ @secret = self.class.generate_secret
+ @upload = nil # calling record_upload would delete the old upload if set
+ end
+
# enforce the usage of Hashed storage when storing to
# remote store as the FileMover doesn't support OS
def base_dir(store = nil)
@@ -110,7 +123,7 @@ class FileUploader < GitlabUploader
end
def markdown_link
- markdown = "[#{markdown_name}](#{secure_url})"
+ markdown = +"[#{markdown_name}](#{secure_url})"
markdown.prepend("!") if image_or_video? || dangerous?
markdown
end
@@ -123,10 +136,6 @@ class FileUploader < GitlabUploader
}
end
- def filename
- self.file.filename
- end
-
def upload=(value)
super
@@ -134,7 +143,7 @@ class FileUploader < GitlabUploader
return if apply_context!(value.uploader_context)
# fallback to the regex based extraction
- if matches = DYNAMIC_PATH_PATTERN.match(value.path)
+ if matches = self.class.extract_dynamic_path(value.path)
@secret = matches[:secret]
@identifier = matches[:identifier]
end
@@ -144,6 +153,27 @@ class FileUploader < GitlabUploader
@secret ||= self.class.generate_secret
end
+ # return a new uploader with a file copy on another project
+ def self.copy_to(uploader, to_project)
+ moved = uploader.dup.tap do |u|
+ u.model = to_project
+ end
+
+ moved.copy_file(uploader.file)
+ moved
+ end
+
+ def copy_file(file)
+ to_path = if file_storage?
+ File.join(self.class.root, store_path)
+ else
+ store_path
+ end
+
+ self.file = file.copy_to(to_path)
+ record_upload # after_store is not triggered
+ end
+
private
def apply_context!(uploader_context)
diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb
index f8a237178d9..719bd6ef418 100644
--- a/app/uploaders/gitlab_uploader.rb
+++ b/app/uploaders/gitlab_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitlabUploader < CarrierWave::Uploader::Base
class_attribute :options
@@ -69,6 +71,28 @@ class GitlabUploader < CarrierWave::Uploader::Base
File.join('/', self.class.base_dir, dynamic_segment, filename)
end
+ def cached_size
+ size
+ end
+
+ def open
+ stream =
+ if file_storage?
+ File.open(path, "rb") if path
+ else
+ ::Gitlab::HttpIO.new(url, cached_size) if url
+ end
+
+ return unless stream
+ return stream unless block_given?
+
+ begin
+ yield(stream)
+ ensure
+ stream.close
+ end
+ end
+
private
# Designed to be overridden by child uploaders that have a dynamic path
diff --git a/app/uploaders/import_export_uploader.rb b/app/uploaders/import_export_uploader.rb
new file mode 100644
index 00000000000..716922bc017
--- /dev/null
+++ b/app/uploaders/import_export_uploader.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class ImportExportUploader < AttachmentUploader
+ EXTENSION_WHITELIST = %w[tar.gz gz].freeze
+
+ def extension_whitelist
+ EXTENSION_WHITELIST
+ end
+
+ def move_to_store
+ true
+ end
+
+ def move_to_cache
+ false
+ end
+end
diff --git a/app/uploaders/job_artifact_uploader.rb b/app/uploaders/job_artifact_uploader.rb
index 2a5a830ce4f..f6af023e0f9 100644
--- a/app/uploaders/job_artifact_uploader.rb
+++ b/app/uploaders/job_artifact_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class JobArtifactUploader < GitlabUploader
extend Workhorse::UploadPath
include ObjectStorage::Concern
@@ -16,14 +18,6 @@ class JobArtifactUploader < GitlabUploader
dynamic_segment
end
- def open
- if file_storage?
- File.open(path, "rb") if path
- else
- ::Gitlab::Ci::Trace::HttpIO.new(url, cached_size) if url
- end
- end
-
private
def dynamic_segment
diff --git a/app/uploaders/legacy_artifact_uploader.rb b/app/uploaders/legacy_artifact_uploader.rb
index efb7893d153..b4d0d752016 100644
--- a/app/uploaders/legacy_artifact_uploader.rb
+++ b/app/uploaders/legacy_artifact_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LegacyArtifactUploader < GitlabUploader
extend Workhorse::UploadPath
include ObjectStorage::Concern
diff --git a/app/uploaders/lfs_object_uploader.rb b/app/uploaders/lfs_object_uploader.rb
index eb521a22ebc..f3d32e6b39d 100644
--- a/app/uploaders/lfs_object_uploader.rb
+++ b/app/uploaders/lfs_object_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LfsObjectUploader < GitlabUploader
extend Workhorse::UploadPath
include ObjectStorage::Concern
diff --git a/app/uploaders/namespace_file_uploader.rb b/app/uploaders/namespace_file_uploader.rb
index 1085ecb1700..52969762b7d 100644
--- a/app/uploaders/namespace_file_uploader.rb
+++ b/app/uploaders/namespace_file_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NamespaceFileUploader < FileUploader
# Re-Override
def self.root
diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb
index b8ecfc4ee2b..dad6e85fb56 100644
--- a/app/uploaders/object_storage.rb
+++ b/app/uploaders/object_storage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'fog/aws'
require 'carrierwave/storage/fog'
diff --git a/app/uploaders/personal_file_uploader.rb b/app/uploaders/personal_file_uploader.rb
index e3898b07730..25474b494ff 100644
--- a/app/uploaders/personal_file_uploader.rb
+++ b/app/uploaders/personal_file_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PersonalFileUploader < FileUploader
# Re-Override
def self.root
diff --git a/app/uploaders/records_uploads.rb b/app/uploaders/records_uploads.rb
index 301f4681fcd..5795065ae11 100644
--- a/app/uploaders/records_uploads.rb
+++ b/app/uploaders/records_uploads.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RecordsUploads
module Concern
extend ActiveSupport::Concern
diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb
index 207928b61d0..2a2b54a9270 100644
--- a/app/uploaders/uploader_helper.rb
+++ b/app/uploaders/uploader_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Extra methods for uploader
module UploaderHelper
IMAGE_EXT = %w[png jpg jpeg gif bmp tiff ico].freeze
diff --git a/app/uploaders/workhorse.rb b/app/uploaders/workhorse.rb
index 782032cf516..84dc2791b9c 100644
--- a/app/uploaders/workhorse.rb
+++ b/app/uploaders/workhorse.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Workhorse
module UploadPath
def workhorse_upload_path
diff --git a/app/validators/abstract_path_validator.rb b/app/validators/abstract_path_validator.rb
index e43b66cbe3a..45ac695c5ec 100644
--- a/app/validators/abstract_path_validator.rb
+++ b/app/validators/abstract_path_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AbstractPathValidator < ActiveModel::EachValidator
extend Gitlab::EncodingHelper
diff --git a/app/validators/certificate_fingerprint_validator.rb b/app/validators/certificate_fingerprint_validator.rb
index 17df756183a..79d78653ec7 100644
--- a/app/validators/certificate_fingerprint_validator.rb
+++ b/app/validators/certificate_fingerprint_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CertificateFingerprintValidator < ActiveModel::EachValidator
FINGERPRINT_PATTERN = /\A([a-zA-Z0-9]{2}[\s\-:]?){16,}\z/.freeze
diff --git a/app/validators/certificate_key_validator.rb b/app/validators/certificate_key_validator.rb
index 8c7bb750339..5b2bbffc066 100644
--- a/app/validators/certificate_key_validator.rb
+++ b/app/validators/certificate_key_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# UrlValidator
#
# Custom validator for private keys.
diff --git a/app/validators/certificate_validator.rb b/app/validators/certificate_validator.rb
index b0c9a1b92a4..de8bb179dfb 100644
--- a/app/validators/certificate_validator.rb
+++ b/app/validators/certificate_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# UrlValidator
#
# Custom validator for private keys.
diff --git a/app/validators/cluster_name_validator.rb b/app/validators/cluster_name_validator.rb
index e7d32550176..85fd63f08e5 100644
--- a/app/validators/cluster_name_validator.rb
+++ b/app/validators/cluster_name_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# ClusterNameValidator
#
# Custom validator for ClusterName.
diff --git a/app/validators/color_validator.rb b/app/validators/color_validator.rb
index 571d0007aa2..1932d042e83 100644
--- a/app/validators/color_validator.rb
+++ b/app/validators/color_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# ColorValidator
#
# Custom validator for web color codes. It requires the leading hash symbol and
diff --git a/app/validators/cron_timezone_validator.rb b/app/validators/cron_timezone_validator.rb
index 542c7d006ad..c5f51d65060 100644
--- a/app/validators/cron_timezone_validator.rb
+++ b/app/validators/cron_timezone_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# CronTimezoneValidator
#
# Custom validator for CronTimezone.
diff --git a/app/validators/cron_validator.rb b/app/validators/cron_validator.rb
index 981fade47a6..bd48a7a6efb 100644
--- a/app/validators/cron_validator.rb
+++ b/app/validators/cron_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# CronValidator
#
# Custom validator for Cron.
diff --git a/app/validators/duration_validator.rb b/app/validators/duration_validator.rb
index 10ff44031c6..811828169ca 100644
--- a/app/validators/duration_validator.rb
+++ b/app/validators/duration_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# DurationValidator
#
# Validate the format conforms with ChronicDuration
diff --git a/app/validators/email_validator.rb b/app/validators/email_validator.rb
index aab07a7ece4..9459edb7515 100644
--- a/app/validators/email_validator.rb
+++ b/app/validators/email_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute, :invalid) unless value =~ Devise.email_regexp
diff --git a/app/validators/key_restriction_validator.rb b/app/validators/key_restriction_validator.rb
index 204be827941..891d13b1596 100644
--- a/app/validators/key_restriction_validator.rb
+++ b/app/validators/key_restriction_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class KeyRestrictionValidator < ActiveModel::EachValidator
FORBIDDEN = -1
diff --git a/app/validators/line_code_validator.rb b/app/validators/line_code_validator.rb
index ed29e5aeb67..a351180790e 100644
--- a/app/validators/line_code_validator.rb
+++ b/app/validators/line_code_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# LineCodeValidator
#
# Custom validator for GitLab line codes.
diff --git a/app/validators/namespace_name_validator.rb b/app/validators/namespace_name_validator.rb
index 2e51af2982d..fb1c241037c 100644
--- a/app/validators/namespace_name_validator.rb
+++ b/app/validators/namespace_name_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# NamespaceNameValidator
#
# Custom validator for GitLab namespace name strings.
diff --git a/app/validators/namespace_path_validator.rb b/app/validators/namespace_path_validator.rb
index 7b0ae4db5d4..c078b272b2f 100644
--- a/app/validators/namespace_path_validator.rb
+++ b/app/validators/namespace_path_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NamespacePathValidator < AbstractPathValidator
extend Gitlab::EncodingHelper
diff --git a/app/validators/project_path_validator.rb b/app/validators/project_path_validator.rb
index 424fd77a6a3..aea0a68e7cf 100644
--- a/app/validators/project_path_validator.rb
+++ b/app/validators/project_path_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectPathValidator < AbstractPathValidator
extend Gitlab::EncodingHelper
diff --git a/app/validators/public_url_validator.rb b/app/validators/public_url_validator.rb
index 1e8118fccbb..3ff880deedd 100644
--- a/app/validators/public_url_validator.rb
+++ b/app/validators/public_url_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# PublicUrlValidator
#
# Custom validator for URLs. This validator works like UrlValidator but
diff --git a/app/validators/top_level_group_validator.rb b/app/validators/top_level_group_validator.rb
index 7e2e735e0cf..b50c9dca154 100644
--- a/app/validators/top_level_group_validator.rb
+++ b/app/validators/top_level_group_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TopLevelGroupValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if value&.subgroup?
diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb
index 6854fec582e..faaf1283078 100644
--- a/app/validators/url_validator.rb
+++ b/app/validators/url_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# UrlValidator
#
# Custom validator for URLs.
diff --git a/app/validators/variable_duplicates_validator.rb b/app/validators/variable_duplicates_validator.rb
index 72660be6c43..90193e85f2a 100644
--- a/app/validators/variable_duplicates_validator.rb
+++ b/app/validators/variable_duplicates_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# VariableDuplicatesValidator
#
# This validator is designed for especially the following condition
@@ -22,8 +24,8 @@ class VariableDuplicatesValidator < ActiveModel::EachValidator
def validate_duplicates(record, attribute, values)
duplicates = values.reject(&:marked_for_destruction?).group_by(&:key).select { |_, v| v.many? }.map(&:first)
if duplicates.any?
- error_message = "have duplicate values (#{duplicates.join(", ")})"
- error_message += " for #{values.first.send(options[:scope])} scope" if options[:scope] # rubocop:disable GitlabSecurity/PublicSend
+ error_message = +"have duplicate values (#{duplicates.join(", ")})"
+ error_message << " for #{values.first.send(options[:scope])} scope" if options[:scope] # rubocop:disable GitlabSecurity/PublicSend
record.errors.add(attribute, error_message)
end
end
diff --git a/app/views/admin/application_settings/_abuse.html.haml b/app/views/admin/application_settings/_abuse.html.haml
index 91993838fc8..5f8bd799d23 100644
--- a/app/views/admin/application_settings/_abuse.html.haml
+++ b/app/views/admin/application_settings/_abuse.html.haml
@@ -1,9 +1,9 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-abuse-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- = f.label :admin_notification_email, 'Abuse reports notification email', class: 'label-light'
+ = f.label :admin_notification_email, 'Abuse reports notification email', class: 'label-bold'
= f.text_field :admin_notification_email, class: 'form-control'
.form-text.text-muted
Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.
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 f40939747f4..7c8243a7a90 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-account-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -8,23 +8,23 @@
= f.label :gravatar_enabled, class: 'form-check-label' do
Gravatar enabled
.form-group
- = f.label :default_projects_limit, class: 'label-light'
+ = f.label :default_projects_limit, class: 'label-bold'
= f.number_field :default_projects_limit, class: 'form-control'
.form-group
- = f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'label-light'
+ = f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'label-bold'
= f.number_field :max_attachment_size, class: 'form-control'
.form-group
- = f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-light'
+ = f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-bold'
= f.number_field :session_expire_delay, class: 'form-control'
%span.form-text.text-muted#session_expire_delay_help_block GitLab restart is required to apply changes
.form-group
- = f.label :user_oauth_applications, 'User OAuth applications', class: 'label-light'
+ = f.label :user_oauth_applications, 'User OAuth applications', class: 'label-bold'
.form-check
= f.check_box :user_oauth_applications, class: 'form-check-input'
= f.label :user_oauth_applications, class: 'form-check-label' do
Allow users to register any application to use GitLab as an OAuth provider
.form-group
- = f.label :user_default_external, 'New users set to external', class: 'label-light'
+ = f.label :user_default_external, 'New users set to external', class: 'label-bold'
.form-check
= f.check_box :user_default_external, class: 'form-check-input'
= f.label :user_default_external, class: 'form-check-label' do
diff --git a/app/views/admin/application_settings/_background_jobs.html.haml b/app/views/admin/application_settings/_background_jobs.html.haml
index fd8e695ed49..7d1a64b645a 100644
--- a/app/views/admin/application_settings/_background_jobs.html.haml
+++ b/app/views/admin/application_settings/_background_jobs.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-background-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -14,12 +14,12 @@
.form-text.text-muted
Limit the amount of resources slow running jobs are assigned.
.form-group
- = f.label :sidekiq_throttling_queues, 'Sidekiq queues to throttle', class: 'label-light'
+ = f.label :sidekiq_throttling_queues, 'Sidekiq queues to throttle', class: 'label-bold'
= f.select :sidekiq_throttling_queues, sidekiq_queue_options_for_select, { include_hidden: false }, multiple: true, class: 'select2 select-wide', data: { field: 'sidekiq_throttling_queues' }
.form-text.text-muted
Choose which queues you wish to throttle.
.form-group
- = f.label :sidekiq_throttling_factor, 'Throttling Factor', class: 'label-light'
+ = f.label :sidekiq_throttling_factor, 'Throttling Factor', class: 'label-bold'
= f.number_field :sidekiq_throttling_factor, class: 'form-control', min: '0.01', max: '0.99', step: '0.01'
.form-text.text-muted
The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.
diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml
index 7c16cafe13f..5037017e38a 100644
--- a/app/views/admin/application_settings/_ci_cd.html.haml
+++ b/app/views/admin/application_settings/_ci_cd.html.haml
@@ -1,17 +1,19 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-ci-cd-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- .form-check
- = f.check_box :auto_devops_enabled, class: 'form-check-input'
- = f.label :auto_devops_enabled, class: 'form-check-label' do
- Enabled Auto DevOps for projects by default
- .form-text.text-muted
- It will automatically build, test, and deploy applications based on a predefined CI/CD configuration
- = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md')
+ .card.auto-devops-card
+ .card-body
+ .form-check
+ = f.check_box :auto_devops_enabled, class: 'form-check-input'
+ = f.label :auto_devops_enabled, class: 'form-check-label' do
+ Default to Auto DevOps pipeline for all projects
+ .form-text.text-muted
+ = s_('CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found.')
+ = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank'
.form-group
- = f.label :auto_devops_domain, class: 'label-light'
+ = f.label :auto_devops_domain, class: 'label-bold'
= f.text_field :auto_devops_domain, class: 'form-control', placeholder: 'domain.com'
.form-text.text-muted
= s_("AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages.")
@@ -21,17 +23,17 @@
= f.label :shared_runners_enabled, class: 'form-check-label' do
Enable shared runners for new projects
.form-group
- = f.label :shared_runners_text, class: 'label-light'
+ = f.label :shared_runners_text, class: 'label-bold'
= f.text_area :shared_runners_text, class: 'form-control', rows: 4
.form-text.text-muted Markdown enabled
.form-group
- = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'label-light'
+ = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'label-bold'
= f.number_field :max_artifacts_size, class: 'form-control'
.form-text.text-muted
Set the maximum file size for each job's artifacts
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size')
.form-group
- = f.label :default_artifacts_expire_in, 'Default artifacts expiration', class: 'label-light'
+ = f.label :default_artifacts_expire_in, 'Default artifacts expiration', class: 'label-bold'
= f.text_field :default_artifacts_expire_in, class: 'form-control'
.form-text.text-muted
Set the default expiration time for each job's artifacts.
diff --git a/app/views/admin/application_settings/_email.html.haml b/app/views/admin/application_settings/_email.html.haml
index 99e44ffa741..86339e61215 100644
--- a/app/views/admin/application_settings/_email.html.haml
+++ b/app/views/admin/application_settings/_email.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-email-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_gitaly.html.haml b/app/views/admin/application_settings/_gitaly.html.haml
index 0b4001c0824..f39d5709811 100644
--- a/app/views/admin/application_settings/_gitaly.html.haml
+++ b/app/views/admin/application_settings/_gitaly.html.haml
@@ -1,22 +1,22 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-gitaly-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- = f.label :gitaly_timeout_default, 'Default Timeout Period', class: 'label-light'
+ = f.label :gitaly_timeout_default, 'Default Timeout Period', class: 'label-bold'
= f.number_field :gitaly_timeout_default, class: 'form-control'
.form-text.text-muted
Timeout for Gitaly calls from the GitLab application (in seconds). This timeout is not enforced
for git fetch/push operations or Sidekiq jobs.
.form-group
- = f.label :gitaly_timeout_fast, 'Fast Timeout Period', class: 'label-light'
+ = f.label :gitaly_timeout_fast, 'Fast Timeout Period', class: 'label-bold'
= f.number_field :gitaly_timeout_fast, class: 'form-control'
.form-text.text-muted
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.
.form-group
- = f.label :gitaly_timeout_medium, 'Medium Timeout Period', class: 'label-light'
+ = f.label :gitaly_timeout_medium, 'Medium Timeout Period', class: 'label-bold'
= f.number_field :gitaly_timeout_medium, class: 'form-control'
.form-text.text-muted
Medium operation timeout (in seconds). This should be a value between the Fast and the Default timeout.
diff --git a/app/views/admin/application_settings/_help_page.html.haml b/app/views/admin/application_settings/_help_page.html.haml
index 1f402fcb786..70c8c74cc5d 100644
--- a/app/views/admin/application_settings/_help_page.html.haml
+++ b/app/views/admin/application_settings/_help_page.html.haml
@@ -1,9 +1,9 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-help-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- = f.label :help_page_text, class: 'label-light'
+ = f.label :help_page_text, class: 'label-bold'
= f.text_area :help_page_text, class: 'form-control', rows: 4
.form-text.text-muted Markdown enabled
.form-group
@@ -12,7 +12,7 @@
= f.label :help_page_hide_commercial_content, class: 'form-check-label' do
Hide marketing-related entries from help
.form-group
- = f.label :help_page_support_url, 'Support page URL', class: 'label-light'
+ = f.label :help_page_support_url, 'Support page URL', class: 'label-bold'
= f.text_field :help_page_support_url, class: 'form-control', placeholder: 'http://company.example.com/getting-help', :'aria-describedby' => 'support_help_block'
%span.form-text.text-muted#support_help_block Alternate support URL for help page
diff --git a/app/views/admin/application_settings/_influx.html.haml b/app/views/admin/application_settings/_influx.html.haml
index 61e8e3199a9..a1eeacd8290 100644
--- a/app/views/admin/application_settings/_influx.html.haml
+++ b/app/views/admin/application_settings/_influx.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-influx-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -14,10 +14,10 @@
= f.label :metrics_enabled, class: 'form-check-label' do
Enable InfluxDB Metrics
.form-group
- = f.label :metrics_host, 'InfluxDB host', class: 'label-light'
+ = f.label :metrics_host, 'InfluxDB host', class: 'label-bold'
= f.text_field :metrics_host, class: 'form-control', placeholder: 'influxdb.example.com'
.form-group
- = f.label :metrics_port, 'InfluxDB port', class: 'label-light'
+ = f.label :metrics_port, 'InfluxDB port', class: 'label-bold'
= f.text_field :metrics_port, class: 'form-control', placeholder: '8089'
.form-text.text-muted
The UDP port to use for connecting to InfluxDB. InfluxDB requires that
@@ -25,7 +25,7 @@
sending messages to this port, without it metrics data will not be
saved.
.form-group
- = f.label :metrics_pool_size, 'Connection pool size', class: 'label-light'
+ = f.label :metrics_pool_size, 'Connection pool size', class: 'label-bold'
= f.number_field :metrics_pool_size, class: 'form-control'
.form-text.text-muted
The amount of InfluxDB connections to open. Connections are opened
@@ -33,25 +33,25 @@
enough connections are available (at minimum the amount of application
server threads).
.form-group
- = f.label :metrics_timeout, 'Connection timeout', class: 'label-light'
+ = f.label :metrics_timeout, 'Connection timeout', class: 'label-bold'
= f.number_field :metrics_timeout, class: 'form-control'
.form-text.text-muted
The amount of seconds after which an InfluxDB connection will time
out.
.form-group
- = f.label :metrics_method_call_threshold, 'Method Call Threshold (ms)', class: 'label-light'
+ = f.label :metrics_method_call_threshold, 'Method Call Threshold (ms)', class: 'label-bold'
= f.number_field :metrics_method_call_threshold, class: 'form-control'
.form-text.text-muted
A method call is only tracked when it takes longer to complete than
the given amount of milliseconds.
.form-group
- = f.label :metrics_sample_interval, 'Sampler Interval (sec)', class: 'label-light'
+ = f.label :metrics_sample_interval, 'Sampler Interval (sec)', class: 'label-bold'
= f.number_field :metrics_sample_interval, class: 'form-control'
.form-text.text-muted
The sampling interval in seconds. Sampled data includes memory usage,
retained Ruby objects, file descriptors and so on.
.form-group
- = f.label :metrics_packet_size, 'Metrics per packet', class: 'label-light'
+ = f.label :metrics_packet_size, 'Metrics per packet', class: 'label-bold'
= f.number_field :metrics_packet_size, class: 'form-control'
.form-text.text-muted
The amount of points to store in a single UDP packet. More points
diff --git a/app/views/admin/application_settings/_ip_limits.html.haml b/app/views/admin/application_settings/_ip_limits.html.haml
index 73d570a5fee..5a5681830b8 100644
--- a/app/views/admin/application_settings/_ip_limits.html.haml
+++ b/app/views/admin/application_settings/_ip_limits.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-ip-limits-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -10,10 +10,10 @@
%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-light'
+ = f.label :throttle_unauthenticated_requests_per_period, 'Max 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-light'
+ = f.label :throttle_unauthenticated_period_in_seconds, 'Rate limit period in seconds', class: 'label-bold'
= f.number_field :throttle_unauthenticated_period_in_seconds, class: 'form-control'
.form-group
.form-check
@@ -23,10 +23,10 @@
%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-light'
+ = f.label :throttle_authenticated_api_requests_per_period, 'Max 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-light'
+ = f.label :throttle_authenticated_api_period_in_seconds, 'Rate limit period in seconds', class: 'label-bold'
= f.number_field :throttle_authenticated_api_period_in_seconds, class: 'form-control'
.form-group
.form-check
@@ -36,10 +36,10 @@
%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-light'
+ = f.label :throttle_authenticated_web_requests_per_period, 'Max 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-light'
+ = f.label :throttle_authenticated_web_period_in_seconds, '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: "btn btn-success"
diff --git a/app/views/admin/application_settings/_koding.html.haml b/app/views/admin/application_settings/_koding.html.haml
index ae60f68f5fe..8b635b08abd 100644
--- a/app/views/admin/application_settings/_koding.html.haml
+++ b/app/views/admin/application_settings/_koding.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-koding-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -10,7 +10,7 @@
.form-text.text-muted
Koding integration has been deprecated since GitLab 10.0. If you disable your Koding integration, you will not be able to enable it again.
.form-group
- = f.label :koding_url, 'Koding URL', class: 'label-light'
+ = f.label :koding_url, 'Koding URL', class: 'label-bold'
= f.text_field :koding_url, class: 'form-control', placeholder: 'http://gitlab.your-koding-instance.com:8090'
.form-text.text-muted
Koding has integration enabled out of the box for the
diff --git a/app/views/admin/application_settings/_logging.html.haml b/app/views/admin/application_settings/_logging.html.haml
index a6e549cd1f0..41b787515b5 100644
--- a/app/views/admin/application_settings/_logging.html.haml
+++ b/app/views/admin/application_settings/_logging.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-logging-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -13,7 +13,7 @@
%a{ href: 'https://getsentry.com', target: '_blank', rel: 'noopener noreferrer' } https://getsentry.com
.form-group
- = f.label :sentry_dsn, 'Sentry DSN', class: 'label-light'
+ = f.label :sentry_dsn, 'Sentry DSN', class: 'label-bold'
= f.text_field :sentry_dsn, class: 'form-control'
.form-group
@@ -26,7 +26,7 @@
%a{ href: 'https://sentry.io/for/javascript/', target: '_blank', rel: 'noopener noreferrer' } https://sentry.io/for/javascript/
.form-group
- = f.label :clientside_sentry_dsn, 'Clientside Sentry DSN', class: 'label-light'
+ = f.label :clientside_sentry_dsn, 'Clientside Sentry DSN', class: 'label-bold'
= f.text_field :clientside_sentry_dsn, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_outbound.html.haml b/app/views/admin/application_settings/_outbound.html.haml
index e046242bee0..f4bfb5af385 100644
--- a/app/views/admin/application_settings/_outbound.html.haml
+++ b/app/views/admin/application_settings/_outbound.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-outbound-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_pages.html.haml b/app/views/admin/application_settings/_pages.html.haml
index f168ec62ffd..ad5c8d4da22 100644
--- a/app/views/admin/application_settings/_pages.html.haml
+++ b/app/views/admin/application_settings/_pages.html.haml
@@ -1,9 +1,9 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-pages-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- = f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'label-light'
+ = f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'label-bold'
= f.number_field :max_pages_size, class: 'form-control'
.form-text.text-muted 0 for unlimited
.form-group
diff --git a/app/views/admin/application_settings/_performance.html.haml b/app/views/admin/application_settings/_performance.html.haml
index ffa25af77ed..e7076e7ed2f 100644
--- a/app/views/admin/application_settings/_performance.html.haml
+++ b/app/views/admin/application_settings/_performance.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-performance-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_performance_bar.html.haml b/app/views/admin/application_settings/_performance_bar.html.haml
index ddbfcc6b77b..44ac8d94764 100644
--- a/app/views/admin/application_settings/_performance_bar.html.haml
+++ b/app/views/admin/application_settings/_performance_bar.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-performance-bar-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -8,7 +8,7 @@
= f.label :performance_bar_enabled, class: 'form-check-label' do
Enable the Performance Bar
.form-group
- = f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'label-light'
+ = f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'label-bold'
= f.text_field :performance_bar_allowed_group_path, class: 'form-control', placeholder: 'my-org/my-group', value: @application_setting.performance_bar_allowed_group&.full_path
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_plantuml.html.haml b/app/views/admin/application_settings/_plantuml.html.haml
index 259f18b3b96..5c2b7114426 100644
--- a/app/views/admin/application_settings/_plantuml.html.haml
+++ b/app/views/admin/application_settings/_plantuml.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-plantuml-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -8,7 +8,7 @@
= f.label :plantuml_enabled, class: 'form-check-label' do
Enable PlantUML
.form-group
- = f.label :plantuml_url, 'PlantUML URL', class: 'label-light'
+ = 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'
.form-text.text-muted
Allow rendering of
diff --git a/app/views/admin/application_settings/_prometheus.html.haml b/app/views/admin/application_settings/_prometheus.html.haml
index ad92b18b2c9..a923568e52d 100644
--- a/app/views/admin/application_settings/_prometheus.html.haml
+++ b/app/views/admin/application_settings/_prometheus.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-prometheus-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_realtime.html.haml b/app/views/admin/application_settings/_realtime.html.haml
index 120cf4909b2..92f0c02eb6a 100644
--- a/app/views/admin/application_settings/_realtime.html.haml
+++ b/app/views/admin/application_settings/_realtime.html.haml
@@ -1,9 +1,9 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-realtime-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- = f.label :polling_interval_multiplier, 'Polling interval multiplier', class: 'label-light'
+ = f.label :polling_interval_multiplier, 'Polling interval multiplier', class: 'label-bold'
= f.text_field :polling_interval_multiplier, class: 'form-control'
.form-text.text-muted
Change this value to influence how frequently the GitLab UI polls for updates.
diff --git a/app/views/admin/application_settings/_registry.html.haml b/app/views/admin/application_settings/_registry.html.haml
index beac70482e5..08c981db13f 100644
--- a/app/views/admin/application_settings/_registry.html.haml
+++ b/app/views/admin/application_settings/_registry.html.haml
@@ -1,9 +1,9 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-registry-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- = f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'label-light'
+ = f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'label-bold'
= f.number_field :container_registry_token_expire_delay, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_repository_check.html.haml b/app/views/admin/application_settings/_repository_check.html.haml
index 57facc380eb..925e39bc0a3 100644
--- a/app/views/admin/application_settings/_repository_check.html.haml
+++ b/app/views/admin/application_settings/_repository_check.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-repository-check-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -38,17 +38,17 @@
Creating pack file bitmaps makes housekeeping take a little longer but
bitmaps should accelerate 'git clone' performance.
.form-group
- = f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-light'
+ = f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-bold'
= f.number_field :housekeeping_incremental_repack_period, class: 'form-control'
.form-text.text-muted
Number of Git pushes after which an incremental 'git repack' is run.
.form-group
- = f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-light'
+ = f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-bold'
= f.number_field :housekeeping_full_repack_period, class: 'form-control'
.form-text.text-muted
Number of Git pushes after which a full 'git repack' is run.
.form-group
- = f.label :housekeeping_gc_period, 'Git GC period', class: 'label-light'
+ = f.label :housekeeping_gc_period, 'Git GC period', class: 'label-bold'
= f.number_field :housekeeping_gc_period, class: 'form-control'
.form-text.text-muted
Number of Git pushes after which 'git gc' is run.
diff --git a/app/views/admin/application_settings/_repository_mirrors_form.html.haml b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
index beeb5169361..c94f4c74820 100644
--- a/app/views/admin/application_settings/_repository_mirrors_form.html.haml
+++ b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
@@ -1,9 +1,9 @@
-= form_for @application_setting, url: admin_application_settings_path do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-mirror-settings') do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- = f.label :mirror_available, 'Enable mirror configuration', class: 'label-light'
+ = f.label :mirror_available, 'Enable mirror configuration', class: 'label-bold'
.form-check
= f.check_box :mirror_available, class: 'form-check-input'
= f.label :mirror_available, class: 'form-check-label' do
diff --git a/app/views/admin/application_settings/_repository_storage.html.haml b/app/views/admin/application_settings/_repository_storage.html.haml
index 5a303666353..4523332493b 100644
--- a/app/views/admin/application_settings/_repository_storage.html.haml
+++ b/app/views/admin/application_settings/_repository_storage.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-repository-storage-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -7,13 +7,13 @@
.form-check
= f.check_box :hashed_storage_enabled, class: 'form-check-input'
= f.label :hashed_storage_enabled, class: 'form-check-label' do
- Create new projects using hashed storage paths
+ Use hashed storage paths for newly created and renamed projects
.form-text.text-muted
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.
%em (EXPERIMENTAL)
.form-group
- = f.label :repository_storages, 'Storage paths for new projects', class: 'label-light'
+ = f.label :repository_storages, 'Storage paths for new projects', class: 'label-bold'
= f.select :repository_storages, repository_storages_options_for_select(@application_setting.repository_storages),
{include_hidden: false}, multiple: true, class: 'form-control'
.form-text.text-muted
@@ -23,27 +23,27 @@
.sub-section
%h4 Circuit breaker
.form-group
- = f.label :circuitbreaker_check_interval, _('Check interval'), class: 'label-light'
+ = f.label :circuitbreaker_check_interval, _('Check interval'), class: 'label-bold'
= f.number_field :circuitbreaker_check_interval, class: 'form-control'
.form-text.text-muted
= circuitbreaker_check_interval_help_text
.form-group
- = f.label :circuitbreaker_access_retries, _('Number of access attempts'), class: 'label-light'
+ = f.label :circuitbreaker_access_retries, _('Number of access attempts'), class: 'label-bold'
= f.number_field :circuitbreaker_access_retries, class: 'form-control'
.form-text.text-muted
= circuitbreaker_access_retries_help_text
.form-group
- = f.label :circuitbreaker_storage_timeout, _('Seconds to wait for a storage access attempt'), class: 'label-light'
+ = f.label :circuitbreaker_storage_timeout, _('Seconds to wait for a storage access attempt'), class: 'label-bold'
= f.number_field :circuitbreaker_storage_timeout, class: 'form-control'
.form-text.text-muted
= circuitbreaker_storage_timeout_help_text
.form-group
- = f.label :circuitbreaker_failure_count_threshold, _('Maximum git storage failures'), class: 'label-light'
+ = f.label :circuitbreaker_failure_count_threshold, _('Maximum git storage failures'), class: 'label-bold'
= f.number_field :circuitbreaker_failure_count_threshold, class: 'form-control'
.form-text.text-muted
= circuitbreaker_failure_count_help_text
.form-group
- = f.label :circuitbreaker_failure_reset_time, _('Seconds before reseting failure information'), class: 'label-light'
+ = f.label :circuitbreaker_failure_reset_time, _('Seconds before reseting failure information'), class: 'label-bold'
= f.number_field :circuitbreaker_failure_reset_time, class: 'form-control'
.form-text.text-muted
= circuitbreaker_failure_reset_time_help_text
diff --git a/app/views/admin/application_settings/_signin.html.haml b/app/views/admin/application_settings/_signin.html.haml
index 69d1a43c511..635a6751e5b 100644
--- a/app/views/admin/application_settings/_signin.html.haml
+++ b/app/views/admin/application_settings/_signin.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-signin-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -21,31 +21,31 @@
must be used to authenticate.
- if omniauth_enabled? && button_based_providers.any?
.form-group
- = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'label-light'
+ = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'label-bold'
= hidden_field_tag 'application_setting[enabled_oauth_sign_in_sources][]'
.btn-group{ data: { toggle: 'buttons' } }
- oauth_providers_checkboxes.each do |source|
= source
.form-group
- = f.label :two_factor_authentication, 'Two-factor authentication', class: 'label-light'
+ = f.label :two_factor_authentication, 'Two-factor authentication', class: 'label-bold'
.form-check
= f.check_box :require_two_factor_authentication, class: 'form-check-input'
= f.label :require_two_factor_authentication, class: 'form-check-label' do
Require all users to setup Two-factor authentication
.form-group
- = f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'label-light'
+ = f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'label-bold'
= f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0'
.form-text.text-muted Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
.form-group
- = f.label :home_page_url, 'Home page URL', class: 'label-light'
+ = f.label :home_page_url, 'Home page URL', class: 'label-bold'
= f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block'
%span.form-text.text-muted#home_help_block We will redirect non-logged in users to this page
.form-group
- = f.label :after_sign_out_path, class: 'label-light'
+ = f.label :after_sign_out_path, class: 'label-bold'
= f.text_field :after_sign_out_path, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'after_sign_out_path_help_block'
%span.form-text.text-muted#after_sign_out_path_help_block We will redirect users to this page after they sign out
.form-group
- = f.label :sign_in_text, class: 'label-light'
+ = f.label :sign_in_text, class: 'label-bold'
= f.text_area :sign_in_text, class: 'form-control', rows: 4
.form-text.text-muted Markdown enabled
diff --git a/app/views/admin/application_settings/_signup.html.haml b/app/views/admin/application_settings/_signup.html.haml
index b9ba9128cc9..a0a58b811ee 100644
--- a/app/views/admin/application_settings/_signup.html.haml
+++ b/app/views/admin/application_settings/_signup.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-signup-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -13,11 +13,11 @@
= f.label :send_user_confirmation_email, class: 'form-check-label' do
Send confirmation email on sign-up
.form-group
- = f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'label-light'
+ = f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'label-bold'
= f.text_area :domain_whitelist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
.form-text.text-muted ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group
- = f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'label-light'
+ = f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'label-bold'
.form-check
= f.check_box :domain_blacklist_enabled, class: 'form-check-input'
= f.label :domain_blacklist_enabled, class: 'form-check-label' do
@@ -34,16 +34,16 @@
.option-title
Enter blacklist manually
.form-group.blacklist-file
- = f.label :domain_blacklist_file, 'Blacklist file', class: 'label-light'
+ = f.label :domain_blacklist_file, 'Blacklist file', class: 'label-bold'
= f.file_field :domain_blacklist_file, class: 'form-control', accept: '.txt,.conf'
.form-text.text-muted Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries.
.form-group.blacklist-raw
- = f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'label-light'
+ = f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'label-bold'
= f.text_area :domain_blacklist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
.form-text.text-muted 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
.form-group
- = f.label :after_sign_up_text, class: 'label-light'
+ = 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
diff --git a/app/views/admin/application_settings/_spam.html.haml b/app/views/admin/application_settings/_spam.html.haml
index 8f0dce962a9..54cda531580 100644
--- a/app/views/admin/application_settings/_spam.html.haml
+++ b/app/views/admin/application_settings/_spam.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-spam-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -10,14 +10,14 @@
%span.form-text.text-muted#recaptcha_help_block Helps prevent bots from creating accounts
.form-group
- = f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'label-light'
+ = f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'label-bold'
= f.text_field :recaptcha_site_key, class: 'form-control'
.form-text.text-muted
Generate site and private keys at
%a{ href: 'http://www.google.com/recaptcha', target: 'blank' } http://www.google.com/recaptcha
.form-group
- = f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'label-light'
+ = f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'label-bold'
= f.text_field :recaptcha_private_key, class: 'form-control'
.form-group
@@ -28,7 +28,7 @@
%span.form-text.text-muted#akismet_help_block Helps prevent bots from creating issues
.form-group
- = f.label :akismet_api_key, 'Akismet API Key', class: 'label-light'
+ = f.label :akismet_api_key, 'Akismet API Key', class: 'label-bold'
= f.text_field :akismet_api_key, class: 'form-control'
.form-text.text-muted
Generate API key at
@@ -43,13 +43,13 @@
Helps prevent malicious users hide their activity
.form-group
- = f.label :unique_ips_limit_per_user, 'IPs per user', class: 'label-light'
+ = f.label :unique_ips_limit_per_user, 'IPs per user', class: 'label-bold'
= f.number_field :unique_ips_limit_per_user, class: 'form-control'
.form-text.text-muted
Maximum number of unique IPs per user
.form-group
- = f.label :unique_ips_limit_time_window, 'IP expiration time', class: 'label-light'
+ = f.label :unique_ips_limit_time_window, 'IP expiration time', class: 'label-bold'
= f.number_field :unique_ips_limit_time_window, class: 'form-control'
.form-text.text-muted
How many seconds an IP will be counted towards the limit
diff --git a/app/views/admin/application_settings/_terminal.html.haml b/app/views/admin/application_settings/_terminal.html.haml
index 543628ff0ee..49980e1e1a7 100644
--- a/app/views/admin/application_settings/_terminal.html.haml
+++ b/app/views/admin/application_settings/_terminal.html.haml
@@ -1,9 +1,9 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-terminal-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- = f.label :terminal_max_session_time, 'Max session time', class: 'label-light'
+ = f.label :terminal_max_session_time, 'Max session time', class: 'label-bold'
= f.number_field :terminal_max_session_time, class: 'form-control'
.form-text.text-muted
Maximum time for web terminal websocket connection (in seconds).
diff --git a/app/views/admin/application_settings/_terms.html.haml b/app/views/admin/application_settings/_terms.html.haml
index d3dc8659d1b..ef58e9b1128 100644
--- a/app/views/admin/application_settings/_terms.html.haml
+++ b/app/views/admin/application_settings/_terms.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-terms-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_third_party_offers.html.haml b/app/views/admin/application_settings/_third_party_offers.html.haml
new file mode 100644
index 00000000000..fae5b0b965f
--- /dev/null
+++ b/app/views/admin/application_settings/_third_party_offers.html.haml
@@ -0,0 +1,13 @@
+- application_setting = local_assigns.fetch(:application_setting)
+
+= form_for application_setting, url: admin_application_settings_path(anchor: 'js-third-party-offers-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_errors(application_setting)
+
+ %fieldset
+ .form-group
+ .form-check
+ = f.check_box :hide_third_party_offers, class: 'form-check-input'
+ = f.label :hide_third_party_offers, class: 'form-check-label' do
+ Do not display offers from third parties within GitLab
+
+ = f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_usage.html.haml b/app/views/admin/application_settings/_usage.html.haml
index 49a3ee33a85..2495defb6a7 100644
--- a/app/views/admin/application_settings/_usage.html.haml
+++ b/app/views/admin/application_settings/_usage.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-usage-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
@@ -23,13 +23,16 @@
periodically collect usage information.
= link_to 'Learn more', help_page_path("user/admin_area/settings/usage_statistics", anchor: "usage-ping")
about what information is shared with GitLab Inc. Visit
- = link_to 'Cohorts', admin_cohorts_path(anchor: 'usage-ping')
+ = link_to _('Cohorts'), instance_statistics_cohorts_path(anchor: 'usage-ping')
to see the JSON payload sent.
- else
The usage ping is disabled, and cannot be configured through this
form. For more information, see the documentation on
= succeed '.' do
= link_to 'deactivating the usage ping', help_page_path('user/admin_area/settings/usage_statistics', anchor: 'deactivate-the-usage-ping')
+ .form-group
+ = f.label :instance_statistics_visibility_private, _('Instance Statistics visibility')
+ = f.select :instance_statistics_visibility_private, options_for_select({_('All users') => false, _('Only admins') => true}, Gitlab::CurrentSettings.instance_statistics_visibility_private?), {}, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
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 4cc3e6a7d03..0725ffb7f6c 100644
--- a/app/views/admin/application_settings/_visibility_and_access.html.haml
+++ b/app/views/admin/application_settings/_visibility_and_access.html.haml
@@ -1,21 +1,21 @@
-= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-visibility-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
- = f.label :default_branch_protection, class: 'label-light'
+ = f.label :default_branch_protection, class: 'label-bold'
= f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
.form-group.visibility-level-setting
- = f.label :default_project_visibility, class: 'label-light'
+ = f.label :default_project_visibility, class: 'label-bold'
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new)
.form-group.visibility-level-setting
- = f.label :default_snippet_visibility, class: 'label-light'
+ = f.label :default_snippet_visibility, class: 'label-bold'
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
.form-group.visibility-level-setting
- = f.label :default_group_visibility, class: 'label-light'
+ = f.label :default_group_visibility, class: 'label-bold'
= render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
.form-group
- = f.label :restricted_visibility_levels, class: 'label-light'
+ = f.label :restricted_visibility_levels, class: 'label-bold'
- checkbox_name = 'application_setting[restricted_visibility_levels][]'
= hidden_field_tag(checkbox_name)
- restricted_level_checkboxes('restricted-visibility-help', checkbox_name, class: 'form-check-input').each do |level|
@@ -25,7 +25,7 @@
Selected levels cannot be used by non-admin users for groups, projects or snippets.
If the public level is restricted, user profiles are only visible to logged in users.
.form-group
- = f.label :import_sources, class: 'label-light'
+ = f.label :import_sources, class: 'label-bold'
= hidden_field_tag 'application_setting[import_sources][]'
- import_sources_checkboxes('import-sources-help', class: 'form-check-input').each do |source|
.form-check= source
@@ -44,7 +44,7 @@
Project export enabled
.form-group
- %label.label-light Enabled Git access protocols
+ %label.label-bold Enabled Git access protocols
= select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control')
%span.form-text.text-muted#clone-protocol-help
Allow only the selected protocols to be used for Git access.
@@ -52,7 +52,7 @@
- ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
- field_name = :"#{type}_key_restriction"
.form-group
- = f.label field_name, "#{type.upcase} SSH keys", class: 'label-light'
+ = f.label field_name, "#{type.upcase} SSH keys", class: 'label-bold'
= f.select field_name, key_restriction_options_for_select(type), {}, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
index bd43504dd37..258d50ad676 100644
--- a/app/views/admin/application_settings/show.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -325,5 +325,40 @@
.settings-content
= render partial: 'repository_mirrors_form'
-= render_if_exists 'admin/application_settings/pseudonymizer_settings', expanded: expanded
+%section.settings.as-third-party-offers.no-animate#js-third-party-offers-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4
+ = _('Third party offers')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ = _('Control the display of third party offers.')
+ .settings-content
+ = render 'third_party_offers', application_setting: @application_setting
+
+= render_if_exists 'admin/application_settings/custom_templates_form', expanded: expanded
+
+%section.settings.no-animate#js-web-ide-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4
+ = _('Web IDE')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ = _('Manage Web IDE features')
+ .settings-content
+ = form_for @application_setting, url: admin_application_settings_path(anchor: "#js-web-ide-settings"), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting)
+
+ %fieldset
+ .form-group
+ .form-check
+ = f.check_box :web_ide_clientside_preview_enabled, class: 'form-check-input'
+ = f.label :web_ide_clientside_preview_enabled, class: 'form-check-label' do
+ = s_('IDE|Client side evaluation')
+ %span.form-text.text-muted
+ = s_('IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation.')
+ = f.submit _('Save changes'), class: "btn btn-success"
+
+= render_if_exists 'admin/application_settings/pseudonymizer_settings', expanded: expanded
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 18f2c1a509f..fac61f9d249 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -91,10 +91,10 @@
%span.light.float-right
= boolean_to_icon gravatar_enabled?
- omniauth = "OmniAuth"
- %p{ "aria-label" => "#{omniauth}: status " + (Gitlab.config.omniauth.enabled ? "on" : "off") }
+ %p{ "aria-label" => "#{omniauth}: status " + (Gitlab::Auth.omniauth_enabled? ? "on" : "off") }
= omniauth
%span.light.float-right
- = boolean_to_icon Gitlab.config.omniauth.enabled
+ = boolean_to_icon Gitlab::Auth.omniauth_enabled?
- reply_email = "Reply by email"
%p{ "aria-label" => "#{reply_email}: status " + (Gitlab::IncomingEmail.enabled? ? "on" : "off") }
= reply_email
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index c8008771236..a3773e90cfb 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -6,7 +6,7 @@
= render_if_exists 'admin/namespace_plan', f: f
.form-group.row.group-description-holder
- = f.label :avatar, "Group avatar", class: 'col-form-label col-sm-2'
+ = f.label :avatar, _("Group avatar"), class: 'col-form-label col-sm-2'
.col-sm-10
= render 'shared/choose_group_avatar_button', f: f
@@ -26,12 +26,12 @@
.alert.alert-info
= render 'shared/group_tips'
.form-actions
- = f.submit 'Create group', class: "btn btn-create"
- = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
+ = f.submit _('Create group'), class: "btn btn-create"
+ = link_to _('Cancel'), admin_groups_path, class: "btn btn-cancel"
- else
.form-actions
- = f.submit 'Save changes', class: "btn btn-save"
- = link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel"
+ = f.submit _('Save changes'), class: "btn btn-save"
+ = link_to _('Cancel'), admin_group_path(@group), class: "btn btn-cancel"
= render_if_exists 'ldap_group_links/ldap_syncrhonizations', group: @group
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index 3f96988c203..0a688b90f3a 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -3,8 +3,8 @@
%li.group-row{ class: css_class }
.controls
- = link_to 'Edit', admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn'
- = link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove'
+ = link_to _('Edit'), admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn'
+ = link_to _('Delete'), [:admin, group], data: { confirm: _("Are you sure you want to remove %{group_name}?") % { group_name: group.name } }, method: :delete, class: 'btn btn-remove'
.stats
%span.badge.badge-pill
= storage_counter(group.storage_size)
diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml
index c2b9807015d..8e9e1a58a17 100644
--- a/app/views/admin/groups/edit.html.haml
+++ b/app/views/admin/groups/edit.html.haml
@@ -1,4 +1,4 @@
-- page_title "Edit", @group.name, "Groups"
-%h3.page-title Edit group: #{@group.name}
+- page_title _("Edit"), @group.name, _("Groups")
+%h3.page-title= _('Edit group: %{group_name}') % { group_name: @group.name }
%hr
= render 'form', visibility_level: @group.visibility_level
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 25946ba6eaf..6a9b85b4109 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -1,5 +1,5 @@
- @no_container = true
-- page_title "Groups"
+- page_title _("Groups")
%div{ class: container_class }
.top-area
@@ -13,7 +13,7 @@
= icon("search", class: "search-icon")
= render "shared/groups/dropdown", options_hash: admin_groups_sort_options_hash
= link_to new_admin_group_path, class: "btn btn-new" do
- New group
+ = _('New group')
%ul.content-list
= render @groups
diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml
index 8f9fe96249f..553e8638e52 100644
--- a/app/views/admin/groups/new.html.haml
+++ b/app/views/admin/groups/new.html.haml
@@ -1,4 +1,4 @@
-- page_title "New Group"
-%h3.page-title New group
+- page_title _("New Group")
+%h3.page-title= _('New group')
%hr
= render 'form', visibility_level: default_group_visibility
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index a40f98ad24f..72b068ea6b5 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -1,61 +1,58 @@
-- add_to_breadcrumbs "Groups", admin_groups_path
+- add_to_breadcrumbs _("Groups"), admin_groups_path
- breadcrumb_title @group.name
-- page_title @group.name, "Groups"
+- page_title @group.name, _("Groups")
%h3.page-title
- Group: #{@group.full_name}
+ = _('Group: %{group_name}') % { group_name: @group.full_name }
= link_to admin_group_edit_path(@group), class: "btn float-right" do
%i.fa.fa-pencil-square-o
- Edit
+ = _('Edit')
%hr
.row
.col-md-6
.card
.card-header
- Group info:
+ = _('Group info:')
%ul.content-list
%li
.avatar-container.s60
= group_icon(@group, class: "avatar s60")
%li
- %span.light Name:
+ %span.light= _('Name:')
%strong= @group.name
%li
- %span.light Path:
+ %span.light= _('Path:')
%strong
= @group.path
%li
- %span.light Description:
+ %span.light= _('Description:')
%strong
= @group.description
%li
- %span.light Visibility level:
+ %span.light= _('Visibility level:')
%strong
= visibility_level_label(@group.visibility_level)
%li
- %span.light Created on:
+ %span.light= _('Created on:')
%strong
= @group.created_at.to_s(:medium)
= render_if_exists 'admin/namespace_plan_info', namespace: @group
%li
- %span.light Storage:
- %strong= storage_counter(@group.storage_size)
- (
- = storage_counter(@group.repository_size)
- repositories,
- = storage_counter(@group.build_artifacts_size)
- build artifacts,
- = storage_counter(@group.lfs_objects_size)
- LFS
- )
+ %span.light= _('Storage:')
+ - counter_storage = storage_counter(@group.storage_size)
+ - counter_repositories = storage_counter(@group.repository_size)
+ - counter_build_artifacts = storage_counter(@group.build_artifacts_size)
+ - counter_lfs_objects = storage_counter(@group.lfs_objects_size)
+ %strong
+ = _("%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)") % { counter_storage: counter_storage, counter_repositories: counter_repositories, counter_build_artifacts: counter_build_artifacts, counter_lfs_objects: counter_lfs_objects }
%li
- %span.light Group Git LFS status:
+ %span.light= _('Group Git LFS status:')
%strong
= group_lfs_status(@group)
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
@@ -67,7 +64,7 @@
.card
.card-header
%h3.card-title
- Projects
+ = _('Projects')
%span.badge.badge-pill
#{@group.projects.count}
%ul.content-list
@@ -85,7 +82,7 @@
- if @group.shared_projects.any?
.card
.card-header
- Projects shared with #{@group.name}
+ = _('Projects shared with %{group_name}') % { group_name: @group.name }
%span.badge.badge-pill
#{@group.shared_projects.count}
%ul.content-list
@@ -102,11 +99,11 @@
- if can?(current_user, :admin_group_member, @group)
.card
.card-header
- Add user(s) to the group:
+ = _('Add user(s) to the group:')
.card-body.form-holder
%p.light
- Read more about project permissions
- %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
+ - link_to_help = link_to(_("here"), help_page_path("user/permissions"), class: "vlink")
+ = _('Read more about project permissions <strong>%{link_to_help}</strong>').html_safe % { link_to_help: link_to_help }
= form_tag admin_group_members_update_path(@group), id: "new_project_member", class: "bulk_import", method: :put do
%div
@@ -114,16 +111,15 @@
.prepend-top-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
- = button_tag 'Add users to group', class: "btn btn-create"
+ = button_tag _('Add users to group'), class: "btn btn-create"
= render 'shared/members/requests', membership_source: @group, requesters: @requesters, force_mobile_view: true
.card
.card-header
- %strong= @group.name
- group members
+ = _("<strong>%{group_name}</strong> group members").html_safe % { group_name: @group.name }
%span.badge.badge-pill= @group.members.size
.float-right
- = link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@group, :members]), class: "btn btn-sm"
+ = link_to icon('pencil-square-o', text: _('Manage access')), polymorphic_url([@group, :members]), class: "btn btn-sm"
%ul.content-list.group-users-list.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member, locals: { show_controls: false }
.card-footer
diff --git a/app/views/admin/hooks/_form.html.haml b/app/views/admin/hooks/_form.html.haml
index 3abde755f0f..072f80b56b9 100644
--- a/app/views/admin/hooks/_form.html.haml
+++ b/app/views/admin/hooks/_form.html.haml
@@ -1,15 +1,15 @@
= form_errors(hook)
.form-group
- = form.label :url, 'URL', class: 'label-light'
+ = form.label :url, 'URL', class: 'label-bold'
= form.text_field :url, class: 'form-control'
.form-group
- = form.label :token, 'Secret Token', class: 'label-light'
+ = form.label :token, 'Secret Token', class: 'label-bold'
= form.text_field :token, class: 'form-control'
%p.form-text.text-muted
Use this token to validate received payloads
.form-group
- = form.label :url, 'Trigger', class: 'label-light'
+ = form.label :url, 'Trigger', class: 'label-bold'
%ul.list-unstyled
%li
.form-text.text-muted
@@ -45,7 +45,7 @@
%p.light
This URL will be triggered when a merge request is created/updated/merged
.form-group
- = form.label :enable_ssl_verification, 'SSL verification', class: 'label-light checkbox'
+ = form.label :enable_ssl_verification, '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' do
diff --git a/app/views/admin/identities/edit.html.haml b/app/views/admin/identities/edit.html.haml
index 1ad6ce969cb..fa09138c502 100644
--- a/app/views/admin/identities/edit.html.haml
+++ b/app/views/admin/identities/edit.html.haml
@@ -1,3 +1,6 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- add_to_breadcrumbs @user.name, admin_user_identities_path(@user)
+- breadcrumb_title "Edit Identity"
- page_title _("Edit"), @identity.provider, _("Identities"), @user.name, _("Users")
%h3.page-title
= _('Edit identity for %{user_name}') % { user_name: @user.name }
diff --git a/app/views/admin/identities/index.html.haml b/app/views/admin/identities/index.html.haml
index 59373ee6752..df3df159947 100644
--- a/app/views/admin/identities/index.html.haml
+++ b/app/views/admin/identities/index.html.haml
@@ -1,3 +1,5 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- breadcrumb_title @user.name
- page_title _("Identities"), @user.name, _("Users")
= render 'admin/users/head'
diff --git a/app/views/admin/identities/new.html.haml b/app/views/admin/identities/new.html.haml
index ee743b0fd3c..c28d22625b5 100644
--- a/app/views/admin/identities/new.html.haml
+++ b/app/views/admin/identities/new.html.haml
@@ -1,3 +1,6 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- add_to_breadcrumbs @user.name, admin_user_identities_path(@user)
+- breadcrumb_title "New Identity"
- page_title _("New Identity")
%h3.page-title= _('New identity')
%hr
diff --git a/app/views/admin/impersonation_tokens/index.html.haml b/app/views/admin/impersonation_tokens/index.html.haml
index 1378dde52ab..9e490713ef3 100644
--- a/app/views/admin/impersonation_tokens/index.html.haml
+++ b/app/views/admin/impersonation_tokens/index.html.haml
@@ -1,3 +1,5 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- breadcrumb_title @user.name
- page_title "Impersonation Tokens", @user.name, "Users"
= render 'admin/users/head'
diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml
index c3ea2352898..dbb7224f5f9 100644
--- a/app/views/admin/labels/_label.html.haml
+++ b/app/views/admin/labels/_label.html.haml
@@ -1,7 +1,7 @@
-%li{ id: dom_id(label) }
- .label-row
- = render_colored_label(label, tooltip: false)
- = markdown_field(label, :description)
- .float-right
- = link_to _('Edit'), edit_admin_label_path(label), class: 'btn btn-sm'
- = link_to _('Delete'), admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Delete this label? Are you sure?"}
+%li.label-list-item{ id: dom_id(label) }
+ = render "shared/label_row", label: label
+ .label-actions-list
+ = link_to edit_admin_label_path(label), class: 'btn btn-transparent label-action has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
+ = sprite_icon('pencil')
+ = link_to admin_label_path(label), class: 'btn btn-transparent remove-row label-action has-tooltip', title: _('Delete'), data: { placement: 'bottom', confirm: "Delete this label? Are you sure?" }, aria_label: _('Delete'), method: :delete, remote: true do
+ = sprite_icon('remove')
diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml
index d3e5247447a..f1b8658f84e 100644
--- a/app/views/admin/labels/index.html.haml
+++ b/app/views/admin/labels/index.html.haml
@@ -7,10 +7,11 @@
= _('Labels')
%hr
-.labels
+.labels.labels-container.admin-labels
- if @labels.present?
- %ul.bordered-list.manage-labels-list
+ %ul.manage-labels-list
= render @labels
+
= paginate @labels, theme: 'gitlab'
- else
.card.bg-light
diff --git a/app/views/admin/projects/_projects.html.haml b/app/views/admin/projects/_projects.html.haml
index 00933d726d9..50296a2afe7 100644
--- a/app/views/admin/projects/_projects.html.haml
+++ b/app/views/admin/projects/_projects.html.haml
@@ -17,10 +17,10 @@
- if project.archived
%span.badge.badge-warning archived
.title
- = link_to [:admin, project.namespace.becomes(Namespace), project] do
+ = link_to(admin_namespace_project_path(project.namespace, project)) do
.dash-project-avatar
.avatar-container.s40
- = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+ = project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40)
%span.project-full-name
%span.namespace-name
- if project.namespace
diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml
index a6cd39edcf0..43937b01339 100644
--- a/app/views/admin/runners/_runner.html.haml
+++ b/app/views/admin/runners/_runner.html.haml
@@ -1,6 +1,6 @@
%tr{ id: dom_id(runner) }
%td
- - if runner.shared?
+ - if runner.instance_type?
%span.badge.badge-success shared
- elsif runner.group_type?
%span.badge.badge-success group
@@ -21,7 +21,7 @@
%td
= runner.ip_address
%td
- - if runner.shared? || runner.group_type?
+ - if runner.instance_type? || runner.group_type?
n/a
- else
= runner.projects.count(:all)
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index 8a0c2bf4c5f..62be38e9dd2 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -2,7 +2,7 @@
%h3.project-title
Runner ##{@runner.id}
.float-right
- - if @runner.shared?
+ - if @runner.instance_type?
%span.runner-state.runner-state-shared
Shared
- else
@@ -11,9 +11,8 @@
- add_to_breadcrumbs _("Runners"), admin_runners_path
- breadcrumb_title "##{@runner.id}"
-- @no_container = true
-- if @runner.shared?
+- if @runner.instance_type?
.bs-callout.bs-callout-success
%h4 This Runner will process jobs from ALL UNASSIGNED projects
%p
diff --git a/app/views/admin/users/keys.html.haml b/app/views/admin/users/keys.html.haml
index 0f644121e62..103bbb3b063 100644
--- a/app/views/admin/users/keys.html.haml
+++ b/app/views/admin/users/keys.html.haml
@@ -1,3 +1,5 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- breadcrumb_title @user.name
- page_title "SSH Keys", @user.name, "Users"
= render 'admin/users/head'
= render 'profiles/keys/key_table', admin: true
diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml
index cf50d45f755..3d39c1da408 100644
--- a/app/views/admin/users/projects.html.haml
+++ b/app/views/admin/users/projects.html.haml
@@ -1,3 +1,5 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- breadcrumb_title @user.name
- page_title "Groups and projects", @user.name, "Users"
= render 'admin/users/head'
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index b0562226f5f..f730fd05176 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -149,8 +149,8 @@
%br
= link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-info", data: { confirm: 'Are you sure?' }
- else
- .card.bg-warning
- .card-header
+ .card.border-warning
+ .card-header.bg-warning.text-white
Block this user
.card-body
%p Blocking user has the following effects:
@@ -170,8 +170,8 @@
%br
= link_to 'Unlock user', unlock_admin_user_path(@user), method: :put, class: "btn btn-info", data: { confirm: 'Are you sure?' }
- .card.bg-danger
- .card-header
+ .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)
@@ -196,8 +196,8 @@
%p
You don't have access to delete this user.
- .card.bg-danger
- .card-header
+ .card.border-danger
+ .card-header.bg-danger.text-white
= s_('AdminUsers|Delete user and contributions')
.card-body
- if can?(current_user, :destroy_user, @user)
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index 37fb8fbab26..13f96b9747c 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -1,16 +1,17 @@
-- link = link_to _("GitLab Runner section"), 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'
+- link = link_to _("Install GitLab Runner"), 'https://docs.gitlab.com/runner/install/', target: '_blank'
.append-bottom-10
%h4= _("Setup a #{type} Runner manually")
%ol
%li
- = _("Install a Runner compatible with GitLab CI")
- = (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe
+ = link.html_safe
%li
= _("Specify the following URL during the Runner setup:")
%code#coordinator_address= root_url(only_path: false)
+ = clipboard_button(target: '#coordinator_address', title: _("Copy URL to clipboard"), class: "btn-transparent btn-clipboard")
%li
= _("Use the following registration token during setup:")
%code#registration_token= registration_token
+ = clipboard_button(target: '#registration_token', title: _("Copy token to clipboard"), class: "btn-transparent btn-clipboard")
%li
= _("Start the Runner!")
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index a676eba2aee..9c246e19faa 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -1,8 +1,8 @@
.nav-block.activities
+ = render 'shared/event_filter'
.controls
= link_to dashboard_projects_path(rss_url_options), class: 'btn rss-btn has-tooltip', title: 'Subscribe' do
%i.fa.fa-rss
- = render 'shared/event_filter'
.content_list
= spinner
diff --git a/app/views/dashboard/milestones/_milestone.html.haml b/app/views/dashboard/milestones/_milestone.html.haml
index 6173ca6ab9b..b876d6fd1f3 100644
--- a/app/views/dashboard/milestones/_milestone.html.haml
+++ b/app/views/dashboard/milestones/_milestone.html.haml
@@ -1,5 +1,5 @@
= render 'shared/milestones/milestone',
- milestone_path: dashboard_milestone_path(milestone.safe_title, title: milestone.title),
+ milestone_path: group_or_dashboard_milestone_path(milestone),
issues_path: issues_dashboard_path(milestone_title: milestone.title),
merge_requests_path: merge_requests_dashboard_path(milestone_title: milestone.title),
milestone: milestone,
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index d5a9cc646a6..8b3974d97f8 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -30,27 +30,33 @@
.todos-filters
.row-content-block.second-block
- = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form' do
- .filter-item.inline
- - if params[:project_id].present?
- = hidden_field_tag(:project_id, params[:project_id])
- = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
- placeholder: 'Search projects', data: { data: todo_projects_options, default_label: 'Project', display: 'static' } })
- .filter-item.inline
- - if params[:author_id].present?
- = hidden_field_tag(:author_id, params[:author_id])
- = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
- placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author', todo_filter: true, todo_state_filter: params[:state] || 'pending' } })
- .filter-item.inline
- - if params[:type].present?
- = hidden_field_tag(:type, params[:type])
- = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit',
- data: { data: todo_types_options, default_label: 'Type' } })
- .filter-item.inline.actions-filter
- - if params[:action_id].present?
- = hidden_field_tag(:action_id, params[:action_id])
- = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit',
- data: { data: todo_actions_options, default_label: 'Action' } })
+ = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form d-sm-flex' do
+ .filter-categories.flex-fill
+ .filter-item.inline
+ - if params[:group_id].present?
+ = hidden_field_tag(:group_id, params[:group_id])
+ = dropdown_tag(group_dropdown_label(params[:group_id], 'Group'), options: { toggle_class: 'js-group-search js-filter-submit', title: 'Filter by group', filter: true, filterInput: 'input#group-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-group js-filter-submit',
+ placeholder: 'Search groups', data: { data: todo_group_options, default_label: 'Group', display: 'static' } })
+ .filter-item.inline
+ - if params[:project_id].present?
+ = hidden_field_tag(:project_id, params[:project_id])
+ = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
+ placeholder: 'Search projects', data: { data: todo_projects_options, default_label: 'Project', display: 'static' } })
+ .filter-item.inline
+ - if params[:author_id].present?
+ = hidden_field_tag(:author_id, params[:author_id])
+ = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
+ placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author', todo_filter: true, todo_state_filter: params[:state] || 'pending' } })
+ .filter-item.inline
+ - if params[:type].present?
+ = hidden_field_tag(:type, params[:type])
+ = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit',
+ data: { data: todo_types_options, default_label: 'Type' } })
+ .filter-item.inline.actions-filter
+ - if params[:action_id].present?
+ = hidden_field_tag(:action_id, params[:action_id])
+ = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit',
+ data: { data: todo_actions_options, default_label: 'Action' } })
.filter-item.sort-filter
.dropdown
%button.dropdown-menu-toggle.dropdown-menu-toggle-sort{ type: 'button', 'data-toggle' => 'dropdown' }
diff --git a/app/views/doorkeeper/applications/_delete_form.html.haml b/app/views/doorkeeper/applications/_delete_form.html.haml
index 84b4ce5b606..ac5cac50699 100644
--- a/app/views/doorkeeper/applications/_delete_form.html.haml
+++ b/app/views/doorkeeper/applications/_delete_form.html.haml
@@ -2,9 +2,9 @@
= form_tag oauth_application_path(application) do
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
- if defined? small
- = button_tag type: "submit", class: "btn btn-transparent", data: { confirm: "Are you sure?" } do
+ = button_tag type: "submit", class: "btn btn-transparent", data: { confirm: _("Are you sure?") } do
%span.sr-only
- Destroy
+ = _('Destroy')
= icon('trash')
- else
- = submit_tag 'Destroy', data: { confirm: "Are you sure?" }, class: submit_btn_css
+ = submit_tag _('Destroy'), data: { confirm: _("Are you sure?") }, class: submit_btn_css
diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml
index be0935b8313..0bc057a8864 100644
--- a/app/views/doorkeeper/applications/_form.html.haml
+++ b/app/views/doorkeeper/applications/_form.html.haml
@@ -2,24 +2,22 @@
= form_errors(application)
.form-group
- = f.label :name, class: 'label-light'
+ = f.label :name, class: 'label-bold'
= f.text_field :name, class: 'form-control', required: true
.form-group
- = f.label :redirect_uri, class: 'label-light'
+ = f.label :redirect_uri, class: 'label-bold'
= f.text_area :redirect_uri, class: 'form-control', required: true
%span.form-text.text-muted
- Use one line per URI
+ = _('Use one line per URI')
- if Doorkeeper.configuration.native_redirect_uri
%span.form-text.text-muted
- Use
- %code= Doorkeeper.configuration.native_redirect_uri
- for local tests
+ = _('Use <code>%{native_redirect_uri}</code> for local tests').html_safe % { native_redirect_uri: Doorkeeper.configuration.native_redirect_uri }
.form-group
- = f.label :scopes, class: 'label-light'
+ = f.label :scopes, class: 'label-bold'
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes
.prepend-top-default
- = f.submit 'Save application', class: "btn btn-create"
+ = f.submit _('Save application'), class: "btn btn-create"
diff --git a/app/views/doorkeeper/applications/edit.html.haml b/app/views/doorkeeper/applications/edit.html.haml
index 49f90298a50..aad4200f240 100644
--- a/app/views/doorkeeper/applications/edit.html.haml
+++ b/app/views/doorkeeper/applications/edit.html.haml
@@ -1,4 +1,4 @@
-- page_title "Edit", @application.name, "Applications"
+- page_title _("Edit"), @application.name, _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
-%h3.page-title Edit application
+%h3.page-title= _('Edit application')
= render 'form', application: @application
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index cdf3ff81bd9..ab3a1b100ce 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -1,4 +1,4 @@
-- page_title "Applications"
+- page_title _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
.row.prepend-top-default
@@ -7,28 +7,27 @@
= page_title
%p
- if user_oauth_applications?
- Manage applications that can use GitLab as an OAuth provider,
- and applications that you've authorized to use your account.
+ = _("Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account.")
- else
- Manage applications that you've authorized to use your account.
+ = _("Manage applications that you've authorized to use your account.")
.col-lg-8
- if user_oauth_applications?
%h5.prepend-top-0
- Add new application
+ = _('Add new application')
= render 'form', application: @application
%hr
- if user_oauth_applications?
.oauth-applications
%h5
- Your applications (#{@applications.size})
+ = _("Your applications (%{size})") % { size: @applications.size }
- if @applications.any?
.table-responsive
%table.table
%thead
%tr
- %th Name
- %th Callback URL
- %th Clients
+ %th= _('Name')
+ %th= _('Callback URL')
+ %th= _('Clients')
%th.last-heading
%tbody
- @applications.each do |application|
@@ -41,25 +40,25 @@
%td
= link_to edit_oauth_application_path(application), class: "btn btn-transparent append-right-5" do
%span.sr-only
- Edit
+ = _('Edit')
= icon('pencil')
= render 'delete_form', application: application, small: true
- else
.settings-message.text-center
- You don't have any applications
+ = _("You don't have any applications")
.oauth-authorized-applications.prepend-top-20.append-bottom-default
- if user_oauth_applications?
%h5
- Authorized applications (#{@authorized_tokens.size})
+ = _("Authorized applications (%{size})") % { size: @authorized_tokens.size }
- if @authorized_tokens.any?
.table-responsive
%table.table.table-striped
%thead
%tr
- %th Name
- %th Authorized At
- %th Scope
+ %th= _('Name')
+ %th= _('Authorized At')
+ %th= _('Scope')
%th
%tbody
- @authorized_apps.each do |app|
@@ -72,12 +71,12 @@
- @authorized_anonymous_tokens.each do |token|
%tr
%td
- Anonymous
+ = _('Anonymous')
.form-text.text-muted
- %em Authorization was granted by entering your username and password in the application.
+ %em= _("Authorization was granted by entering your username and password in the application.")
%td= token.created_at
%td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', token: token
- else
.settings-message.text-center
- You don't have any authorized applications
+ = _("You don't have any authorized applications")
diff --git a/app/views/doorkeeper/applications/new.html.haml b/app/views/doorkeeper/applications/new.html.haml
index d3692d1f759..a66fab20d7c 100644
--- a/app/views/doorkeeper/applications/new.html.haml
+++ b/app/views/doorkeeper/applications/new.html.haml
@@ -1,6 +1,6 @@
-- page_title "New Application"
+- page_title _("New Application")
-%h3.page-title New Application
+%h3.page-title= _("New Application")
%hr
diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml
index 89ad626f73f..bb76ac6d5f6 100644
--- a/app/views/doorkeeper/applications/show.html.haml
+++ b/app/views/doorkeeper/applications/show.html.haml
@@ -1,27 +1,27 @@
-- add_to_breadcrumbs "Applications", oauth_applications_path
+- add_to_breadcrumbs _("Applications"), oauth_applications_path
- breadcrumb_title @application.name
-- page_title @application.name, "Applications"
+- page_title @application.name, _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
%h3.page-title
- Application: #{@application.name}
+ = _("Application: %{name}") % { name: @application.name }
.table-holder.oauth-application-show
%table.table
%tr
%td
- Application Id
+ = _('Application Id')
%td
%code#application_id= @application.uid
%tr
%td
- Secret:
+ = _('Secret:')
%td
%code#secret= @application.secret
%tr
%td
- Callback url
+ = _('Callback url')
%td
- @application.redirect_uri.split.each do |uri|
%div
@@ -30,5 +30,5 @@
= render "shared/tokens/scopes_list", token: @application
.form-actions
- = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide float-left'
+ = link_to _('Edit'), edit_oauth_application_path(@application), class: 'btn btn-primary wide float-left'
= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml
index 6117b00149f..32b4ccb0fe6 100644
--- a/app/views/doorkeeper/authorizations/error.html.haml
+++ b/app/views/doorkeeper/authorizations/error.html.haml
@@ -1,3 +1,3 @@
-%h3.page-title An error has occurred
+%h3.page-title= _("An error has occurred")
%main{ :role => "main" }
%pre= @pre_auth.error_response.body[:error_description]
diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml
index 28cdc7607e0..74791b81ccd 100644
--- a/app/views/doorkeeper/authorizations/new.html.haml
+++ b/app/views/doorkeeper/authorizations/new.html.haml
@@ -3,34 +3,28 @@
.modal-content
.modal-header
%h3.page-title
- Authorize
- = link_to @pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer'
- to use your account?
+ - link_to_client = link_to(@pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer')
+ = _("Authorize %{link_to_client} to use your account?").html_safe % { link_to_client: link_to_client }
.modal-body
- if current_user.admin?
.text-warning
%p
= icon("exclamation-triangle fw")
- You are an admin, which means granting access to
- %strong= @pre_auth.client.name
- will allow them to interact with GitLab as an admin as well. Proceed with caution.
+ = _('You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution.').html_safe % { client_name: @pre_auth.client.name }
%p
- An application called
- = link_to @pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer'
- is requesting access to your GitLab account.
+ - link_to_client = link_to(@pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer')
+ = _("An application called %{link_to_client} is requesting access to your GitLab account.").html_safe % { link_to_client: link_to_client }
- auth_app_owner = @pre_auth.client.application.owner
- if auth_app_owner
- This application was created by
- = succeed "." do
- = link_to auth_app_owner.name, user_path(auth_app_owner)
+ - link_to_owner = link_to(auth_app_owner.name, user_path(auth_app_owner))
+ = _("This application was created by %{link_to_owner}.").html_safe % { link_to_owner: link_to_owner }
- Please note that this application is not provided by GitLab and you should verify its authenticity before
- allowing access.
+ = _("Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access.")
- if @pre_auth.scopes
%p
- This application will be able to:
+ = _("This application will be able to:")
%ul
- @pre_auth.scopes.each do |scope|
%li
@@ -44,7 +38,7 @@
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= hidden_field_tag :nonce, @pre_auth.nonce
- = submit_tag "Deny", class: "btn btn-danger"
+ = submit_tag _("Deny"), class: "btn btn-danger"
= form_tag oauth_authorization_path, method: :post, class: 'inline' do
= hidden_field_tag :client_id, @pre_auth.client.uid
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
@@ -52,4 +46,4 @@
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= hidden_field_tag :nonce, @pre_auth.nonce
- = submit_tag "Authorize", class: "btn btn-success prepend-left-10"
+ = submit_tag _("Authorize"), class: "btn btn-success prepend-left-10"
diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml
index 44e868e6782..e4bfd69e7f8 100644
--- a/app/views/doorkeeper/authorizations/show.html.haml
+++ b/app/views/doorkeeper/authorizations/show.html.haml
@@ -1,3 +1,3 @@
-%h3.page-title Authorization code:
+%h3.page-title= _("Authorization code:")
%main{ :role => "main" }
%code#authorization_code= params[:code]
diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
index 11c1e67878e..08f2442f025 100644
--- a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
+++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
@@ -6,4 +6,4 @@
= form_tag path do
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
- = submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-remove btn-sm'
+ = submit_tag _('Revoke'), onclick: "return confirm('#{_('Are you sure?')}')", class: 'btn btn-remove btn-sm'
diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml
index 30c9d02b72e..8e73298b250 100644
--- a/app/views/doorkeeper/authorized_applications/index.html.haml
+++ b/app/views/doorkeeper/authorized_applications/index.html.haml
@@ -1,12 +1,12 @@
%header
- %h1 Your authorized applications
+ %h1= _("Your authorized applications")
%main{ :role => "main" }
.table-holder
%table.table.table-striped
%thead
%tr
- %th Application
- %th Created At
+ %th= _('Application')
+ %th= _('Created At')
%th
%th
%tbody
diff --git a/app/views/explore/_head.html.haml b/app/views/explore/_head.html.haml
index a3b0709e261..eefc797cf03 100644
--- a/app/views/explore/_head.html.haml
+++ b/app/views/explore/_head.html.haml
@@ -1,6 +1,6 @@
.explore-title.text-center
%h2
- Explore GitLab
+ = _("Explore GitLab")
%p.lead
- Discover projects, groups and snippets. Share your projects with others
+ = _("Discover projects, groups and snippets. Share your projects with others")
%br
diff --git a/app/views/explore/groups/_nav.html.haml b/app/views/explore/groups/_nav.html.haml
index ab4787c6d05..c337149a2f3 100644
--- a/app/views/explore/groups/_nav.html.haml
+++ b/app/views/explore/groups/_nav.html.haml
@@ -2,7 +2,7 @@
%ul.nav-links.nav.nav-tabs
= nav_link(page: explore_groups_path) do
= link_to explore_groups_path do
- Explore Groups
+ = _("Explore Groups")
.nav-controls
= render 'shared/groups/search_form'
= render 'shared/groups/dropdown'
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 0643b9cfbc5..387c37b7a91 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -1,6 +1,6 @@
- @hide_top_links = true
-- page_title "Groups"
-- header_title "Groups", dashboard_groups_path
+- page_title _("Groups")
+- header_title _("Groups"), dashboard_groups_path
- if current_user
= render 'dashboard/groups_head'
@@ -10,14 +10,14 @@
- if cookies[:explore_groups_landing_dismissed] != 'true'
.explore-groups.landing.content-block.js-explore-groups-landing.hide
- %button.dismiss-button{ type: 'button', 'aria-label' => 'Dismiss' }= icon('times')
+ %button.dismiss-button{ type: 'button', 'aria-label' => _('Dismiss') }= icon('times')
.svg-container
= custom_icon('icon_explore_groups_splash')
.inner-content
- %p Below you will find all the groups that are public.
- %p You can easily contribute to them by requesting to join these groups.
+ %p= _("Below you will find all the groups that are public.")
+ %p= _("You can easily contribute to them by requesting to join these groups.")
- if params[:filter].blank? && @groups.empty?
- .nothing-here-block No public groups
+ .nothing-here-block= _("No public groups")
- else
= render 'groups'
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 6abb56ba6d2..b694103ccaf 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -2,16 +2,16 @@
.dropdown
%button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' }
= icon('globe')
- %span.light Visibility:
+ %span.light= _("Visibility:")
- if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i)
- else
- Any
+ = _('Any')
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right
%li
= link_to filter_projects_path(visibility_level: nil) do
- Any
+ = _('Any')
- Gitlab::VisibilityLevel.values.each do |level|
%li{ class: active_when(level.to_s == params[:visibility_level]) || 'light' }
= link_to filter_projects_path(visibility_level: level) do
diff --git a/app/views/explore/projects/_nav.html.haml b/app/views/explore/projects/_nav.html.haml
index 558cd26f1e0..bf65c19b720 100644
--- a/app/views/explore/projects/_nav.html.haml
+++ b/app/views/explore/projects/_nav.html.haml
@@ -2,13 +2,13 @@
%ul.nav-links.nav.nav-tabs
= nav_link(page: [trending_explore_projects_path, explore_root_path]) do
= link_to trending_explore_projects_path do
- Trending
+ = _('Trending')
= nav_link(page: starred_explore_projects_path) do
= link_to starred_explore_projects_path do
- Most stars
+ = _('Most stars')
= nav_link(page: explore_projects_path) do
= link_to explore_projects_path do
- All
+ = _('All')
.nav-controls
- unless current_user
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index f00802e0af7..452f390695c 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -1,6 +1,6 @@
- @hide_top_links = true
-- page_title "Projects"
-- header_title "Projects", dashboard_projects_path
+- page_title _("Projects")
+- header_title _("Projects"), dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index f00802e0af7..452f390695c 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -1,6 +1,6 @@
- @hide_top_links = true
-- page_title "Projects"
-- header_title "Projects", dashboard_projects_path
+- page_title _("Projects")
+- header_title _("Projects"), dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index f00802e0af7..452f390695c 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -1,6 +1,6 @@
- @hide_top_links = true
-- page_title "Projects"
-- header_title "Projects", dashboard_projects_path
+- page_title _("Projects")
+- header_title _("Projects"), dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/groups/_activities.html.haml b/app/views/groups/_activities.html.haml
index 577c63503a8..82a497289f3 100644
--- a/app/views/groups/_activities.html.haml
+++ b/app/views/groups/_activities.html.haml
@@ -1,8 +1,8 @@
.nav-block.activities
+ = render 'shared/event_filter'
.controls
= link_to group_path(@group, rss_url_options), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do
%i.fa.fa-rss
- = render 'shared/event_filter'
.content_list
= spinner
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 8037cf4b69d..5e1ae1dbe38 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -9,7 +9,7 @@
= render 'shared/issuable/nav', type: :issues
.nav-controls
= render 'shared/issuable/feed_buttons'
- = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", type: :issues
+ = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", type: :issues, with_feature_enabled: 'issues'
= render 'shared/issuable/search_bar', type: :issues
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 4ccd16f3e11..e2a317dbf67 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -7,7 +7,7 @@
= render 'shared/issuable/nav', type: :merge_requests
- if current_user
.nav-controls
- = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests, with_feature_enabled: 'merge_requests'
= render 'shared/issuable/search_bar', type: :merge_requests
diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml
index 64786d24266..0e225fe33a5 100644
--- a/app/views/groups/settings/_general.html.haml
+++ b/app/views/groups/settings/_general.html.haml
@@ -1,20 +1,21 @@
= form_for @group, html: { multipart: true, class: 'gl-show-field-errors' }, authenticity_token: true do |f|
+ %input{ type: 'hidden', name: 'update_section', value: 'js-general-settings' }
= form_errors(@group)
%fieldset
.row
.form-group.col-md-9
- = f.label :name, class: 'label-light' do
+ = f.label :name, class: 'label-bold' do
Group name
= f.text_field :name, class: 'form-control'
.form-group.col-md-3
- = f.label :id, class: 'label-light' do
+ = f.label :id, class: 'label-bold' do
Group ID
= f.text_field :id, class: 'form-control', readonly: true
.form-group
- = f.label :description, class: 'label-light' do
+ = f.label :description, class: 'label-bold' do
Group description
%span.light (optional)
= f.text_area :description, class: 'form-control', rows: 3, maxlength: 250
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index f1f67af1d1e..ffce2d4b14f 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -1,4 +1,5 @@
= form_for @group, html: { multipart: true, class: 'gl-show-field-errors' }, authenticity_token: true do |f|
+ %input{ type: 'hidden', name: 'update_section', value: 'js-permissions-settings' }
= form_errors(@group)
%fieldset
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index c23fe0b5c49..37b56f92030 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -182,6 +182,12 @@
%tr
%td.shortcut
%kbd g
+ %kbd l
+ %td
+ Go to metrics
+ %tr
+ %td.shortcut
+ %kbd g
%kbd k
%td
Go to kubernetes
diff --git a/app/views/ide/index.html.haml b/app/views/ide/index.html.haml
index 9f8b0acd763..4cae9c51acc 100644
--- a/app/views/ide/index.html.haml
+++ b/app/views/ide/index.html.haml
@@ -1,11 +1,17 @@
- @body_class = 'ide'
- page_title 'IDE'
+- content_for :page_specific_javascripts do
+ = stylesheet_link_tag 'page_bundles/ide'
+
#ide.ide-loading{ data: {"empty-state-svg-path" => image_path('illustrations/multi_file_editor_empty.svg'),
"no-changes-state-svg-path" => image_path('illustrations/multi-editor_no_changes_empty.svg'),
"committed-state-svg-path" => image_path('illustrations/multi-editor_all_changes_committed_empty.svg'),
"pipelines-empty-state-svg-path": image_path('illustrations/pipelines_empty.svg'),
- "ci-help-page-path" => help_page_path('ci/quick_start/README'), } }
+ "promotion-svg-path": image_path('illustrations/web-ide_promotion.svg'),
+ "ci-help-page-path" => help_page_path('ci/quick_start/README'),
+ "web-ide-help-page-path" => help_page_path('user/project/web_ide/index.html'),
+ "clientside-preview-enabled": Gitlab::CurrentSettings.current_application_settings.web_ide_clientside_preview_enabled.to_s } }
.text-center
= icon('spinner spin 2x')
%h2.clgray= _('Loading the GitLab IDE...')
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index f7094375023..f0d1e837317 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -21,26 +21,16 @@
%th= _('Status')
%tbody
- @already_added_projects.each do |project|
- %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
+ %tr{ id: "project_#{project.id}", class: project_status_css_class(project.import_status) }
%td
= provider_project_link(provider, project.import_source)
%td
= link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status
- - if project.import_status == 'finished'
- %span
- %i.fa.fa-check
- = _('Done')
- - elsif project.import_status == 'started'
- %i.fa.fa-spinner.fa-spin
- = _('Started')
- - elsif project.import_status == 'failed'
- = _('Failed')
- - else
- = project.human_import_status_name
+ = render 'import/project_status', project: project
- @repos.each do |repo|
- %tr{ id: "repo_#{repo.id}" }
+ %tr{ id: "repo_#{repo.id}", data: { qa: { repo_path: repo.full_name } } }
%td
= provider_project_link(provider, repo.full_name)
%td.import-target
@@ -50,7 +40,7 @@
- if current_user.can_select_namespace?
- selected = params[:namespace_id] || :current_user
- opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner.login, path: repo.owner.login) } : {}
- = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'input-group-text select2 js-select-namespace', tabindex: 1 }
+ = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'input-group-text select2 js-select-namespace qa-project-namespace-select', tabindex: 1 }
- else
= text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
%span.input-group-prepend
@@ -61,6 +51,6 @@
= has_ci_cd_only_params? ? _('Connect') : _('Import')
= icon("spinner spin", class: "loading-icon")
-.js-importer-status{ data: { jobs_import_path: "#{url_for([:jobs, :import, provider])}",
- import_path: "#{url_for([:import, provider])}",
- ci_cd_only: "#{has_ci_cd_only_params?}" } }
+.js-importer-status{ data: { jobs_import_path: url_for([:jobs, :import, provider]),
+ import_path: url_for([:import, provider]),
+ ci_cd_only: has_ci_cd_only_params?.to_s } }
diff --git a/app/views/import/_project_status.html.haml b/app/views/import/_project_status.html.haml
new file mode 100644
index 00000000000..280bcbc1e63
--- /dev/null
+++ b/app/views/import/_project_status.html.haml
@@ -0,0 +1,11 @@
+- case project.import_status
+- when 'finished'
+ = icon('check')
+ = _('Done')
+- when 'started'
+ = icon("spinner spin")
+ = _('Started')
+- when 'failed'
+ = _('Failed')
+- else
+ = project.human_import_status_name
diff --git a/app/views/import/bitbucket/deploy_key.js.haml b/app/views/import/bitbucket/deploy_key.js.haml
index 81b34ab5c9d..99e8ac1afa1 100644
--- a/app/views/import/bitbucket/deploy_key.js.haml
+++ b/app/views/import/bitbucket/deploy_key.js.haml
@@ -1,3 +1,3 @@
:plain
job = $("tr#repo_#{@repo_id}")
- job.find(".import-actions").html("<p class='alert alert-danger'>Access denied! Please verify you can add deploy keys to this repository.</p>")
+ job.find(".import-actions").html("<p class='alert alert-danger'>#{_('Access denied! Please verify you can add deploy keys to this repository.')}</p>")
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 4e8f715db4f..a75b7aa9dd2 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -1,22 +1,22 @@
-- page_title 'Bitbucket import'
-- header_title 'Projects', root_path
+- page_title _('Bitbucket import')
+- header_title _('Projects'), root_path
%h3.page-title
%i.fa.fa-bitbucket
- Import projects from Bitbucket
+ = _('Import projects from Bitbucket')
- if @repos.any?
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%hr
%p
- if @incompatible_repos.any?
= button_tag class: 'btn btn-import btn-success js-import-all' do
- Import all compatible projects
+ = _('Import all compatible projects')
= icon('spinner spin', class: 'loading-icon')
- else
= button_tag class: 'btn btn-import btn-success js-import-all' do
- Import all projects
+ = _('Import all projects')
= icon('spinner spin', class: 'loading-icon')
.table-responsive
@@ -26,9 +26,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From Bitbucket
- %th To GitLab
- %th Status
+ %th= _('From Bitbucket')
+ %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)}" }
@@ -40,10 +40,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _('done')
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _('started')
- else
= project.human_import_status_name
@@ -66,7 +66,7 @@
= text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
%td.import-actions.job-status
= button_tag class: 'btn btn-import js-add-to-import' do
- Import
+ = _('Import')
= icon('spinner spin', class: 'loading-icon')
- @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
@@ -74,16 +74,13 @@
= link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
%td.import-target
%td.import-actions-job-status
- = label_tag 'Incompatible Project', nil, class: 'label badge-danger'
+ = label_tag _('Incompatible Project'), nil, class: 'label badge-danger'
- if @incompatible_repos.any?
%p
- One or more of your Bitbucket projects cannot be imported into GitLab
- directly because they use Subversion or Mercurial for version control,
- rather than Git. Please convert
- = link_to 'them to Git,', 'https://www.atlassian.com/git/tutorials/migrating-overview'
- and go through the
- = link_to 'import flow', status_import_bitbucket_path
- again.
+ = _("One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
+ - link_to_git = link_to(_('Git'), 'https://www.atlassian.com/git/tutorials/migrating-overview')
+ - link_to_import_flow = link_to(_('import flow'), status_import_bitbucket_path)
+ = _("Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again.").html_safe % { link_to_git: link_to_git, link_to_import_flow: link_to_import_flow }
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_path}", import_path: "#{import_bitbucket_path}" } }
diff --git a/app/views/import/bitbucket_server/new.html.haml b/app/views/import/bitbucket_server/new.html.haml
new file mode 100644
index 00000000000..ac86be8fa7a
--- /dev/null
+++ b/app/views/import/bitbucket_server/new.html.haml
@@ -0,0 +1,26 @@
+- title = _('Bitbucket Server Import')
+- page_title title
+- breadcrumb_title title
+- header_title "Projects", root_path
+
+%h3.page-title
+ = icon 'bitbucket-square', text: _('Import repositories from Bitbucket Server')
+
+%p
+ = _('Enter in your Bitbucket Server URL and personal access token below')
+
+= form_tag configure_import_bitbucket_server_path, method: :post do
+ .form-group.row
+ = label_tag :bitbucket_server_url, 'Bitbucket Server URL', class: 'col-form-label col-md-2'
+ .col-md-4
+ = text_field_tag :bitbucket_server_url, '', class: 'form-control append-right-8', placeholder: _('https://your-bitbucket-server'), size: 40
+ .form-group.row
+ = label_tag :bitbucket_server_url, 'Username', class: 'col-form-label col-md-2'
+ .col-md-4
+ = text_field_tag :bitbucket_username, '', class: 'form-control append-right-8', placeholder: _('username'), size: 40
+ .form-group.row
+ = label_tag :personal_access_token, 'Password/Personal Access Token', class: 'col-form-label col-md-2'
+ .col-md-4
+ = password_field_tag :personal_access_token, '', class: 'form-control append-right-8', placeholder: _('Personal Access Token'), size: 40
+ .form-actions
+ = submit_tag _('List your Bitbucket Server repositories'), class: 'btn btn-success'
diff --git a/app/views/import/bitbucket_server/status.html.haml b/app/views/import/bitbucket_server/status.html.haml
new file mode 100644
index 00000000000..3d05a5e696f
--- /dev/null
+++ b/app/views/import/bitbucket_server/status.html.haml
@@ -0,0 +1,87 @@
+- page_title 'Bitbucket Server import'
+- header_title 'Projects', root_path
+
+%h3.page-title
+ %i.fa.fa-bitbucket-square
+ = _('Import projects from Bitbucket Server')
+
+- if @repos.any?
+ %p.light
+ = _('Select projects you want to import.')
+ .btn-group
+ - if @incompatible_repos.any?
+ = button_tag class: 'btn btn-import btn-success js-import-all' do
+ = _('Import all compatible projects')
+ = icon('spinner spin', class: 'loading-icon')
+ - else
+ = button_tag class: 'btn btn-import btn-success js-import-all' do
+ = _('Import all projects')
+ = icon('spinner spin', class: 'loading-icon')
+ .btn-group
+ = link_to('Reconfigure', configure_import_bitbucket_server_path, class: 'btn btn-primary', method: :post)
+
+.table-responsive.prepend-top-10
+ %table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
+ %thead
+ %tr
+ %th= _('From Bitbucket Server')
+ %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, project.import_source, target: '_blank', rel: 'noopener noreferrer'
+ %td
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - if project.import_status == 'finished'
+ = icon('check', text: 'Done')
+ - elsif project.import_status == 'started'
+ = icon('spin', text: 'started')
+ - else
+ = project.human_import_status_name
+
+ - @repos.each do |repo|
+ %tr{ id: "repo_#{repo.project_key}___#{repo.slug}", data: { project: repo.project_key, repository: repo.slug } }
+ %td
+ = link_to repo.browse_url, repo.browse_url, target: '_blank', rel: 'noopener noreferrer'
+ %td.import-target
+ %fieldset.row
+ .input-group
+ .project-path.input-group-prepend
+ - if current_user.can_select_namespace?
+ - selected = params[:namespace_id] || :extra_group
+ - opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.project_key, path: repo.project_key) } : {}
+ = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'input-group-text select2 js-select-namespace', tabindex: 1 }
+ - else
+ = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
+ %span.input-group-prepend
+ .input-group-text /
+ = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
+ %td.import-actions.job-status
+ = button_tag class: 'btn btn-import js-add-to-import' do
+ Import
+ = icon('spinner spin', class: 'loading-icon')
+ - @incompatible_repos.each do |repo|
+ %tr{ id: "repo_#{repo.project_key}___#{repo.slug}" }
+ %td
+ = link_to repo.browse_url, repo.browse_url, 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 Bitbucket Server projects cannot be imported into GitLab
+ directly because they use Subversion or Mercurial for version control,
+ rather than Git. Please convert
+ = link_to 'them to Git,', 'https://www.atlassian.com/git/tutorials/migrating-overview'
+ and go through the
+ = link_to 'import flow', status_import_bitbucket_server_path
+ again.
+
+.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_server_path}", import_path: "#{import_bitbucket_server_path}" } }
diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml
index 74d686b6703..b54b1af1e0c 100644
--- a/app/views/import/fogbugz/new.html.haml
+++ b/app/views/import/fogbugz/new.html.haml
@@ -1,26 +1,24 @@
-- page_title "FogBugz Import"
-- header_title "Projects", root_path
+- page_title _("FogBugz Import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-bug
- Import projects from FogBugz
+ = _('Import projects from FogBugz')
%hr
= form_tag callback_import_fogbugz_path do
%p
- To get started you enter your FogBugz URL and login information below.
- In the next steps, you'll be able to map users and select the projects
- you want to import.
+ = _("To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import.")
.form-group.row
- = label_tag :uri, 'FogBugz URL', class: 'col-form-label col-md-2'
+ = label_tag :uri, _('FogBugz URL'), class: 'col-form-label col-md-2'
.col-md-4
= text_field_tag :uri, nil, placeholder: 'https://mycompany.fogbugz.com', class: 'form-control'
.form-group.row
- = label_tag :email, 'FogBugz Email', class: 'col-form-label col-md-2'
+ = label_tag :email, _('FogBugz Email'), class: 'col-form-label col-md-2'
.col-md-4
= text_field_tag :email, nil, class: 'form-control'
.form-group.row
- = label_tag :password, 'FogBugz Password', class: 'col-form-label col-md-2'
+ = label_tag :password, _('FogBugz Password'), class: 'col-form-label col-md-2'
.col-md-4
= password_field_tag :password, nil, class: 'form-control'
.form-actions
- = submit_tag 'Continue to the next step', class: 'btn btn-create'
+ = submit_tag _('Continue to the next step'), class: 'btn btn-create'
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
index d27c5d3c36d..ff2f989c509 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -1,39 +1,33 @@
-- page_title 'User map', 'FogBugz import'
-- header_title "Projects", root_path
+- page_title _('User map'), _('FogBugz import')
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-bug
- Import projects from FogBugz
+ = _('Import projects from FogBugz')
%hr
= form_tag create_user_map_import_fogbugz_path do
%p
- 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.
+ = _("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.")
%p
- 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.
+ = _("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.")
%ul
%li
- %strong Default: Map a FogBugz account ID to a full name
+ %strong= _("Default: Map a FogBugz account ID to a full name")
%p
- An empty GitLab User field will add the FogBugz user's full name
- (e.g. "By John Smith") in the description of all issues and comments.
- It will also associate and/or assign these issues and comments with
- the project creator.
+ = _("An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator.")
%li
- %strong Map a FogBugz account ID to a GitLab user
+ %strong= _("Map a FogBugz account ID to a GitLab user")
%p
- Selecting a GitLab user will add a link to the GitLab user in the descriptions
- of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also
- associate and/or assign these issues and comments with the selected user.
+ = _('Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also associate and/or assign these issues and comments with the selected user.').html_safe
.table-holder
%table.table
%thead
%tr
- %th ID
- %th Name
- %th Email
- %th GitLab User
+ %th= _("ID")
+ %th= _("Name")
+ %th= _("Email")
+ %th= _("GitLab User")
%tbody
- @user_map.each do |id, user|
%tr
@@ -45,4 +39,4 @@
scope: :all, email_user: true, selected: user[:gitlab_user])
.form-actions
- = submit_tag 'Continue to the next step', class: 'btn btn-create'
+ = submit_tag _('Continue to the next step'), class: 'btn btn-create'
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index 7b832c6a23a..830d141ebea 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -1,20 +1,19 @@
-- page_title "FogBugz import"
-- header_title "Projects", root_path
+- page_title _("FogBugz import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-bug
- Import projects from FogBugz
+ = _('Import projects from FogBugz')
- if @repos.any?
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%p.light
- Optionally, you can
- = link_to 'customize', new_user_map_import_fogbugz_path
- how FogBugz email addresses and usernames are imported into GitLab.
+ - link_to_customize = link_to('customize', new_user_map_import_fogbugz_path)
+ = _('Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab.').html_safe % { link_to_customize: link_to_customize }
%hr
%p
= button_tag class: 'btn btn-import btn-success js-import-all' do
- Import all projects
+ = _('Import all projects')
= icon("spinner spin", class: "loading-icon")
.table-responsive
@@ -24,9 +23,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From FogBugz
- %th To GitLab
- %th Status
+ %th= _("From FogBugz")
+ %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)}" }
@@ -38,10 +37,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _("done")
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _("started")
- else
= project.human_import_status_name
@@ -53,7 +52,7 @@
#{current_user.username}/#{repo.name}
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
- Import
+ = _("Import")
= icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_fogbugz_path}", import_path: "#{import_fogbugz_path}" } }
diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml
index 581576a8a3d..2b3102f9af9 100644
--- a/app/views/import/gitea/new.html.haml
+++ b/app/views/import/gitea/new.html.haml
@@ -1,23 +1,22 @@
-- page_title "Gitea Import"
-- header_title "Projects", root_path
+- page_title _("Gitea Import")
+- header_title _("Projects"), root_path
%h3.page-title
= custom_icon('go_logo')
- Import Projects from Gitea
+ = _('Import Projects from Gitea')
%p
- To get started, please enter your Gitea Host URL and a
- = succeed '.' do
- = link_to 'Personal Access Token', 'https://github.com/gogits/go-gogs-client/wiki#access-token'
+ - link_to_personal_token = link_to(_('Personal Access Token'), 'https://github.com/gogits/go-gogs-client/wiki#access-token')
+ = _('To get started, please enter your Gitea Host URL and a %{link_to_personal_token}.').html_safe % { link_to_personal_token: link_to_personal_token }
= form_tag personal_access_token_import_gitea_path do
.form-group.row
- = label_tag :gitea_host_url, 'Gitea Host URL', class: 'col-form-label col-sm-2'
+ = label_tag :gitea_host_url, _('Gitea Host URL'), class: 'col-form-label col-sm-2'
.col-sm-4
= text_field_tag :gitea_host_url, nil, placeholder: 'https://try.gitea.io', class: 'form-control'
.form-group.row
- = label_tag :personal_access_token, 'Personal Access Token', class: 'col-form-label col-sm-2'
+ = label_tag :personal_access_token, _('Personal Access Token'), class: 'col-form-label col-sm-2'
.col-sm-4
= text_field_tag :personal_access_token, nil, class: 'form-control'
.form-actions
- = submit_tag 'List Your Gitea Repositories', class: 'btn btn-create'
+ = submit_tag _('List Your Gitea Repositories'), class: 'btn btn-create'
diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml
index 589ca27e45d..88244fde16b 100644
--- a/app/views/import/gitea/status.html.haml
+++ b/app/views/import/gitea/status.html.haml
@@ -1,7 +1,7 @@
-- page_title "Gitea Import"
-- header_title "Projects", root_path
+- page_title _("Gitea Import")
+- header_title _("Projects"), root_path
%h3.page-title
= custom_icon('go_logo')
- Import Projects from Gitea
+ = _('Import Projects from Gitea')
= render 'import/githubish_status', provider: 'gitea'
diff --git a/app/views/import/github/new.html.haml b/app/views/import/github/new.html.haml
index b9ebb1a39d9..6ff25f2c842 100644
--- a/app/views/import/github/new.html.haml
+++ b/app/views/import/github/new.html.haml
@@ -1,7 +1,7 @@
- title = has_ci_cd_only_params? ? _('Connect repositories from GitHub') : _('GitHub import')
- page_title title
- breadcrumb_title title
-- header_title "Projects", root_path
+- header_title _("Projects"), root_path
%h3.page-title
= icon 'github', text: import_github_title
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index b00b972d9c9..be057be6d1a 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -1,7 +1,7 @@
- title = has_ci_cd_only_params? ? _('Connect repositories from GitHub') : _('GitHub import')
- page_title title
- breadcrumb_title title
-- header_title "Projects", root_path
+- header_title _("Projects"), root_path
%h3.page-title
= icon 'github', text: import_github_title
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index 37734414835..b7bfbae5edf 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -1,15 +1,15 @@
-- page_title "GitLab.com import"
-- header_title "Projects", root_path
+- page_title _("GitLab.com import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-heart
- Import projects from GitLab.com
+ = _('Import projects from GitLab.com')
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%hr
%p
= button_tag class: "btn btn-import btn-success js-import-all" do
- Import all projects
+ = _('Import all projects')
= icon("spinner spin", class: "loading-icon")
.table-responsive
@@ -19,9 +19,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From GitLab.com
- %th To this GitLab instance
- %th Status
+ %th= _('From GitLab.com')
+ %th= _('To this GitLab instance')
+ %th= _('Status')
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
@@ -33,10 +33,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _('done')
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _('started')
- else
= project.human_import_status_name
@@ -48,7 +48,7 @@
= import_project_target(repo['namespace']['path'], repo['name'])
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
- Import
+ = _('Import')
= icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitlab_path}", import_path: "#{import_gitlab_path}" } }
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index cc672a5ea7c..4225ee19217 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -1,15 +1,15 @@
-- page_title "GitLab Import"
-- header_title "Projects", root_path
+- page_title _("GitLab Import")
+- header_title _("Projects"), root_path
%h3.page-title
= icon('gitlab')
- Import an exported GitLab project
+ = _('Import an exported GitLab project')
%hr
= form_tag import_gitlab_project_path, class: 'new_project', multipart: true do
.row
.form-group.col-12.col-sm-6
- = label_tag :namespace_id, 'Project path', class: 'label-light'
+ = label_tag :namespace_id, 'Project path', class: 'label-bold'
.form-group
.input-group
- if current_user.can_select_namespace?
@@ -24,19 +24,19 @@
#{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-12.col-sm-6.project-path
- = label_tag :path, 'Project name', class: 'label-light'
+ = label_tag :path, _('Project name'), class: 'label-bold'
= text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true
.row
.form-group.col-md-12
- To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here.
+ = _("To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here.")
.row
.form-group.col-sm-12
= hidden_field_tag :namespace_id, @namespace.id
- = label_tag :file, 'GitLab project export', class: 'label-light'
+ = label_tag :file, _('GitLab project export'), class: 'label-bold'
.form-group
= file_field_tag :file, class: ''
.row
.form-actions.col-sm-12
- = submit_tag 'Import project', class: 'btn btn-create'
- = link_to 'Cancel', new_project_path, class: 'btn btn-cancel'
+ = submit_tag _('Import project'), class: 'btn btn-create'
+ = link_to _('Cancel'), new_project_path, class: 'btn btn-cancel'
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
index 2f1fb8d9c56..fd6e4726fc5 100644
--- a/app/views/import/google_code/new.html.haml
+++ b/app/views/import/google_code/new.html.haml
@@ -1,62 +1,62 @@
-- page_title "Google Code import"
-- header_title "Projects", root_path
+- page_title _("Google Code import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-google
- Import projects from Google Code
+ = _('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.
+ = _('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
- Go to
- #{link_to "Google Takeout", "https://www.google.com/settings/takeout", target: '_blank', rel: 'noopener noreferrer'}.
+ - 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.
+ = _("Make sure you're logged into the account that owns the projects you'd like to import.")
%li
%p
- Click the <strong>Select none</strong> button on the right, since we only need "Google Code Project Hosting".
+ = _('Click the <strong>Select none</strong> button on the right, since we only need "Google Code Project Hosting".').html_safe
%li
%p
- Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right.
+ = _('Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right.').html_safe
%li
%p
- Choose <strong>Next</strong> at the bottom of the page.
+ = _('Choose <strong>Next</strong> at the bottom of the page.').html_safe
%li
%p
- Leave the "File type" and "Delivery method" options on their default values.
+ = _('Leave the "File type" and "Delivery method" options on their default values.')
%li
%p
- Choose <strong>Create archive</strong> and wait for archiving to complete.
+ = _('Choose <strong>Create archive</strong> and wait for archiving to complete.').html_safe
%li
%p
- Click the <strong>Download</strong> button and wait for downloading to complete.
+ = _('Click the <strong>Download</strong> button and wait for downloading to complete.').html_safe
%li
%p
- Find the downloaded ZIP file and decompress it.
+ = _('Find the downloaded ZIP file and decompress it.')
%li
%p
- Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file.
+ = _('Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file.').html_safe
%li
%p
- Upload <code>GoogleCodeProjectHosting.json</code> here:
+ = _('Upload <code>GoogleCodeProjectHosting.json</code> here:').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?
+ = _('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.
+ = _('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.
+ = _('Yes, let me map Google Code users to full names or GitLab users.')
%li
%p
- = submit_tag 'Continue to the next step', class: "btn btn-create"
+ = submit_tag _('Continue to the next step'), class: "btn btn-create"
diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml
index 91c774f575c..baaaf6bdc63 100644
--- a/app/views/import/google_code/new_user_map.html.haml
+++ b/app/views/import/google_code/new_user_map.html.haml
@@ -1,44 +1,36 @@
-- page_title "User map", "Google Code import"
-- header_title "Projects", root_path
+- page_title _("User map"), _("Google Code import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-google
- Import projects from Google Code
+ = _('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.
+ = _("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
- 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>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side.
+ = _("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>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side.").html_safe
%ul
%li
- %strong Default: Directly import the Google Code email address or username
+ %strong= _("Default: Directly import the Google Code email address or username")
%p
- <code>"johnsmith@example.com": "johnsm...@example.com"</code>
- 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>"johnsmith@example.com": "johnsm...@example.com"</code> 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.').html_safe
%li
- %strong Map a Google Code user to a GitLab user
+ %strong= _("Map a Google Code user to a GitLab user")
%p
- <code>"johnsmith@example.com": "@johnsmith"</code>
- will add "By <a href="#">@johnsmith</a>" to all issues and comments originally created by johnsmith@example.com,
- and will set <a href="#">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com.
+ = _('<code>"johnsmith@example.com": "@johnsmith"</code> will add "By <a href="#">@johnsmith</a>" to all issues and comments originally created by johnsmith@example.com, and will set <a href="#">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com.').html_safe
%li
- %strong Map a Google Code user to a full name
+ %strong= _("Map a Google Code user to a full name")
%p
- <code>"johnsmith@example.com": "John Smith"</code>
- will add "By John Smith" to all issues and comments originally created by johnsmith@example.com.
+ = _('<code>"johnsmith@example.com": "John Smith"</code> will add "By John Smith" to all issues and comments originally created by johnsmith@example.com.').html_safe
%li
- %strong Map a Google Code user to a full email address
+ %strong= _("Map a Google Code user to a full email address")
%p
- <code>"johnsmith@example.com": "johnsmith@example.com"</code>
- will add "By <a href="#">johnsmith@example.com</a>" 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>"johnsmith@example.com": "johnsmith@example.com"</code> will add "By <a href="#">johnsmith@example.com</a>" 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.').html_safe
.form-group.row
.col-sm-12
= text_area_tag :user_map, JSON.pretty_generate(@user_map), class: 'form-control', rows: 15
.form-actions
- = submit_tag 'Continue to the next step', class: "btn btn-create"
+ = submit_tag _('Continue to the next step'), class: "btn btn-create"
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index acf7a108cb0..347e2820f94 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -1,25 +1,24 @@
-- page_title "Google Code import"
-- header_title "Projects", root_path
+- page_title _("Google Code import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-google
- Import projects from Google Code
+ = _('Import projects from Google Code')
- if @repos.any?
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%p.light
- Optionally, you can
- = link_to "customize", new_user_map_import_google_code_path
- how Google Code email addresses and usernames are imported into GitLab.
+ - 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
+ = _("Import all compatible projects")
= icon("spinner spin", class: "loading-icon")
- else
= button_tag class: "btn btn-import btn-success js-import-all" do
- Import all projects
+ = _("Import all projects")
= icon("spinner spin", class: "loading-icon")
.table-responsive
@@ -29,9 +28,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From Google Code
- %th To GitLab
- %th Status
+ %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)}" }
@@ -43,10 +42,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _("done")
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _("started")
- else
= project.human_import_status_name
@@ -58,7 +57,7 @@
#{current_user.username}/#{repo.name}
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
- Import
+ = _("Import")
= icon("spinner spin", class: "loading-icon")
- @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.id}" }
@@ -66,15 +65,12 @@
= 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"
+ = 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. Please convert them to Git on Google Code, and go
- through the
- = link_to "import flow", new_import_google_code_path
- again.
+ = _("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
new file mode 100644
index 00000000000..78c7fadb019
--- /dev/null
+++ b/app/views/import/manifest/_form.html.haml
@@ -0,0 +1,23 @@
+= form_tag upload_import_manifest_path, multipart: true do
+ .form-group
+ = label_tag :group_id, nil, class: 'label-bold' do
+ = _('Group')
+ .input-group
+ .input-group-prepend.has-tooltip{ title: root_url }
+ .input-group-text
+ = root_url
+ = select_tag :group_id, namespaces_options(nil, display_path: true, groups_only: true), { class: 'select2 js-select-namespace' }
+ .form-text.text-muted
+ = _('Choose the top-level group for your repository imports.')
+
+ .form-group
+ = label_tag :manifest, class: 'label-bold' do
+ = _('Manifest')
+ = file_field_tag :manifest, class: 'form-control-file', required: true
+ .form-text.text-muted
+ = _('Import multiple repositories by uploading a manifest file.')
+ = link_to icon('question-circle'), help_page_path('user/project/import/manifest')
+
+ .append-bottom-10
+ = submit_tag _('List available repositories'), class: 'btn btn-success'
+ = link_to _('Cancel'), new_project_path, class: 'btn btn-cancel'
diff --git a/app/views/import/manifest/new.html.haml b/app/views/import/manifest/new.html.haml
new file mode 100644
index 00000000000..056e4922b9e
--- /dev/null
+++ b/app/views/import/manifest/new.html.haml
@@ -0,0 +1,12 @@
+- page_title "Manifest file import"
+- header_title "Projects", root_path
+
+%h3.page-title
+ = _('Manifest file import')
+
+- if @errors.present?
+ .alert.alert-danger
+ - @errors.each do |error|
+ = error
+
+= render 'form'
diff --git a/app/views/import/manifest/status.html.haml b/app/views/import/manifest/status.html.haml
new file mode 100644
index 00000000000..5b2e1005398
--- /dev/null
+++ b/app/views/import/manifest/status.html.haml
@@ -0,0 +1,42 @@
+- page_title "Manifest import"
+- header_title "Projects", root_path
+- provider = 'manifest'
+
+%h3.page-title
+ = _('Manifest file import')
+
+%p
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ = import_all_githubish_repositories_button_label
+ = icon("spinner spin", class: "loading-icon")
+
+.table-responsive
+ %table.table.import-jobs
+ %thead
+ %tr
+ %th= _('Repository URL')
+ %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
+ = project.import_url
+ %td
+ = link_to_project project
+ %td.job-status
+ = render 'import/project_status', project: project
+
+ - @pending_repositories.each do |repository|
+ %tr{ id: "repo_#{repository[:id]}" }
+ %td
+ = repository[:url]
+ %td.import-target
+ = import_project_target(@group.full_path, repository[:path])
+ %td.import-actions.job-status
+ = button_tag class: "btn btn-import js-add-to-import" do
+ = _('Import')
+ = icon("spinner spin", class: "loading-icon")
+
+.js-importer-status{ data: { jobs_import_path: url_for([:jobs, :import, provider]),
+ import_path: url_for([:import, provider]) } }
diff --git a/app/views/admin/cohorts/_cohorts_table.html.haml b/app/views/instance_statistics/cohorts/_cohorts_table.html.haml
index 701a4e62b39..701a4e62b39 100644
--- a/app/views/admin/cohorts/_cohorts_table.html.haml
+++ b/app/views/instance_statistics/cohorts/_cohorts_table.html.haml
diff --git a/app/views/admin/cohorts/_usage_ping.html.haml b/app/views/instance_statistics/cohorts/_usage_ping.html.haml
index 3dda386fcf7..3dda386fcf7 100644
--- a/app/views/admin/cohorts/_usage_ping.html.haml
+++ b/app/views/instance_statistics/cohorts/_usage_ping.html.haml
diff --git a/app/views/admin/cohorts/index.html.haml b/app/views/instance_statistics/cohorts/index.html.haml
index 5e9a8c083af..5e9a8c083af 100644
--- a/app/views/admin/cohorts/index.html.haml
+++ b/app/views/instance_statistics/cohorts/index.html.haml
diff --git a/app/views/admin/conversational_development_index/_callout.html.haml b/app/views/instance_statistics/conversational_development_index/_callout.html.haml
index 33a4dab1e00..33a4dab1e00 100644
--- a/app/views/admin/conversational_development_index/_callout.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/_callout.html.haml
diff --git a/app/views/admin/conversational_development_index/_card.html.haml b/app/views/instance_statistics/conversational_development_index/_card.html.haml
index 57eda06630b..57eda06630b 100644
--- a/app/views/admin/conversational_development_index/_card.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/_card.html.haml
diff --git a/app/views/admin/conversational_development_index/_disabled.html.haml b/app/views/instance_statistics/conversational_development_index/_disabled.html.haml
index 0a741b50960..0a741b50960 100644
--- a/app/views/admin/conversational_development_index/_disabled.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/_disabled.html.haml
diff --git a/app/views/admin/conversational_development_index/_no_data.html.haml b/app/views/instance_statistics/conversational_development_index/_no_data.html.haml
index d69c46194b4..d69c46194b4 100644
--- a/app/views/admin/conversational_development_index/_no_data.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/_no_data.html.haml
diff --git a/app/views/admin/conversational_development_index/show.html.haml b/app/views/instance_statistics/conversational_development_index/index.html.haml
index e3d1aa31dc2..e3d1aa31dc2 100644
--- a/app/views/admin/conversational_development_index/show.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/index.html.haml
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 9253a0652da..ac5916d129c 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -3,6 +3,11 @@
- site_name = "GitLab"
%head{ prefix: "og: http://ogp.me/ns#" }
%meta{ charset: "utf-8" }
+
+ - if Feature.enabled?('asset_host_prefetch') && ActionController::Base.asset_host
+ %link{ rel: 'dns-prefetch', href: ActionController::Base.asset_host }
+ %link{ rel: 'preconnnect', href: ActionController::Base.asset_host, crossorigin: '' }
+
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
-# Open Graph - http://ogp.me/
diff --git a/app/views/layouts/_recaptcha_verification.html.haml b/app/views/layouts/_recaptcha_verification.html.haml
index e6f87ddd383..3405fb9d5ef 100644
--- a/app/views/layouts/_recaptcha_verification.html.haml
+++ b/app/views/layouts/_recaptcha_verification.html.haml
@@ -1,10 +1,10 @@
- humanized_resource_name = spammable.class.model_name.human.downcase
%h3.page-title
- Anti-spam verification
+ = _('Anti-spam verification')
%hr
%p
- #{"We detected potential spam in the #{humanized_resource_name}. Please solve the reCAPTCHA to proceed."}
+ = _("We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed.") % { humanized_resource_name: humanized_resource_name }
= render 'shared/recaptcha_form', spammable: spammable
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 91f796adb2e..9a7a67cfa83 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -1,33 +1,31 @@
- if controller.controller_path =~ /^groups/ && @group.persisted?
- - label = 'This group'
+ - label = _('This group')
- if controller.controller_path =~ /^projects/ && @project.persisted?
- - label = 'This project'
+ - label = _('This project')
- if @group && @group.persisted? && @group.path
- group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) }
- if @project && @project.persisted?
- project_data_attrs = { project_path: j(@project.path), name: j(@project.name), issues_path: project_issues_path(@project), mr_path: project_merge_requests_path(@project), issues_disabled: !@project.issues_enabled? }
-.search.search-form{ class: "#{'has-location-badge' if label.present?}" }
+.search.search-form
= form_tag search_path, method: :get, class: 'form-inline' do |f|
.search-input-container
- - if label.present?
- .location-badge= label
.search-input-wrap
.dropdown{ data: { url: search_autocomplete_path } }
- = search_field_tag 'search', nil, placeholder: 'Search',
+ = search_field_tag 'search', nil, placeholder: _('Search or jump to…'),
class: 'search-input dropdown-menu-toggle no-outline js-search-dashboard-options',
spellcheck: false,
tabindex: '1',
autocomplete: 'off',
data: { issues_path: issues_dashboard_path,
mr_path: merge_requests_dashboard_path },
- aria: { label: 'Search' }
+ aria: { label: _('Search or jump to…') }
%button.hidden.js-dropdown-search-toggle{ type: 'button', data: { toggle: 'dropdown' } }
.dropdown-menu.dropdown-select
= dropdown_content do
%ul
%li.dropdown-menu-empty-item
%a
- Loading...
+ = _('Loading...')
= dropdown_loading
= sprite_icon('search', size: 16, css_class: 'search-icon')
= sprite_icon('close', size: 16, css_class: 'clear-icon js-clear-input')
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index 8595157a997..31259b8ac25 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,5 +1,5 @@
-- page_title "Admin Area"
-- header_title "Admin Area", admin_root_path
+- page_title _("Admin Area")
+- header_title _("Admin Area"), admin_root_path
- nav "admin"
- @left_sidebar = true
diff --git a/app/views/layouts/dashboard.html.haml b/app/views/layouts/dashboard.html.haml
index cb96bcc2cf4..489ef245a4d 100644
--- a/app/views/layouts/dashboard.html.haml
+++ b/app/views/layouts/dashboard.html.haml
@@ -1,5 +1,5 @@
-- page_title "Dashboard"
-- header_title "Dashboard", root_path unless header_title
+- page_title _("Dashboard")
+- header_title _("Dashboard"), root_path unless header_title
- sidebar "dashboard"
= render template: "layouts/application"
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 81f35615555..43bd07679ba 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -17,12 +17,11 @@
- if current_appearance&.description?
= brand_text
- else
- %h3 Open source software to collaborate on code
+ %h3
+ = _('Open source software to collaborate on code')
%p
- Manage Git repositories with fine-grained access controls that keep your code secure.
- Perform code reviews and enhance collaboration with merge requests.
- Each project can also have an issue tracker and a wiki.
+ = _('Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki.')
- if Gitlab::CurrentSettings.sign_in_text.present?
= markdown_field(Gitlab::CurrentSettings.current_application_settings, :sign_in_text)
@@ -32,6 +31,6 @@
%hr.footer-fixed
.container.footer-container
.footer-links
- = link_to "Explore", explore_root_path
- = link_to "Help", help_path
- = link_to "About GitLab", "https://about.gitlab.com/"
+ = link_to _("Explore"), explore_root_path
+ = link_to _("Help"), help_path
+ = link_to _("About GitLab"), "https://about.gitlab.com/"
diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml
index 52805e0da73..663e5b24368 100644
--- a/app/views/layouts/devise_empty.html.haml
+++ b/app/views/layouts/devise_empty.html.haml
@@ -12,6 +12,6 @@
%hr
.container
.footer-links
- = link_to "Explore", explore_root_path
- = link_to "Help", help_path
- = link_to "About GitLab", "https://about.gitlab.com/"
+ = link_to _("Explore"), explore_root_path
+ = link_to _("Help"), help_path
+ = link_to _("About GitLab"), "https://about.gitlab.com/"
diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml
index df65792be73..2ab9e55441b 100644
--- a/app/views/layouts/explore.html.haml
+++ b/app/views/layouts/explore.html.haml
@@ -1,5 +1,5 @@
-- page_title "Explore"
+- page_title = _("Explore")
- unless current_user
- - header_title "Explore GitLab", explore_root_path
+ - header_title = _("Explore GitLab"), explore_root_path
= render template: "layouts/application"
diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml
index 66b115e36de..14c5f0ce04c 100644
--- a/app/views/layouts/group_settings.html.haml
+++ b/app/views/layouts/group_settings.html.haml
@@ -1,4 +1,4 @@
-- page_title "Settings"
+- page_title = _("Settings")
- nav "group"
= render template: "layouts/group"
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index a74ea246eaf..9ed05d6e3d0 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -17,11 +17,7 @@
= link_to _("Help"), help_path
- if current_user_menu?(:help) || current_user_menu?(:settings) || current_user_menu?(:profile)
%li.divider
- %li
- = link_to "https://about.gitlab.com/contributing", target: '_blank', class: 'text-nowrap' do
- = _("Contribute to GitLab")
- = sprite_icon('external-link', size: 16)
- %li.divider
+ = render 'shared/user_dropdown_contributing_link'
- if current_user_menu?(:sign_out)
%li
= link_to _("Sign out"), destroy_user_session_path, class: "sign-out-link"
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 5cec443e969..e8d31992149 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -4,7 +4,7 @@
.header-content
.title-container
%h1.title
- = link_to root_path, title: 'Dashboard', id: 'logo' do
+ = link_to root_path, title: _('Dashboard'), id: 'logo' do
= brand_header_logo
- logo_text = brand_header_logo_type
- if logo_text.present?
@@ -21,29 +21,29 @@
- if current_user
= render 'layouts/header/new_dropdown'
- if header_link?(:search)
- %li.nav-item.d-none.d-sm-none.d-md-block
+ %li.nav-item.d-none.d-sm-none.d-md-block.m-auto
= render 'layouts/search' unless current_controller?(:search)
%li.nav-item.d-inline-block.d-sm-none.d-md-none
- = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to search_path, title: _('Search'), aria: { label: _("Search") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('search', size: 16)
- if header_link?(:issues)
= nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do
- = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues', aria: { label: _("Issues") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('issues', size: 16)
- issues_count = assigned_issuables_count(:issues)
%span.badge.badge-pill.issues-count{ class: ('hidden' if issues_count.zero?) }
= number_with_delimiter(issues_count)
- if header_link?(:merge_requests)
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do
- = link_to assigned_mrs_dashboard_path, title: 'Merge requests', class: 'dashboard-shortcuts-merge_requests', aria: { label: "Merge requests" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to assigned_mrs_dashboard_path, title: _('Merge requests'), class: 'dashboard-shortcuts-merge_requests', aria: { label: _("Merge requests") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('git-merge', size: 16)
- merge_requests_count = assigned_issuables_count(:merge_requests)
%span.badge.badge-pill.merge-requests-count{ class: ('hidden' if merge_requests_count.zero?) }
= number_with_delimiter(merge_requests_count)
- if header_link?(:todos)
= nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
- = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to dashboard_todos_path, title: _('Todos'), aria: { label: _("Todos") }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('todo-done', size: 16)
%span.badge.badge-pill.todos-count{ class: ('hidden' if todos_pending_count.zero?) }
= todos_count_format(todos_pending_count)
@@ -56,14 +56,16 @@
= render 'layouts/header/current_user_dropdown'
- if header_link?(:admin_impersonation)
%li.nav-item.impersonation
- = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: "Stop impersonation", aria: { label: 'Stop impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
+ = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: _("Stop impersonation"), aria: { label: _('Stop impersonation') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('user-secret')
- if header_link?(:sign_in)
%li.nav-item
%div
- = link_to "Sign in / Register", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
+ - sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in')
+ = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
+
%button.navbar-toggler.d-block.d-sm-none{ type: 'button' }
- %span.sr-only Toggle navigation
- = sprite_icon('more', size: 12, css_class: 'more-icon js-navbar-toggle-right')
+ %span.sr-only= _("Toggle navigation")
+ = sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon js-navbar-toggle-right')
= sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml
index d35df706036..e134f416c70 100644
--- a/app/views/layouts/header/_new_dropdown.haml
+++ b/app/views/layouts/header/_new_dropdown.haml
@@ -1,5 +1,5 @@
%li.header-new.dropdown
- = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do
+ = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: _("New..."), ref: 'tooltip', aria: { label: _("New...") }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do
= sprite_icon('plus-square', size: 16)
= sprite_icon('angle-down', css_class: 'caret-down')
.dropdown-menu.dropdown-menu-right
@@ -8,13 +8,14 @@
- create_group_project = can?(current_user, :create_projects, @group)
- create_group_subgroup = can?(current_user, :create_subgroup, @group)
- if create_group_project || create_group_subgroup
- %li.dropdown-bold-header This group
+ %li.dropdown-bold-header
+ = _('This group')
- if create_group_project
%li.header-new-group-project
- = link_to 'New project', new_project_path(namespace_id: @group.id)
+ = link_to _('New project'), new_project_path(namespace_id: @group.id)
- if create_group_subgroup
%li
- = link_to 'New subgroup', new_group_path(parent_id: @group.id)
+ = link_to _('New subgroup'), new_group_path(parent_id: @group.id)
%li.divider
%li.dropdown-bold-header GitLab
@@ -23,23 +24,24 @@
- merge_project = merge_request_source_project_for_project(@project)
- create_project_snippet = can?(current_user, :create_project_snippet, @project)
- if create_project_issue || merge_project || create_project_snippet
- %li.dropdown-bold-header This project
+ %li.dropdown-bold-header
+ = _('This project')
- if create_project_issue
%li
- = link_to 'New issue', new_project_issue_path(@project)
+ = link_to _('New issue'), new_project_issue_path(@project)
- if merge_project
%li
- = link_to 'New merge request', project_new_merge_request_path(merge_project)
+ = link_to _('New merge request'), project_new_merge_request_path(merge_project)
- if create_project_snippet
%li.header-new-project-snippet
- = link_to 'New snippet', new_project_snippet_path(@project)
+ = link_to _('New snippet'), new_project_snippet_path(@project)
%li.divider
%li.dropdown-bold-header GitLab
- if current_user.can_create_project?
%li
- = link_to 'New project', new_project_path
+ = link_to _('New project'), new_project_path, class: 'qa-global-new-project-link'
- if current_user.can_create_group?
%li
- = link_to 'New group', new_group_path
+ = link_to _('New group'), new_group_path
%li
- = link_to 'New snippet', new_snippet_path
+ = link_to _('New snippet'), new_snippet_path
diff --git a/app/views/layouts/help.html.haml b/app/views/layouts/help.html.haml
index 78927bfffcd..a913bea0c93 100644
--- a/app/views/layouts/help.html.haml
+++ b/app/views/layouts/help.html.haml
@@ -1,5 +1,5 @@
-- @breadcrumb_title = "Help"
-- page_title "Help"
-- header_title "Help", help_path
+- @breadcrumb_title = _("Help")
+- page_title _("Help")
+- header_title _("Help"), help_path
= render template: "layouts/application"
diff --git a/app/views/layouts/instance_statistics.html.haml b/app/views/layouts/instance_statistics.html.haml
new file mode 100644
index 00000000000..bebd9c4536f
--- /dev/null
+++ b/app/views/layouts/instance_statistics.html.haml
@@ -0,0 +1,6 @@
+- page_title _('Instance Statistics')
+- header_title _('Instance Statistics'), instance_statistics_root_path
+- nav 'instance_statistics'
+- @left_sidebar = true
+
+= render template: 'layouts/application'
diff --git a/app/views/layouts/koding.html.haml b/app/views/layouts/koding.html.haml
index 22319bba745..45ccd38f687 100644
--- a/app/views/layouts/koding.html.haml
+++ b/app/views/layouts/koding.html.haml
@@ -1,5 +1,5 @@
-- page_title "Koding"
-- page_description "Koding Dashboard"
-- header_title "Koding", koding_path
+- page_title _("Koding")
+- page_description _("Koding Dashboard")
+- header_title _("Koding"), koding_path
= render template: "layouts/application"
diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb
index 8e20c4a4b2a..8e11174f8d7 100644
--- a/app/views/layouts/mailer.text.erb
+++ b/app/views/layouts/mailer.text.erb
@@ -1,4 +1,4 @@
<%= yield -%>
-- <%# signature marker %>
-You're receiving this email because of your account on <%= Gitlab.config.gitlab.host %>.
+<%= _("You're receiving this email because of your account on %{host}.") % { host: Gitlab.config.gitlab.host } %>
diff --git a/app/views/layouts/mailer/devise.html.haml b/app/views/layouts/mailer/devise.html.haml
index 054b2c2fa26..beaaaa5cd68 100644
--- a/app/views/layouts/mailer/devise.html.haml
+++ b/app/views/layouts/mailer/devise.html.haml
@@ -3,17 +3,17 @@
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:13px;line-height:1.6;color:#5c5c5c;" }
%div
- Everyone can contribute
+ = _('Everyone can contribute')
%div
- = link_to 'Blog', 'https://about.gitlab.com/blog/', style: "color:#3777b0;text-decoration:none;"
+ = link_to _('Blog'), 'https://about.gitlab.com/blog/', style: "color:#3777b0;text-decoration:none;"
&middot;
- = link_to 'Twitter', 'https://twitter.com/gitlab', style: "color:#3777b0;text-decoration:none;"
+ = link_to _('Twitter'), 'https://twitter.com/gitlab', style: "color:#3777b0;text-decoration:none;"
&middot;
- = link_to 'Facebook', 'https://www.facebook.com/gitlab/', style: "color:#3777b0;text-decoration:none;"
+ = link_to _('Facebook'), 'https://www.facebook.com/gitlab/', style: "color:#3777b0;text-decoration:none;"
&middot;
- = link_to 'YouTube', 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg', style: "color:#3777b0;text-decoration:none;"
+ = link_to _('YouTube'), 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg', style: "color:#3777b0;text-decoration:none;"
&middot;
- = link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com', style: "color:#3777b0;text-decoration:none;"
+ = link_to _('LinkedIn'), 'https://www.linkedin.com/company/gitlab-com', style: "color:#3777b0;text-decoration:none;"
= render layout: 'layouts/mailer' do
%tr
diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml
index 002922e13f1..f53bd2b5e4d 100644
--- a/app/views/layouts/nav/_breadcrumbs.html.haml
+++ b/app/views/layouts/nav/_breadcrumbs.html.haml
@@ -5,7 +5,7 @@
.breadcrumbs-container
- if defined?(@left_sidebar)
= button_tag class: 'toggle-mobile-nav', type: 'button' do
- %span.sr-only Open sidebar
+ %span.sr-only= _("Open sidebar")
= icon ('bars')
.breadcrumbs-links.js-title-container
%ul.list-unstyled.breadcrumbs-list.js-breadcrumbs-list
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 7647e25e804..5e467c862ab 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,79 +1,81 @@
%ul.list-unstyled.navbar-sub-nav
- if dashboard_nav_link?(:projects)
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects qa-projects-dropdown" }) do
- %a{ href: "#", data: { toggle: "dropdown" } }
- Projects
+ %button{ type: 'button', data: { toggle: "dropdown" } }
+ = _('Projects')
= sprite_icon('angle-down', css_class: 'caret-down')
- .dropdown-menu.projects-dropdown-menu
+ .dropdown-menu.frequent-items-dropdown-menu
= render "layouts/nav/projects_dropdown/show"
- if dashboard_nav_link?(:groups)
- = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { class: "d-none d-sm-block" }) do
- = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups qa-groups-link', title: 'Groups' do
- Groups
+ = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { id: 'nav-groups-dropdown', class: "home dropdown header-groups qa-groups-dropdown" }) do
+ %button{ type: 'button', data: { toggle: "dropdown" } }
+ = _('Groups')
+ = sprite_icon('angle-down', css_class: 'caret-down')
+ .dropdown-menu.frequent-items-dropdown-menu
+ = render "layouts/nav/groups_dropdown/show"
- if dashboard_nav_link?(:activity)
= nav_link(path: 'dashboard#activity', html_options: { class: "d-none d-lg-block d-xl-block" }) do
- = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
- Activity
+ = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: _('Activity') do
+ = _('Activity')
- if dashboard_nav_link?(:milestones)
= nav_link(controller: 'dashboard/milestones', html_options: { class: "d-none d-lg-block d-xl-block" }) do
- = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
- Milestones
+ = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: _('Milestones') do
+ = _('Milestones')
- if dashboard_nav_link?(:snippets)
= nav_link(controller: 'dashboard/snippets', html_options: { class: "d-none d-lg-block d-xl-block" }) do
- = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
- Snippets
+ = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: _('Snippets') do
+ = _('Snippets')
- if any_dashboard_nav_link?([:groups, :milestones, :activity, :snippets])
%li.header-more.dropdown.d-lg-none.d-xl-none
%a{ href: "#", data: { toggle: "dropdown" } }
- More
+ = _('More')
= sprite_icon('angle-down', css_class: 'caret-down')
.dropdown-menu
%ul
- - if dashboard_nav_link?(:groups)
- = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { class: "d-block d-sm-none" }) do
- = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', title: 'Groups' do
- Groups
-
- if dashboard_nav_link?(:activity)
= nav_link(path: 'dashboard#activity') do
- = link_to activity_dashboard_path, title: 'Activity' do
- Activity
+ = link_to activity_dashboard_path, title: _('Activity') do
+ = _('Activity')
- if dashboard_nav_link?(:milestones)
= nav_link(controller: 'dashboard/milestones') do
- = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
- Milestones
+ = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: _('Milestones') do
+ = _('Milestones')
- if dashboard_nav_link?(:snippets)
= nav_link(controller: 'dashboard/snippets') do
- = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
- Snippets
+ = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: _('Snippets') do
+ = _('Snippets')
-# Shortcut to Dashboard > Projects
- if dashboard_nav_link?(:projects)
%li.hidden
- = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
- Projects
+ = link_to dashboard_projects_path, title: _('Projects'), class: 'dashboard-shortcuts-projects' do
+ = _('Projects')
- if current_controller?('ide')
%li.line-separator.d-none.d-sm-block
= nav_link(controller: 'ide') do
- = link_to '#', class: 'dashboard-shortcuts-web-ide', title: 'Web IDE' do
- Web IDE
+ = link_to '#', class: 'dashboard-shortcuts-web-ide', title: _('Web IDE') do
+ = _('Web IDE')
- - if current_user.admin? || Gitlab::Sherlock.enabled?
+ - if Gitlab::Sherlock.enabled? || can?(current_user, :read_instance_statistics)
%li.line-separator.d-none.d-sm-block
+ - if can?(current_user, :read_instance_statistics)
+ = nav_link(controller: [:conversational_development_index, :cohorts]) do
+ = link_to instance_statistics_root_path, title: _('Instance Statistics'), aria: { label: _('Instance Statistics') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = sprite_icon('chart', size: 18)
- if current_user.admin?
= nav_link(controller: 'admin/dashboard') do
- = link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: 'Admin area', aria: { label: "Admin area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: _('Admin area'), aria: { label: _('Admin area') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('admin', size: 18)
- if Gitlab::Sherlock.enabled?
%li
- = link_to sherlock_transactions_path, class: 'admin-icon', title: 'Sherlock Transactions',
+ = link_to sherlock_transactions_path, class: 'admin-icon', title: _('Sherlock Transactions'),
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('tachometer fw')
diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml
index 50bde9d1754..7d18cd8978b 100644
--- a/app/views/layouts/nav/_explore.html.haml
+++ b/app/views/layouts/nav/_explore.html.haml
@@ -1,15 +1,15 @@
%ul.list-unstyled.navbar-sub-nav
- if explore_nav_link?(:projects)
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
- = link_to explore_root_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
- Projects
+ = link_to explore_root_path, title: _('Projects'), class: 'dashboard-shortcuts-projects' do
+ = _('Projects')
- if explore_nav_link?(:groups)
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
- = link_to explore_groups_path, title: 'Groups', class: 'dashboard-shortcuts-groups' do
- Groups
+ = link_to explore_groups_path, title: _('Groups'), class: 'dashboard-shortcuts-groups' do
+ = _('Groups')
- if explore_nav_link?(:snippets)
= nav_link(controller: :snippets) do
- = link_to explore_snippets_path, title: 'Snippets', class: 'dashboard-shortcuts-snippets' do
- Snippets
+ = link_to explore_snippets_path, title: _('Snippets'), class: 'dashboard-shortcuts-snippets' do
+ = _('Snippets')
%li
- = link_to "Help", help_path, title: 'About GitLab CE'
+ = link_to _("Help"), help_path, title: _('About GitLab CE')
diff --git a/app/views/layouts/nav/groups_dropdown/_show.html.haml b/app/views/layouts/nav/groups_dropdown/_show.html.haml
new file mode 100644
index 00000000000..3ce1fa6bcca
--- /dev/null
+++ b/app/views/layouts/nav/groups_dropdown/_show.html.haml
@@ -0,0 +1,12 @@
+- group_meta = { id: @group.id, name: @group.name, namespace: @group.full_name, web_url: group_path(@group), avatar_url: @group.avatar_url } if @group&.persisted?
+.frequent-items-dropdown-container
+ .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
+ = _('Your groups')
+ = nav_link(path: 'groups#explore') do
+ = link_to explore_groups_path 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 5809d6f7fea..f2170f71532 100644
--- a/app/views/layouts/nav/projects_dropdown/_show.html.haml
+++ b/app/views/layouts/nav/projects_dropdown/_show.html.haml
@@ -1,6 +1,6 @@
- project_meta = { id: @project.id, name: @project.name, namespace: @project.full_name, web_url: project_path(@project), avatar_url: @project.avatar_url } if @project&.persisted?
-.projects-dropdown-container
- .project-dropdown-sidebar.qa-projects-dropdown-sidebar
+.frequent-items-dropdown-container
+ .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
@@ -11,5 +11,5 @@
= nav_link(path: 'projects#trending') do
= link_to explore_root_path do
= _('Explore projects')
- .project-dropdown-content
+ .frequent-items-dropdown-content
#js-projects-dropdown{ data: { user_name: current_user.username, project: project_meta } }
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 62402c32e08..ff25b040913 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -1,138 +1,135 @@
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header
- = link_to admin_root_path, title: 'Admin Overview' do
+ = link_to admin_root_path, title: _('Admin Overview') do
.avatar-container.s40.settings-avatar
= sprite_icon('admin', size: 24)
- .sidebar-context-title Admin Area
+ .sidebar-context-title
+ = _('Admin Area')
%ul.sidebar-top-level-items
- = nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts conversational_development_index), html_options: {class: 'home'}) do
+ = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers), html_options: {class: 'home'}) do
= link_to admin_root_path, class: 'shortcuts-tree' do
.nav-icon-container
= sprite_icon('overview')
%span.nav-item-name
- Overview
+ = _('Overview')
%ul.sidebar-sub-level-items
- = nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts conversational_development_index), html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers), html_options: { class: "fly-out-top-item" } ) do
= link_to admin_root_path do
%strong.fly-out-top-item-name
- #{ _('Overview') }
+ = _('Overview')
%li.divider.fly-out-top-item
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
- = link_to admin_root_path, title: 'Overview' do
+ = link_to admin_root_path, title: _('Overview') do
%span
- Dashboard
+ = _('Dashboard')
= nav_link(controller: [:admin, :projects]) do
- = link_to admin_projects_path, title: 'Projects' do
+ = link_to admin_projects_path, title: _('Projects') do
%span
- Projects
+ = _('Projects')
= nav_link(controller: :users) do
- = link_to admin_users_path, title: 'Users' do
+ = link_to admin_users_path, title: _('Users') do
%span
- Users
+ = _('Users')
= nav_link(controller: :groups) do
- = link_to admin_groups_path, title: 'Groups' do
+ = link_to admin_groups_path, title: _('Groups') do
%span
- Groups
+ = _('Groups')
= nav_link path: 'jobs#index' do
- = link_to admin_jobs_path, title: 'Jobs' do
+ = link_to admin_jobs_path, title: _('Jobs') do
%span
- Jobs
+ = _('Jobs')
= nav_link path: ['runners#index', 'runners#show'] do
- = link_to admin_runners_path, title: 'Runners' do
+ = link_to admin_runners_path, title: _('Runners') do
%span
- Runners
- = nav_link path: 'cohorts#index' do
- = link_to admin_cohorts_path, title: 'Cohorts' do
+ = _('Runners')
+ = nav_link(controller: :gitaly_servers) do
+ = link_to admin_gitaly_servers_path, title: 'Gitaly Servers' do
%span
- Cohorts
- = nav_link(controller: :conversational_development_index) do
- = link_to admin_conversational_development_index_path, title: 'ConvDev Index' do
- %span
- ConvDev Index
+ = _('Gitaly Servers')
= nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles)) do
= link_to admin_system_info_path do
.nav-icon-container
= sprite_icon('monitor')
%span.nav-item-name
- Monitoring
+ = _('Monitoring')
%ul.sidebar-sub-level-items
= nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles), html_options: { class: "fly-out-top-item" } ) do
= link_to admin_system_info_path do
%strong.fly-out-top-item-name
- #{ _('Monitoring') }
+ = _('Monitoring')
%li.divider.fly-out-top-item
= nav_link(controller: :system_info) do
- = link_to admin_system_info_path, title: 'System Info' do
+ = link_to admin_system_info_path, title: _('System Info') do
%span
- System Info
+ = _('System Info')
= nav_link(controller: :background_jobs) do
- = link_to admin_background_jobs_path, title: 'Background Jobs' do
+ = link_to admin_background_jobs_path, title: _('Background Jobs') do
%span
- Background Jobs
+ = _('Background Jobs')
= nav_link(controller: :logs) do
- = link_to admin_logs_path, title: 'Logs' do
+ = link_to admin_logs_path, title: _('Logs') do
%span
- Logs
+ = _('Logs')
= nav_link(controller: :health_check) do
- = link_to admin_health_check_path, title: 'Health Check' do
+ = link_to admin_health_check_path, title: _('Health Check') do
%span
- Health Check
+ = _('Health Check')
= nav_link(controller: :requests_profiles) do
- = link_to admin_requests_profiles_path, title: 'Requests Profiles' do
+ = link_to admin_requests_profiles_path, title: _('Requests Profiles') do
%span
- Requests Profiles
+ = _('Requests Profiles')
= nav_link(controller: :broadcast_messages) do
= link_to admin_broadcast_messages_path do
.nav-icon-container
= sprite_icon('messages')
%span.nav-item-name
- Messages
+ = _('Messages')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :broadcast_messages, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_broadcast_messages_path do
%strong.fly-out-top-item-name
- #{ _('Messages') }
+ = _('Messages')
= nav_link(controller: [:hooks, :hook_logs]) do
= link_to admin_hooks_path do
.nav-icon-container
= sprite_icon('hook')
%span.nav-item-name
- System Hooks
+ = _('System Hooks')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: [:hooks, :hook_logs], html_options: { class: "fly-out-top-item" } ) do
= link_to admin_hooks_path do
%strong.fly-out-top-item-name
- #{ _('System Hooks') }
+ = _('System Hooks')
= nav_link(controller: :applications) do
= link_to admin_applications_path do
.nav-icon-container
= sprite_icon('applications')
%span.nav-item-name
- Applications
+ = _('Applications')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :applications, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_applications_path do
%strong.fly-out-top-item-name
- #{ _('Applications') }
+ = _('Applications')
= nav_link(controller: :abuse_reports) do
= link_to admin_abuse_reports_path do
.nav-icon-container
= sprite_icon('slight-frown')
%span.nav-item-name
- Abuse Reports
+ = _('Abuse Reports')
%span.badge.badge-pill.count= number_with_delimiter(AbuseReport.count(:all))
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :abuse_reports, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_abuse_reports_path do
%strong.fly-out-top-item-name
- #{ _('Abuse Reports') }
+ = _('Abuse Reports')
%span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(AbuseReport.count(:all))
- if akismet_enabled?
@@ -141,71 +138,71 @@
.nav-icon-container
= sprite_icon('spam')
%span.nav-item-name
- Spam Logs
+ = _('Spam Logs')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :spam_logs, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_spam_logs_path do
%strong.fly-out-top-item-name
- #{ _('Spam Logs') }
+ = _('Spam Logs')
= nav_link(controller: :deploy_keys) do
= link_to admin_deploy_keys_path do
.nav-icon-container
= sprite_icon('key')
%span.nav-item-name
- Deploy Keys
+ = _('Deploy Keys')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :deploy_keys, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_deploy_keys_path do
%strong.fly-out-top-item-name
- #{ _('Deploy Keys') }
+ = _('Deploy Keys')
= nav_link(controller: :services) do
= link_to admin_application_settings_services_path do
.nav-icon-container
= sprite_icon('template')
%span.nav-item-name
- Service Templates
+ = _('Service Templates')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :services, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_application_settings_services_path do
%strong.fly-out-top-item-name
- #{ _('Service Templates') }
+ = _('Service Templates')
= nav_link(controller: :labels) do
= link_to admin_labels_path do
.nav-icon-container
= sprite_icon('labels')
%span.nav-item-name
- Labels
+ = _('Labels')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :labels, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_labels_path do
%strong.fly-out-top-item-name
- #{ _('Labels') }
+ = _('Labels')
= nav_link(controller: :appearances) do
= link_to admin_appearances_path do
.nav-icon-container
= sprite_icon('appearance')
%span.nav-item-name
- Appearance
+ = _('Appearance')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :appearances, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_appearances_path do
%strong.fly-out-top-item-name
- #{ _('Appearance') }
+ = _('Appearance')
= nav_link(controller: :application_settings) do
= link_to admin_application_settings_path do
.nav-icon-container
= sprite_icon('settings')
%span.nav-item-name
- Settings
+ = _('Settings')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :application_settings, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_application_settings_path do
%strong.fly-out-top-item-name
- #{ _('Settings') }
+ = _('Settings')
= render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 39a033337ff..d471dd84550 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -15,26 +15,26 @@
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do
= link_to group_path(@group) do
.nav-icon-container
- = sprite_icon('project')
+ = sprite_icon('home')
%span.nav-item-name
- Overview
+ = _('Overview')
%ul.sidebar-sub-level-items
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
= link_to group_path(@group) do
%strong.fly-out-top-item-name
- #{ _('Overview') }
+ = _('Overview')
%li.divider.fly-out-top-item
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
- = link_to group_path(@group), title: 'Group details' do
+ = link_to group_path(@group), title: _('Group details') do
%span
- Details
+ = _('Details')
- if group_sidebar_link?(:activity)
= nav_link(path: 'groups#activity') do
- = link_to activity_group_path(@group), title: 'Activity' do
+ = link_to activity_group_path(@group), title: _('Activity') do
%span
- Activity
+ = _('Activity')
- if group_sidebar_link?(:issues)
= nav_link(path: issues_sub_menu_items) do
@@ -42,21 +42,21 @@
.nav-icon-container
= sprite_icon('issues')
%span.nav-item-name
- Issues
+ = _('Issues')
%span.badge.badge-pill.count= number_with_delimiter(issues_count)
%ul.sidebar-sub-level-items
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do
= link_to issues_group_path(@group) do
%strong.fly-out-top-item-name
- #{ _('Issues') }
+ = _('Issues')
%span.badge.badge-pill.count.issue_counter.fly-out-badge= number_with_delimiter(issues_count)
%li.divider.fly-out-top-item
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
- = link_to issues_group_path(@group), title: 'List' do
+ = link_to issues_group_path(@group), title: _('List') do
%span
- List
+ = _('List')
- if group_sidebar_link?(:boards)
= nav_link(path: ['boards#index', 'boards#show']) do
@@ -66,15 +66,15 @@
- if group_sidebar_link?(:labels)
= nav_link(path: 'labels#index') do
- = link_to group_labels_path(@group), title: 'Labels' do
+ = link_to group_labels_path(@group), title: _('Labels') do
%span
- Labels
+ = _('Labels')
- if group_sidebar_link?(:milestones)
= nav_link(path: 'milestones#index') do
- = link_to group_milestones_path(@group), title: 'Milestones' do
+ = link_to group_milestones_path(@group), title: _('Milestones') do
%span
- Milestones
+ = _('Milestones')
- if group_sidebar_link?(:merge_requests)
= nav_link(path: 'groups#merge_requests') do
@@ -82,13 +82,13 @@
.nav-icon-container
= sprite_icon('git-merge')
%span.nav-item-name
- Merge Requests
+ = _('Merge Requests')
%span.badge.badge-pill.count= number_with_delimiter(merge_requests_count)
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do
= link_to merge_requests_group_path(@group) do
%strong.fly-out-top-item-name
- #{ _('Merge Requests') }
+ = _('Merge Requests')
%span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests_count)
- if group_sidebar_link?(:group_members)
@@ -97,12 +97,12 @@
.nav-icon-container
= sprite_icon('users')
%span.nav-item-name
- Members
+ = _('Members')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
= link_to group_group_members_path(@group) do
%strong.fly-out-top-item-name
- #{ _('Members') }
+ = _('Members')
- if group_sidebar_link?(:settings)
= nav_link(path: group_nav_link_paths) do
@@ -110,17 +110,17 @@
.nav-icon-container
= sprite_icon('settings')
%span.nav-item-name
- Settings
+ = _('Settings')
%ul.sidebar-sub-level-items
= nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show], html_options: { class: "fly-out-top-item" } ) do
= link_to edit_group_path(@group) do
%strong.fly-out-top-item-name
- #{ _('Settings') }
+ = _('Settings')
%li.divider.fly-out-top-item
= nav_link(path: 'groups#edit') do
- = link_to edit_group_path(@group), title: 'General' do
+ = link_to edit_group_path(@group), title: _('General') do
%span
- General
+ = _('General')
= nav_link(controller: :badges) do
= link_to group_settings_badges_path(@group), title: _('Project Badges') do
@@ -129,13 +129,13 @@
= nav_link(path: 'groups#projects') do
- = link_to projects_group_path(@group), title: 'Projects' do
+ = link_to projects_group_path(@group), title: _('Projects') do
%span
- Projects
+ = _('Projects')
= nav_link(controller: :ci_cd) do
- = link_to group_settings_ci_cd_path(@group), title: 'CI / CD' do
+ = link_to group_settings_ci_cd_path(@group), title: _('CI / CD') do
%span
- CI / CD
+ = _('CI / CD')
= render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_instance_statistics.html.haml b/app/views/layouts/nav/sidebar/_instance_statistics.html.haml
new file mode 100644
index 00000000000..b8ff448f261
--- /dev/null
+++ b/app/views/layouts/nav/sidebar/_instance_statistics.html.haml
@@ -0,0 +1,33 @@
+.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
+ .nav-sidebar-inner-scroll
+ .context-header
+ = link_to instance_statistics_root_path, title: _('Instance Statistics') do
+ .avatar-container.s40.settings-avatar
+ = sprite_icon('chart', size: 24)
+ .sidebar-context-title= _('Instance Statistics')
+ %ul.sidebar-top-level-items
+ = nav_link(controller: :conversational_development_index) do
+ = link_to instance_statistics_conversational_development_index_index_path do
+ .nav-icon-container
+ = sprite_icon('comment')
+ %span.nav-item-name
+ = _('ConvDev Index')
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(controller: :conversational_development_index, html_options: { class: "fly-out-top-item" } ) do
+ = link_to instance_statistics_conversational_development_index_index_path do
+ %strong.fly-out-top-item-name
+ = _('ConvDev Index')
+
+ = nav_link(controller: :cohorts) do
+ = link_to instance_statistics_cohorts_path do
+ .nav-icon-container
+ = sprite_icon('users')
+ %span.nav-item-name
+ = _('Cohorts')
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(controller: :cohorts, html_options: { class: "fly-out-top-item" } ) do
+ = link_to instance_statistics_cohorts_path do
+ %strong.fly-out-top-item-name
+ = _('Cohorts')
+
+ = render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index 6cbd163dd41..d65f153b451 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -1,7 +1,7 @@
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header
- = link_to profile_path, title: 'Profile Settings' do
+ = link_to profile_path, title: _('Profile Settings') do
.avatar-container.s40.settings-avatar
= sprite_icon('user', size: 24)
.sidebar-context-title User Settings
@@ -11,145 +11,145 @@
.nav-icon-container
= sprite_icon('profile')
%span.nav-item-name
- Profile
+ = _('Profile')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'profiles#show', html_options: { class: "fly-out-top-item" } ) do
= link_to profile_path do
%strong.fly-out-top-item-name
- #{ _('Profile') }
+ = _('Profile')
= nav_link(controller: [:accounts, :two_factor_auths]) do
= link_to profile_account_path do
.nav-icon-container
= sprite_icon('account')
%span.nav-item-name
- Account
+ = _('Account')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: [:accounts, :two_factor_auths], html_options: { class: "fly-out-top-item" } ) do
= link_to profile_account_path do
%strong.fly-out-top-item-name
- #{ _('Account') }
+ = _('Account')
- if Gitlab::CurrentSettings.user_oauth_applications?
= nav_link(controller: 'oauth/applications') do
= link_to applications_profile_path do
.nav-icon-container
= sprite_icon('applications')
%span.nav-item-name
- Applications
+ = _('Applications')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: 'oauth/applications', html_options: { class: "fly-out-top-item" } ) do
= link_to applications_profile_path do
%strong.fly-out-top-item-name
- #{ _('Applications') }
+ = _('Applications')
= nav_link(controller: :chat_names) do
= link_to profile_chat_names_path do
.nav-icon-container
= sprite_icon('comment')
%span.nav-item-name
- Chat
+ = _('Chat')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :chat_names, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_chat_names_path do
%strong.fly-out-top-item-name
- #{ _('Chat') }
+ = _('Chat')
= nav_link(controller: :personal_access_tokens) do
= link_to profile_personal_access_tokens_path do
.nav-icon-container
= sprite_icon('token')
%span.nav-item-name
- Access Tokens
+ = _('Access Tokens')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :personal_access_tokens, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_personal_access_tokens_path do
%strong.fly-out-top-item-name
- #{ _('Access Tokens') }
+ = _('Access Tokens')
= nav_link(controller: :emails) do
= link_to profile_emails_path do
.nav-icon-container
= sprite_icon('mail')
%span.nav-item-name
- Emails
+ = _('Emails')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :emails, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_emails_path do
%strong.fly-out-top-item-name
- #{ _('Emails') }
+ = _('Emails')
- if current_user.allow_password_authentication?
= nav_link(controller: :passwords) do
= link_to edit_profile_password_path do
.nav-icon-container
= sprite_icon('lock')
%span.nav-item-name
- Password
+ = _('Password')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :passwords, html_options: { class: "fly-out-top-item" } ) do
= link_to edit_profile_password_path do
%strong.fly-out-top-item-name
- #{ _('Password') }
+ = _('Password')
= nav_link(controller: :notifications) do
= link_to profile_notifications_path do
.nav-icon-container
= sprite_icon('notifications')
%span.nav-item-name
- Notifications
+ = _('Notifications')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :notifications, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_notifications_path do
%strong.fly-out-top-item-name
- #{ _('Notifications') }
+ = _('Notifications')
= nav_link(controller: :keys) do
= link_to profile_keys_path do
.nav-icon-container
= sprite_icon('key')
%span.nav-item-name
- SSH Keys
+ = _('SSH Keys')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :keys, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_keys_path do
%strong.fly-out-top-item-name
- #{ _('SSH Keys') }
+ = _('SSH Keys')
= nav_link(controller: :gpg_keys) do
= link_to profile_gpg_keys_path do
.nav-icon-container
- = sprite_icon('key-2')
+ = sprite_icon('key-modern')
%span.nav-item-name
- GPG Keys
+ = _('GPG Keys')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :gpg_keys, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_gpg_keys_path do
%strong.fly-out-top-item-name
- #{ _('GPG Keys') }
+ = _('GPG Keys')
= nav_link(controller: :preferences) do
= link_to profile_preferences_path do
.nav-icon-container
= sprite_icon('preferences')
%span.nav-item-name
- Preferences
+ = _('Preferences')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :preferences, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_preferences_path do
%strong.fly-out-top-item-name
- #{ _('Preferences') }
+ = _('Preferences')
= nav_link(controller: :active_sessions) do
= link_to profile_active_sessions_path do
.nav-icon-container
= sprite_icon('monitor-lines')
%span.nav-item-name
- Active Sessions
+ = _('Active Sessions')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :active_sessions, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_active_sessions_path do
%strong.fly-out-top-item-name
- #{ _('Active Sessions') }
+ = _('Active Sessions')
= nav_link(path: 'profiles#audit_log') do
= link_to audit_log_profile_path do
.nav-icon-container
= sprite_icon('log')
%span.nav-item-name
- Authentication log
+ = _('Authentication log')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'profiles#audit_log', html_options: { class: "fly-out-top-item" } ) do
= link_to audit_log_profile_path do
%strong.fly-out-top-item-name
- #{ _('Authentication Log') }
+ = _('Authentication Log')
= render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 33416bf76d7..34f47806205 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -4,14 +4,14 @@
.context-header
= link_to project_path(@project), title: @project.name do
.avatar-container.s40.project-avatar
- = project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile')
+ = project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile', width: 40, height: 40)
.sidebar-context-title
= @project.name
%ul.sidebar-top-level-items
= nav_link(path: sidebar_projects_paths, html_options: { class: 'home' }) do
= link_to project_path(@project), class: 'shortcuts-project' do
.nav-icon-container
- = sprite_icon('project')
+ = sprite_icon('home')
%span.nav-item-name
= _('Project')
@@ -40,7 +40,7 @@
= nav_link(controller: sidebar_repository_paths) do
= link_to project_tree_path(@project), class: 'shortcuts-tree' do
.nav-icon-container
- = sprite_icon('doc_text')
+ = sprite_icon('doc-text')
%span.nav-item-name
= _('Repository')
@@ -105,7 +105,7 @@
= number_with_delimiter(@project.open_issues_count(current_user))
%li.divider.fly-out-top-item
= nav_link(controller: :issues, action: :index) do
- = link_to project_issues_path(@project), title: 'Issues' do
+ = link_to project_issues_path(@project), title: _('Issues') do
%span
= _('List')
@@ -115,14 +115,14 @@
= boards_link_text
= nav_link(controller: :labels) do
- = link_to project_labels_path(@project), title: 'Labels' do
+ = link_to project_labels_path(@project), title: _('Labels') do
%span
= _('Labels')
= render_if_exists 'projects/sidebar/issues_service_desk'
= nav_link(controller: :milestones) do
- = link_to project_milestones_path(@project), title: 'Milestones' do
+ = link_to project_milestones_path(@project), title: _('Milestones'), class: 'qa-milestones-link' do
%span
= _('Milestones')
- if project_nav_tab? :external_issue_tracker
@@ -172,31 +172,31 @@
%li.divider.fly-out-top-item
- if project_nav_tab? :pipelines
= nav_link(path: ['pipelines#index', 'pipelines#show']) do
- = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
+ = link_to project_pipelines_path(@project), title: _('Pipelines'), class: 'shortcuts-pipelines' do
%span
= _('Pipelines')
- if project_nav_tab? :builds
= nav_link(controller: [:jobs, :artifacts]) do
- = link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
+ = link_to project_jobs_path(@project), title: _('Jobs'), class: 'shortcuts-builds' do
%span
= _('Jobs')
- if project_nav_tab? :pipelines
= nav_link(controller: :pipeline_schedules) do
- = link_to pipeline_schedules_path(@project), title: 'Schedules', class: 'shortcuts-builds' do
+ = link_to pipeline_schedules_path(@project), title: _('Schedules'), class: 'shortcuts-builds' do
%span
= _('Schedules')
- if @project.feature_available?(:builds, current_user) && !@project.empty_repo?
= nav_link(path: 'pipelines#charts') do
- = link_to charts_project_pipelines_path(@project), title: 'Charts', class: 'shortcuts-pipelines-charts' do
+ = link_to charts_project_pipelines_path(@project), title: _('Charts'), class: 'shortcuts-pipelines-charts' do
%span
= _('Charts')
- if project_nav_tab? :operations
= nav_link(controller: [:environments, :clusters, :user, :gcp]) do
- = link_to project_environments_path(@project), class: 'shortcuts-operations' do
+ = link_to metrics_project_environments_path(@project), class: 'shortcuts-operations' do
.nav-icon-container
= sprite_icon('cloud-gear')
%span.nav-item-name
@@ -204,14 +204,19 @@
%ul.sidebar-sub-level-items
= nav_link(controller: [:environments, :clusters, :user, :gcp], html_options: { class: "fly-out-top-item" } ) do
- = link_to project_environments_path(@project) do
+ = link_to metrics_project_environments_path(@project) do
%strong.fly-out-top-item-name
= _('Operations')
%li.divider.fly-out-top-item
- if project_nav_tab? :environments
- = nav_link(controller: :environments) do
- = link_to project_environments_path(@project), title: 'Environments', class: 'shortcuts-environments' do
+ = nav_link(controller: :environments, action: [:metrics, :metrics_redirect]) do
+ = link_to metrics_project_environments_path(@project), title: _('Metrics'), class: 'shortcuts-metrics' do
+ %span
+ = _('Metrics')
+
+ = nav_link(controller: :environments, action: [:index, :folder, :show, :new, :edit, :create, :update, :stop, :terminal]) do
+ = link_to project_environments_path(@project), title: _('Environments'), class: 'shortcuts-environments' do
%span
= _('Environments')
@@ -237,7 +242,7 @@
%p= _('Allows you to add and manage Kubernetes clusters.')
%p
= _('Protip:')
- = link_to 'Auto DevOps', help_page_path('topics/autodevops/index.md')
+ = link_to _('Auto DevOps'), help_page_path('topics/autodevops/index.md')
%span= _('uses Kubernetes clusters to deploy your code!')
%hr
%button.btn.btn-create.btn-sm.dismiss-feature-highlight{ type: 'button' }
@@ -300,11 +305,11 @@
= _('Settings')
%li.divider.fly-out-top-item
= nav_link(path: %w[projects#edit]) do
- = link_to edit_project_path(@project), title: 'General' do
+ = link_to edit_project_path(@project), title: _('General') do
%span
= _('General')
= nav_link(controller: :project_members) do
- = link_to project_project_members_path(@project), title: 'Members' do
+ = link_to project_project_members_path(@project), title: _('Members') do
%span
= _('Members')
- if can_edit
@@ -314,21 +319,21 @@
= _('Badges')
- if can_edit
= nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do
- = link_to project_settings_integrations_path(@project), title: 'Integrations' do
+ = link_to project_settings_integrations_path(@project), title: _('Integrations') do
%span
= _('Integrations')
= nav_link(controller: :repository) do
- = link_to project_settings_repository_path(@project), title: 'Repository' do
+ = link_to project_settings_repository_path(@project), title: _('Repository') do
%span
= _('Repository')
- if @project.feature_available?(:builds, current_user)
= nav_link(controller: :ci_cd) do
- = link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do
+ = link_to project_settings_ci_cd_path(@project), title: _('CI / CD') do
%span
= _('CI / CD')
- if @project.pages_available?
= nav_link(controller: :pages) do
- = link_to project_pages_path(@project), title: 'Pages' do
+ = link_to project_pages_path(@project), title: _('Pages') do
%span
= _('Pages')
@@ -336,7 +341,7 @@
- else
= nav_link(controller: :project_members) do
- = link_to project_settings_members_path(@project), title: 'Members', class: 'shortcuts-tree' do
+ = link_to project_settings_members_path(@project), title: _('Members'), class: 'shortcuts-tree' do
.nav-icon-container
= sprite_icon('users')
%span.nav-item-name
@@ -351,41 +356,41 @@
-# Shortcut to Project > Activity
%li.hidden
- = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
+ = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do
%span
- Activity
+ = _('Activity')
-# Shortcut to Repository > Graph (formerly, Network)
- if project_nav_tab? :network
%li.hidden
- = link_to project_network_path(@project, current_ref), title: 'Network', class: 'shortcuts-network' do
- Graph
+ = link_to project_network_path(@project, current_ref), title: _('Network'), class: 'shortcuts-network' do
+ = _('Graph')
-# Shortcut to Repository > Charts (formerly, top-nav item "Graphs")
- unless @project.empty_repo?
%li.hidden
- = link_to charts_project_graph_path(@project, current_ref), title: 'Charts', class: 'shortcuts-repository-charts' do
- Charts
+ = link_to charts_project_graph_path(@project, current_ref), title: _('Charts'), class: 'shortcuts-repository-charts' do
+ = _('Charts')
-# Shortcut to Issues > New Issue
- if project_nav_tab?(:issues)
%li.hidden
= link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do
- Create a new issue
+ = _('Create a new issue')
-# Shortcut to Pipelines > Jobs
- if project_nav_tab? :builds
%li.hidden
- = link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
- Jobs
+ = link_to project_jobs_path(@project), title: _('Jobs'), class: 'shortcuts-builds' do
+ = _('Jobs')
-# Shortcut to commits page
- if project_nav_tab? :commits
%li.hidden
- = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
- Commits
+ = link_to project_commits_path(@project), title: _('Commits'), class: 'shortcuts-commits' do
+ = _('Commits')
-# Shortcut to issue boards
- if project_nav_tab?(:issues)
%li.hidden
- = link_to 'Issue Boards', project_boards_path(@project), title: 'Issue Boards', class: 'shortcuts-issue-boards'
+ = link_to _('Issue Boards'), project_boards_path(@project), title: _('Issue Boards'), class: 'shortcuts-issue-boards'
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index ab8b1271212..1c3e05e07f4 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -14,13 +14,12 @@
%br
- if @target_url
- if @reply_by_email
- Reply to this email directly or
- #{link_to "view it on GitLab", @target_url}.
+ = _('Reply to this email directly or %{view_it_on_gitlab}.').html_safe % { view_it_on_gitlab: link_to(_("view it on GitLab"), @target_url) }
- else
- #{link_to "View it on GitLab", @target_url}.
+ #{link_to _("View it on GitLab"), @target_url}.
%br
-# Don't link the host in the line below, one link in the email is easier to quickly click than two.
- You're receiving this email because #{notification_reason_text(@reason)}.
+ = _("You're receiving this email because %{reason}.") % { reason: notification_reason_text(@reason) }
If you'd like to receive fewer emails, you can
- if @labels_url
adjust your #{link_to 'label subscriptions', @labels_url}.
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 67aa05b655c..7aca64663e0 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -1,5 +1,5 @@
-- page_title "User Settings"
-- header_title "User Settings", profile_path unless header_title
+- page_title _("User Settings")
+- header_title _("User Settings"), profile_path unless header_title
- sidebar "dashboard"
- nav "profile"
- @left_sidebar = true
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index 4bc94bd132d..93214c2a674 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -1,4 +1,4 @@
-- page_title "Settings"
+- page_title _("Settings")
- nav "project"
= render template: "layouts/project"
diff --git a/app/views/layouts/search.html.haml b/app/views/layouts/search.html.haml
index fd4c7ad21a7..dd4b9e45207 100644
--- a/app/views/layouts/search.html.haml
+++ b/app/views/layouts/search.html.haml
@@ -1,4 +1,4 @@
-- page_title "Search"
-- header_title "Search", search_path
+- page_title _("Search")
+- header_title _("Search"), search_path
= render template: "layouts/application"
diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml
index 849075a0ba5..6418500d5d1 100644
--- a/app/views/layouts/snippets.html.haml
+++ b/app/views/layouts/snippets.html.haml
@@ -1,4 +1,4 @@
-- header_title "Snippets", snippets_path
+- header_title _("Snippets"), snippets_path
- content_for :page_specific_javascripts do
- if @snippet && current_user
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 9c95b6281ba..2220b4eee96 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -29,7 +29,7 @@
%p
Activate signin with one of the following services
.col-lg-8
- %label.label-light
+ %label.label-bold
Connected Accounts
%p Click on icon to activate signin with one of the following services
- button_based_providers.each do |provider|
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index a5db9dbe7f8..04a19ab14dd 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -12,7 +12,7 @@
Add email address
= form_for 'email', url: profile_emails_path do |f|
.form-group
- = f.label :email, class: 'label-light'
+ = f.label :email, class: 'label-bold'
= f.text_field :email, class: 'form-control'
.prepend-top-default
= f.submit 'Add email address', class: 'btn btn-create'
diff --git a/app/views/profiles/gpg_keys/_form.html.haml b/app/views/profiles/gpg_keys/_form.html.haml
index 3fcf563d970..aa9b0aad034 100644
--- a/app/views/profiles/gpg_keys/_form.html.haml
+++ b/app/views/profiles/gpg_keys/_form.html.haml
@@ -3,7 +3,7 @@
= form_errors(@gpg_key)
.form-group
- = f.label :key, class: 'label-light'
+ = f.label :key, class: 'label-bold'
= f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: "Don't paste the private part of the GPG key. Paste the public part which begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'."
.prepend-top-default
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index c14700794ce..5207921d6fe 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -3,13 +3,20 @@
= form_errors(@key)
.form-group
- = f.label :key, class: 'label-light'
+ = f.label :key, class: 'label-bold'
%p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key.")
- = f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: 'Typically starts with "ssh-rsa …"'
+ = f.text_area :key, class: "form-control js-add-ssh-key-validation-input", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"')
.form-group
- = f.label :title, class: 'label-light'
- = f.text_field :title, class: "form-control", required: true, placeholder: 'e.g. My MacBook key'
+ = f.label :title, class: 'label-bold'
+ = f.text_field :title, class: "form-control input-lg", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
%p.form-text.text-muted= _('Name your individual key via a title')
+ .js-add-ssh-key-validation-warning.hide
+ .bs-callout.bs-callout-warning{ role: 'alert', aria_live: 'assertive' }
+ %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?")
+
+ %button.btn.btn-create.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
+
.prepend-top-default
- = f.submit 'Add key', class: "btn btn-create"
+ = f.submit s_('Profiles|Add key'), class: "btn btn-create js-add-ssh-key-validation-original-submit"
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 71ea625e5d5..712eb2a4573 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -23,10 +23,10 @@
= form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f|
.form-group
- = f.label :notification_email, class: "label-light"
+ = f.label :notification_email, class: "label-bold"
= f.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2"
- = label_tag :global_notification_level, "Global notification level", class: "label-light"
+ = label_tag :global_notification_level, "Global notification level", class: "label-bold"
%br
.clearfix
.form-group.float-left.global-notification-setting
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 8a51a30191a..9c8cc9c059b 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -18,15 +18,15 @@
- unless @user.password_automatically_set?
.form-group
- = f.label :current_password, class: 'label-light'
+ = f.label :current_password, class: 'label-bold'
= f.password_field :current_password, required: true, class: 'form-control'
%p.form-text.text-muted
You must provide your current password in order to change it.
.form-group
- = f.label :password, 'New password', class: 'label-light'
+ = f.label :password, 'New password', class: 'label-bold'
= f.password_field :password, required: true, class: 'form-control'
.form-group
- = f.label :password_confirmation, class: 'label-light'
+ = f.label :password_confirmation, class: 'label-bold'
= f.password_field :password_confirmation, required: true, class: 'form-control'
.prepend-top-default.append-bottom-default
= f.submit 'Save password', class: "btn btn-create append-right-10"
diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index d111113c646..c10d4ea1a4d 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -40,7 +40,7 @@
%p
It cannot be used to access any other data.
.col-lg-8.feed-token-reset
- = label_tag :feed_token, 'Feed token', class: "label-light"
+ = label_tag :feed_token, 'Feed token', class: "label-bold"
= text_field_tag :feed_token, current_user.feed_token, class: 'form-control', readonly: true, onclick: 'this.select()'
%p.form-text.text-muted
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.
@@ -59,7 +59,7 @@
%p
It cannot be used to access any other data.
.col-lg-8.incoming-email-token-reset
- = label_tag :incoming_email_token, 'Incoming email token', class: "label-light"
+ = label_tag :incoming_email_token, 'Incoming email token', class: "label-bold"
= text_field_tag :incoming_email_token, current_user.incoming_email_token, class: 'form-control', readonly: true, onclick: 'this.select()'
%p.form-text.text-muted
Keep this token secret. Anyone who gets ahold of it can create issues as if they were you.
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 8f1078bd41d..fd6dd74e1c5 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -42,17 +42,17 @@
= link_to 'Learn more', help_page_path('user/profile/preferences', anchor: 'behavior'), target: '_blank'
.col-lg-8
.form-group
- = f.label :layout, class: 'label-light' do
+ = f.label :layout, class: 'label-bold' do
Layout width
= f.select :layout, layout_choices, {}, class: 'form-control'
.form-text.text-muted
Choose between fixed (max. 1200px) and fluid (100%) application layout.
.form-group
- = f.label :dashboard, class: 'label-light' do
+ = f.label :dashboard, class: 'label-bold' do
Default dashboard
= f.select :dashboard, dashboard_choices, {}, class: 'form-control'
.form-group
- = f.label :project_view, class: 'label-light' do
+ = f.label :project_view, class: 'label-bold' do
Project overview content
= f.select :project_view, project_view_choices, {}, class: 'form-control'
.form-text.text-muted
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 507cd5dcc12..6f08a294c5d 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -30,6 +30,38 @@
- if @user.avatar?
%hr
= link_to _('Remove avatar'), profile_avatar_path, data: { confirm: _('Avatar will be removed. Are you sure?') }, method: :delete, class: 'btn btn-danger btn-inverted'
+
+ %hr
+ .row
+ .col-lg-4.profile-settings-sidebar
+ %h4.prepend-top-0= s_("User|Current status")
+ %p= s_("Profiles|This emoji and message will appear on your profile and throughout the interface.")
+ .col-lg-8
+ = f.fields_for :status, @user.status do |status_form|
+ - emoji_button = button_tag type: :button,
+ class: 'js-toggle-emoji-menu emoji-menu-toggle-button btn has-tooltip',
+ title: s_("Profiles|Add status emoji") do
+ - if @user.status
+ = emoji_icon @user.status.emoji
+ %span#js-no-emoji-placeholder.no-emoji-placeholder{ class: ('hidden' if @user.status) }
+ = sprite_icon('emoji_slightly_smiling_face', css_class: 'award-control-icon-neutral')
+ = sprite_icon('emoji_smiley', css_class: 'award-control-icon-positive')
+ = sprite_icon('emoji_smile', css_class: 'award-control-icon-super-positive')
+ - reset_message_button = button_tag type: :button,
+ id: 'js-clear-user-status-button',
+ class: 'clear-user-status btn has-tooltip',
+ title: s_("Profiles|Clear status") do
+ = sprite_icon("close")
+
+ = status_form.hidden_field :emoji, id: 'js-status-emoji-field'
+ = status_form.text_field :message,
+ id: 'js-status-message-field',
+ class: 'form-control input-lg',
+ label: s_("Profiles|Your status"),
+ prepend: emoji_button,
+ append: reset_message_button,
+ placeholder: s_("Profiles|What's your status?")
+
%hr
.row
.col-lg-4.profile-settings-sidebar
@@ -69,6 +101,12 @@
= f.text_field :location
= f.text_field :organization
= f.text_area :bio, rows: 4, maxlength: 250, help: 'Tell us about yourself in fewer than 250 characters.'
+ %hr
+ %h5 Private profile
+ - private_profile_label = capture do
+ Don't display activity-related personal information on your profile
+ = link_to icon('question-circle'), help_page_path('user/profile/index.md', anchor: 'private-profile')
+ = f.check_box :private_profile, label: private_profile_label
.prepend-top-default.append-bottom-default
= f.submit 'Update profile settings', class: 'btn btn-success'
= link_to 'Cancel', user_path(current_user), class: 'btn btn-cancel'
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index e35ebdea435..cd10b8758f6 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -13,10 +13,16 @@
- if current_user.two_factor_otp_enabled?
%p
You've already enabled two-factor authentication using mobile authenticator applications. In order to register a different device, you must first disable two-factor authentication.
+ %p
+ If you lose your recovery codes you can generate new ones, invalidating all previous codes.
+ %div
= link_to 'Disable two-factor authentication', profile_two_factor_auth_path,
method: :delete,
data: { confirm: "Are you sure? This will invalidate your registered applications and U2F devices." },
- class: 'btn btn-danger'
+ class: 'btn btn-danger append-right-10'
+ = form_tag codes_profile_two_factor_auth_path, {style: 'display: inline-block', method: :post} do |f|
+ = submit_tag 'Regenerate recovery codes', class: 'btn'
+
- else
%p
Download the Google Authenticator application from App Store or Google Play Store and scan this code.
@@ -43,7 +49,7 @@
.alert.alert-danger
= @error
.form-group
- = label_tag :pin_code, nil, class: "label-light"
+ = label_tag :pin_code, nil, class: "label-bold"
= text_field_tag :pin_code, nil, class: "form-control", required: true
.prepend-top-default
= submit_tag 'Register with two-factor app', class: 'btn btn-success'
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 1f701f2aa1b..6bf21570d41 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -1,10 +1,9 @@
%div{ class: container_class }
.nav-block.activity-filter-block.activities
+ = render 'shared/event_filter'
.controls
= link_to project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'btn rss-btn has-tooltip' do
= icon('rss')
- = render 'shared/event_filter'
-
.content_list.project-activity{ :"data-href" => activity_project_path(@project) }
= spinner
diff --git a/app/views/projects/_export.html.haml b/app/views/projects/_export.html.haml
index f4d4888bd15..aa980da7e95 100644
--- a/app/views/projects/_export.html.haml
+++ b/app/views/projects/_export.html.haml
@@ -31,7 +31,7 @@
%li Any encrypted tokens
%p
Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page.
- - if project.export_project_path
+ - if project.export_status == :finished
= link_to 'Download export', download_export_project_path(project),
rel: 'nofollow', download: '', method: :get, class: "btn btn-default"
= link_to 'Generate new export', generate_new_export_project_path(project),
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 89940512bc6..fbe88ec9618 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -3,7 +3,7 @@
.project-home-panel.text-center{ class: ("empty-project" if empty_repo) }
.limit-container-width{ class: container_class }
.avatar-container.s70.project-avatar
- = project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile')
+ = project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile', width: 70, height: 70)
%h1.project-title.qa-project-name
= @project.name
%span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
@@ -12,6 +12,9 @@
.project-home-desc
- if @project.description.present?
= markdown_field(@project, :description)
+ - if can?(current_user, :read_project, @project)
+ .text-secondary.prepend-top-8
+ = s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
- if @project.forked?
%p
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 8f535b9d789..70e1c557547 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -1,49 +1,66 @@
- active_tab = local_assigns.fetch(:active_tab, 'blank')
-- f = local_assigns.fetch(:f)
.project-import
.form-group.import-btn-container.clearfix
- = f.label :visibility_level, class: 'label-light' do #the label here seems wrong
+ %h5
Import project from
.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' do
= icon('gitlab', text: 'GitLab export')
- %div
- - if github_import_enabled?
+
+ - if github_import_enabled?
+ %div
= link_to new_import_github_path, class: 'btn js-import-github' do
= icon('github', text: 'GitHub')
- %div
- - if bitbucket_import_enabled?
+
+ - if bitbucket_import_enabled?
+ %div
= link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}" do
- = icon('bitbucket', text: 'Bitbucket')
+ = icon('bitbucket', text: 'Bitbucket Cloud')
- unless bitbucket_import_configured?
= render 'bitbucket_import_modal'
- %div
- - if gitlab_import_enabled?
+ - if bitbucket_server_import_enabled?
+ %div
+ = link_to status_import_bitbucket_server_path, class: "btn import_bitbucket" do
+ = icon('bitbucket-square', text: '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?}" do
= icon('gitlab', text: 'GitLab.com')
- unless gitlab_import_configured?
= render 'gitlab_import_modal'
- %div
- - if google_code_import_enabled?
+
+ - if google_code_import_enabled?
+ %div
= link_to new_import_google_code_path, class: 'btn import_google_code' do
= icon('google', text: 'Google Code')
- %div
- - if fogbugz_import_enabled?
+
+ - if fogbugz_import_enabled?
+ %div
= link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
= icon('bug', text: 'Fogbugz')
- %div
- - if gitea_import_enabled?
+
+ - if gitea_import_enabled?
+ %div
= link_to new_import_gitea_path, class: 'btn import_gitea' do
= custom_icon('go_logo')
Gitea
- %div
- - if git_import_enabled?
+
+ - if git_import_enabled?
+ %div
%button.btn.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active' } }
= icon('git', text: 'Repo by URL')
+
+ - if manifest_import_enabled?
+ %div
+ = link_to new_import_manifest_path, class: 'btn import_manifest' do
+ = icon('file-text-o', text: 'Manifest file')
+
.js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
- %hr
+ = form_for @project, html: { class: 'new_project' } do |f|
+ %hr
= render "shared/import_form", f: f
= render 'new_project_fields', f: f, project_name_id: "import-url-name"
diff --git a/app/views/projects/_merge_request_merge_method_settings.html.haml b/app/views/projects/_merge_request_merge_method_settings.html.haml
index 3bb220ac6d0..540e996e4d8 100644
--- a/app/views/projects/_merge_request_merge_method_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_method_settings.html.haml
@@ -2,7 +2,7 @@
- project = local_assigns.fetch(:project)
.form-group
- = label_tag :merge_method_merge, class: 'label-light' do
+ = label_tag :merge_method_merge, class: 'label-bold' do
Merge method
.form-check
= form.radio_button :merge_method, :merge, class: "js-merge-method-radio form-check-input"
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index 6f957533287..ad8c7911fad 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -4,7 +4,7 @@
.row{ id: project_name_id }
= f.hidden_field :ci_cd_only, value: ci_cd_only
.form-group.project-path.col-sm-6
- = f.label :namespace_id, class: 'label-light' do
+ = f.label :namespace_id, class: 'label-bold' do
%span
Project path
.input-group
@@ -12,7 +12,13 @@
.input-group-prepend.has-tooltip{ title: root_url }
.input-group-text
= root_url
- = f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), {}, { class: 'select2 js-select-namespace qa-project-namespace-select', tabindex: 1}
+ - namespace_id = namespace_id_from(params)
+ = f.select(:namespace_id,
+ namespaces_options(namespace_id || :current_user,
+ display_path: true,
+ extra_group: namespace_id),
+ {},
+ { class: 'select2 js-select-namespace qa-project-namespace-select', tabindex: 1})
- else
.input-group-prepend.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
@@ -20,7 +26,7 @@
#{user_url(current_user.username)}/
= f.hidden_field :namespace_id, value: current_user.namespace_id
.form-group.project-path.col-sm-6
- = f.label :path, class: 'label-light' do
+ = f.label :path, class: 'label-bold' do
%span
Project name
= f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
@@ -30,15 +36,25 @@
= link_to "Create a group", new_group_path
.form-group
- = f.label :description, class: 'label-light' do
+ = f.label :description, class: 'label-bold' do
Project description
%span (optional)
= f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250
-= f.label :visibility_level, class: 'label-light' do
+= f.label :visibility_level, class: 'label-bold' do
Visibility Level
= link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer'
= render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false
+.form-group.row.initialize-with-readme-setting
+ %div{ :class => "col-sm-12" }
+ .form-check
+ = check_box_tag 'project[initialize_with_readme]', '1', false, class: 'form-check-input'
+ = label_tag 'project[initialize_with_readme]', class: 'form-check-label' do
+ .option-title
+ %strong Initialize repository with a README
+ .option-description
+ Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository.
+
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml
index d08807b5135..e90a6355214 100644
--- a/app/views/projects/_project_templates.html.haml
+++ b/app/views/projects/_project_templates.html.haml
@@ -1,27 +1,8 @@
-.project-templates-buttons.import-buttons
- - Gitlab::ProjectTemplate.all.each do |template|
- .template-option
- = custom_icon(template.logo)
- .template-title= template.title
- .template-description= template.description
- %label.btn.btn-success.template-button.choose-template.append-right-10{ for: template.name }
- %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name }
- %span Use template
- %a.btn.btn-default{ href: template.preview, rel: 'noopener noreferrer', target: '_blank' } Preview
+- f ||= local_assigns[:f]
- .project-fields-form
- .row
- .form-group.col-sm-12
- %label.label-light
- Template
- .input-group.template-input-group
- .input-group-prepend
- .input-group-text
- .selected-icon
- - Gitlab::ProjectTemplate.all.each do |template|
- = custom_icon(template.logo)
- .selected-template
- .input-group-append
- %button.btn.btn-default.change-template{ type: "button" } Change template
+.project-templates-buttons.import-buttons.col-sm-12
+ = render 'projects/project_templates/built_in_templates'
- = render 'new_project_fields', f: f, project_name_id: "template-project-name"
+.project-fields-form
+ = render 'projects/project_templates/project_fields_form'
+ = render 'projects/new_project_fields', f: f, project_name_id: "template-project-name"
diff --git a/app/views/projects/artifacts/file.html.haml b/app/views/projects/artifacts/file.html.haml
index aac7a1870df..f7174d6b2c6 100644
--- a/app/views/projects/artifacts/file.html.haml
+++ b/app/views/projects/artifacts/file.html.haml
@@ -27,6 +27,6 @@
.btn-group{ role: "group" }<
= copy_blob_source_button(blob)
- = open_raw_blob_button(blob)
+ = download_blob_button(blob)
= render 'projects/blob/content', blob: blob
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index e90916e340d..ef6f5c76de6 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -18,7 +18,7 @@
- commit = blame_group[:commit]
%td.blame-commit{ class: age_map_class(commit.committed_date, project_duration) }
.commit
- = author_avatar(commit, size: 36)
+ = author_avatar(commit, size: 36, has_tooltip: false)
.commit-row-title
%span.item-title.str-truncated-100
= link_to_markdown commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title
diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml
index 0a0b3ce1d6f..84ccd816d80 100644
--- a/app/views/projects/blob/_header.html.haml
+++ b/app/views/projects/blob/_header.html.haml
@@ -8,8 +8,8 @@
.btn-group{ role: "group" }<
= copy_blob_source_button(blob) unless blame
= open_raw_blob_button(blob)
+ = download_blob_button(blob)
= view_on_environment_button(@commit.sha, @path, @environment) if @environment
-
.btn-group{ role: "group" }<
= render_if_exists 'projects/blob/header_file_locks_link'
= edit_blob_button
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index efb8175398b..a0b0384d78d 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -3,6 +3,9 @@
- page_title @blob.path, @ref
+- signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.full_path, project_id: @project.path, id: @last_commit)
+.js-signature-container{ data: { 'signatures-path': signatures_path } }
+
%div{ class: container_class }
= render 'projects/last_push'
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index d8b4266143e..a2dc2730ecc 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,5 +1,5 @@
- if current_user
- %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
+ %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }>
- if current_user.starred?(@project)
= sprite_icon('star')
%span.starred= _('Unstar')
diff --git a/app/views/projects/clusters/_dropdown.html.haml b/app/views/projects/clusters/_dropdown.html.haml
deleted file mode 100644
index d55a9c60b64..00000000000
--- a/app/views/projects/clusters/_dropdown.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-%h4.prepend-top-0= s_('ClusterIntegration|Choose how to set up Kubernetes cluster integration')
-
-.dropdown.clusters-dropdown
- %button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', data: { toggle: 'dropdown' }, 'aria-haspopup': true, 'aria-expanded': false }
- %span.dropdown-toggle-text
- = dropdown_text
- = icon('chevron-down')
- %ul.dropdown-menu.clusters-dropdown-menu.dropdown-menu-full-width
- %li
- = link_to(s_('ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine'), gcp_new_namespace_project_clusters_path(@project.namespace, @project))
- %li
- = link_to(s_('ClusterIntegration|Add an existing Kubernetes cluster'), user_new_namespace_project_clusters_path(@project.namespace, @project))
diff --git a/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml b/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
index 9298d93663d..73b11d509d3 100644
--- a/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
+++ b/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
@@ -1,12 +1,12 @@
- link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
-.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert' }
+.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert' }
%button.close{ type: "button", data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } } &times;
- %div
- .col-sm-2.gcp-logo
- = image_tag 'illustrations/logos/google-cloud-platform_logo.svg'
- .col-sm-10
- %h4= s_('ClusterIntegration|Redeem up to $500 in free credit for Google Cloud Platform')
+ .gcp-signup-offer--content
+ .gcp-signup-offer--icon.append-right-8
+ = sprite_icon("information", size: 16)
+ .gcp-signup-offer--copy
+ %h4= s_('ClusterIntegration|Did you know?')
%p= s_('ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab\'s Google Kubernetes Engine Integration.').html_safe % { sign_up_link: link }
- %a.btn.btn-info{ href: 'https://goo.gl/AaJzRW', target: '_blank', rel: 'noopener noreferrer' }
+ %a.btn.btn-default{ href: 'https://goo.gl/AaJzRW', target: '_blank', rel: 'noopener noreferrer' }
Apply for credit
diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml
index b8e40b0a38b..9133de6559d 100644
--- a/app/views/projects/clusters/gcp/_form.html.haml
+++ b/app/views/projects/clusters/gcp/_form.html.haml
@@ -10,18 +10,20 @@
- link_to_help_page = link_to(s_('ClusterIntegration|help page'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
= s_('ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration.').html_safe % { link_to_help_page: link_to_help_page}
-= form_for @cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: gcp_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
- = form_errors(@cluster)
+%p= link_to('Select a different Google account', @authorize_url)
+
+= form_for @gcp_cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: create_gcp_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
+ = form_errors(@gcp_cluster)
.form-group
- = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light'
+ = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
.form-group
- = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-light'
+ = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
= field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
- = field.fields_for :provider_gcp, @cluster.provider_gcp do |provider_gcp_field|
+ = field.fields_for :provider_gcp, @gcp_cluster.provider_gcp do |provider_gcp_field|
.form-group
- = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'), class: 'label-light'
+ = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'), class: 'label-bold'
.js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } }
= provider_gcp_field.hidden_field :gcp_project_id
.dropdown
@@ -32,7 +34,7 @@
%span.form-text.text-muted &nbsp;
.form-group
- = provider_gcp_field.label :zone, s_('ClusterIntegration|Zone'), class: 'label-light'
+ = provider_gcp_field.label :zone, s_('ClusterIntegration|Zone'), class: 'label-bold'
.js-gcp-zone-dropdown-entry-point
= provider_gcp_field.hidden_field :zone
.dropdown
@@ -44,11 +46,11 @@
= 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 }
.form-group
- = provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes'), class: 'label-light'
+ = provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes'), class: 'label-bold'
= provider_gcp_field.text_field :num_nodes, class: 'form-control', placeholder: '3'
.form-group
- = provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type'), class: 'label-light'
+ = provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type'), class: 'label-bold'
.js-gcp-machine-type-dropdown-entry-point
= provider_gcp_field.hidden_field :machine_type
.dropdown
diff --git a/app/views/projects/clusters/gcp/_header.html.haml b/app/views/projects/clusters/gcp/_header.html.haml
index fa989943492..a2ad3cd64df 100644
--- a/app/views/projects/clusters/gcp/_header.html.haml
+++ b/app/views/projects/clusters/gcp/_header.html.haml
@@ -1,4 +1,4 @@
-%h4.prepend-top-20
+%h4
= s_('ClusterIntegration|Enter the details for your Kubernetes cluster')
%p
= s_('ClusterIntegration|Please make sure that your Google account meets the following requirements:')
diff --git a/app/views/projects/clusters/gcp/login.html.haml b/app/views/projects/clusters/gcp/login.html.haml
deleted file mode 100644
index 96c7a648676..00000000000
--- a/app/views/projects/clusters/gcp/login.html.haml
+++ /dev/null
@@ -1,21 +0,0 @@
-- breadcrumb_title 'Kubernetes'
-- page_title _("Login")
-
-= render_gcp_signup_offer
-
-.row.prepend-top-default
- .col-sm-4
- = render 'projects/clusters/sidebar'
- .col-sm-8
- = render 'projects/clusters/dropdown', dropdown_text: s_('ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine')
- = render 'header'
-.row
- .col-sm-8.offset-sm-4.signin-with-google
- - if @authorize_url
- = link_to(image_tag('auth_buttons/signin_with_google.png', width: '191px'), @authorize_url)
- = _('or')
- = link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer')
- - else
- .settings-message.text-center
- - link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer')
- = s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link }
diff --git a/app/views/projects/clusters/gcp/new.html.haml b/app/views/projects/clusters/gcp/new.html.haml
deleted file mode 100644
index ea78d66d883..00000000000
--- a/app/views/projects/clusters/gcp/new.html.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-- breadcrumb_title 'Kubernetes'
-- page_title _("New Kubernetes Cluster")
-
-.row.prepend-top-default
- .col-sm-4
- = render 'projects/clusters/sidebar'
- .col-sm-8
- = render 'projects/clusters/dropdown', dropdown_text: s_('ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine')
- = render 'header'
- = render 'form'
diff --git a/app/views/projects/clusters/new.html.haml b/app/views/projects/clusters/new.html.haml
index 828e2a84753..a38003f5750 100644
--- a/app/views/projects/clusters/new.html.haml
+++ b/app/views/projects/clusters/new.html.haml
@@ -1,15 +1,36 @@
- breadcrumb_title 'Kubernetes'
- page_title _("Kubernetes Cluster")
+- active_tab = local_assigns.fetch(:active_tab, 'gcp')
+= javascript_include_tag 'https://apis.google.com/js/api.js'
= render_gcp_signup_offer
.row.prepend-top-default
- .col-sm-4
+ .col-md-3
= render 'sidebar'
- .col-sm-8
- %h4.prepend-top-0= s_('ClusterIntegration|Choose how to set up Kubernetes cluster integration')
+ .col-md-9.js-toggle-container
+ %ul.nav-links.nav-tabs.gitlab-tabs.nav{ role: 'tablist' }
+ %li.nav-item{ role: 'presentation' }
+ %a.nav-link{ href: '#create-gcp-cluster-pane', id: 'create-gcp-cluster-tab', class: active_when(active_tab == 'gcp'), data: { toggle: 'tab' }, role: 'tab' }
+ %span Create new Cluster on GKE
+ %li.nav-item{ role: 'presentation' }
+ %a.nav-link{ href: '#add-user-cluster-pane', id: 'add-user-cluster-tab', class: active_when(active_tab == 'user'), data: { toggle: 'tab' }, role: 'tab' }
+ %span Add existing cluster
- %p= s_('ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab')
- = link_to s_('ClusterIntegration|Create on Google Kubernetes Engine'), gcp_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20'
- %p= s_('ClusterIntegration|Enter the details for an existing Kubernetes cluster')
- = link_to s_('ClusterIntegration|Add an existing Kubernetes cluster'), user_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20'
+ .tab-content.gitlab-tab-content
+ .tab-pane{ id: 'create-gcp-cluster-pane', class: active_when(active_tab == 'gcp'), role: 'tabpanel' }
+ = render 'projects/clusters/gcp/header'
+ - if @valid_gcp_token
+ = render 'projects/clusters/gcp/form'
+ - elsif @authorize_url
+ .signin-with-google
+ = link_to(image_tag('auth_buttons/signin_with_google.png', width: '191px'), @authorize_url)
+ = _('or')
+ = link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer')
+ - else
+ - link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer')
+ = s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link }
+
+ .tab-pane{ id: 'add-user-cluster-pane', class: active_when(active_tab == 'user'), role: 'tabpanel' }
+ = render 'projects/clusters/user/header'
+ = render 'projects/clusters/user/form'
diff --git a/app/views/projects/clusters/user/_form.html.haml b/app/views/projects/clusters/user/_form.html.haml
index d45ae6ec91f..e8ef0008802 100644
--- a/app/views/projects/clusters/user/_form.html.haml
+++ b/app/views/projects/clusters/user/_form.html.haml
@@ -1,28 +1,28 @@
-= form_for @cluster, url: user_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
- = form_errors(@cluster)
+= form_for @user_cluster, url: create_user_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
+ = form_errors(@user_cluster)
.form-group
- = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light'
+ = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
- if has_multiple_clusters?(@project)
.form-group
- = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-light'
+ = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
= field.text_field :environment_scope, class: 'form-control', placeholder: s_('ClusterIntegration|Environment scope')
- = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
+ = field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
- = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light'
+ = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold'
= platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
.form-group
- = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light'
+ = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-bold'
= platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
.form-group
- = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light'
+ = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-bold'
= platform_kubernetes_field.text_field :token, class: 'form-control', placeholder: s_('ClusterIntegration|Service token'), autocomplete: 'off'
.form-group
- = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light'
+ = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
.form-group
diff --git a/app/views/projects/clusters/user/_header.html.haml b/app/views/projects/clusters/user/_header.html.haml
index 37f6a788518..749177fa6c1 100644
--- a/app/views/projects/clusters/user/_header.html.haml
+++ b/app/views/projects/clusters/user/_header.html.haml
@@ -1,4 +1,4 @@
-%h4.prepend-top-20
+%h4
= s_('ClusterIntegration|Enter the details for your Kubernetes cluster')
%p
- link_to_help_page = link_to(s_('ClusterIntegration|documentation'), help_page_path('user/project/clusters/index', anchor: 'adding-an-existing-kubernetes-cluster'), target: '_blank', rel: 'noopener noreferrer')
diff --git a/app/views/projects/clusters/user/_show.html.haml b/app/views/projects/clusters/user/_show.html.haml
index 4d117f435dc..20a07d6695e 100644
--- a/app/views/projects/clusters/user/_show.html.haml
+++ b/app/views/projects/clusters/user/_show.html.haml
@@ -1,20 +1,20 @@
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
= form_errors(@cluster)
.form-group
- = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light'
+ = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
- = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light'
+ = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold'
= platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
.form-group
- = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light'
+ = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-bold'
= platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
.form-group
- = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light'
+ = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-bold'
.input-group
= platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token', type: 'password', placeholder: s_('ClusterIntegration|Token'), autocomplete: 'off'
%span.input-group-append.clipboard-addon
@@ -23,7 +23,7 @@
= s_('ClusterIntegration|Show')
.form-group
- = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light'
+ = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
.form-group
diff --git a/app/views/projects/clusters/user/new.html.haml b/app/views/projects/clusters/user/new.html.haml
deleted file mode 100644
index 7fb75cd9cc7..00000000000
--- a/app/views/projects/clusters/user/new.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-- breadcrumb_title 'Kubernetes'
-- page_title _("New Kubernetes cluster")
-
-.row.prepend-top-default
- .col-sm-4
- = render 'projects/clusters/sidebar'
- .col-sm-8
- = render 'projects/clusters/dropdown', dropdown_text: s_('ClusterIntegration|Add an existing Kubernetes cluster')
- = render 'header'
- .prepend-top-20
- = render 'form'
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index 3d97e93c9e9..afd70ef5774 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -11,7 +11,7 @@
- branch_label = s_('ChangeTypeActionLabel|Pick into branch')
- title = commit.merged_merge_request(current_user) ? _('Cherry-pick this merge request') : _('Cherry-pick this commit')
-.modal{ id: "modal-#{type}-commit" }
+.modal{ id: "modal-#{type}-commit", tabindex: -1 }
.modal-dialog
.modal-content
.modal-header
@@ -23,7 +23,7 @@
%p= description
= form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "js-#{type}-form js-requires-input" do
.form-group.branch
- = label_tag 'start_branch', branch_label, class: 'label-light'
+ = label_tag 'start_branch', branch_label, class: 'label-bold'
= hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch'
= dropdown_tag(@project.default_branch, options: { title: s_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: s_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: project_branches_path(@project), submit_form_on_click: false } })
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 886dd73c33b..aab5712d197 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -10,9 +10,10 @@
%span.d-none.d-sm-inline authored
#{time_ago_with_tooltip(@commit.authored_date)}
%span= s_('ByAuthor|by')
- = author_avatar(@commit, size: 24)
+ = author_avatar(@commit, size: 24, has_tooltip: false)
%strong
= commit_author_link(@commit, avatar: true, size: 24)
+ = user_status(@commit.author)
- if @commit.different_committer?
%span.light= _('Committed by')
%strong
@@ -54,7 +55,7 @@
%h3.commit-title
= markdown_field(@commit, :title)
- if @commit.description.present?
- .commit-description<
+ %pre.commit-description<
= preserve(markdown_field(@commit, :description))
.info-well
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 90e55fd0fb0..7951a5ddc9e 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -19,7 +19,7 @@
%li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" }
.avatar-cell.d-none.d-sm-block
- = author_avatar(commit, size: 36)
+ = author_avatar(commit, size: 36, has_tooltip: false)
.commit-detail.flex-list
.commit-content.qa-commit-content
@@ -44,7 +44,7 @@
#{ commit_text.html_safe }
- if commit.description?
- %pre.commit-row-description.js-toggle-content.prepend-top-8.append-bottom-8
+ %pre.commit-row-description.js-toggle-content.append-bottom-8
= preserve(markdown_field(commit, :description))
.commit-actions.flex-row.d-none.d-sm-flex
diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml
index 5ad8091a02b..f8ab0c1ec54 100644
--- a/app/views/projects/deploy_keys/_form.html.haml
+++ b/app/views/projects/deploy_keys/_form.html.haml
@@ -1,10 +1,10 @@
= form_for [@project.namespace.becomes(Namespace), @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input container" } do |f|
= form_errors(@deploy_keys.new_key)
.form-group.row
- = f.label :title, class: "label-light"
+ = f.label :title, class: "label-bold"
= f.text_field :title, class: 'form-control', required: true
.form-group.row
- = f.label :key, class: "label-light"
+ = f.label :key, class: "label-bold"
= f.text_area :key, class: "form-control", rows: 5, required: true
.form-group.row
%p.light.append-bottom-0
diff --git a/app/views/projects/deploy_keys/_index.html.haml b/app/views/projects/deploy_keys/_index.html.haml
index fb1ea471dec..062aa423bde 100644
--- a/app/views/projects/deploy_keys/_index.html.haml
+++ b/app/views/projects/deploy_keys/_index.html.haml
@@ -1,5 +1,5 @@
- expanded = Rails.env.test?
-%section.qa-deploy-keys-settings.settings.no-animate{ class: ('expanded' if expanded) }
+%section.qa-deploy-keys-settings.settings.no-animate#js-deploy-keys-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
Deploy Keys
diff --git a/app/views/projects/deploy_tokens/_form.html.haml b/app/views/projects/deploy_tokens/_form.html.haml
index f8db30df7b4..8b7535397bc 100644
--- a/app/views/projects/deploy_tokens/_form.html.haml
+++ b/app/views/projects/deploy_tokens/_form.html.haml
@@ -1,29 +1,29 @@
%p.profile-settings-content
= s_("DeployTokens|Pick a name for the application, and we'll give you a unique deploy token.")
-= form_for token, url: create_deploy_token_namespace_project_settings_repository_path(project.namespace, project), method: :post do |f|
+= form_for token, url: create_deploy_token_namespace_project_settings_repository_path(project.namespace, project, anchor: 'js-deploy-tokens'), method: :post do |f|
= form_errors(token)
.form-group
- = f.label :name, class: 'label-light'
+ = f.label :name, class: 'label-bold'
= f.text_field :name, class: 'form-control', required: true
.form-group
- = f.label :expires_at, class: 'label-light'
+ = f.label :expires_at, class: 'label-bold'
= f.text_field :expires_at, class: 'datepicker form-control', value: f.object.expires_at
.form-group
- = f.label :scopes, class: 'label-light'
- %fieldset
- = f.check_box :read_repository
- = label_tag ("deploy_token_read_repository"), 'read_repository'
- %span= s_('DeployTokens|Allows read-only access to the repository')
+ = f.label :scopes, class: 'label-bold'
+ %fieldset.form-group.form-check
+ = f.check_box :read_repository, class: 'form-check-input'
+ = label_tag ("deploy_token_read_repository"), 'read_repository', class: 'label-bold form-check-label'
+ .text-secondary= s_('DeployTokens|Allows read-only access to the repository')
- if container_registry_enabled?(project)
- %fieldset
- = f.check_box :read_registry
- = label_tag ("deploy_token_read_registry"), 'read_registry'
- %span= s_('DeployTokens|Allows read-only access to the registry images')
+ %fieldset.form-group.form-check
+ = f.check_box :read_registry, class: 'form-check-input'
+ = label_tag ("deploy_token_read_registry"), 'read_registry', class: 'label-bold form-check-label'
+ .text-secondary= s_('DeployTokens|Allows read-only access to the registry images')
.prepend-top-default
= f.submit s_('DeployTokens|Create deploy token'), class: 'btn btn-success'
diff --git a/app/views/projects/deploy_tokens/_revoke_modal.html.haml b/app/views/projects/deploy_tokens/_revoke_modal.html.haml
index a67c3a0c841..35eacae2c2e 100644
--- a/app/views/projects/deploy_tokens/_revoke_modal.html.haml
+++ b/app/views/projects/deploy_tokens/_revoke_modal.html.haml
@@ -1,4 +1,4 @@
-.modal{ id: "revoke-modal-#{token.id}" }
+.modal{ id: "revoke-modal-#{token.id}", tabindex: -1 }
.modal-dialog
.modal-content
.modal-header
diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml
index e0ecf56525a..f4c91377ecb 100644
--- a/app/views/projects/deployments/_actions.haml
+++ b/app/views/projects/deployments/_actions.haml
@@ -3,13 +3,12 @@
- if actions.present?
.btn-group
.dropdown
- %button.dropdown.dropdown-new.btn.btn-default{ type: 'button', 'data-toggle' => 'dropdown' }
- = custom_icon('icon_play')
+ %button.dropdown.dropdown-new.btn.btn-default.has-tooltip{ type: 'button', 'data-toggle' => 'dropdown', title: s_('Environments|Deploy to...') }
+ = sprite_icon('play')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-right
- actions.each do |action|
- next unless can?(current_user, :update_build, action)
%li
- = link_to [:play, @project.namespace.becomes(Namespace), @project, action], method: :post, rel: 'nofollow' do
- = custom_icon('icon_play')
+ = link_to [:play, @project.namespace.becomes(Namespace), @project, action], method: :post, rel: 'nofollow', class: 'btn' do
%span= action.name.humanize
diff --git a/app/views/projects/deployments/_rollback.haml b/app/views/projects/deployments/_rollback.haml
index 95f950948ab..281e042c915 100644
--- a/app/views/projects/deployments/_rollback.haml
+++ b/app/views/projects/deployments/_rollback.haml
@@ -1,6 +1,7 @@
- if can?(current_user, :create_deployment, deployment) && deployment.deployable
- = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build' do
+ - tooltip = deployment.last? ? s_('Environments|Re-deploy to environment') : s_('Environments|Rollback environment')
+ = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build has-tooltip', title: tooltip do
- if deployment.last?
- = _("Re-deploy")
+ = sprite_icon('repeat')
- else
- = _("Rollback")
+ = sprite_icon('redo')
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index c2d900cbcf7..30544dde451 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -15,20 +15,21 @@
.settings-content
.project-edit-errors
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f|
+ %input{ name: 'update_section', type: 'hidden', value: 'js-general-project-settings' }
%fieldset
.row
.form-group.col-md-9
- = f.label :name, class: 'label-light', for: 'project_name_edit' do
+ = f.label :name, class: 'label-bold', for: 'project_name_edit' do
Project name
= f.text_field :name, class: "form-control", id: "project_name_edit"
.form-group.col-md-3
- = f.label :id, class: 'label-light' do
+ = f.label :id, class: 'label-bold' do
Project ID
= f.text_field :id, class: 'form-control', readonly: true
.form-group
- = f.label :description, class: 'label-light' do
+ = f.label :description, class: 'label-bold' do
Project description
%span.light (optional)
= f.text_area :description, class: "form-control", rows: 3, maxlength: 250
@@ -37,13 +38,13 @@
- unless @project.empty_repo?
.form-group
- = f.label :default_branch, "Default Branch", class: 'label-light'
+ = f.label :default_branch, "Default Branch", class: 'label-bold'
= f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'})
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :project
.form-group
- = f.label :tag_list, "Tags", class: 'label-light'
+ = f.label :tag_list, "Tags", class: 'label-bold'
= f.text_field :tag_list, value: @project.tag_list.sort.join(', '), maxlength: 2000, class: "form-control"
%p.form-text.text-muted Separate tags with commas.
%fieldset.features
@@ -51,7 +52,7 @@
.form-group
- if @project.avatar?
.avatar-container.s160.append-bottom-15
- = project_icon(@project.full_path, alt: '', class: 'avatar project-avatar s160')
+ = project_icon(@project.full_path, alt: '', class: 'avatar project-avatar s160', width: 160, height: 160)
- if @project.avatar_in_git
%p.light
= _("Project avatar in repository: %{link}").html_safe % { link: @project.avatar_in_git }
@@ -75,6 +76,7 @@
Enable or disable certain project features and choose access levels.
.settings-content
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "sharing-permissions-form" }, authenticity_token: true do |f|
+ %input{ name: 'update_section', type: 'hidden', value: 'js-shared-permissions' }
-# haml-lint:disable InlineJavaScript
%script.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data(@project)
.js-project-permissions-form
@@ -94,6 +96,7 @@
= render_if_exists 'shared/promotions/promote_mr_features'
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f|
+ %input{ name: 'update_section', type: 'hidden', value: 'js-merge-request-settings' }
= render 'projects/merge_request_settings', form: f
= f.submit 'Save changes', class: "btn btn-save qa-save-merge-request-changes"
@@ -144,12 +147,12 @@
= render 'projects/errors'
= form_for([@project.namespace.becomes(Namespace), @project]) do |f|
.form-group.project_name_holder
- = f.label :name, class: 'label-light' do
+ = f.label :name, class: 'label-bold' do
Project name
.form-group
= f.text_field :name, class: "form-control"
.form-group
- = f.label :path, class: 'label-light' do
+ = f.label :path, class: 'label-bold' do
%span Path
.form-group
.input-group
@@ -169,7 +172,7 @@
Transfer project
= form_for([@project.namespace.becomes(Namespace), @project], url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } ) do |f|
.form-group
- = label_tag :new_namespace_id, nil, class: 'label-light' do
+ = label_tag :new_namespace_id, nil, class: 'label-bold' do
%span Select a new namespace
.form-group
= select_tag :new_namespace_id, namespaces_options(nil), include_blank: true, class: 'select2'
diff --git a/app/views/projects/environments/_external_url.html.haml b/app/views/projects/environments/_external_url.html.haml
index a264252e095..4694bc39d54 100644
--- a/app/views/projects/environments/_external_url.html.haml
+++ b/app/views/projects/environments/_external_url.html.haml
@@ -1,4 +1,4 @@
- if environment.external_url && can?(current_user, :read_environment, environment)
- = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url' do
+ = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url has-tooltip', title: s_('Environments|Open live environment') do
= sprite_icon('external-link')
View deployment
diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml
index 1605f3a3351..0586dbdf0e2 100644
--- a/app/views/projects/environments/_form.html.haml
+++ b/app/views/projects/environments/_form.html.haml
@@ -11,10 +11,10 @@
= form_errors(@environment)
.form-group
- = f.label :name, 'Name', class: 'label-light'
+ = f.label :name, 'Name', class: 'label-bold'
= f.text_field :name, required: true, class: 'form-control'
.form-group
- = f.label :external_url, 'External URL', class: 'label-light'
+ = f.label :external_url, 'External URL', class: 'label-bold'
= f.url_field :external_url, class: 'form-control'
.form-actions
diff --git a/app/views/projects/environments/_stop.html.haml b/app/views/projects/environments/_stop.html.haml
deleted file mode 100644
index c35f9af2873..00000000000
--- a/app/views/projects/environments/_stop.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- if can?(current_user, :create_deployment, environment) && environment.stop_action?
- .inline
- = link_to stop_project_environment_path(@project, environment), method: :post,
- class: 'btn stop-env-link', rel: 'nofollow', data: { confirm: 'Are you sure you want to stop this environment?' } do
- = icon('stop', class: 'stop-env-icon')
diff --git a/app/views/projects/environments/empty.html.haml b/app/views/projects/environments/empty.html.haml
new file mode 100644
index 00000000000..1413930ebdb
--- /dev/null
+++ b/app/views/projects/environments/empty.html.haml
@@ -0,0 +1,14 @@
+- page_title _("Metrics")
+
+.row
+ .col-sm-12
+ .svg-content
+ = image_tag 'illustrations/operations_metrics_empty.svg'
+.row.empty-environments
+ .col-sm-12.text-center
+ %h4
+ = s_('Metrics|No deployed environments')
+ .state-description
+ = s_('Metrics|Check out the CI/CD documentation on deploying to an environment')
+ .prepend-top-10
+ = link_to s_("Metrics|Learn about environments"), help_page_path('ci/environments'), class: 'btn btn-success'
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index d6f0b230b58..af86b8e8e67 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -9,15 +9,4 @@
Environment:
= link_to @environment.name, environment_path(@environment)
- #prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'),
- "clusters-path": project_clusters_path(@project),
- "documentation-path": help_page_path('administration/monitoring/prometheus/index.md'),
- "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'),
- "empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'),
- "empty-no-data-svg-path": image_path('illustrations/monitoring/no_data.svg'),
- "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'),
- "metrics-endpoint": additional_metrics_project_environment_path(@project, @environment, format: :json),
- "deployment-endpoint": project_environment_deployments_path(@project, @environment, format: :json),
- "project-path": project_path(@project),
- "tags-path": project_tags_path(@project),
- "has-metrics": "#{@environment.has_metrics?}" } }
+ #prometheus-graphs{ data: metrics_data(@project, @environment) }
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index add394a6356..c7890b37381 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -4,6 +4,33 @@
- page_title "Environments"
%div{ class: container_class }
+ - if can?(current_user, :stop_environment, @environment)
+ #stop-environment-modal.modal.fade{ tabindex: -1 }
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %h4.modal-title.d-flex.mw-100
+ Stopping
+ %span.has-tooltip.text-truncate.ml-1.mr-1.flex-fill{ title: @environment.name, data: { container: '#stop-environment-modal' } }
+ = @environment.name
+ ?
+ .modal-body
+ %p= s_('Environments|Are you sure you want to stop this environment?')
+ - unless @environment.stop_action_available?
+ .warning_message
+ %p= s_('Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file.').html_safe % { emphasis_start: '<strong>'.html_safe,
+ emphasis_end: '</strong>'.html_safe,
+ ci_config_link_start: '<a href="https://docs.gitlab.com/ee/ci/yaml/" target="_blank" rel="noopener noreferrer">'.html_safe,
+ ci_config_link_end: '</a>'.html_safe }
+ %a{ href: 'https://docs.gitlab.com/ee/ci/environments.html#stopping-an-environment',
+ target: '_blank',
+ rel: 'noopener noreferrer' }
+ = s_('Environments|Learn more about stopping environments')
+ .modal-footer
+ = button_tag _('Cancel'), type: 'button', class: 'btn btn-cancel', data: { dismiss: 'modal' }
+ = button_to stop_project_environment_path(@project, @environment), class: 'btn btn-danger has-tooltip', method: :post do
+ = s_('Environments|Stop environment')
+
.row.top-area.adjust
.col-md-7
%h3.page-title= @environment.name
@@ -15,7 +42,10 @@
- if can?(current_user, :update_environment, @environment)
= link_to 'Edit', edit_project_environment_path(@project, @environment), class: 'btn'
- if can?(current_user, :stop_environment, @environment)
- = link_to 'Stop', stop_project_environment_path(@project, @environment), data: { confirm: 'Are you sure you want to stop this environment?' }, class: 'btn btn-danger', method: :post
+ = button_tag class: 'btn btn-danger', type: 'button', data: { toggle: 'modal',
+ target: '#stop-environment-modal' } do
+ = sprite_icon('stop')
+ = s_('Environments|Stop')
.environments-container
- if @deployments.blank?
diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml
index 8a549d431ee..12cf40bb65f 100644
--- a/app/views/projects/forks/_fork_button.html.haml
+++ b/app/views/projects/forks/_fork_button.html.haml
@@ -5,7 +5,7 @@
.bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.append-bottom-default.forked
= link_to project_path(forked_project) do
- if /no_((\w*)_)*avatar/.match(avatar)
- = project_identicon(namespace, class: "avatar s100 identicon")
+ = project_icon(namespace, class: "avatar s100 identicon")
- else
.avatar-container.s100
= image_tag(avatar, class: "avatar s100")
@@ -18,7 +18,7 @@
class: ("disabled has-tooltip" unless can_create_project),
title: (_('You have reached your project limit') unless can_create_project) do
- if /no_((\w*)_)*avatar/.match(avatar)
- = project_identicon(namespace, class: "avatar s100 identicon")
+ = project_icon(namespace, class: "avatar s100 identicon")
- else
.avatar-container.s100
= image_tag(avatar, class: "avatar s100")
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index 16c4f21279d..8ce822c43b7 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -5,12 +5,12 @@
%hr
- if @project.import_failed?
- .card.bg-danger
- .card-header The repository could not be imported.
+ .card.border-danger
+ .card-header.bg-danger.text-white The repository could not be imported.
.card-body
%pre
:preserve
- #{h(sanitize_repo_path(@project, @project.import_error))}
+ #{h(@project.import_error)}
= form_for @project, url: project_import_path(@project), method: :post do |f|
= render "shared/import_form", f: f
diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml
index 2c5ffd85372..1e4e9450ffa 100644
--- a/app/views/projects/issues/_form.html.haml
+++ b/app/views/projects/issues/_form.html.haml
@@ -1,2 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @issue],
+ html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' },
+ data: { markdown_version: @issue.cached_markdown_version } do |f|
= render 'shared/issuable/form', f: f, issuable: @issue
diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml
index 459150c1067..759efd4e9d4 100644
--- a/app/views/projects/jobs/_sidebar.html.haml
+++ b/app/views/projects/jobs/_sidebar.html.haml
@@ -1,6 +1,10 @@
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" } }
.sidebar-container
.blocks-container
+ - if can?(current_user, :create_build_terminal, @build)
+ .block
+ = link_to terminal_project_job_path(@project, @build), class: 'terminal-button pull-right btn visible-md-block visible-lg-block', title: 'Terminal' do
+ Terminal
#js-details-block-vue{ data: { can_user_retry: can?(current_user, :update_build, @build) && @build.retryable? } }
@@ -14,8 +18,8 @@
#{time_ago_with_tooltip(@build.artifacts_expire_at)}
- elsif @build.has_expiring_artifacts?
%p.build-detail-row
- The artifacts will be removed in
- %span= time_ago_with_tooltip @build.artifacts_expire_at
+ The artifacts will be removed
+ #{time_ago_with_tooltip(@build.artifacts_expire_at)}
- if @build.artifacts?
.btn-group.d-flex{ role: :group }
@@ -82,7 +86,7 @@
- HasStatus::ORDERED_STATUSES.each do |build_status|
- builds.select{|build| build.status == build_status}.each do |build|
.build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } }
- - tooltip = build.tooltip_message
+ - tooltip = sanitize(build.tooltip_message.dup)
= link_to(project_job_path(@project, build), data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' }) do
= sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right')
%span{ class: "ci-status-icon-#{build.status}" }
diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml
index 1f33bb3a129..078f40c4477 100644
--- a/app/views/projects/jobs/show.html.haml
+++ b/app/views/projects/jobs/show.html.haml
@@ -22,7 +22,7 @@
%br
Go to
- = link_to project_runners_path(@build.project) do
+ = link_to project_runners_path(@build.project, anchor: 'js-runners-settings') do
Runners page
- if @build.starts_environment?
diff --git a/app/views/projects/jobs/terminal.html.haml b/app/views/projects/jobs/terminal.html.haml
new file mode 100644
index 00000000000..f7e7535ee92
--- /dev/null
+++ b/app/views/projects/jobs/terminal.html.haml
@@ -0,0 +1,11 @@
+- @no_container = true
+- add_to_breadcrumbs 'Jobs', project_jobs_path(@project)
+- add_to_breadcrumbs "##{@build.id}", project_job_path(@project, @build)
+- breadcrumb_title 'Terminal'
+- page_title 'Terminal', "#{@build.name} (##{@build.id})", 'Jobs'
+
+- content_for :page_specific_javascripts do
+ = stylesheet_link_tag "xterm.css"
+
+.terminal-container{ class: container_class }
+ #terminal{ data: { project_path: terminal_project_job_path(@project, @build, format: :ws) } }
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index fb5b0fc15c9..768ce9bd103 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -2,32 +2,45 @@
- page_title "Labels"
- can_admin_label = can?(current_user, :admin_label, @project)
- hide_class = ''
+- search = params[:search]
- if can_admin_label
- content_for(:header_content) do
.nav-controls
= link_to _('New label'), new_project_label_path(@project), class: "btn btn-new"
-- if @labels.exists? || @prioritized_labels.exists?
+- if @labels.exists? || @prioritized_labels.exists? || search.present?
#promote-label-modal
%div{ class: container_class }
.top-area.adjust
.nav-text
= _('Labels can be applied to issues and merge requests.')
- - if can_admin_label
- = _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.')
- .labels-container.prepend-top-5
+ .nav-controls
+ = form_tag project_labels_path(@project), method: :get do
+ .input-group
+ = search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false }
+ %span.input-group-append
+ %button.btn.btn-default{ type: "submit", "aria-label" => _('Submit search') }
+ = icon("search")
+
+ .labels-container.prepend-top-10
- if can_admin_label
+ - if search.blank?
+ %p.text-muted
+ = _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.')
-# Only show it in the first page
- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1')
.prioritized-labels{ class: ('hide' if hide) }
%h5.prepend-top-10= _('Prioritized Labels')
.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) }
- #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" }
+ #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty? && search.blank?}" }
= render 'shared/empty_states/priority_labels'
- if @prioritized_labels.present?
= render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label, locals: { force_priority: true }
+ - elsif search.present?
+ .nothing-here-block
+ = _('No prioritised labels with such name or description')
- if @labels.present?
.other-labels
@@ -36,6 +49,18 @@
.content-list.manage-labels-list.js-other-labels
= render partial: 'shared/label', subject: @project, collection: @labels, as: :label
= paginate @labels, theme: 'gitlab'
+ - elsif search.present?
+ .other-labels
+ - if @available_labels.any?
+ %h5
+ = _('Other Labels')
+ .nothing-here-block
+ = _('No other labels with such name or description')
+ - else
+ .nothing-here-block
+ = _('No labels with such name or description')
+
+
- else
= render 'shared/empty_states/labels'
diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml
index 179c1fcc684..5a59f956cb5 100644
--- a/app/views/projects/merge_requests/_form.html.haml
+++ b/app/views/projects/merge_requests/_form.html.haml
@@ -1,2 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @merge_request],
+ html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' },
+ data: { markdown_version: @merge_request.cached_markdown_version } do |f|
= render 'shared/issuable/form', f: f, issuable: @merge_request
diff --git a/app/views/projects/merge_requests/_mr_box.html.haml b/app/views/projects/merge_requests/_mr_box.html.haml
index 8a390cf8700..1a9ab288683 100644
--- a/app/views/projects/merge_requests/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/_mr_box.html.haml
@@ -1,4 +1,4 @@
-.detail-page-description.content-block
+.detail-page-description
%h2.title
= markdown_field(@merge_request, :title)
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 4d84ee2488b..8181267184a 100644
--- a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
+++ b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
@@ -9,7 +9,7 @@
.resolve-info
= translation.html_safe
.col-md-8
- %label.label-light{ "for" => "commit-message" }
+ %label.label-bold{ "for" => "commit-message" }
#{ _('Commit message') }
.commit-message-container
.max-width-marker
diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml
index ca0f7d6098f..afa7eb06cb4 100644
--- a/app/views/projects/merge_requests/creations/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml
@@ -27,7 +27,7 @@
= dropdown_filter(_("Search branches"))
= dropdown_content
= dropdown_loading
- .panel-footer
+ .card-footer
.text-center= icon('spinner spin', class: 'js-source-loading')
%ul.list-unstyled.mr_source_commit
diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml
index b2eacabc21a..f7a5d85500f 100644
--- a/app/views/projects/merge_requests/creations/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml
@@ -24,23 +24,28 @@
There are no commits yet.
= custom_icon ('illustration_no_commits')
- else
- %ul.merge-request-tabs.nav.nav-tabs.nav-links.no-top.no-bottom
- %li.commits-tab
- = link_to url_for(safe_params), data: {target: 'div#commits', action: 'new', toggle: 'tab'} do
- Commits
- %span.badge.badge-pill= @commits.size
- - if @pipelines.any?
- %li.builds-tab
- = link_to url_for(safe_params.merge(action: 'pipelines')), data: {target: 'div#pipelines', action: 'pipelines', toggle: 'tab'} do
- Pipelines
- %span.badge.badge-pill= @pipelines.size
- %li.diffs-tab
- = link_to url_for(safe_params.merge(action: 'diffs')), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do
- Changes
- %span.badge.badge-pill= @merge_request.diff_size
+ .merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
+ .merge-request-tabs-container
+ .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
+ .fade-left= icon('angle-left')
+ .fade-right= icon('angle-right')
+ %ul.merge-request-tabs.nav.nav-tabs.nav-links.no-top.no-bottom.js-tabs-affix
+ %li.commits-tab.new-tab
+ = link_to url_for(safe_params), data: {target: 'div#commits', action: 'new', toggle: 'tabvue'} do
+ Commits
+ %span.badge.badge-pill= @commits.size
+ - if @pipelines.any?
+ %li.builds-tab
+ = link_to url_for(safe_params.merge(action: 'pipelines')), data: {target: 'div#pipelines', action: 'pipelines', toggle: 'tabvue'} do
+ Pipelines
+ %span.badge.badge-pill= @pipelines.size
+ %li.diffs-tab
+ = link_to url_for(safe_params.merge(action: 'diffs')), data: {target: 'div#diffs', action: 'diffs', toggle: 'tabvue'} do
+ Changes
+ %span.badge.badge-pill= @merge_request.diff_size
- .tab-content
- #commits.commits.tab-pane.active
+ #diff-notes-app.tab-content
+ #new.commits.tab-pane.active
= render "projects/merge_requests/commits"
#diffs.diffs.tab-pane
-# This tab is always loaded via AJAX
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 4cc59718715..28f0a167128 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -1,16 +1,18 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'milestone-form common-note-form js-quick-submit js-requires-input'} do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @milestone],
+ html: {class: 'milestone-form common-note-form js-quick-submit js-requires-input'},
+ data: { markdown_version: @milestone.cached_markdown_version } do |f|
= form_errors(@milestone)
.row
.col-md-6
.form-group.row
= f.label :title, "Title", class: "col-form-label col-sm-2"
.col-sm-10
- = f.text_field :title, maxlength: 255, class: "form-control", required: true, autofocus: true
+ = f.text_field :title, maxlength: 255, class: "qa-milestone-title form-control", required: true, autofocus: true
.form-group.row.milestone-description
= f.label :description, "Description", class: "col-form-label col-sm-2"
.col-sm-10
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project) } do
- = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: 'Write milestone description...'
+ = render 'projects/zen', f: f, attr: :description, classes: 'qa-milestone-description note-textarea', placeholder: 'Write milestone description...'
= render 'shared/notes/hints'
.clearfix
.error-alert
@@ -18,7 +20,7 @@
.form-actions
- if @milestone.new_record?
- = f.submit 'Create milestone', class: "btn-create btn"
+ = f.submit 'Create milestone', class: "btn-create btn qa-milestone-create-button"
= link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel"
- else
= f.submit 'Save changes', class: "btn-save btn"
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index 5b0197ed58c..26d2ea8447b 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -8,7 +8,7 @@
.nav-controls
= render 'shared/milestones_sort_dropdown'
- if can?(current_user, :admin_milestone, @project)
- = link_to new_project_milestone_path(@project), class: "btn btn-new", title: 'New milestone' do
+ = link_to new_project_milestone_path(@project), class: "btn btn-new qa-new-project-milestone", title: 'New milestone' do
New milestone
.milestones
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index f7b04c436a6..2a9e20c2caa 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -60,7 +60,7 @@
= icon('angle-double-left')
.detail-page-description.milestone-detail
- %h2.title
+ %h2.title.qa-milestone-title
= markdown_field(@milestone, :title)
%div
diff --git a/app/views/projects/mirrors/_push.html.haml b/app/views/projects/mirrors/_push.html.haml
index 2b2871a81e5..08375e09816 100644
--- a/app/views/projects/mirrors/_push.html.haml
+++ b/app/views/projects/mirrors/_push.html.haml
@@ -32,11 +32,11 @@
.form-group
= rm_form.check_box :enabled, class: "float-left"
.prepend-left-20
- = rm_form.label :enabled, "Remote mirror repository", class: "label-light append-bottom-0"
+ = rm_form.label :enabled, "Remote mirror repository", class: "label-bold append-bottom-0"
%p.light.append-bottom-0
Automatically update the remote mirror's branches, tags, and commits from this repository every time someone pushes to it.
.form-group.has-feedback
- = rm_form.label :url, "Git repository URL", class: "label-light"
+ = rm_form.label :url, "Git repository URL", class: "label-bold"
= rm_form.text_field :url, class: "form-control", placeholder: 'https://username:password@gitlab.company.com/group/project.git'
= render "projects/mirrors/instructions"
@@ -44,7 +44,7 @@
.form-group
= rm_form.check_box :only_protected_branches, class: 'float-left'
.prepend-left-20
- = rm_form.label :only_protected_branches, class: 'label-light'
+ = rm_form.label :only_protected_branches, class: 'label-bold'
= link_to icon('question-circle'), help_page_path('user/project/protected_branches')
= f.submit 'Save changes', class: 'btn btn-create', name: 'update_remote_mirror'
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 5bb1bfb7059..6c363345e38 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -55,13 +55,12 @@
= render 'project_templates', f: f
.tab-pane.import-project-pane.js-toggle-container{ id: 'import-project-pane', class: active_when(active_tab == 'import'), role: 'tabpanel' }
- = form_for @project, html: { class: 'new_project' } do |f|
- - if import_sources_enabled?
- = render 'import_project_pane', f: f, active_tab: active_tab
- - else
- .nothing-here-block
- %h4 No import options available
- %p Contact an administrator to enable options for importing your project.
+ - if import_sources_enabled?
+ = render 'import_project_pane', active_tab: active_tab
+ - else
+ .nothing-here-block
+ %h4 No import options available
+ %p Contact an administrator to enable options for importing your project.
.save-project-loader.d-none
.center
diff --git a/app/views/projects/pages/_destroy.haml b/app/views/projects/pages/_destroy.haml
index 9b77c4e3494..ae8c801b705 100644
--- a/app/views/projects/pages/_destroy.haml
+++ b/app/views/projects/pages/_destroy.haml
@@ -1,7 +1,7 @@
- if @project.pages_deployed?
- if can?(current_user, :remove_pages, @project)
- .card.bg-danger
- .card-header Remove pages
+ .card.border-danger
+ .card-header.bg-danger.text-white Remove pages
.errors-holder
.card-body
%p
diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml
index 1cdf981fcb4..9a981d53ab6 100644
--- a/app/views/projects/pipeline_schedules/_form.html.haml
+++ b/app/views/projects/pipeline_schedules/_form.html.haml
@@ -2,25 +2,25 @@
= form_errors(@schedule)
.form-group.row
.col-md-9
- = f.label :description, _('Description'), class: 'label-light'
+ = f.label :description, _('Description'), class: 'label-bold'
= f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: s_('PipelineSchedules|Provide a short description for this pipeline')
.form-group.row
.col-md-9
- = f.label :cron, _('Interval Pattern'), class: 'label-light'
+ = f.label :cron, _('Interval Pattern'), class: 'label-bold'
#interval-pattern-input{ data: { initial_interval: @schedule.cron } }
.form-group.row
.col-md-9
- = f.label :cron_timezone, _('Cron Timezone'), class: 'label-light'
+ = f.label :cron_timezone, _('Cron Timezone'), class: 'label-bold'
= dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
= f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true
.form-group.row
.col-md-9
- = f.label :ref, _('Target Branch'), class: 'label-light'
+ = f.label :ref, _('Target Branch'), class: 'label-bold'
= dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
= f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true
.form-group.row.js-ci-variable-list-section
.col-md-9
- %label.label-light
+ %label.label-bold
#{ s_('PipelineSchedules|Variables') }
%ul.ci-variable-list
- @schedule.variables.each do |variable|
@@ -34,7 +34,7 @@
= n_('Reveal value', 'Reveal values', @schedule.variables.size)
.form-group.row
.col-md-9
- = f.label :active, s_('PipelineSchedules|Activated'), class: 'label-light'
+ = f.label :active, s_('PipelineSchedules|Activated'), class: 'label-bold'
%div
= f.check_box :active, required: false, value: @schedule.active?
= _('Active')
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 04131a90a57..ccb83148ded 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -1,35 +1,34 @@
-#js-pipeline-header-vue.pipeline-header-container
+.commit-box
+ %h3.commit-title
+ = markdown(commit.title, pipeline: :single_line)
+ - if commit.description.present?
+ %pre.commit-description<
+ = preserve(markdown(commit.description, pipeline: :single_line))
-- if @commit.present?
- .commit-box
- %h3.commit-title
- = markdown(@commit.title, pipeline: :single_line)
- - if @commit.description.present?
- .commit-description<
- = preserve(markdown(@commit.description, pipeline: :single_line))
+.info-well
+ - if commit.status
+ .well-segment.pipeline-info
+ .icon-container
+ = icon('clock-o')
+ = pluralize @pipeline.total_size, "job"
+ - if @pipeline.ref
+ from
+ = link_to @pipeline.ref, project_ref_path(@project, @pipeline.ref), class: "ref-name"
+ - if @pipeline.duration
+ in
+ = time_interval_in_words(@pipeline.duration)
+ - if @pipeline.queued_duration
+ = "(queued for #{time_interval_in_words(@pipeline.queued_duration)})"
- .info-well
- - if @commit.status
- .well-segment.pipeline-info
- .icon-container
- = icon('clock-o')
- = pluralize @pipeline.total_size, "job"
- - if @pipeline.ref
- from
- = link_to @pipeline.ref, project_ref_path(@project, @pipeline.ref), class: "ref-name"
- - if @pipeline.duration
- in
- = time_interval_in_words(@pipeline.duration)
- - if @pipeline.queued_duration
- = "(queued for #{time_interval_in_words(@pipeline.queued_duration)})"
+ .well-segment.branch-info
+ .icon-container.commit-icon
+ = custom_icon("icon_commit")
+ = link_to commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short"
+ = link_to("#", class: "js-details-expand d-none d-sm-none d-md-inline") do
+ %span.text-expander
+ = sprite_icon('ellipsis_h', size: 12)
+ %span.js-details-content.hide
+ = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full"
+ = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
- .well-segment.branch-info
- .icon-container.commit-icon
- = custom_icon("icon_commit")
- = link_to @commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short"
- = link_to("#", class: "js-details-expand d-none d-sm-none d-md-inline") do
- %span.text-expander
- = sprite_icon('ellipsis_h', size: 12)
- %span.js-details-content.hide
- = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full"
- = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
+ = render_if_exists "projects/pipelines/info_extension", pipeline: @pipeline
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index 951f80b378d..c63ff070f70 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -12,6 +12,7 @@
= link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do
= _("Failed Jobs")
%span.badge.badge-pill.js-failures-counter= @pipeline.failed_builds.count
+ = render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project
.tab-content
#js-tab-pipeline.tab-pane
@@ -75,4 +76,4 @@
%pre.build-trace.build-trace-rounded
%code.bash.js-build-output
= build_summary(build)
-
+ = render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index 956f8fef6b8..c13e3194340 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -10,7 +10,7 @@
= form_errors(@pipeline)
.form-group.row
.col-sm-12
- = f.label :ref, s_('Pipeline|Create for')
+ = f.label :ref, s_('Pipeline|Create for'), class: 'col-form-label'
= hidden_field_tag 'pipeline[ref]', params[:ref] || @project.default_branch
= dropdown_tag(params[:ref] || @project.default_branch,
options: { toggle_class: 'js-branch-select wide git-revision-dropdown-toggle',
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index a7d7c923957..ff0ed3ed30d 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -4,8 +4,10 @@
- page_title "Pipeline"
.js-pipeline-container{ class: container_class, data: { controller_action: "#{controller.action_name}" } }
- - if @commit
- = render "projects/pipelines/info"
+ #js-pipeline-header-vue.pipeline-header-container
+
+ - if @pipeline.commit.present?
+ = render "projects/pipelines/info", commit: @pipeline.commit
= render "projects/pipelines/with_tabs", pipeline: @pipeline
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index 5064db8d5cd..6272687be1c 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -2,10 +2,10 @@
.col-sm-12
= form_for @project_member, as: :project_member, url: project_project_members_path(@project), html: { class: 'users-project-form' } do |f|
.form-group
- = label_tag :user_ids, "Select members to invite", class: "label-light"
+ = label_tag :user_ids, "Select members to invite", class: "label-bold"
= users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
.form-group
- = label_tag :access_level, "Choose a role permission", class: "label-light"
+ = label_tag :access_level, "Choose a role permission", class: "label-bold"
.select-wrapper
= select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select select-control"
= icon('chevron-down')
@@ -14,7 +14,7 @@
about role permissions
.form-group
.clearable-input
- = label_tag :expires_at, 'Access expiration date', class: 'label-light'
+ = label_tag :expires_at, 'Access expiration date', class: 'label-bold'
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
%i.clear-icon.js-clear-input
= f.submit "Add to project", class: "btn btn-create"
diff --git a/app/views/projects/project_members/_new_shared_group.html.haml b/app/views/projects/project_members/_new_shared_group.html.haml
index 684219735e2..d7227c32833 100644
--- a/app/views/projects/project_members/_new_shared_group.html.haml
+++ b/app/views/projects/project_members/_new_shared_group.html.haml
@@ -2,10 +2,10 @@
.col-sm-12
= form_tag project_group_links_path(@project), class: 'js-requires-input', method: :post do
.form-group
- = label_tag :link_group_id, "Select a group to share with", class: "label-light"
+ = label_tag :link_group_id, "Select a group to share with", class: "label-bold"
= groups_select_tag(:link_group_id, data: { skip_groups: @skip_groups }, class: "input-clamp", required: true)
.form-group
- = label_tag :link_group_access, "Max access level", class: "label-light"
+ = label_tag :link_group_access, "Max access level", class: "label-bold"
.select-wrapper
= select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
= icon('chevron-down')
@@ -13,7 +13,7 @@
= link_to "Read more", help_page_path("user/permissions"), class: "vlink"
about role permissions
.form-group
- = label_tag :expires_at, 'Access expiration date', class: 'label-light'
+ = label_tag :expires_at, 'Access expiration date', class: 'label-bold'
.clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date-groups', placeholder: 'Expiration date', id: 'expires_at_groups'
%i.clear-icon.js-clear-input
diff --git a/app/views/projects/project_templates/_built_in_templates.html.haml b/app/views/projects/project_templates/_built_in_templates.html.haml
new file mode 100644
index 00000000000..e7636099be6
--- /dev/null
+++ b/app/views/projects/project_templates/_built_in_templates.html.haml
@@ -0,0 +1,17 @@
+- Gitlab::ProjectTemplate.all.each do |template|
+ .template-option.d-flex.align-items-center
+ .logo.append-right-10
+ = custom_icon(template.logo, size: 40)
+ .description
+ %strong
+ = template.title
+ %br
+ .text-muted
+ = template.description
+ .controls.d-flex.align-items-center
+ %label.btn.btn-success.template-button.choose-template.append-right-10.append-bottom-0{ for: template.name }
+ %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name }
+ %span
+ = _("Use template")
+ %a.btn.btn-default{ href: template.preview, rel: 'noopener noreferrer', target: '_blank' }
+ = _("Preview")
diff --git a/app/views/projects/project_templates/_project_fields_form.html.haml b/app/views/projects/project_templates/_project_fields_form.html.haml
new file mode 100644
index 00000000000..c96010550d8
--- /dev/null
+++ b/app/views/projects/project_templates/_project_fields_form.html.haml
@@ -0,0 +1,12 @@
+.row
+ .form-group.col-sm-12
+ %label.label-bold
+ = _('Template')
+ .input-group.template-input-group
+ .input-group-prepend
+ .input-group-text
+ .selected-icon.append-right-10
+ .selected-template
+ .input-group-append
+ %button.btn.btn-default.change-template{ type: "button" }
+ = _('Change template')
diff --git a/app/views/projects/protected_branches/_update_protected_branch.html.haml b/app/views/projects/protected_branches/_update_protected_branch.html.haml
index f242459f69b..74bfaa9ff80 100644
--- a/app/views/projects/protected_branches/_update_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/_update_protected_branch.html.haml
@@ -6,5 +6,5 @@
%td
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_levels.first.access_level
= dropdown_tag( (protected_branch.push_access_levels.first.humanize || 'Select') ,
- options: { toggle_class: 'js-allowed-to-push qa-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
+ options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
data: { field_name: "allowed_to_push_#{protected_branch.id}", access_level_id: protected_branch.push_access_levels.first.id }})
diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
index c2d6c034e35..df2dcf19ed4 100644
--- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
@@ -1,4 +1,5 @@
= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f|
+ %input{ type: 'hidden', name: 'update_section', value: 'js-protected-branches-settings' }
.card
.card-header
%h3.card-title
diff --git a/app/views/projects/protected_branches/shared/_index.html.haml b/app/views/projects/protected_branches/shared/_index.html.haml
index 4f1c6c92484..539b184e5c2 100644
--- a/app/views/projects/protected_branches/shared/_index.html.haml
+++ b/app/views/projects/protected_branches/shared/_index.html.haml
@@ -1,6 +1,6 @@
- expanded = Rails.env.test?
-%section.qa-protected-branches-settings.settings.no-animate{ class: ('expanded' if expanded) }
+%section.qa-protected-branches-settings.settings.no-animate#js-protected-branches-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
Protected Branches
diff --git a/app/views/projects/protected_branches/shared/_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
index 82ef08272d3..81b07af22ad 100644
--- a/app/views/projects/protected_branches/shared/_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
@@ -2,7 +2,7 @@
%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) } }
%td
- %span.ref-name.qa-protected-branch-name= protected_branch.name
+ %span.ref-name= protected_branch.name
- if @project.root_ref?(protected_branch.name)
%span.badge.badge-info.prepend-left-5 default
@@ -21,4 +21,4 @@
- if can_admin_project
%td
- = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], disabled: local_assigns[:disabled], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning"
+ = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning"
diff --git a/app/views/projects/protected_tags/_protected_tag.html.haml b/app/views/projects/protected_tags/_protected_tag.html.haml
index da1f97c8d6a..e0912bf39c0 100644
--- a/app/views/projects/protected_tags/_protected_tag.html.haml
+++ b/app/views/projects/protected_tags/_protected_tag.html.haml
@@ -1,2 +1,4 @@
= render layout: 'projects/protected_tags/shared/protected_tag', locals: { protected_tag: protected_tag } do
- = render partial: 'projects/protected_tags/update_protected_tag', locals: { protected_tag: protected_tag }
+ %td
+ = render 'projects/protected_tags/protected_tag_create_access_levels', protected_tag: protected_tag, create_access_level: protected_tag.create_access_levels.for_role.first
+ = render_if_exists 'projects/protected_tags/protected_tag_extra_create_access_levels', protected_tag: protected_tag
diff --git a/app/views/projects/protected_tags/_protected_tag_create_access_levels.haml b/app/views/projects/protected_tags/_protected_tag_create_access_levels.haml
new file mode 100644
index 00000000000..1d4e9565156
--- /dev/null
+++ b/app/views/projects/protected_tags/_protected_tag_create_access_levels.haml
@@ -0,0 +1,8 @@
+- protected_tag = local_assigns.fetch(:protected_tag)
+- create_access_level = local_assigns.fetch(:create_access_level)
+- dropdown_label = create_access_level&.humanize || 'Select'
+
+= hidden_field_tag "allowed_to_create_#{protected_tag.id}", create_access_level&.access_level
+= dropdown_tag(dropdown_label,
+ options: { toggle_class: 'js-allowed-to-create', dropdown_class: 'dropdown-menu-selectable capitalize-header js-allowed-to-create-container',
+ data: { field_name: "allowed_to_create_#{protected_tag.id}", access_level_id: create_access_level&.id }})
diff --git a/app/views/projects/protected_tags/_update_protected_tag.haml b/app/views/projects/protected_tags/_update_protected_tag.haml
deleted file mode 100644
index cc80bd04dd0..00000000000
--- a/app/views/projects/protected_tags/_update_protected_tag.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-%td
- = hidden_field_tag "allowed_to_create_#{protected_tag.id}", protected_tag.create_access_levels.first.access_level
- = dropdown_tag( (protected_tag.create_access_levels.first.humanize || 'Select') ,
- options: { toggle_class: 'js-allowed-to-create', dropdown_class: 'dropdown-menu-selectable capitalize-header js-allowed-to-create-container',
- data: { field_name: "allowed_to_create_#{protected_tag.id}", access_level_id: protected_tag.create_access_levels.first.id }})
diff --git a/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml
index 0ae5ca3ff36..f98781b77f4 100644
--- a/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml
+++ b/app/views/projects/protected_tags/shared/_create_protected_tag.html.haml
@@ -1,4 +1,5 @@
= form_for [@project.namespace.becomes(Namespace), @project, @protected_tag], html: { class: 'new-protected-tag js-new-protected-tag' } do |f|
+ %input{ type: 'hidden', name: 'update_section', value: 'js-protected-tags-settings' }
.card
.card-header
%h3.card-title
diff --git a/app/views/projects/protected_tags/shared/_protected_tag.html.haml b/app/views/projects/protected_tags/shared/_protected_tag.html.haml
index 1702e38df7e..cc6f0309123 100644
--- a/app/views/projects/protected_tags/shared/_protected_tag.html.haml
+++ b/app/views/projects/protected_tags/shared/_protected_tag.html.haml
@@ -19,4 +19,4 @@
- if can? current_user, :admin_project, @project
%td
- = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_tag], data: { confirm: 'Tag will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning'
+ = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_tag, { update_section: 'js-protected-tags-settings' }], data: { confirm: 'Tag will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning'
diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml
index d6f758608a0..8093cc2c2d7 100644
--- a/app/views/projects/releases/edit.html.haml
+++ b/app/views/projects/releases/edit.html.haml
@@ -11,7 +11,9 @@
%strong= @tag.name
- = form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name), html: { class: 'common-note-form release-form js-quick-submit' }) do |f|
+ = form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name),
+ html: { class: 'common-note-form release-form js-quick-submit' },
+ data: { markdown_version: @release.cached_markdown_version }) do |f|
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
= render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here…"
= render 'shared/notes/hints'
diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml
index a23f5d6f0c3..6ee83fae25e 100644
--- a/app/views/projects/runners/_runner.html.haml
+++ b/app/views/projects/runners/_runner.html.haml
@@ -26,7 +26,7 @@
- else
- runner_project = @project.runner_projects.find_by(runner_id: runner)
= link_to _('Disable for this project'), project_runner_project_path(@project, runner_project), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn btn-danger btn-sm'
- - elsif !(runner.is_shared? || runner.group_type?) # We can simplify this to `runner.project_type?` when migrating #runner_type is complete
+ - elsif runner.project_type?
= form_for [@project.namespace.becomes(Namespace), @project, @project.runner_projects.new] do |f|
= f.hidden_field :runner_id, value: runner.id
= f.submit _('Enable for this project'), class: 'btn btn-sm'
diff --git a/app/views/projects/services/prometheus/_metrics.html.haml b/app/views/projects/services/prometheus/_metrics.html.haml
new file mode 100644
index 00000000000..98d64fafe86
--- /dev/null
+++ b/app/views/projects/services/prometheus/_metrics.html.haml
@@ -0,0 +1,30 @@
+- project = local_assigns.fetch(:project)
+
+.card.js-panel-monitored-metrics{ data: { active_metrics: active_common_project_prometheus_metrics_path(project, :json), metrics_help_path: help_page_path('user/project/integrations/prometheus_library/metrics') } }
+ .card-header
+ %h3.card-title
+ = s_('PrometheusService|Common metrics')
+ %span.badge.badge-pill.js-monitored-count 0
+ .card-body
+ .loading-metrics.js-loading-metrics
+ %p.prepend-top-10.prepend-left-10
+ = icon('spinner spin', class: 'metrics-load-spinner')
+ = s_('PrometheusService|Finding and configuring metrics...')
+ .empty-metrics.hidden.js-empty-metrics
+ %p.text-tertiary.prepend-top-10.prepend-left-10
+ = s_('PrometheusService|Waiting for your first deployment to an environment to find common metrics')
+ %ul.list-unstyled.metrics-list.hidden.js-metrics-list
+
+.card.hidden.js-panel-missing-env-vars
+ .card-header
+ %h3.card-title
+ = icon('caret-right lg fw', class: 'panel-toggle js-panel-toggle', 'aria-label' => 'Toggle panel')
+ = s_('PrometheusService|Missing environment variable')
+ %span.badge.badge-pill.js-env-var-count 0
+ .card-body.hidden
+ .flash-container
+ .flash-notice
+ .flash-text
+ = s_("PrometheusService|To set up automatic monitoring, add the environment variable %{variable} to exporter's queries." % { variable: "<code>$CI_ENVIRONMENT_SLUG</code>" }).html_safe
+ = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus', anchor: 'metrics-and-labels')
+ %ul.list-unstyled.metrics-list.js-missing-var-metrics-list
diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml
index bda597cc02b..1d0b0265bb7 100644
--- a/app/views/projects/services/prometheus/_show.html.haml
+++ b/app/views/projects/services/prometheus/_show.html.haml
@@ -3,35 +3,8 @@
%h4.prepend-top-0
= s_('PrometheusService|Metrics')
%p
- = s_('PrometheusService|Metrics are automatically configured and monitored based on a library of metrics from popular exporters.')
- = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus')
+ = s_('PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters.')
+ = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer"
.col-lg-9
- .card.js-panel-monitored-metrics{ data: { active_metrics: active_common_project_prometheus_metrics_path(@project, :json), metrics_help_path: help_page_path('user/project/integrations/prometheus_library/metrics') } }
- .card-header
- %h3.card-title
- = s_('PrometheusService|Common metrics')
- %span.badge.badge-pill.js-monitored-count 0
- .card-body
- .loading-metrics.js-loading-metrics
- %p.prepend-top-10.prepend-left-10
- = icon('spinner spin', class: 'metrics-load-spinner')
- = s_('PrometheusService|Finding and configuring metrics...')
- .empty-metrics.hidden.js-empty-metrics
- %p.text-tertiary.prepend-top-10.prepend-left-10
- = s_('PrometheusService|Waiting for your first deployment to an environment to find common metrics')
- %ul.list-unstyled.metrics-list.hidden.js-metrics-list
-
- .card.hidden.js-panel-missing-env-vars
- .card-header
- %h3.card-title
- = icon('caret-right lg fw', class: 'panel-toggle js-panel-toggle', 'aria-label' => 'Toggle panel')
- = s_('PrometheusService|Missing environment variable')
- %span.badge.badge-pill.js-env-var-count 0
- .card-body.hidden
- .flash-container
- .flash-notice
- .flash-text
- = s_("PrometheusService|To set up automatic monitoring, add the environment variable %{variable} to exporter's queries." % { variable: "<code>$CI_ENVIRONMENT_SLUG</code>" }).html_safe
- = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus', anchor: 'metrics-and-labels')
- %ul.list-unstyled.metrics-list.js-missing-var-metrics-list
+ = render 'projects/services/prometheus/metrics', project: @project
diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
index 4359362bb05..ab9ba5c7569 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -1,6 +1,6 @@
.row
.col-lg-12
- = form_for @project, url: project_settings_ci_cd_path(@project) do |f|
+ = form_for @project, url: project_settings_ci_cd_path(@project, anchor: 'autodevops-settings') do |f|
= form_errors(@project)
%fieldset.builds-feature.js-auto-devops-settings
.form-group
@@ -13,23 +13,15 @@
.card.auto-devops-card
.card-body
.form-check
- = form.radio_button :enabled, 'true', class: 'form-check-input js-toggle-extra-settings'
- = form.label :enabled_true, class: 'form-check-label' do
- %strong= s_('CICD|Enable Auto DevOps')
+ = form.check_box :enabled, class: 'form-check-input js-toggle-extra-settings', checked: @project.auto_devops_enabled?
+ = form.label :enabled, class: 'form-check-label' do
+ %strong= s_('CICD|Default to Auto DevOps pipeline')
+ - if @project.has_auto_devops_implicitly_enabled?
+ %span.badge.badge-info.js-instance-default-badge= s_('CICD|instance enabled')
.form-text.text-muted
- = s_('CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project.').html_safe % { ci_file: ci_file_formatted }
-
- .card.auto-devops-card
- .card-body
- .form-check
- = form.radio_button :enabled, '', class: 'form-check-input js-toggle-extra-settings'
- = form.label :enabled_, class: 'form-check-label' do
- %strong= s_('CICD|Instance default (%{state})') % { state: "#{Gitlab::CurrentSettings.auto_devops_enabled? ? _('enabled') : _('disabled')}" }
- .form-text.text-muted
- = s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted }
-
- .card.auto-devops-card.js-extra-settings{ class: form.object&.enabled == false ? 'hidden' : nil }
- .card-body.bg-light
+ = s_('CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found.')
+ = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank'
+ .card-footer.js-extra-settings{ class: @project.auto_devops_enabled? || 'hidden' }
= form.label :domain do
%strong= _('Domain')
= form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
@@ -46,21 +38,12 @@
.form-check
= form.radio_button :deploy_strategy, 'continuous', class: 'form-check-input'
= form.label :deploy_strategy_continuous, class: 'form-check-label' do
- %strong= s_('CICD|Continuous deployment to production')
+ = s_('CICD|Continuous deployment to production')
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-deploy'), target: '_blank'
.form-check
= form.radio_button :deploy_strategy, 'manual', class: 'form-check-input'
= form.label :deploy_strategy_manual, class: 'form-check-label' do
- %strong= s_('CICD|Automatic deployment to staging, manual deployment to production')
+ = s_('CICD|Automatic deployment to staging, manual deployment to production')
= link_to icon('question-circle'), help_page_path('ci/environments.md', anchor: 'manually-deploying-to-environments'), target: '_blank'
- .card.auto-devops-card
- .card-body
- .form-check
- = form.radio_button :enabled, 'false', class: 'form-check-input js-toggle-extra-settings', data: { hide_extra_settings: true }
- = form.label :enabled_false, class: 'form-check-label' do
- %strong= s_('CICD|Disable Auto DevOps')
- .form-text.text-muted
- = s_('CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery.').html_safe % { ci_file: ci_file_formatted }
-
- = f.submit 'Save changes', class: "btn btn-success prepend-top-15"
+ = f.submit _('Save changes'), class: "btn btn-success prepend-top-15"
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index 7142a9d635e..434aed2f603 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -1,23 +1,23 @@
.row.prepend-top-default
.col-lg-12
- = form_for @project, url: project_settings_ci_cd_path(@project) do |f|
+ = form_for @project, url: project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings') do |f|
= form_errors(@project)
%fieldset.builds-feature
.form-group.append-bottom-default.js-secret-runner-token
- = f.label :runners_token, "Runner token", class: 'label-light'
+ = f.label :runners_token, _("Runner token"), class: 'label-bold'
.form-control.js-secret-value-placeholder
= '*' * 20
= f.text_field :runners_token, class: "form-control hide js-secret-value", placeholder: 'xEeFCaDAB89'
- %p.form-text.text-muted The secure token used by the Runner to checkout the project
+ %p.form-text.text-muted= _("The secure token used by the Runner to checkout the project")
%button.btn.btn-info.prepend-top-10.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: 'false' } }
= _('Reveal value')
%hr
.form-group
%h5.prepend-top-0
- Git strategy for pipelines
+ = _("Git strategy for pipelines")
%p
- Choose between <code>clone</code> or <code>fetch</code> to get the recent application code
+ = _("Choose between <code>clone</code> or <code>fetch</code> to get the recent application code")
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'git-strategy'), target: '_blank'
.form-check
= f.radio_button :build_allow_git_fetch, 'false', { class: 'form-check-input' }
@@ -25,29 +25,29 @@
%strong git clone
%br
%span.descr
- Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job
+ = _("Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job")
.form-check
= f.radio_button :build_allow_git_fetch, 'true', { class: 'form-check-input' }
= f.label :build_allow_git_fetch_true, class: 'form-check-label' do
%strong git fetch
%br
%span.descr
- Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)
+ = _("Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)")
%hr
.form-group
- = f.label :build_timeout_human_readable, 'Timeout', class: 'label-light'
+ = f.label :build_timeout_human_readable, _('Timeout'), class: 'label-bold'
= f.text_field :build_timeout_human_readable, class: 'form-control'
%p.form-text.text-muted
- Per job. If a job passes this threshold, it will be marked as failed
+ = _("Per job. If a job passes this threshold, it will be marked as failed")
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'timeout'), target: '_blank'
%hr
.form-group
- = f.label :ci_config_path, 'Custom CI config path', class: 'label-light'
+ = f.label :ci_config_path, _('Custom CI config path'), class: 'label-bold'
= f.text_field :ci_config_path, class: 'form-control', placeholder: '.gitlab-ci.yml'
%p.form-text.text-muted
- The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>
+ = _("The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>")
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'custom-ci-config-path'), target: '_blank'
%hr
@@ -55,36 +55,35 @@
.form-check
= f.check_box :public_builds, { class: 'form-check-input' }
= f.label :public_builds, class: 'form-check-label' do
- %strong Public pipelines
+ %strong= _("Public pipelines")
.form-text.text-muted
- Allow public access to pipelines and job details, including output logs and artifacts
+ = _("Allow public access to pipelines and job details, including output logs and artifacts")
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'visibility-of-pipelines'), target: '_blank'
.bs-callout.bs-callout-info
- %p If enabled:
+ %p #{_("If enabled")}:
%ul
%li
- For public projects, anyone can view pipelines and access job details (output logs and artifacts)
+ = _("For public projects, anyone can view pipelines and access job details (output logs and artifacts)")
%li
- For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)
+ = _("For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)")
%li
- For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)
+ = _("For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)")
%p
- If disabled, the access level will depend on the user's
- permissions in the project.
+ = _("If disabled, the access level will depend on the user's permissions in the project.")
%hr
.form-group
.form-check
= f.check_box :auto_cancel_pending_pipelines, { class: 'form-check-input' }, 'enabled', 'disabled'
= f.label :auto_cancel_pending_pipelines, class: 'form-check-label' do
- %strong Auto-cancel redundant, pending pipelines
+ %strong= _("Auto-cancel redundant, pending pipelines")
.form-text.text-muted
- New pipelines will cancel older, pending pipelines on the same branch
+ = _("New pipelines will cancel older, pending pipelines on the same branch")
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'auto-cancel-pending-pipelines'), target: '_blank'
%hr
.form-group
- = f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
+ = f.label :build_coverage_regex, _("Test coverage parsing"), class: 'label-bold'
.input-group
%span.input-group-prepend
.input-group-text /
@@ -92,11 +91,10 @@
%span.input-group-append
.input-group-text /
%p.form-text.text-muted
- A regular expression that will be used to find the test coverage
- output in the job trace. Leave blank to disable
+ = _("A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable")
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'test-coverage-parsing'), target: '_blank'
.bs-callout.bs-callout-info
- %p Below are examples of regex for existing tools:
+ %p= _("Below are examples of regex for existing tools:")
%ul
%li
Simplecov (Ruby) -
@@ -119,8 +117,11 @@
%li
JaCoCo (Java/Kotlin)
%code Total.*?([0-9]{1,3})%
+ %li
+ go test -cover (Go)
+ %code coverage: \d+.\d+% of statements
- = f.submit 'Save changes', class: "btn btn-save"
+ = f.submit _('Save changes'), class: "btn btn-save"
%hr
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index 4d096699a70..16961784e00 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -1,6 +1,6 @@
- @content_class = "limit-container-width" unless fluid_layout
-- page_title "CI / CD Settings"
-- page_title "CI / CD"
+- page_title _("CI / CD Settings")
+- page_title _("CI / CD")
- expanded = Rails.env.test?
- general_expanded = @project.errors.empty? ? expanded : true
@@ -8,11 +8,11 @@
%section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) }
.settings-header
%h4
- General pipelines
+ = _("General pipelines")
%button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
+ = expanded ? _('Collapse') : _('Expand')
%p
- Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report.
+ = _("Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report.")
.settings-content
= render 'form'
@@ -28,14 +28,16 @@
.settings-content
= render 'autodevops_form'
-%section.qa-runners-settings.settings.no-animate{ class: ('expanded' if expanded) }
+= render_if_exists 'projects/settings/ci_cd/protected_environments', expanded: expanded
+
+%section.qa-runners-settings.settings.no-animate#js-runners-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
- Runners
+ = _("Runners")
%button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
+ = expanded ? _('Collapse') : _('Expand')
%p
- Register and see your runners for this project.
+ = _("Register and see your runners for this project.")
.settings-content
= render 'projects/runners/index'
@@ -45,7 +47,7 @@
= _('Variables')
= link_to icon('question-circle'), help_page_path('ci/variables/README', anchor: 'variables'), target: '_blank', rel: 'noopener noreferrer'
%button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
+ = expanded ? _('Collapse') : _('Expand')
%p.append-bottom-0
= render "ci/variables/content"
.settings-content
@@ -54,12 +56,10 @@
%section.settings.no-animate#js-pipeline-triggers{ class: ('expanded' if expanded) }
.settings-header
%h4
- Pipeline triggers
+ = _("Pipeline triggers")
%button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
+ = expanded ? _('Collapse') : _('Expand')
%p
- Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will
- impersonate their associated user including their access to projects and their project
- permissions.
+ = _("Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions.")
.settings-content
= render 'projects/triggers/index'
diff --git a/app/views/projects/settings/integrations/_project_hook.html.haml b/app/views/projects/settings/integrations/_project_hook.html.haml
index 77d88aed883..ef445f2e139 100644
--- a/app/views/projects/settings/integrations/_project_hook.html.haml
+++ b/app/views/projects/settings/integrations/_project_hook.html.haml
@@ -8,9 +8,9 @@
%span.badge.badge-gray.deploy-project-label= event.to_s.titleize
.col-md-4.col-lg-5.text-right-lg.prepend-top-5
%span.append-right-10.inline
- SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
- = link_to 'Edit', edit_project_hook_path(@project, hook), class: 'btn btn-sm'
+ #{_("SSL Verification")}: #{hook.enable_ssl_verification ? _('enabled') : _('disabled')}
+ = link_to _('Edit'), edit_project_hook_path(@project, hook), class: 'btn btn-sm'
= render 'shared/web_hooks/test_button', triggers: ProjectHook.triggers, hook: hook, button_class: 'btn-sm'
- = link_to project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-transparent' do
- %span.sr-only Remove
+ = link_to project_hook_path(@project, hook), data: { confirm: _('Are you sure?') }, method: :delete, class: 'btn btn-transparent' do
+ %span.sr-only= _("Remove")
= icon('trash')
diff --git a/app/views/projects/settings/integrations/show.html.haml b/app/views/projects/settings/integrations/show.html.haml
index 2f1a548e119..76770290f36 100644
--- a/app/views/projects/settings/integrations/show.html.haml
+++ b/app/views/projects/settings/integrations/show.html.haml
@@ -1,5 +1,5 @@
- @content_class = "limit-container-width" unless fluid_layout
-- breadcrumb_title "Integrations Settings"
-- page_title 'Integrations'
+- breadcrumb_title _("Integrations Settings")
+- page_title _('Integrations')
= render 'projects/hooks/index'
= render 'projects/services/index'
diff --git a/app/views/projects/settings/members/show.html.haml b/app/views/projects/settings/members/show.html.haml
index ea2cd36b212..5fca734222b 100644
--- a/app/views/projects/settings/members/show.html.haml
+++ b/app/views/projects/settings/members/show.html.haml
@@ -1,5 +1,5 @@
- @content_class = "limit-container-width" unless fluid_layout
-- page_title "Members"
+- page_title _("Members")
= render "projects/project_members/index"
diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml
index 5dda2ec28b4..98c609d7bd4 100644
--- a/app/views/projects/settings/repository/show.html.haml
+++ b/app/views/projects/settings/repository/show.html.haml
@@ -1,5 +1,5 @@
-- breadcrumb_title "Repository Settings"
-- page_title "Repository"
+- breadcrumb_title _("Repository Settings")
+- page_title _("Repository")
- @content_class = "limit-container-width" unless fluid_layout
= render "projects/mirrors/show"
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index e28accd5b43..8a5abb64515 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -8,16 +8,21 @@
= render partial: 'flash_messages', locals: { project: @project }
+- if @project.repository_exists? && !@project.empty_repo?
+ - signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.full_path, project_id: @project.path, id: @project.default_branch)
+ .js-signature-container{ data: { 'signatures-path': signatures_path } }
+
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render "projects/last_push"
= render "home_panel"
- if can?(current_user, :download_code, @project)
- %nav.project-stats{ class: container_class }
+ %nav.project-stats{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
-
+ - if Feature.enabled?(:repository_languages, @project.namespace.becomes(Namespace))
+ = repository_languages_bar(@project.repository_languages)
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
- if @project.archived?
diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml
index b3a9fa9dd91..4a3aa3dc626 100644
--- a/app/views/projects/snippets/_actions.html.haml
+++ b/app/views/projects/snippets/_actions.html.haml
@@ -3,34 +3,34 @@
.d-none.d-sm-block
- if can?(current_user, :update_project_snippet, @snippet)
= link_to edit_project_snippet_path(@project, @snippet), class: "btn btn-grouped" do
- Edit
+ = _('Edit')
- if can?(current_user, :update_project_snippet, @snippet)
- = link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-inverted btn-remove", title: 'Delete Snippet' do
- Delete
+ = link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: _("Are you sure?") }, class: "btn btn-grouped btn-inverted btn-remove", title: _('Delete Snippet') do
+ = _('Delete')
- if can?(current_user, :create_project_snippet, @project)
- = link_to new_project_snippet_path(@project), class: 'btn btn-grouped btn-inverted btn-create', title: "New snippet" do
- New snippet
+ = link_to new_project_snippet_path(@project), class: 'btn btn-grouped btn-inverted btn-create', title: _("New snippet") do
+ = _('New snippet')
- if @snippet.submittable_as_spam_by?(current_user)
- = link_to 'Submit as spam', mark_as_spam_project_snippet_path(@project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam'
+ = link_to _('Submit as spam'), mark_as_spam_project_snippet_path(@project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: _('Submit as spam')
- if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet)
.d-block.d-sm-none.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
- Options
+ = _('Options')
= icon('caret-down')
.dropdown-menu.dropdown-menu-full-width
%ul
- if can?(current_user, :create_project_snippet, @project)
%li
- = link_to new_project_snippet_path(@project), title: "New snippet" do
- New snippet
+ = link_to new_project_snippet_path(@project), title: _("New snippet") do
+ = _('New snippet')
- if can?(current_user, :update_project_snippet, @snippet)
%li
- = link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
- Delete
+ = link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: _("Are you sure?") }, title: _('Delete Snippet') do
+ = _('Delete')
- if can?(current_user, :update_project_snippet, @snippet)
%li
= link_to edit_project_snippet_path(@project, @snippet) do
- Edit
+ = _('Edit')
- if @snippet.submittable_as_spam_by?(current_user)
%li
- = link_to 'Submit as spam', mark_as_spam_project_snippet_path(@project, @snippet), method: :post
+ = link_to _('Submit as spam'), mark_as_spam_project_snippet_path(@project, @snippet), method: :post
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
index 32844f5204a..6dbd67df886 100644
--- a/app/views/projects/snippets/edit.html.haml
+++ b/app/views/projects/snippets/edit.html.haml
@@ -1,8 +1,8 @@
-- add_to_breadcrumbs "Snippets", project_snippets_path(@project)
+- add_to_breadcrumbs _("Snippets"), project_snippets_path(@project)
- breadcrumb_title @snippet.to_reference
-- page_title "Edit", "#{@snippet.title} (#{@snippet.to_reference})", "Snippets"
+- page_title _("Edit"), "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
%h3.page-title
- Edit Snippet
+ = _("Edit Snippet")
%hr
= render "shared/snippets/form", url: project_snippet_path(@project, @snippet)
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index 65efc083fdd..1c4c73dc776 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,4 +1,4 @@
-- page_title "Snippets"
+- page_title _("Snippets")
- if current_user
.top-area
@@ -7,6 +7,6 @@
.nav-controls
- if can?(current_user, :create_project_snippet, @project)
- = link_to "New snippet", new_project_snippet_path(@project), class: "btn btn-new", title: "New snippet"
+ = link_to _("New snippet"), new_project_snippet_path(@project), class: "btn btn-new", title: _("New snippet")
= render 'snippets/snippets'
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
index 1359a815429..26b333d4ecf 100644
--- a/app/views/projects/snippets/new.html.haml
+++ b/app/views/projects/snippets/new.html.haml
@@ -1,8 +1,8 @@
-- add_to_breadcrumbs "Snippets", project_snippets_path(@project)
-- breadcrumb_title "New"
-- page_title "New Snippets"
+- add_to_breadcrumbs _("Snippets"), project_snippets_path(@project)
+- breadcrumb_title _("New")
+- page_title _("New Snippets")
%h3.page-title
- New Snippet
+ = _('New Snippet')
%hr
= render "shared/snippets/form", url: project_snippets_path(@project, @snippet)
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index 7062c5b765e..f495b4eaf30 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -1,7 +1,7 @@
- @content_class = "limit-container-width limited-inner-width-container" unless fluid_layout
-- add_to_breadcrumbs "Snippets", project_snippets_path(@project)
+- add_to_breadcrumbs _("Snippets"), project_snippets_path(@project)
- breadcrumb_title @snippet.to_reference
-- page_title "#{@snippet.title} (#{@snippet.to_reference})", "Snippets"
+- page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
= render 'shared/snippets/header'
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index 96ecac815c0..dab95ba09f2 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -26,6 +26,8 @@
= link_to new_project_tag_path(@project), class: 'btn btn-create new-tag-btn' do
= s_('TagsPage|New tag')
+ = render_if_exists 'projects/commits/mirror_status'
+
.tags
- if @tags.any?
%ul.flex-list.content-list
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 3b4057e56d0..9d2aee7a8bd 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -1,11 +1,14 @@
- @no_container = true
- breadcrumb_title _("Repository")
- @content_class = "limit-container-width" unless fluid_layout
+- signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.full_path, project_id: @project.path, id: @last_commit)
- page_title @path.presence || _("Files"), @ref
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
+.js-signature-container{ data: { 'signatures-path': signatures_path } }
+
%div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] }
= render 'projects/last_push'
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)
diff --git a/app/views/projects/triggers/_form.html.haml b/app/views/projects/triggers/_form.html.haml
index 3539aea3580..1a5fc56f429 100644
--- a/app/views/projects/triggers/_form.html.haml
+++ b/app/views/projects/triggers/_form.html.haml
@@ -3,9 +3,9 @@
- if @trigger.token
.form-group
- %label.label-light Token
+ %label.label-bold Token
%p.form-control-plaintext= @trigger.token
.form-group
- = f.label :key, "Description", class: "label-light"
+ = f.label :key, "Description", class: "label-bold"
= f.text_field :description, class: "form-control", required: true, title: 'Trigger description is required.', placeholder: "Trigger description"
= f.submit btn_text, class: "btn btn-save"
diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml
index 1a353953838..e8681da6528 100644
--- a/app/views/projects/update.js.haml
+++ b/app/views/projects/update.js.haml
@@ -1,6 +1,7 @@
- if @project.valid?
:plain
- location.href = "#{edit_project_path(@project)}";
+ location.href = "#{edit_project_path(@project, anchor: params[:update_section])}";
+ location.reload();
- else
:plain
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 26fe1de31fe..de692466fe5 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,7 +1,9 @@
- commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}")
- commit_message = commit_message % { page_title: @page.title }
-= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post,
+ html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' },
+ data: { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION } do |f|
= form_errors(@page)
- if @page.persisted?
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index cadda0a33c2..8d91f411f89 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -4,6 +4,6 @@
= s_("Wiki|New page")
= link_to project_wiki_history_path(@project, @page), class: "btn" do
= s_("Wiki|Page history")
- - if can?(current_user, :create_wiki, @project) && @page.latest?
+ - if can?(current_user, :create_wiki, @project) && @page.latest? && @valid_encoding
= link_to project_wiki_edit_path(@project, @page), class: "btn js-wiki-edit" do
= _("Edit")
diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml
index a23396dc0d8..28353927135 100644
--- a/app/views/projects/wikis/_sidebar.html.haml
+++ b/app/views/projects/wikis/_sidebar.html.haml
@@ -11,9 +11,11 @@
.blocks-container
.block.block-first
- %ul.wiki-pages
- = render @sidebar_wiki_entries, context: 'sidebar'
-
+ - if @sidebar_page
+ = render_wiki_content(@sidebar_page)
+ - else
+ %ul.wiki-pages
+ = render @sidebar_wiki_entries, context: 'sidebar'
.block
= link_to project_wikis_pages_path(@project), class: 'btn btn-block' do
= s_("Wiki|More Pages")
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index ecb5b1c6ebc..7afb7b3a93b 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,4 +1,4 @@
-.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
+.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller.flex-fill
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
%ul.nav-links.event-filter.scrolling-tabs.nav.nav-tabs
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
index 356e12cf9f8..7b593ca4f76 100644
--- a/app/views/shared/_import_form.html.haml
+++ b/app/views/shared/_import_form.html.haml
@@ -1,7 +1,7 @@
- ci_cd_only = local_assigns.fetch(:ci_cd_only, false)
.form-group.import-url-data
- = f.label :import_url, class: 'label-light' do
+ = f.label :import_url, class: 'label-bold' do
%span
= _('Git repository URL')
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index e93925b5ef9..2c3cbd0b986 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -17,13 +17,13 @@
- if can?(current_user, :admin_label, @project)
%li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label),
dom_id: dom_id(label), type: label.type } }
- %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'top' }, aria_label: _('Prioritize label') }
+ %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'bottom' }, aria_label: _('Prioritize label') }
= sprite_icon('star-o')
- %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', data: { placement: 'top' }, aria_label: _('Deprioritize label') }
+ %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', data: { placement: 'bottom' }, aria_label: _('Deprioritize label') }
= sprite_icon('star')
- if can?(current_user, :admin_label, label)
%li.inline
- = link_to edit_label_path(label), class: 'btn btn-transparent label-action edit', aria_label: 'Edit label' do
+ = link_to edit_label_path(label), class: 'btn btn-transparent label-action edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
= sprite_icon('pencil')
%li.inline
.dropdown
diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml
index 0ae3ab8f090..c5ea15a7f63 100644
--- a/app/views/shared/_label_row.html.haml
+++ b/app/views/shared/_label_row.html.haml
@@ -1,14 +1,17 @@
- subject = local_assigns[:subject]
- force_priority = local_assigns.fetch(:force_priority, false)
-- show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project)
-- show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project)
+- show_label_issues_link = defined?(@project) && show_label_issuables_link?(label, :issues, project: @project)
+- show_label_merge_requests_link = defined?(@project) && show_label_issuables_link?(label, :merge_requests, project: @project)
.label-name
- = link_to_label(label, subject: @project, tooltip: false)
+ - if defined?(@project)
+ = link_to_label(label, subject: @project, tooltip: false)
+ - else
+ = render_colored_label(label, tooltip: false)
.label-description
.append-right-default.prepend-left-default
- if label.description.present?
- .description-text.append-bottom-10
+ .description-text
= markdown_field(label, :description)
%ul.label-links
- if show_label_issues_link
@@ -19,5 +22,5 @@
%li.label-link-item.inline
= link_to_label(label, subject: subject, type: :merge_request) { _('Merge requests') }
- if force_priority
- %li.label-link-item.js-priority-badge.inline.prepend-left-10
+ %li.label-link-item.priority-badge.js-priority-badge.inline.prepend-left-10
.label-badge.label-badge-blue= _('Prioritized label')
diff --git a/app/views/shared/_milestone_expired.html.haml b/app/views/shared/_milestone_expired.html.haml
index 5e9007aaaac..099e3ac8462 100644
--- a/app/views/shared/_milestone_expired.html.haml
+++ b/app/views/shared/_milestone_expired.html.haml
@@ -1,7 +1,6 @@
- if milestone.expired? and not milestone.closed?
- %span.cred (Expired)
+ .status-box.status-box-expired.append-bottom-5 Expired
- if milestone.upcoming?
- %span.clgray (Upcoming)
-- if milestone.due_date || milestone.start_date
- %span
- = milestone_date_range(milestone)
+ .status-box.status-box-mr-merged.append-bottom-5 Upcoming
+- if milestone.closed?
+ .status-box.status-box-closed.append-bottom-5 Closed
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
index ac2ebb701a5..d38d161047b 100644
--- a/app/views/shared/_new_project_item_select.html.haml
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -1,7 +1,7 @@
- if any_projects?(@projects)
.project-item-select-holder.btn-group
- %a.btn.btn-new.new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } }
+ %a.btn.btn-new.new-project-item-link.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } }
= icon('spinner spin')
= project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path] }, with_feature_enabled: local_assigns[:with_feature_enabled]
- %button.btn.btn-new.new-project-item-select-button
+ %button.btn.btn-new.new-project-item-select-button.qa-new-project-item-select-button
= icon('caret-down')
diff --git a/app/views/shared/_personal_access_tokens_form.html.haml b/app/views/shared/_personal_access_tokens_form.html.haml
index 28407b543b9..58d310fac16 100644
--- a/app/views/shared/_personal_access_tokens_form.html.haml
+++ b/app/views/shared/_personal_access_tokens_form.html.haml
@@ -11,18 +11,18 @@
.row
.form-group.col-md-6
- = f.label :name, class: 'label-light'
+ = f.label :name, class: 'label-bold'
= f.text_field :name, class: "form-control", required: true
.row
.form-group.col-md-6
- = f.label :expires_at, class: 'label-light'
+ = f.label :expires_at, class: 'label-bold'
.input-icon-wrapper
= f.text_field :expires_at, class: "datepicker form-control", placeholder: 'YYYY-MM-DD'
= icon('calendar', { class: 'input-icon-right' })
.form-group
- = f.label :scopes, class: 'label-light'
+ = f.label :scopes, class: 'label-bold'
= render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: token, scopes: scopes
.prepend-top-default
diff --git a/app/views/shared/_user_dropdown_contributing_link.html.haml b/app/views/shared/_user_dropdown_contributing_link.html.haml
new file mode 100644
index 00000000000..333d6fa3489
--- /dev/null
+++ b/app/views/shared/_user_dropdown_contributing_link.html.haml
@@ -0,0 +1,5 @@
+%li
+ = link_to "https://about.gitlab.com/contributing", target: '_blank', class: 'text-nowrap' do
+ = _("Contribute to GitLab")
+ = sprite_icon('external-link', size: 16)
+%li.divider
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index a88d8f61fb4..28e6fe1b16d 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -2,8 +2,8 @@
- group = local_assigns.fetch(:group, false)
- @no_breadcrumb_container = true
- @no_container = true
-- @content_class = "issue-boards-content"
-- breadcrumb_title _("Issue Board")
+- @content_class = "issue-boards-content js-focus-mode-board"
+- breadcrumb_title _("Issue Boards")
- page_title _("Boards")
- content_for :page_specific_javascripts do
@@ -11,10 +11,11 @@
-# haml-lint:disable InlineJavaScript
%script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board"
%script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal
+ %script#js-board-promotion{ type: "text/x-template" }= render_if_exists "shared/promotions/promote_issue_board"
#board-app.boards-app{ "v-cloak" => true, data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
.d-none.d-sm-none.d-md-block
- = render 'shared/issuable/search_bar', type: :boards
+ = render 'shared/issuable/search_bar', type: :boards, board: board
.boards-list
.boards-app-loading.text-center{ "v-if" => "loading" }
diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml
index 65de6172d89..e26f5260e5b 100644
--- a/app/views/shared/boards/components/_board.html.haml
+++ b/app/views/shared/boards/components/_board.html.haml
@@ -6,12 +6,13 @@
%i.fa.fa-fw.board-title-expandable-toggle{ "v-if": "list.isExpandable",
":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded }",
"aria-hidden": "true" }
+ = render_if_exists "shared/boards/components/list_milestone"
%a.user-avatar-link.js-no-trigger{ "v-if": "list.type === \"assignee\"", ":href": "list.assignee.path" }
-# haml-lint:disable AltText
%img.avatar.s20.has-tooltip{ height: "20", width: "20", ":src": "list.assignee.avatar", ":alt": "list.assignee.name" }
- %span.board-title-text.has-tooltip{ "v-if": "list.type !== \"label\"",
+ %span.board-title-text.has-tooltip.block-truncated{ "v-if": "list.type !== \"label\"",
":title" => '((list.label && list.label.description) || list.title || "")', data: { container: "body" } }
{{ list.title }}
@@ -32,19 +33,22 @@
"v-if" => "!list.preset && list.id" }
%button.board-delete.has-tooltip.float-right{ type: "button", title: _("Delete list"), "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
= icon("trash")
- .issue-count-badge.clearfix{ "v-if" => 'list.type !== "blank"' }
- %span.issue-count-badge-count.float-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
+ .issue-count-badge.text-secondary{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"', ":title": "counterTooltip", "v-tooltip": true, data: { placement: "top" } }
+ %span.issue-count-badge-count
+ %icon.mr-1{ name: "issues" }
{{ list.issuesSize }}
- - if can?(current_user, :admin_list, current_board_parent)
- %button.issue-count-badge-add-button.btn.btn-sm.btn-default.has-tooltip.js-no-trigger-collapse{ type: "button",
- "@click" => "showNewIssueForm",
- "v-if" => 'list.type !== "closed"',
- "aria-label" => _("New issue"),
- "title" => _("New issue"),
- data: { placement: "top", container: "body" } }
- = icon("plus", class: "js-no-trigger-collapse")
+ = render_if_exists "shared/boards/components/list_weight"
- %board-list{ "v-if" => 'list.type !== "blank"',
+ - if can?(current_user, :admin_list, current_board_parent)
+ %button.issue-count-badge-add-button.btn.btn-sm.btn-default.ml-1.has-tooltip.js-no-trigger-collapse{ type: "button",
+ "@click" => "showNewIssueForm",
+ "v-if" => 'list.type !== "closed"',
+ "aria-label" => _("New issue"),
+ "title" => _("New issue"),
+ data: { placement: "top", container: "body" } }
+ = icon("plus", class: "js-no-trigger-collapse")
+
+ %board-list{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"',
":list" => "list",
":issues" => "list.issues",
":loading" => "list.loading",
@@ -55,3 +59,4 @@
"ref" => "board-list" }
- if can?(current_user, :admin_list, current_board_parent)
%board-blank-state{ "v-if" => 'list.id == "blank"' }
+ = render_if_exists 'shared/boards/board_promotion_state'
diff --git a/app/views/shared/boards/components/_sidebar.html.haml b/app/views/shared/boards/components/_sidebar.html.haml
index 774dafe5f2c..1ff956649ed 100644
--- a/app/views/shared/boards/components/_sidebar.html.haml
+++ b/app/views/shared/boards/components/_sidebar.html.haml
@@ -8,6 +8,7 @@
{{ issue.title }}
%br/
%span
+ = render_if_exists "shared/boards/components/sidebar/issue_project_path"
= precede "#" do
{{ issue.iid }}
%a.gutter-toggle.float-right{ role: "button",
@@ -17,9 +18,11 @@
= custom_icon("icon_close", size: 15)
.js-issuable-update
= render "shared/boards/components/sidebar/assignee"
+ = render_if_exists "shared/boards/components/sidebar/epic"
= render "shared/boards/components/sidebar/milestone"
= render "shared/boards/components/sidebar/due_date"
= render "shared/boards/components/sidebar/labels"
+ = render_if_exists "shared/boards/components/sidebar/weight"
= render "shared/boards/components/sidebar/notifications"
%remove-btn{ ":issue" => "issue",
":issue-update" => "issue.sidebarInfoEndpoint",
diff --git a/app/views/shared/boards/components/sidebar/_labels.html.haml b/app/views/shared/boards/components/sidebar/_labels.html.haml
index 607e7f471c9..532045f3697 100644
--- a/app/views/shared/boards/components/sidebar/_labels.html.haml
+++ b/app/views/shared/boards/components/sidebar/_labels.html.haml
@@ -19,6 +19,7 @@
":value" => "label.id" }
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-board-sidebar{ type: "button",
+ "v-bind:data-selected" => "selectedLabels",
data: { toggle: "dropdown",
field_name: "issue[label_names][]",
show_no: "true",
@@ -28,7 +29,7 @@
namespace_path: @namespace_path,
project_path: @project.try(:path) } }
%span.dropdown-toggle-text
- = _("Label")
+ {{ labelDropdownTitle }}
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default"
diff --git a/app/views/shared/builds/_build_output.html.haml b/app/views/shared/builds/_build_output.html.haml
index 0e18128a8f1..380fac4d0e4 100644
--- a/app/views/shared/builds/_build_output.html.haml
+++ b/app/views/shared/builds/_build_output.html.haml
@@ -1,3 +1,6 @@
%pre.build-trace#build-trace
%code.bash.js-build-output
.build-loader-animation.js-build-refresh
+ .dot
+ .dot
+ .dot
diff --git a/app/views/shared/empty_states/_issues.html.haml b/app/views/shared/empty_states/_issues.html.haml
index c7c33288e9d..2e26fe63d3e 100644
--- a/app/views/shared/empty_states/_issues.html.haml
+++ b/app/views/shared/empty_states/_issues.html.haml
@@ -16,7 +16,7 @@
- if has_button
.text-center
- if project_select_button
- = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues
+ = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues, with_feature_enabled: 'issues'
- else
= link_to 'New issue', button_path, class: 'btn btn-success', title: 'New issue', id: 'new_issue_link'
- else
diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml
index 014220761a9..186139f3526 100644
--- a/app/views/shared/empty_states/_merge_requests.html.haml
+++ b/app/views/shared/empty_states/_merge_requests.html.haml
@@ -15,7 +15,7 @@
= _("Interested parties can even contribute by pushing commits if they want to.")
.text-center
- if project_select_button
- = render 'shared/new_project_item_select', path: 'merge_requests/new', label: _('New merge request'), type: :merge_requests
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: _('New merge request'), type: :merge_requests, with_feature_enabled: 'merge_requests'
- else
= link_to _('New merge request'), button_path, class: 'btn btn-new', title: _('New merge request'), id: 'new_merge_request_link'
- else
diff --git a/app/views/shared/hook_logs/_content.html.haml b/app/views/shared/hook_logs/_content.html.haml
index 532712ee6d1..f3b56df0c96 100644
--- a/app/views/shared/hook_logs/_content.html.haml
+++ b/app/views/shared/hook_logs/_content.html.haml
@@ -30,7 +30,7 @@
%h5 Request body:
%pre
- :plain
+ :escaped
#{JSON.pretty_generate(hook_log.request_data)}
%h5 Response headers:
%pre
@@ -40,5 +40,5 @@
%h5 Response body:
%pre
- :plain
+ :escaped
#{hook_log.response_body}
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
index 37625a4a163..c2da363b8c6 100644
--- a/app/views/shared/issuable/_milestone_dropdown.html.haml
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -7,7 +7,7 @@
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone")
- if selected.present? || params[:milestone_title].present?
= hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id)
-= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
+= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "qa-issuable-milestone-dropdown js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "qa-issuable-dropdown-menu-milestone dropdown-menu-selectable dropdown-menu-milestone",
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, show_started: show_started, field_name: name, selected: selected_text, project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if project
%ul.dropdown-footer-list
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index ef9ea2194ee..9ce7f6fe269 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -1,9 +1,14 @@
- type = local_assigns.fetch(:type)
+- board = local_assigns.fetch(:board, nil)
- block_css_class = type != :boards_modal ? 'row-content-block second-block' : ''
- full_path = @project.present? ? @project.full_path : @group.full_path
+- user_can_admin_list = board && can?(current_user, :admin_list, board.parent)
.issues-filters
.issues-details-filters.filtered-search-block{ class: block_css_class, "v-pre" => type == :boards_modal }
+ - if type == :boards
+ #js-multiple-boards-switcher.inline.boards-switcher{ "v-cloak" => true }
+ = render_if_exists "shared/boards/switcher", board: board
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search]), method: :get, class: 'filter-form js-filter-form' do
- if params[:search].present?
= hidden_field_tag :search, params[:search]
@@ -99,13 +104,18 @@
%gl-emoji
%span.js-data-value.prepend-left-10
{{name}}
+
+ = render_if_exists 'shared/issuable/filter_weight', type: type
+
%button.clear-search.hidden{ type: 'button' }
= icon('times')
.filter-dropdown-container
- if type == :boards
- - if can?(current_user, :admin_list, board.parent)
- = render_if_exists 'shared/issuable/board_create_list_dropdown', board: board
+ .js-board-config{ data: { can_admin_list: user_can_admin_list, has_scope: board.scoped? } }
+ - if user_can_admin_list
+ = render 'shared/issuable/board_create_list_dropdown', board: board
- if @project
#js-add-issues-btn.prepend-left-10{ data: { can_admin_list: can?(current_user, :admin_list, @project) } }
+ #js-toggle-focus-btn
- elsif type != :boards_modal
= render 'shared/sort_dropdown'
diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml
index bd87bb38e77..3b017c62a80 100644
--- a/app/views/shared/issuable/form/_metadata.html.haml
+++ b/app/views/shared/issuable/form/_metadata.html.haml
@@ -18,7 +18,7 @@
= form.label :milestone_id, "Milestone", 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
- = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, show_started: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
+ = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, show_started: false, extra_class: "qa-issuable-milestone-dropdown js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
.form-group.row
- has_labels = @labels && @labels.any?
= form.label :label_ids, "Labels", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
diff --git a/app/views/shared/issuable/form/_title.html.haml b/app/views/shared/issuable/form/_title.html.haml
index c35d0b3751f..e49bdec386a 100644
--- a/app/views/shared/issuable/form/_title.html.haml
+++ b/app/views/shared/issuable/form/_title.html.haml
@@ -6,7 +6,7 @@
%div{ class: div_class }
= form.text_field :title, required: true, maxlength: 255, autofocus: true,
- autocomplete: 'off', class: 'form-control pad qa-issuable-form-title'
+ autocomplete: 'off', class: 'form-control pad qa-issuable-form-title', placeholder: _('Title')
- if issuable.respond_to?(:work_in_progress?)
%p.form-text.text-muted
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index 46debe1f2b9..af29c0fe59e 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -11,6 +11,7 @@
= image_tag avatar_icon_for_user(user, 40), class: "avatar s40", alt: ''
.user-info
= link_to user.name, user_path(user), class: 'member'
+ = user_status(user)
%span.cgray= user.to_reference
- if user == current_user
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 09bbd04c2bf..c559945a9c9 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -1,76 +1,59 @@
- dashboard = local_assigns[:dashboard]
- custom_dom_id = dom_id(milestone.try(:milestones) ? milestone.milestones.first : milestone)
+- milestone_type = milestone.group_milestone? ? 'Group Milestone' : 'Project Milestone'
%li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id }
.row
.col-sm-6
- %strong= link_to truncate(milestone.title, length: 100), milestone_path
- - if milestone.group_milestone?
- %span - Group Milestone
- - else
- %span - Project Milestone
+ .append-bottom-5
+ %strong= link_to truncate(milestone.title, length: 100), milestone_path
+ - if @group
+ = " - #{milestone_type}"
- .col-sm-6
- .float-right.light #{milestone.percent_complete(current_user)}% complete
- .row
- .col-sm-6
+ - if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone?
+ - if milestone.due_date || milestone.start_date
+ .milestone-range.append-bottom-5
+ = milestone_date_range(milestone)
+ %div
+ = render('shared/milestone_expired', milestone: milestone)
+ - if milestone.legacy_group_milestone?
+ .projects
+ - milestone.milestones.each do |milestone|
+ = link_to milestone_path(milestone) do
+ %span.label-badge.label-badge-blue.d-inline-block.append-bottom-5
+ = dashboard ? milestone.project.full_name : milestone.project.name
+
+ .col-sm-4.milestone-progress
+ = milestone_progress_bar(milestone)
= link_to pluralize(milestone.total_issues_count(current_user), 'Issue'), issues_path
&middot;
= link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path
- .col-sm-6= milestone_progress_bar(milestone)
- - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone?
- .row
- .col-sm-6
- - if milestone.legacy_group_milestone?
- .expiration= render('shared/milestone_expired', milestone: milestone)
- .projects
- - milestone.milestones.each do |milestone|
- = link_to milestone_path(milestone) do
- %span.badge.badge-gray
- = dashboard ? milestone.project.full_name : milestone.project.name
- - if @group
- .col-sm-6.milestone-actions
+ .float-lg-right.light #{milestone.percent_complete(current_user)}% complete
+ .col-sm-2
+ .milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end
+ - if @project
+ - if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
+ - if @project.group
+ %button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'),
+ disabled: true,
+ type: 'button',
+ data: { url: promote_project_milestone_path(milestone.project, milestone),
+ milestone_title: milestone.title,
+ group_name: @project.group.name,
+ target: '#promote-milestone-modal',
+ container: 'body',
+ toggle: 'modal' } }
+ = sprite_icon('level-up', size: 14)
+
+ = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped"
+ - unless milestone.active?
+ = link_to 'Reopen Milestone', project_milestone_path(@project, milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
+ - if @group
- if can?(current_user, :admin_milestones, @group)
- - if milestone.group_milestone?
- = link_to edit_group_milestone_path(@group, milestone), class: "btn btn-sm btn-grouped" do
- Edit
- \
- if milestone.closed?
= link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
- else
= link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close"
-
- - if @project
- .row
- .col-sm-6
- = render('shared/milestone_expired', milestone: milestone)
- .col-sm-6.milestone-actions
- - if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
- = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-sm btn-grouped" do
- Edit
- \
-
- - if @project.group
- %button.js-promote-project-milestone-button.btn.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'),
- disabled: true,
- type: 'button',
- data: { url: promote_project_milestone_path(milestone.project, milestone),
- milestone_title: milestone.title,
- group_name: @project.group.name,
- target: '#promote-milestone-modal',
- container: 'body',
- toggle: 'modal' } }
- = _('Promote')
-
- = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped"
-
- %button.js-delete-milestone-button.btn.btn-sm.btn-grouped.btn-danger{ data: { toggle: 'modal',
- target: '#delete-milestone-modal',
- milestone_id: milestone.id,
- milestone_title: markdown_field(milestone, :title),
- milestone_url: project_milestone_path(milestone.project, milestone),
- milestone_issue_count: milestone.issues.count,
- milestone_merge_request_count: milestone.merge_requests.count },
- disabled: true }
- = _('Delete')
- = icon('spin spinner', class: 'js-loading-icon hidden' )
+ - if dashboard
+ .status-box.status-box-milestone
+ = milestone_type
diff --git a/app/views/shared/notes/_form.html.haml b/app/views/shared/notes/_form.html.haml
index 6b2715b47a7..c360f1ffe2a 100644
--- a/app/views/shared/notes/_form.html.haml
+++ b/app/views/shared/notes/_form.html.haml
@@ -40,5 +40,5 @@
= yield(:note_actions)
- %a.btn.btn-cancel.js-note-discard{ role: "button", data: {cancel_text: "Discard draft" } }
+ %a.btn.btn-cancel.js-note-discard{ role: "button", data: {cancel_text: "Cancel" } }
Discard draft
diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml
index d4e8f30e458..84adbd444c5 100644
--- a/app/views/shared/notes/_note.html.haml
+++ b/app/views/shared/notes/_note.html.haml
@@ -31,7 +31,9 @@
.note-header
.note-header-info
%a{ href: user_path(note.author) }
- %span.note-header-author-name= sanitize(note.author.name)
+ %span.note-header-author-name
+ = sanitize(note.author.name)
+ = user_status(note.author)
%span.note-headline-light
= note.author.to_reference
%span.note-headline-light
@@ -52,7 +54,7 @@
.note-text.md
= markdown_field(note, :note)
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago')
- .original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
+ .original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore, markdown_version: note.cached_markdown_version } }
#{note.note}
- if note_editable
= render 'shared/notes/edit', note: note
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index e0832fd9136..9dd1c24fdfa 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -13,7 +13,7 @@
.flash-container.timeline-content
.timeline-icon.d-none.d-sm-none.d-md-block
- %a.author_link{ href: user_path(current_user) }
+ %a.author-link{ href: user_path(current_user) }
= image_tag avatar_icon_for_user(current_user), alt: current_user.to_reference, class: 'avatar s40'
.timeline-content.timeline-content-form
= render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 88f0675f795..be053d481e4 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -4,7 +4,7 @@
- ci = false unless local_assigns[:ci] == true
- skip_namespace = false unless local_assigns[:skip_namespace] == true
- user = local_assigns[:user]
-- access = user&.max_member_access_for_project(project.id) unless user.nil?
+- access = max_project_member_access(project)
- css_class = '' unless local_assigns[:css_class]
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && can_show_last_commit_in_list?(project)
- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
@@ -19,7 +19,7 @@
- if project.creator && use_creator_avatar
= image_tag avatar_icon_for_user(project.creator, 40), class: "avatar s40", alt:''
- else
- = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+ = project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40)
.project-details
%h3.prepend-top-0.append-bottom-0
= link_to project_path(project), class: 'text-plain' do
diff --git a/app/views/shared/runners/show.html.haml b/app/views/shared/runners/show.html.haml
index 96527fcb4f2..362569bfbaf 100644
--- a/app/views/shared/runners/show.html.haml
+++ b/app/views/shared/runners/show.html.haml
@@ -3,7 +3,7 @@
%h3.page-title
Runner ##{@runner.id}
.float-right
- - if @runner.shared?
+ - if @runner.instance_type?
%span.runner-state.runner-state-shared
Shared
- elsif @runner.group_type?
diff --git a/app/views/shared/snippets/_embed.html.haml b/app/views/shared/snippets/_embed.html.haml
index 36f56fbad1a..c7f0511d1de 100644
--- a/app/views/shared/snippets/_embed.html.haml
+++ b/app/views/shared/snippets/_embed.html.haml
@@ -2,7 +2,7 @@
.gitlab-embed-snippets
.js-file-title.file-title-flex-parent
.file-header-content
- = external_snippet_icon('doc_text')
+ = external_snippet_icon('doc-text')
%strong.file-title-name
%a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil) }
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 858adc8be37..5e5c050d5c3 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -2,7 +2,9 @@
= page_specific_javascript_tag('lib/ace.js')
.snippet-form-holder
- = form_for @snippet, url: url, html: { class: "snippet-form js-requires-input js-quick-submit common-note-form" } do |f|
+ = form_for @snippet, url: url,
+ html: { class: "snippet-form js-requires-input js-quick-submit common-note-form" },
+ data: { markdown_version: @snippet.cached_markdown_version } do |f|
= form_errors(@snippet)
.form-group.row
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index 828ec870dc0..10bfc30492a 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -8,6 +8,7 @@
Authored
= time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago')
by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title", avatar_class: "d-none d-sm-inline")}
+ = user_status(@snippet.author)
.detail-page-header-actions
- if @snippet.project_id?
diff --git a/app/views/shared/tokens/_scopes_form.html.haml b/app/views/shared/tokens/_scopes_form.html.haml
index dcb3fca23f2..af9db5f59a8 100644
--- a/app/views/shared/tokens/_scopes_form.html.haml
+++ b/app/views/shared/tokens/_scopes_form.html.haml
@@ -5,5 +5,5 @@
- scopes.each do |scope|
%fieldset.form-group.form-check
= check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}", class: 'form-check-input'
- = label_tag ("#{prefix}_scopes_#{scope}"), scope, class: 'label-light form-check-label'
+ = label_tag ("#{prefix}_scopes_#{scope}"), scope, class: 'label-bold form-check-label'
.text-secondary= t scope, scope: [:doorkeeper, :scope_desc]
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index 660769fa50d..07ebb8680d2 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -1,15 +1,15 @@
= form_errors(hook)
.form-group
- = form.label :url, 'URL', class: 'label-light'
+ = form.label :url, 'URL', class: 'label-bold'
= form.text_field :url, class: 'form-control', placeholder: 'http://example.com/trigger-ci.json'
.form-group
- = form.label :token, 'Secret Token', class: 'label-light'
+ = form.label :token, 'Secret Token', class: 'label-bold'
= form.text_field :token, class: 'form-control', placeholder: ''
%p.form-text.text-muted
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, 'Trigger', class: 'label-light'
+ = form.label :url, 'Trigger', class: 'label-bold'
%ul.list-unstyled.prepend-left-20
%li
= form.check_box :push_events, class: 'form-check-input'
@@ -72,7 +72,7 @@
%p.light.ml-1
This URL will be triggered when a wiki page is created/updated
.form-group
- = form.label :enable_ssl_verification, 'SSL verification', class: 'label-light checkbox'
+ = form.label :enable_ssl_verification, '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
diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml
index 37747faed62..ddc089b0bd7 100644
--- a/app/views/sherlock/queries/_general.html.haml
+++ b/app/views/sherlock/queries/_general.html.haml
@@ -27,7 +27,7 @@
.card-header
.float-right
%button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
- %i.fa.fa-clipboard
+ = sprite_icon('duplicate')
%pre.hidden
= @query.formatted_query
%strong
@@ -42,7 +42,7 @@
.card-header
.float-right
%button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
- %i.fa.fa-clipboard
+ = sprite_icon('duplicate')
%pre.hidden
= @query.explain
%strong
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index b2ec7166832..7a38d290915 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -23,8 +23,9 @@
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn',
title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('exclamation-circle')
- = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
- = icon('rss')
+ - if can?(current_user, :read_user_profile, @user)
+ = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
+ = icon('rss')
- if current_user && current_user.admin?
= link_to [:admin, @user], class: 'btn btn-default', title: 'View user in admin area',
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
@@ -39,11 +40,18 @@
.cover-title
= @user.name
+ - if @user.status
+ .cover-status
+ = emoji_icon(@user.status.emoji)
+ = markdown_field(@user.status, :message)
+
.cover-desc.member-date
- %span.middle-dot-divider
- @#{@user.username}
- %span.middle-dot-divider
- Member since #{@user.created_at.to_date.to_s(:long)}
+ %p
+ %span.middle-dot-divider
+ @#{@user.username}
+ - if can?(current_user, :read_user_profile, @user)
+ %span.middle-dot-divider
+ Member since #{@user.created_at.to_date.to_s(:long)}
.cover-desc
- unless @user.public_email.blank?
@@ -78,30 +86,31 @@
%p.profile-user-bio
= @user.bio
- .scrolling-tabs-container
- .fade-left= icon('angle-left')
- .fade-right= icon('angle-right')
- %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs
- - if profile_tab?(:activity)
- %li.js-activity-tab
- = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
- Activity
- - if profile_tab?(:groups)
- %li.js-groups-tab
- = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
- Groups
- - if profile_tab?(:contributed)
- %li.js-contributed-tab
- = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
- Contributed projects
- - if profile_tab?(:projects)
- %li.js-projects-tab
- = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
- Personal projects
- - if profile_tab?(:snippets)
- %li.js-snippets-tab
- = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
- Snippets
+ - unless profile_tabs.empty?
+ .scrolling-tabs-container
+ .fade-left= icon('angle-left')
+ .fade-right= icon('angle-right')
+ %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs
+ - if profile_tab?(:activity)
+ %li.js-activity-tab
+ = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
+ Activity
+ - if profile_tab?(:groups)
+ %li.js-groups-tab
+ = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
+ Groups
+ - if profile_tab?(:contributed)
+ %li.js-contributed-tab
+ = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
+ Contributed projects
+ - if profile_tab?(:projects)
+ %li.js-projects-tab
+ = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
+ Personal projects
+ - if profile_tab?(:snippets)
+ %li.js-snippets-tab
+ = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
+ Snippets
%div{ class: container_class }
.tab-content
@@ -137,3 +146,13 @@
.loading-status
= spinner
+
+ - if profile_tabs.empty?
+ .row
+ .col-12
+ .svg-content
+ = image_tag 'illustrations/profile_private_mode.svg'
+ .col-12.text-center
+ .text-content
+ %h4
+ This user has a private profile
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index d06f51b1828..f95df7ecf03 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -13,13 +13,13 @@
- cronjob:repository_archive_cache
- cronjob:repository_check_dispatch
- cronjob:requests_profiles
-- cronjob:schedule_update_user_activity
- cronjob:stuck_ci_jobs
- cronjob:stuck_import_jobs
- cronjob:stuck_merge_jobs
- cronjob:ci_archive_traces_cron
- cronjob:trending_projects
- cronjob:issue_due_scheduler
+- cronjob:prune_web_hook_logs
- gcp_cluster:cluster_install_app
- gcp_cluster:cluster_provision
@@ -45,7 +45,6 @@
- mail_scheduler:mail_scheduler_issue_due
- mail_scheduler:mail_scheduler_notification_service
-- object_storage_upload
- object_storage:object_storage_background_move
- object_storage:object_storage_migrate_uploads
@@ -74,6 +73,12 @@
- repository_check:repository_check_batch
- repository_check:repository_check_single_repository
+- todos_destroyer:todos_destroyer_confidential_issue
+- todos_destroyer:todos_destroyer_entity_leave
+- todos_destroyer:todos_destroyer_project_private
+- todos_destroyer:todos_destroyer_private_features
+- todos_destroyer:todos_destroyer_group_private
+
- default
- mailers # ActionMailer::DeliveryJob.queue_name
@@ -114,9 +119,9 @@
- storage_migrator
- system_hook_push
- update_merge_requests
-- update_user_activity
- upload_checksum
- web_hook
- repository_update_remote_mirror
- create_note_diff_file
- delete_diff_files
+- detect_repository_languages
diff --git a/app/workers/archive_trace_worker.rb b/app/workers/archive_trace_worker.rb
index 9169f21af2a..c6f89a17729 100644
--- a/app/workers/archive_trace_worker.rb
+++ b/app/workers/archive_trace_worker.rb
@@ -5,7 +5,7 @@ class ArchiveTraceWorker
include PipelineBackgroundQueue
def perform(job_id)
- Ci::Build.find_by(id: job_id).try do |job|
+ Ci::Build.without_archived_trace.find_by(id: job_id).try do |job|
job.trace.archive!
end
end
diff --git a/app/workers/background_migration_worker.rb b/app/workers/background_migration_worker.rb
index eaec7d48f35..7d006cc348e 100644
--- a/app/workers/background_migration_worker.rb
+++ b/app/workers/background_migration_worker.rb
@@ -6,10 +6,22 @@ class BackgroundMigrationWorker
# The minimum amount of time between processing two jobs of the same migration
# class.
#
- # This interval is set to 5 minutes so autovacuuming and other maintenance
- # related tasks have plenty of time to clean up after a migration has been
- # performed.
- MIN_INTERVAL = 5.minutes.to_i
+ # This interval is set to 2 or 5 minutes so autovacuuming and other
+ # maintenance related tasks have plenty of time to clean up after a migration
+ # has been performed.
+ def self.minimum_interval
+ if enable_health_check?
+ 2.minutes.to_i
+ else
+ 5.minutes.to_i
+ end
+ end
+
+ def self.enable_health_check?
+ Rails.env.development? ||
+ Rails.env.test? ||
+ Feature.enabled?('background_migration_health_check')
+ end
# Performs the background migration.
#
@@ -27,7 +39,8 @@ class BackgroundMigrationWorker
# running a migration of this class or we ran one recently. In this case
# we'll reschedule the job in such a way that it is picked up again around
# the time the lease expires.
- self.class.perform_in(ttl || MIN_INTERVAL, class_name, arguments)
+ self.class
+ .perform_in(ttl || self.class.minimum_interval, class_name, arguments)
end
end
@@ -39,17 +52,51 @@ class BackgroundMigrationWorker
[true, nil]
else
lease = lease_for(class_name)
+ perform = !!lease.try_obtain
+
+ # If we managed to acquire the lease but the DB is not healthy, then we
+ # want to simply reschedule our job and try again _after_ the lease
+ # expires.
+ if perform && !healthy_database?
+ database_unhealthy_counter.increment
- [lease.try_obtain, lease.ttl]
+ perform = false
+ end
+
+ [perform, lease.ttl]
end
end
def lease_for(class_name)
Gitlab::ExclusiveLease
- .new("#{self.class.name}:#{class_name}", timeout: MIN_INTERVAL)
+ .new(lease_key_for(class_name), timeout: self.class.minimum_interval)
+ end
+
+ def lease_key_for(class_name)
+ "#{self.class.name}:#{class_name}"
end
def always_perform?
Rails.env.test?
end
+
+ # Returns true if the database is healthy enough to allow the migration to be
+ # performed.
+ #
+ # class_name - The name of the background migration that we might want to
+ # run.
+ def healthy_database?
+ return true unless self.class.enable_health_check?
+
+ return true unless Gitlab::Database.postgresql?
+
+ !Postgresql::ReplicationSlot.lag_too_great?
+ end
+
+ def database_unhealthy_counter
+ Gitlab::Metrics.counter(
+ :background_migration_database_health_reschedules,
+ 'The number of times a background migration is rescheduled because the database is unhealthy.'
+ )
+ end
end
diff --git a/app/workers/ci/archive_traces_cron_worker.rb b/app/workers/ci/archive_traces_cron_worker.rb
index 7016edde698..7d4e9660a4e 100644
--- a/app/workers/ci/archive_traces_cron_worker.rb
+++ b/app/workers/ci/archive_traces_cron_worker.rb
@@ -12,6 +12,7 @@ module Ci
Ci::Build.finished.with_live_trace.find_each(batch_size: 100) do |build|
begin
build.trace.archive!
+ rescue ::Gitlab::Ci::Trace::AlreadyArchivedError
rescue => e
failed_archive_counter.increment
Rails.logger.error "Failed to archive stale live trace. id: #{build.id} message: #{e.message}"
diff --git a/app/workers/ci/build_trace_chunk_flush_worker.rb b/app/workers/ci/build_trace_chunk_flush_worker.rb
index 6376c6d32cf..9dbf2e5e1ac 100644
--- a/app/workers/ci/build_trace_chunk_flush_worker.rb
+++ b/app/workers/ci/build_trace_chunk_flush_worker.rb
@@ -7,7 +7,7 @@ module Ci
def perform(build_trace_chunk_id)
::Ci::BuildTraceChunk.find_by(id: build_trace_chunk_id).try do |build_trace_chunk|
- build_trace_chunk.use_database!
+ build_trace_chunk.persist_data!
end
end
end
diff --git a/app/workers/concerns/each_shard_worker.rb b/app/workers/concerns/each_shard_worker.rb
index d0a728fb495..00f589f957e 100644
--- a/app/workers/concerns/each_shard_worker.rb
+++ b/app/workers/concerns/each_shard_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EachShardWorker
extend ActiveSupport::Concern
include ::Gitlab::Utils::StrongMemoize
diff --git a/app/workers/concerns/gitlab/github_import/object_importer.rb b/app/workers/concerns/gitlab/github_import/object_importer.rb
index 100d86e38c8..eeeff6e93a0 100644
--- a/app/workers/concerns/gitlab/github_import/object_importer.rb
+++ b/app/workers/concerns/gitlab/github_import/object_importer.rb
@@ -22,7 +22,7 @@ module Gitlab
importer_class.new(object, project, client).execute
- counter.increment(project: project.full_path)
+ counter.increment
end
def counter
diff --git a/app/workers/concerns/todos_destroyer_queue.rb b/app/workers/concerns/todos_destroyer_queue.rb
new file mode 100644
index 00000000000..8e2b1d30579
--- /dev/null
+++ b/app/workers/concerns/todos_destroyer_queue.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+##
+# Concern for setting Sidekiq settings for the various Todos Destroyers.
+#
+module TodosDestroyerQueue
+ extend ActiveSupport::Concern
+
+ included do
+ queue_namespace :todos_destroyer
+ end
+end
diff --git a/app/workers/create_gpg_signature_worker.rb b/app/workers/create_gpg_signature_worker.rb
index a2da1bda11f..a1aeeb7c4fc 100644
--- a/app/workers/create_gpg_signature_worker.rb
+++ b/app/workers/create_gpg_signature_worker.rb
@@ -3,15 +3,27 @@
class CreateGpgSignatureWorker
include ApplicationWorker
- def perform(commit_sha, project_id)
+ def perform(commit_shas, project_id)
+ # Older versions of GitPushService may push a single commit ID on the stack.
+ # We need this to be backwards compatible.
+ commit_shas = Array(commit_shas)
+
+ return if commit_shas.empty?
+
project = Project.find_by(id: project_id)
return unless project
- commit = project.commit(commit_sha)
+ commits = project.commits_by(oids: commit_shas)
- return unless commit
+ return if commits.empty?
# This calculates and caches the signature in the database
- Gitlab::Gpg::Commit.new(commit).signature
+ commits.each do |commit|
+ begin
+ Gitlab::Gpg::Commit.new(commit).signature
+ rescue => e
+ Rails.logger.error("Failed to create signature for commit #{commit.id}. Error: #{e.message}")
+ end
+ end
end
end
diff --git a/app/workers/delete_diff_files_worker.rb b/app/workers/delete_diff_files_worker.rb
index bb8fbb9c373..0874a0b75e8 100644
--- a/app/workers/delete_diff_files_worker.rb
+++ b/app/workers/delete_diff_files_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteDiffFilesWorker
include ApplicationWorker
diff --git a/app/workers/detect_repository_languages_worker.rb b/app/workers/detect_repository_languages_worker.rb
new file mode 100644
index 00000000000..537b8fd5963
--- /dev/null
+++ b/app/workers/detect_repository_languages_worker.rb
@@ -0,0 +1,33 @@
+class DetectRepositoryLanguagesWorker
+ include ApplicationWorker
+ include ExceptionBacktrace
+ include ExclusiveLeaseGuard
+
+ sidekiq_options retry: 1
+
+ LEASE_TIMEOUT = 300
+
+ attr_reader :project
+
+ def perform(project_id, user_id)
+ @project = Project.find_by(id: project_id)
+ user = User.find_by(id: user_id)
+ return unless project && user
+
+ return if Feature.disabled?(:repository_languages, project.namespace)
+
+ try_obtain_lease do
+ ::Projects::DetectRepositoryLanguagesService.new(project, user).execute
+ end
+ end
+
+ private
+
+ def lease_timeout
+ LEASE_TIMEOUT
+ end
+
+ def lease_key
+ "gitlab:detect_repository_languages:#{project.id}"
+ end
+end
diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb
index f9f0efb302a..12706613ac2 100644
--- a/app/workers/email_receiver_worker.rb
+++ b/app/workers/email_receiver_worker.rb
@@ -15,14 +15,14 @@ class EmailReceiverWorker
private
- def handle_failure(raw, e)
- Rails.logger.warn("Email can not be processed: #{e}\n\n#{raw}")
+ def handle_failure(raw, error)
+ Rails.logger.warn("Email can not be processed: #{error}\n\n#{raw}")
return unless raw.present?
can_retry = false
reason =
- case e
+ case error
when Gitlab::Email::UnknownIncomingEmail
"We couldn't figure out what the email is for. Please create your issue or comment through the web interface."
when Gitlab::Email::SentNotificationNotFoundError
@@ -42,7 +42,7 @@ class EmailReceiverWorker
"The thread you are replying to no longer exists, perhaps it was deleted? If you believe this is in error, contact a staff member."
when Gitlab::Email::InvalidRecordError
can_retry = true
- e.message
+ error.message
end
if reason
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index 8d0cfc73ccd..17ad1d5ab88 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -51,7 +51,7 @@ class EmailsOnPushWorker
end
end
- recipients.split.each do |recipient|
+ valid_recipients(recipients).each do |recipient|
begin
send_email(
recipient,
@@ -89,4 +89,10 @@ class EmailsOnPushWorker
email.header[:skip_premailer] = true if skip_premailer
email.deliver_now
end
+
+ def valid_recipients(recipients)
+ recipients.split.select do |recipient|
+ recipient.include?('@')
+ end
+ end
end
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
index fd49bc18161..2d381c6fd6c 100644
--- a/app/workers/git_garbage_collect_worker.rb
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -65,10 +65,10 @@ class GitGarbageCollectWorker
client.repack_incremental
end
rescue GRPC::NotFound => e
- Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
+ Gitlab::GitLogger.error("#{__method__} failed:\nRepository not found")
raise Gitlab::Git::Repository::NoRepository.new(e)
rescue GRPC::BadStatus => e
- Gitlab::GitLogger.error("#{method} failed:\n#{e}")
+ Gitlab::GitLogger.error("#{__method__} failed:\n#{e}")
raise Gitlab::Git::CommandError.new(e)
end
diff --git a/app/workers/object_storage/migrate_uploads_worker.rb b/app/workers/object_storage/migrate_uploads_worker.rb
index a3ecfa8e711..01d03ec7888 100644
--- a/app/workers/object_storage/migrate_uploads_worker.rb
+++ b/app/workers/object_storage/migrate_uploads_worker.rb
@@ -1,6 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
-# rubocop:disable Style/Documentation
module ObjectStorage
class MigrateUploadsWorker
diff --git a/app/workers/object_storage_upload_worker.rb b/app/workers/object_storage_upload_worker.rb
deleted file mode 100644
index f17980a83d8..00000000000
--- a/app/workers/object_storage_upload_worker.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-# @Deprecated - remove once the `object_storage_upload` queue is empty
-# The queue has been renamed `object_storage:object_storage_background_upload`
-#
-class ObjectStorageUploadWorker
- include ApplicationWorker
-
- sidekiq_options retry: 5
-
- def perform(uploader_class_name, subject_class_name, file_field, subject_id)
- uploader_class = uploader_class_name.constantize
- subject_class = subject_class_name.constantize
-
- return unless uploader_class < ObjectStorage::Concern
- return unless uploader_class.object_store_enabled?
- return unless uploader_class.background_upload_enabled?
-
- subject = subject_class.find(subject_id)
- uploader = subject.public_send(file_field) # rubocop:disable GitlabSecurity/PublicSend
- uploader.migrate!(ObjectStorage::Store::REMOTE)
- end
-end
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index ed39b4a1ea8..c9f6df9b56d 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -79,9 +79,10 @@ class ProcessCommitWorker
# Avoid reprocessing commits that already exist in the upstream
# when project is forked. This will also prevent duplicated system notes.
def commit_exists_in_upstream?(project, commit_hash)
- return false unless project.forked?
+ upstream_project = project.fork_source
+
+ return false unless upstream_project
- upstream_project = project.forked_from_project
commit_id = commit_hash.with_indifferent_access[:id]
upstream_project.commit(commit_id).present?
end
diff --git a/app/workers/project_migrate_hashed_storage_worker.rb b/app/workers/project_migrate_hashed_storage_worker.rb
index 9e4d66250a4..ad0003e7bff 100644
--- a/app/workers/project_migrate_hashed_storage_worker.rb
+++ b/app/workers/project_migrate_hashed_storage_worker.rb
@@ -5,13 +5,13 @@ class ProjectMigrateHashedStorageWorker
LEASE_TIMEOUT = 30.seconds.to_i
- def perform(project_id)
+ def perform(project_id, old_disk_path = nil)
project = Project.find_by(id: project_id)
return if project.nil? || project.pending_delete?
uuid = lease_for(project_id).try_obtain
if uuid
- ::Projects::HashedStorageMigrationService.new(project, logger).execute
+ ::Projects::HashedStorageMigrationService.new(project, old_disk_path || project.full_path, logger: logger).execute
else
false
end
diff --git a/app/workers/prune_web_hook_logs_worker.rb b/app/workers/prune_web_hook_logs_worker.rb
new file mode 100644
index 00000000000..45c7d32f7eb
--- /dev/null
+++ b/app/workers/prune_web_hook_logs_worker.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+# Worker that deletes a fixed number of outdated rows from the "web_hook_logs"
+# table.
+class PruneWebHookLogsWorker
+ include ApplicationWorker
+ include CronjobQueue
+
+ # The maximum number of rows to remove in a single job.
+ DELETE_LIMIT = 50_000
+
+ def perform
+ # MySQL doesn't allow "DELETE FROM ... WHERE id IN ( ... )" if the inner
+ # query refers to the same table. To work around this we wrap the IN body in
+ # another sub query.
+ WebHookLog
+ .where(
+ 'id IN (SELECT id FROM (?) ids_to_remove)',
+ WebHookLog
+ .select(:id)
+ .where('created_at < ?', 90.days.ago.beginning_of_day)
+ .limit(DELETE_LIMIT)
+ )
+ .delete_all
+ end
+end
diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb
index 051382a08a9..07559ea479b 100644
--- a/app/workers/repository_check/batch_worker.rb
+++ b/app/workers/repository_check/batch_worker.rb
@@ -4,9 +4,11 @@ module RepositoryCheck
class BatchWorker
include ApplicationWorker
include RepositoryCheckQueue
+ include ExclusiveLeaseGuard
RUN_TIME = 3600
BATCH_SIZE = 10_000
+ LEASE_TIMEOUT = 1.hour
attr_reader :shard_name
@@ -16,6 +18,20 @@ module RepositoryCheck
return unless Gitlab::CurrentSettings.repository_checks_enabled
return unless Gitlab::ShardHealthCache.healthy_shard?(shard_name)
+ try_obtain_lease do
+ perform_repository_checks
+ end
+ end
+
+ def lease_timeout
+ LEASE_TIMEOUT
+ end
+
+ def lease_key
+ "repository_check_batch_worker:#{shard_name}"
+ end
+
+ def perform_repository_checks
start = Time.now
# This loop will break after a little more than one hour ('a little
@@ -26,7 +42,7 @@ module RepositoryCheck
project_ids.each do |project_id|
break if Time.now - start >= RUN_TIME
- next unless try_obtain_lease(project_id)
+ next unless try_obtain_lease_for_project(project_id)
SingleRepositoryWorker.new.perform(project_id)
end
@@ -60,7 +76,7 @@ module RepositoryCheck
Project.where(repository_storage: shard_name)
end
- def try_obtain_lease(id)
+ def try_obtain_lease_for_project(id)
# Use a 24-hour timeout because on servers/projects where 'git fsck' is
# super slow we definitely do not want to run it twice in parallel.
Gitlab::ExclusiveLease.new(
diff --git a/app/workers/repository_check/dispatch_worker.rb b/app/workers/repository_check/dispatch_worker.rb
index 891a273afd7..0a7d9a14c6a 100644
--- a/app/workers/repository_check/dispatch_worker.rb
+++ b/app/workers/repository_check/dispatch_worker.rb
@@ -1,15 +1,26 @@
+# frozen_string_literal: true
+
module RepositoryCheck
class DispatchWorker
include ApplicationWorker
include CronjobQueue
include ::EachShardWorker
+ include ExclusiveLeaseGuard
+
+ LEASE_TIMEOUT = 1.hour
def perform
return unless Gitlab::CurrentSettings.repository_checks_enabled
- each_eligible_shard do |shard_name|
- RepositoryCheck::BatchWorker.perform_async(shard_name)
+ try_obtain_lease do
+ each_eligible_shard do |shard_name|
+ RepositoryCheck::BatchWorker.perform_async(shard_name)
+ end
end
end
+
+ def lease_timeout
+ LEASE_TIMEOUT
+ end
end
end
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index 5ef9b744db3..68ec66e8499 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -23,9 +23,7 @@ class RepositoryForkWorker
def fork_repository(target_project, source_repository_storage_name, source_disk_path)
return unless start_fork(target_project)
- Gitlab::Metrics.add_event(:fork_repository,
- source_path: source_disk_path,
- target_path: target_project.disk_path)
+ Gitlab::Metrics.add_event(:fork_repository)
result = gitlab_shell.fork_repository(source_repository_storage_name, source_disk_path,
target_project.repository_storage, target_project.disk_path)
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 25fec542ac7..82189a3c9f5 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -7,13 +7,11 @@ class RepositoryImportWorker
include ProjectImportOptions
def perform(project_id)
- project = Project.find(project_id)
+ @project = Project.find(project_id)
- return unless start_import(project)
+ return unless start_import
- Gitlab::Metrics.add_event(:import_repository,
- import_url: project.import_url,
- path: project.full_path)
+ Gitlab::Metrics.add_event(:import_repository)
service = Projects::ImportService.new(project, project.creator)
result = service.execute
@@ -23,7 +21,7 @@ class RepositoryImportWorker
return if service.async?
if result[:status] == :error
- fail_import(project, result[:message]) if project.gitlab_project_import?
+ fail_import(result[:message]) if template_import?
raise result[:message]
end
@@ -33,14 +31,20 @@ class RepositoryImportWorker
private
- def start_import(project)
+ attr_reader :project
+
+ def start_import
return true if start(project)
Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while importing.")
false
end
- def fail_import(project, message)
+ def fail_import(message)
project.mark_import_as_failed(message)
end
+
+ def template_import?
+ project.gitlab_project_import?
+ end
end
diff --git a/app/workers/schedule_update_user_activity_worker.rb b/app/workers/schedule_update_user_activity_worker.rb
deleted file mode 100644
index ff42fb8f0e5..00000000000
--- a/app/workers/schedule_update_user_activity_worker.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-class ScheduleUpdateUserActivityWorker
- include ApplicationWorker
- include CronjobQueue
-
- def perform(batch_size = 500)
- Gitlab::UserActivities.new.each_slice(batch_size) do |batch|
- UpdateUserActivityWorker.perform_async(Hash[batch])
- end
- end
-end
diff --git a/app/workers/todos_destroyer/confidential_issue_worker.rb b/app/workers/todos_destroyer/confidential_issue_worker.rb
new file mode 100644
index 00000000000..9d640c14963
--- /dev/null
+++ b/app/workers/todos_destroyer/confidential_issue_worker.rb
@@ -0,0 +1,10 @@
+module TodosDestroyer
+ class ConfidentialIssueWorker
+ include ApplicationWorker
+ include TodosDestroyerQueue
+
+ def perform(issue_id)
+ ::Todos::Destroy::ConfidentialIssueService.new(issue_id).execute
+ end
+ end
+end
diff --git a/app/workers/todos_destroyer/entity_leave_worker.rb b/app/workers/todos_destroyer/entity_leave_worker.rb
new file mode 100644
index 00000000000..e62d9876f4a
--- /dev/null
+++ b/app/workers/todos_destroyer/entity_leave_worker.rb
@@ -0,0 +1,10 @@
+module TodosDestroyer
+ class EntityLeaveWorker
+ include ApplicationWorker
+ include TodosDestroyerQueue
+
+ def perform(user_id, entity_id, entity_type)
+ ::Todos::Destroy::EntityLeaveService.new(user_id, entity_id, entity_type).execute
+ end
+ end
+end
diff --git a/app/workers/todos_destroyer/group_private_worker.rb b/app/workers/todos_destroyer/group_private_worker.rb
new file mode 100644
index 00000000000..3e47eec7461
--- /dev/null
+++ b/app/workers/todos_destroyer/group_private_worker.rb
@@ -0,0 +1,10 @@
+module TodosDestroyer
+ class GroupPrivateWorker
+ include ApplicationWorker
+ include TodosDestroyerQueue
+
+ def perform(group_id)
+ ::Todos::Destroy::GroupPrivateService.new(group_id).execute
+ end
+ end
+end
diff --git a/app/workers/todos_destroyer/private_features_worker.rb b/app/workers/todos_destroyer/private_features_worker.rb
new file mode 100644
index 00000000000..f457d5e0471
--- /dev/null
+++ b/app/workers/todos_destroyer/private_features_worker.rb
@@ -0,0 +1,10 @@
+module TodosDestroyer
+ class PrivateFeaturesWorker
+ include ApplicationWorker
+ include TodosDestroyerQueue
+
+ def perform(project_id, user_id = nil)
+ ::Todos::Destroy::PrivateFeaturesService.new(project_id, user_id).execute
+ end
+ end
+end
diff --git a/app/workers/todos_destroyer/project_private_worker.rb b/app/workers/todos_destroyer/project_private_worker.rb
new file mode 100644
index 00000000000..7a853c36370
--- /dev/null
+++ b/app/workers/todos_destroyer/project_private_worker.rb
@@ -0,0 +1,10 @@
+module TodosDestroyer
+ class ProjectPrivateWorker
+ include ApplicationWorker
+ include TodosDestroyerQueue
+
+ def perform(project_id)
+ ::Todos::Destroy::ProjectPrivateService.new(project_id).execute
+ end
+ end
+end
diff --git a/app/workers/update_user_activity_worker.rb b/app/workers/update_user_activity_worker.rb
deleted file mode 100644
index 15f01a70337..00000000000
--- a/app/workers/update_user_activity_worker.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-class UpdateUserActivityWorker
- include ApplicationWorker
-
- def perform(pairs)
- pairs = cast_data(pairs)
- ids = pairs.keys
- conditions = 'WHEN id = ? THEN ? ' * ids.length
-
- User.where(id: ids)
- .update_all([
- "last_activity_on = CASE #{conditions} ELSE last_activity_on END",
- *pairs.to_a.flatten
- ])
-
- Gitlab::UserActivities.new.delete(*ids)
- end
-
- private
-
- def cast_data(pairs)
- pairs.each_with_object({}) do |(key, value), new_pairs|
- new_pairs[key.to_i] = Time.at(value.to_i).to_s(:db)
- end
- end
-end
diff --git a/bin/changelog b/bin/changelog
index d7b2a1a2de9..758c036161e 100755
--- a/bin/changelog
+++ b/bin/changelog
@@ -23,6 +23,8 @@ module ChangelogHelpers
Abort = Class.new(StandardError)
Done = Class.new(StandardError)
+ MAX_FILENAME_LENGTH = 140 # ecryptfs has a limit of 140 characters
+
def capture_stdout(cmd)
output = IO.popen(cmd, &:read)
fail_with "command failed: #{cmd.join(' ')}" unless $?.success?
@@ -142,7 +144,9 @@ class ChangelogEntry
def initialize(options)
@options = options
+ end
+ def execute
assert_feature_branch!
assert_title!
assert_new_file!
@@ -221,10 +225,12 @@ class ChangelogEntry
end
def file_path
- File.join(
+ base_path = File.join(
unreleased_path,
- branch_name.gsub(/[^\w-]/, '-') << '.yml'
- )
+ branch_name.gsub(/[^\w-]/, '-'))
+
+ # Add padding for .yml extension
+ base_path[0..MAX_FILENAME_LENGTH - 5] + '.yml'
end
def unreleased_path
@@ -250,7 +256,7 @@ end
if $0 == __FILE__
begin
options = ChangelogOptionParser.parse(ARGV)
- ChangelogEntry.new(options)
+ ChangelogEntry.new(options).execute
rescue ChangelogHelpers::Abort => ex
$stderr.puts ex.message
exit 1
diff --git a/changelogs/no-rm-rf-gitlab-basics.yml b/changelogs/no-rm-rf-gitlab-basics.yml
deleted file mode 100644
index d5aa1091b45..00000000000
--- a/changelogs/no-rm-rf-gitlab-basics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
- title: Do not use '-f' with 'rm' in gitlab-basics docs
- merge_request: 18027
- author: Elias Werberich
- type: changed
diff --git a/changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml b/changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml
new file mode 100644
index 00000000000..f7521ff9225
--- /dev/null
+++ b/changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml
@@ -0,0 +1,4 @@
+title: First Improvements made to the contributor on-boarding experience.
+merge_request: 20682
+author: Eddie Stubbington
+type: added
diff --git a/changelogs/unreleased/1756-set-iid-via-api.yml b/changelogs/unreleased/1756-set-iid-via-api.yml
new file mode 100644
index 00000000000..680a9464ab4
--- /dev/null
+++ b/changelogs/unreleased/1756-set-iid-via-api.yml
@@ -0,0 +1,5 @@
+---
+title: Allow issues API to receive an internal ID (iid) on create
+merge_request: 20626
+author: Jamie Schembri
+type: fixed
diff --git a/changelogs/unreleased/19439-api-file-sha56-and-head.yml b/changelogs/unreleased/19439-api-file-sha56-and-head.yml
deleted file mode 100644
index 4bc1e560631..00000000000
--- a/changelogs/unreleased/19439-api-file-sha56-and-head.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add SHA256 and HEAD on File API
-merge_request: 19439
-author: ahmet2mir
-type: added
diff --git a/changelogs/unreleased/23705-add-single-file-download-in-repo.yml b/changelogs/unreleased/23705-add-single-file-download-in-repo.yml
new file mode 100644
index 00000000000..f156bfb1101
--- /dev/null
+++ b/changelogs/unreleased/23705-add-single-file-download-in-repo.yml
@@ -0,0 +1,5 @@
+---
+title: Add download button for single file (including raw files) in repository
+merge_request: 20480
+author: Kia Mei Somabes
+type: added
diff --git a/changelogs/unreleased/25990-improve-web-terminal.yml b/changelogs/unreleased/25990-improve-web-terminal.yml
new file mode 100644
index 00000000000..3f8a8c6211c
--- /dev/null
+++ b/changelogs/unreleased/25990-improve-web-terminal.yml
@@ -0,0 +1,5 @@
+---
+title: Move xterm to a node dependency and remove it from vendor's folder
+merge_request: 20588
+author:
+type: other
diff --git a/changelogs/unreleased/25990-interactive-web-terminals-authorization.yml b/changelogs/unreleased/25990-interactive-web-terminals-authorization.yml
new file mode 100644
index 00000000000..0a2853c20c6
--- /dev/null
+++ b/changelogs/unreleased/25990-interactive-web-terminals-authorization.yml
@@ -0,0 +1,5 @@
+---
+title: Fix authorization for interactive web terminals
+merge_request: 20811
+author:
+type: fixed
diff --git a/changelogs/unreleased/27456-improve-feedback-when-dev-cannot-push-to-empty-repo.yml b/changelogs/unreleased/27456-improve-feedback-when-dev-cannot-push-to-empty-repo.yml
new file mode 100644
index 00000000000..55d82c4ee5d
--- /dev/null
+++ b/changelogs/unreleased/27456-improve-feedback-when-dev-cannot-push-to-empty-repo.yml
@@ -0,0 +1,5 @@
+---
+title: Improve feedback when a developer is unable to push to an empty repository
+merge_request: 20519
+author:
+type: changed
diff --git a/changelogs/unreleased/29278-commits-page-tooltips.yml b/changelogs/unreleased/29278-commits-page-tooltips.yml
new file mode 100644
index 00000000000..d54301a1cf0
--- /dev/null
+++ b/changelogs/unreleased/29278-commits-page-tooltips.yml
@@ -0,0 +1,5 @@
+---
+title: Remove tooltips from commit author avatar and name in commit lists
+merge_request: 20674
+author:
+type: other
diff --git a/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml b/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml
new file mode 100644
index 00000000000..21d9d25d342
--- /dev/null
+++ b/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml
@@ -0,0 +1,5 @@
+---
+title: Redirect commits to root if no ref is provided (31576)
+merge_request: 20738
+author: Kia Mei Somabes
+type: added
diff --git a/changelogs/unreleased/32783-api-all-members-with-ancestors.yml b/changelogs/unreleased/32783-api-all-members-with-ancestors.yml
new file mode 100644
index 00000000000..ca53d02845d
--- /dev/null
+++ b/changelogs/unreleased/32783-api-all-members-with-ancestors.yml
@@ -0,0 +1,6 @@
+---
+title: Adds API endpoint /api/v4/(project/group)/:id/members/all to list also inherited
+ members
+merge_request: 19748
+author: Jacopo Beschi @jacopo-beschi
+type: added
diff --git a/changelogs/unreleased/32821-better-error-message-add-invalid-user-to-project.yml b/changelogs/unreleased/32821-better-error-message-add-invalid-user-to-project.yml
new file mode 100644
index 00000000000..587d7209c2f
--- /dev/null
+++ b/changelogs/unreleased/32821-better-error-message-add-invalid-user-to-project.yml
@@ -0,0 +1,5 @@
+---
+title: Improve error message when adding invalid user to a project
+merge_request: 20885
+author: Jacopo Beschi @jacopo-beschi
+type: added
diff --git a/changelogs/unreleased/34572-ssh-certificates.yml b/changelogs/unreleased/34572-ssh-certificates.yml
new file mode 100644
index 00000000000..76a08a188de
--- /dev/null
+++ b/changelogs/unreleased/34572-ssh-certificates.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for SSH certificate authentication
+merge_request: 19911
+author: Ævar Arnfjörð Bjarmason
+type: added
diff --git a/changelogs/unreleased/35158-snippets-api-visibility.yml b/changelogs/unreleased/35158-snippets-api-visibility.yml
deleted file mode 100644
index f06015dda46..00000000000
--- a/changelogs/unreleased/35158-snippets-api-visibility.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Expose visibility via Snippets API
-merge_request: 19620
-author: Jan Beckmann
-type: added
diff --git a/changelogs/unreleased/35952-keep-admin-settings-open-after-submit.yml b/changelogs/unreleased/35952-keep-admin-settings-open-after-submit.yml
new file mode 100644
index 00000000000..e1e5c8db046
--- /dev/null
+++ b/changelogs/unreleased/35952-keep-admin-settings-open-after-submit.yml
@@ -0,0 +1,5 @@
+---
+title: Keep admin settings sections open after submitting forms
+merge_request: 21040
+author:
+type: other
diff --git a/changelogs/unreleased/36409-frontend-for-clarifying-the-usefulness-of-the-search-bar.yml b/changelogs/unreleased/36409-frontend-for-clarifying-the-usefulness-of-the-search-bar.yml
new file mode 100644
index 00000000000..efa13c9ab3c
--- /dev/null
+++ b/changelogs/unreleased/36409-frontend-for-clarifying-the-usefulness-of-the-search-bar.yml
@@ -0,0 +1,5 @@
+---
+title: UX improvements to top nav search bar
+merge_request: 20537
+author:
+type: changed
diff --git a/changelogs/unreleased/37561-add-id-settings.yml b/changelogs/unreleased/37561-add-id-settings.yml
deleted file mode 100644
index 122ac23cb53..00000000000
--- a/changelogs/unreleased/37561-add-id-settings.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allows settings sections to expand by default when linking to them
-merge_request: 20211
-author:
-type: other
diff --git a/changelogs/unreleased/38604-add-private-profile.yml b/changelogs/unreleased/38604-add-private-profile.yml
new file mode 100644
index 00000000000..e40e7d9321e
--- /dev/null
+++ b/changelogs/unreleased/38604-add-private-profile.yml
@@ -0,0 +1,5 @@
+---
+title: Add an option to have a private profile on GitLab.
+merge_request: 20387
+author: jxterry
+type: added
diff --git a/changelogs/unreleased/39604-update-top-right-avatar-after-changing-avatar.yml b/changelogs/unreleased/39604-update-top-right-avatar-after-changing-avatar.yml
deleted file mode 100644
index 17192673996..00000000000
--- a/changelogs/unreleased/39604-update-top-right-avatar-after-changing-avatar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Change avatar image in the header when user updates their avatar.
-merge_request: 20119
-author: Jamie Schembri
-type: added
diff --git a/changelogs/unreleased/40005-u2f-unspported-browsers.yml b/changelogs/unreleased/40005-u2f-unspported-browsers.yml
deleted file mode 100644
index eb5ff99246e..00000000000
--- a/changelogs/unreleased/40005-u2f-unspported-browsers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve U2F workflow when using unsupported browsers
-merge_request: 19938
-author: Jan Beckmann
-type: changed
diff --git a/changelogs/unreleased/40484-ordered-lists-copy-gfm.yml b/changelogs/unreleased/40484-ordered-lists-copy-gfm.yml
deleted file mode 100644
index f4b34909ae9..00000000000
--- a/changelogs/unreleased/40484-ordered-lists-copy-gfm.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Keep lists ordered when copying only list items
-merge_request: 18522
-author: Jan Beckmann
-type: fixed
diff --git a/changelogs/unreleased/40973-disable-rack-attack-by-default.yml b/changelogs/unreleased/40973-disable-rack-attack-by-default.yml
new file mode 100644
index 00000000000..681aa761e2a
--- /dev/null
+++ b/changelogs/unreleased/40973-disable-rack-attack-by-default.yml
@@ -0,0 +1,5 @@
+---
+title: Rack attack is now disabled by default
+merge_request: 16669
+author:
+type: changed
diff --git a/changelogs/unreleased/41416-making-instance-wide-data-tools-more-accessible.yml b/changelogs/unreleased/41416-making-instance-wide-data-tools-more-accessible.yml
new file mode 100644
index 00000000000..b980b719d68
--- /dev/null
+++ b/changelogs/unreleased/41416-making-instance-wide-data-tools-more-accessible.yml
@@ -0,0 +1,5 @@
+---
+title: Allow non-admins to view instance statistics (if permitted by the instance admins)
+merge_request: 20874
+author:
+type: changed
diff --git a/changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml b/changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml
new file mode 100644
index 00000000000..c6a0dc4f129
--- /dev/null
+++ b/changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml
@@ -0,0 +1,5 @@
+---
+title: "Fixing milestone date change when editing"
+merge_request: 20279
+author: Orlando Del Aguila
+type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/41784-monitoring-graph-popovers.yml b/changelogs/unreleased/41784-monitoring-graph-popovers.yml
new file mode 100644
index 00000000000..757445d7e0c
--- /dev/null
+++ b/changelogs/unreleased/41784-monitoring-graph-popovers.yml
@@ -0,0 +1,5 @@
+---
+title: Update design for system metrics popovers
+merge_request: 20655
+author:
+type: fixed
diff --git a/changelogs/unreleased/42342-teams-pipeline-notifications.yml b/changelogs/unreleased/42342-teams-pipeline-notifications.yml
deleted file mode 100644
index 4ef3a35465b..00000000000
--- a/changelogs/unreleased/42342-teams-pipeline-notifications.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes Microsoft Teams notifications for pipeline events
-merge_request: 19632
-author: Jeff Brown
-type: fixed
diff --git a/changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml b/changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml
new file mode 100644
index 00000000000..cabe5216045
--- /dev/null
+++ b/changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml
@@ -0,0 +1,5 @@
+---
+title: Adds with_projects optional parameter to GET /groups/:id API endpoint
+merge_request: 20494
+author:
+type: changed
diff --git a/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml b/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml
new file mode 100644
index 00000000000..b60aeba860a
--- /dev/null
+++ b/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Vue datatype errors for markdownVersion parsing
+merge_request: 20800
+author:
+type: fixed
diff --git a/changelogs/unreleased/43312-remove_user_activity_workers.yml b/changelogs/unreleased/43312-remove_user_activity_workers.yml
new file mode 100644
index 00000000000..6dfd018e350
--- /dev/null
+++ b/changelogs/unreleased/43312-remove_user_activity_workers.yml
@@ -0,0 +1,5 @@
+---
+title: Delete UserActivities and related workers
+merge_request: 20597
+author:
+type: performance
diff --git a/changelogs/unreleased/43472-remove-environment-scope-field-on-cluster-creation-form-for-core-starter-plans.yml b/changelogs/unreleased/43472-remove-environment-scope-field-on-cluster-creation-form-for-core-starter-plans.yml
deleted file mode 100644
index 7d2804f0310..00000000000
--- a/changelogs/unreleased/43472-remove-environment-scope-field-on-cluster-creation-form-for-core-starter-plans.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removes the environment scope field for users that cannot edit it
-merge_request: 19643
-author:
-type: changed
diff --git a/changelogs/unreleased/44127-board-label-edit-drop-down-is-showing-incorrect-selected-labels-summary.yml b/changelogs/unreleased/44127-board-label-edit-drop-down-is-showing-incorrect-selected-labels-summary.yml
new file mode 100644
index 00000000000..de991ef475a
--- /dev/null
+++ b/changelogs/unreleased/44127-board-label-edit-drop-down-is-showing-incorrect-selected-labels-summary.yml
@@ -0,0 +1,5 @@
+---
+title: Board label edit dropdown shows incorrect selected labels summary
+merge_request: 20673
+author:
+type: fixed
diff --git a/changelogs/unreleased/44674-use-one-column-form-layout-on-admin-area-settings-page.yml b/changelogs/unreleased/44674-use-one-column-form-layout-on-admin-area-settings-page.yml
deleted file mode 100644
index 69733889d5a..00000000000
--- a/changelogs/unreleased/44674-use-one-column-form-layout-on-admin-area-settings-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use one column form layout on Admin Area Settings page
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml b/changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml
new file mode 100644
index 00000000000..ddc878ee710
--- /dev/null
+++ b/changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml
@@ -0,0 +1,5 @@
+---
+title: Adds foreign key to notification_settings.user_id
+merge_request: 20567
+author: Jacopo Beschi @jacopo-beschi
+type: added
diff --git a/changelogs/unreleased/45318-junit-FE.yml b/changelogs/unreleased/45318-junit-FE.yml
new file mode 100644
index 00000000000..bbc08f54484
--- /dev/null
+++ b/changelogs/unreleased/45318-junit-FE.yml
@@ -0,0 +1,5 @@
+---
+title: Adds frontend support to render test reports on the MR widget
+merge_request: 20936
+author:
+type: added
diff --git a/changelogs/unreleased/45318-vuex-store.yml b/changelogs/unreleased/45318-vuex-store.yml
new file mode 100644
index 00000000000..5ea89034bce
--- /dev/null
+++ b/changelogs/unreleased/45318-vuex-store.yml
@@ -0,0 +1,5 @@
+---
+title: Adds Vuex store for reports section in MR widget
+merge_request: 20709
+author:
+type: added
diff --git a/changelogs/unreleased/45400-automatically-created-mr-uses-wrong-target-branch-when-branching-from-tag.yml b/changelogs/unreleased/45400-automatically-created-mr-uses-wrong-target-branch-when-branching-from-tag.yml
deleted file mode 100644
index 5aba62435ed..00000000000
--- a/changelogs/unreleased/45400-automatically-created-mr-uses-wrong-target-branch-when-branching-from-tag.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Set MR target branch to default branch if target branch is not valid
-merge_request: 19067
-author:
-type: fixed
diff --git a/changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml b/changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml
new file mode 100644
index 00000000000..b18f7aec546
--- /dev/null
+++ b/changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve "Unable to save user profile update with Safari"
+merge_request: 20676
+author:
+type: fixed
diff --git a/changelogs/unreleased/45487-slack-tag-push-notifs.yml b/changelogs/unreleased/45487-slack-tag-push-notifs.yml
deleted file mode 100644
index 647000bd97c..00000000000
--- a/changelogs/unreleased/45487-slack-tag-push-notifs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix chat service tag notifications not sending when only default branch enabled
-merge_request: 19864
-author:
-type: fixed
diff --git a/changelogs/unreleased/45557-machine-type-help-links.yml b/changelogs/unreleased/45557-machine-type-help-links.yml
deleted file mode 100644
index 870a650e10b..00000000000
--- a/changelogs/unreleased/45557-machine-type-help-links.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Add machine type and pricing documentation links, add class to labels to make
- bold
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/45575-invalid-characters-signup.yml b/changelogs/unreleased/45575-invalid-characters-signup.yml
deleted file mode 100644
index 679bd13e59b..00000000000
--- a/changelogs/unreleased/45575-invalid-characters-signup.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Fix username validation order on signup, resolves #45575'
-merge_request: 19610
-author: Jan Beckmann
-type: fixed
diff --git a/changelogs/unreleased/45703-open-web-ide-file-tree.yml b/changelogs/unreleased/45703-open-web-ide-file-tree.yml
deleted file mode 100644
index abee9cad2d5..00000000000
--- a/changelogs/unreleased/45703-open-web-ide-file-tree.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update WebIDE to show file in tree on load
-merge_request: 19887
-author:
-type: changed
diff --git a/changelogs/unreleased/45933-webide-fade-uneditable-area.yml b/changelogs/unreleased/45933-webide-fade-uneditable-area.yml
deleted file mode 100644
index dfb186122e7..00000000000
--- a/changelogs/unreleased/45933-webide-fade-uneditable-area.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fade uneditable area in Web IDE
-merge_request: 20008
-author:
-type: changed
diff --git a/changelogs/unreleased/46165-web-ide-branch-picker.yml b/changelogs/unreleased/46165-web-ide-branch-picker.yml
new file mode 100644
index 00000000000..ff879cb3d37
--- /dev/null
+++ b/changelogs/unreleased/46165-web-ide-branch-picker.yml
@@ -0,0 +1,5 @@
+---
+title: Create branch and MR picker for Web IDE
+merge_request: 20978
+author:
+type: changed
diff --git a/changelogs/unreleased/46202-webide-file-states.yml b/changelogs/unreleased/46202-webide-file-states.yml
deleted file mode 100644
index 8d697b643be..00000000000
--- a/changelogs/unreleased/46202-webide-file-states.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Web IDE file tree styles
-merge_request: 19969
-author:
-type: changed
diff --git a/changelogs/unreleased/46396-recognise-when-a-user-is-trying-to-validate-a-private-ssh-key-part-1.yml b/changelogs/unreleased/46396-recognise-when-a-user-is-trying-to-validate-a-private-ssh-key-part-1.yml
deleted file mode 100644
index d8c7d612c3d..00000000000
--- a/changelogs/unreleased/46396-recognise-when-a-user-is-trying-to-validate-a-private-ssh-key-part-1.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update new SSH key page to improve copy
-merge_request: 19994
-author:
-type: other
diff --git a/changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml b/changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml
deleted file mode 100644
index b564fb0174f..00000000000
--- a/changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allows you to create another deploy token dimmediately after creating one
-merge_request: 19639
-author:
-type: changed
diff --git a/changelogs/unreleased/46535-orphaned-uploads.yml b/changelogs/unreleased/46535-orphaned-uploads.yml
new file mode 100644
index 00000000000..1cd087a6aad
--- /dev/null
+++ b/changelogs/unreleased/46535-orphaned-uploads.yml
@@ -0,0 +1,5 @@
+---
+title: Clean orphaned files in object storage
+merge_request: 20918
+author:
+type: added
diff --git a/changelogs/unreleased/46546-do-not-pre-select-previous-user-s-when-creating-protected-branches.yml b/changelogs/unreleased/46546-do-not-pre-select-previous-user-s-when-creating-protected-branches.yml
deleted file mode 100644
index 7d42d971022..00000000000
--- a/changelogs/unreleased/46546-do-not-pre-select-previous-user-s-when-creating-protected-branches.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: CE port gitlab-ee!6112
-merge_request: 19714
-author:
-type: other
diff --git a/changelogs/unreleased/46571-webhooks-nil-password.yml b/changelogs/unreleased/46571-webhooks-nil-password.yml
deleted file mode 100644
index 34c5f09478f..00000000000
--- a/changelogs/unreleased/46571-webhooks-nil-password.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix webhook error when password is not present
-merge_request: 19945
-author: Jan Beckmann
-type: fixed
diff --git a/changelogs/unreleased/46703-group-dashboard-line-height-is-too-tall-for-group-names.yml b/changelogs/unreleased/46703-group-dashboard-line-height-is-too-tall-for-group-names.yml
new file mode 100644
index 00000000000..5b91c6d5a9f
--- /dev/null
+++ b/changelogs/unreleased/46703-group-dashboard-line-height-is-too-tall-for-group-names.yml
@@ -0,0 +1,5 @@
+---
+title: Solves group dashboard line height is too tall for group names.
+merge_request: 21033
+author:
+type: fixed
diff --git a/changelogs/unreleased/46783-removed-omniauth-provider-causing-invalid-application-setting.yml b/changelogs/unreleased/46783-removed-omniauth-provider-causing-invalid-application-setting.yml
deleted file mode 100644
index d5ecf5163d4..00000000000
--- a/changelogs/unreleased/46783-removed-omniauth-provider-causing-invalid-application-setting.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ignore unknown OAuth sources in ApplicationSetting
-merge_request: 20129
-author:
-type: fixed
diff --git a/changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml b/changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml
deleted file mode 100644
index e0e2b481b69..00000000000
--- a/changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removes unused bootstrap 4 scss files
-merge_request: 19423
-author:
-type: deprecated
diff --git a/changelogs/unreleased/46861-issuable-title-with-longer-username.yml b/changelogs/unreleased/46861-issuable-title-with-longer-username.yml
deleted file mode 100644
index 9df6879deb6..00000000000
--- a/changelogs/unreleased/46861-issuable-title-with-longer-username.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix CSS for buttons not to be hidden on issues/MR title
-merge_request: 19176
-author: Takuya Noguchi
-type: fixed
diff --git a/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml b/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml
new file mode 100644
index 00000000000..d490df58144
--- /dev/null
+++ b/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml
@@ -0,0 +1,5 @@
+---
+title: Allow cloning LFS repositories through DeployTokens
+merge_request: 20729
+author:
+type: other
diff --git a/changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml b/changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml
new file mode 100644
index 00000000000..d95714a5267
--- /dev/null
+++ b/changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml
@@ -0,0 +1,5 @@
+---
+title: Fix updated_at if created_at is set for Note API
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/46940-hashed-storage-extend-enable-hashed-storage-for-all-new-projects-to-for-all-new-and-renamed-projects.yml b/changelogs/unreleased/46940-hashed-storage-extend-enable-hashed-storage-for-all-new-projects-to-for-all-new-and-renamed-projects.yml
new file mode 100644
index 00000000000..71e523e6de8
--- /dev/null
+++ b/changelogs/unreleased/46940-hashed-storage-extend-enable-hashed-storage-for-all-new-projects-to-for-all-new-and-renamed-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Enable hashed storage for all newly created or renamed projects
+merge_request: 19747
+author:
+type: changed
diff --git a/changelogs/unreleased/47050-quick-actions-case-insensitive.yml b/changelogs/unreleased/47050-quick-actions-case-insensitive.yml
deleted file mode 100644
index 176aba627b9..00000000000
--- a/changelogs/unreleased/47050-quick-actions-case-insensitive.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make quick commands case insensitive
-merge_request: 19614
-author: Jan Beckmann
-type: fixed
diff --git a/changelogs/unreleased/47145-quick-actions-confidential.yml b/changelogs/unreleased/47145-quick-actions-confidential.yml
deleted file mode 100644
index 7ae4e2268af..00000000000
--- a/changelogs/unreleased/47145-quick-actions-confidential.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add /confidential quick action
-merge_request:
-author: Jan Beckmann
-type: added
diff --git a/changelogs/unreleased/47156-improve-auto-devops-settings.yml b/changelogs/unreleased/47156-improve-auto-devops-settings.yml
new file mode 100644
index 00000000000..d8993565047
--- /dev/null
+++ b/changelogs/unreleased/47156-improve-auto-devops-settings.yml
@@ -0,0 +1,5 @@
+---
+title: Improve and simplify Auto DevOps settings flow
+merge_request: 20946
+author:
+type: other
diff --git a/changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml b/changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml
deleted file mode 100644
index 94c58a3863a..00000000000
--- a/changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update new group page to better explain what groups are
-merge_request: 19991
-author:
-type: other
diff --git a/changelogs/unreleased/47274-help-users-find-our-contributing-page.yml b/changelogs/unreleased/47274-help-users-find-our-contributing-page.yml
deleted file mode 100644
index ed13c917a2e..00000000000
--- a/changelogs/unreleased/47274-help-users-find-our-contributing-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add a link to the contributing page in the user dropdown
-merge_request: 19708
-author:
-type: added
diff --git a/changelogs/unreleased/47419-Fix-breadcrumbs.yml b/changelogs/unreleased/47419-Fix-breadcrumbs.yml
new file mode 100644
index 00000000000..1a7f8196683
--- /dev/null
+++ b/changelogs/unreleased/47419-Fix-breadcrumbs.yml
@@ -0,0 +1,5 @@
+---
+title: Fix breadcrumbs in Admin/User interface.
+merge_request: 19608
+author: Robin Naundorf
+type: fixed
diff --git a/changelogs/unreleased/47516-pipe-scroll.yml b/changelogs/unreleased/47516-pipe-scroll.yml
deleted file mode 100644
index 3e283f649bd..00000000000
--- a/changelogs/unreleased/47516-pipe-scroll.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent pipeline job tooltip from scrolling off dropdown container
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/47548-monospace-commit-messages.yml b/changelogs/unreleased/47548-monospace-commit-messages.yml
new file mode 100644
index 00000000000..7344f72207b
--- /dev/null
+++ b/changelogs/unreleased/47548-monospace-commit-messages.yml
@@ -0,0 +1,5 @@
+---
+title: Update commit message styles with monospace font and overflow-x
+merge_request: 20988
+author:
+type: changed
diff --git a/changelogs/unreleased/47631-operations-kubernetes-option-is-always-visible-when-repository-or-builds-are-disabled.yml b/changelogs/unreleased/47631-operations-kubernetes-option-is-always-visible-when-repository-or-builds-are-disabled.yml
deleted file mode 100644
index 5c23b3ef320..00000000000
--- a/changelogs/unreleased/47631-operations-kubernetes-option-is-always-visible-when-repository-or-builds-are-disabled.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Omits operartions and kubernetes item from project sidebar when repository or builds are disabled
-merge_request: 19835
-author:
-type: fixed
diff --git a/changelogs/unreleased/47661-merge-request-box-disappearing-on-chrome.yml b/changelogs/unreleased/47661-merge-request-box-disappearing-on-chrome.yml
deleted file mode 100644
index 7e6ab8d448b..00000000000
--- a/changelogs/unreleased/47661-merge-request-box-disappearing-on-chrome.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Replace deprecated bs.affix in merge request tabs with sticky polyfill
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/47728-mr-api-documentation-changes.yml b/changelogs/unreleased/47728-mr-api-documentation-changes.yml
new file mode 100644
index 00000000000..12720f280a1
--- /dev/null
+++ b/changelogs/unreleased/47728-mr-api-documentation-changes.yml
@@ -0,0 +1,5 @@
+---
+title: Remove changes_count from MR API documentation where necessary
+merge_request: 19745
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/47768-web-ide-redesign-header.yml b/changelogs/unreleased/47768-web-ide-redesign-header.yml
new file mode 100644
index 00000000000..49133158164
--- /dev/null
+++ b/changelogs/unreleased/47768-web-ide-redesign-header.yml
@@ -0,0 +1,5 @@
+---
+title: Redesign Web IDE back button and context header
+merge_request: 20850
+author:
+type: changed
diff --git a/changelogs/unreleased/47769-fix_ambiguous_due_date_for_issue_scopes.yml b/changelogs/unreleased/47769-fix_ambiguous_due_date_for_issue_scopes.yml
deleted file mode 100644
index b8bb70b3266..00000000000
--- a/changelogs/unreleased/47769-fix_ambiguous_due_date_for_issue_scopes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix ambiguous due_date column for Issue scopes
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/47794-environment-scope-cluster-page.yml b/changelogs/unreleased/47794-environment-scope-cluster-page.yml
deleted file mode 100644
index 75eb7ec209c..00000000000
--- a/changelogs/unreleased/47794-environment-scope-cluster-page.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Change environment scope text depending on number of project clusters. Update
- form to only include form-groups
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/47865-changelog-for-style-updates.yml b/changelogs/unreleased/47865-changelog-for-style-updates.yml
deleted file mode 100644
index 2e4fbbda000..00000000000
--- a/changelogs/unreleased/47865-changelog-for-style-updates.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Minor style changes to personal access token form and scope checkboxes
-merge_request: 20052
-author:
-type: other
diff --git a/changelogs/unreleased/48036-fix-web-ide-blob-crash.yml b/changelogs/unreleased/48036-fix-web-ide-blob-crash.yml
new file mode 100644
index 00000000000..6ff209b5181
--- /dev/null
+++ b/changelogs/unreleased/48036-fix-web-ide-blob-crash.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Web IDE crashing on directories named 'blob'
+merge_request: 20712
+author:
+type: fixed
diff --git a/changelogs/unreleased/48050-add-full-commit-sha.yml b/changelogs/unreleased/48050-add-full-commit-sha.yml
deleted file mode 100644
index 30376fe35e0..00000000000
--- a/changelogs/unreleased/48050-add-full-commit-sha.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Uses long sha version of the merged commit in MR widget copy to clipboard button
-merge_request: 19955
-author:
-type: other
diff --git a/changelogs/unreleased/48055-web-ide-resize-handles.yml b/changelogs/unreleased/48055-web-ide-resize-handles.yml
new file mode 100644
index 00000000000..0f650cdda6f
--- /dev/null
+++ b/changelogs/unreleased/48055-web-ide-resize-handles.yml
@@ -0,0 +1,5 @@
+---
+title: Increase width of Web IDE sidebar resize handles
+merge_request: 20818
+author:
+type: fixed
diff --git a/changelogs/unreleased/48098-mutual-auth-cluster-applications.yml b/changelogs/unreleased/48098-mutual-auth-cluster-applications.yml
new file mode 100644
index 00000000000..43125ef25c4
--- /dev/null
+++ b/changelogs/unreleased/48098-mutual-auth-cluster-applications.yml
@@ -0,0 +1,6 @@
+---
+title: Ensure installed Helm Tiller For GitLab Managed Apps Is protected by mutual
+ auth
+merge_request: 20928
+author:
+type: changed
diff --git a/changelogs/unreleased/48100-fix-branch-not-shown.yml b/changelogs/unreleased/48100-fix-branch-not-shown.yml
deleted file mode 100644
index 917c5c23f67..00000000000
--- a/changelogs/unreleased/48100-fix-branch-not-shown.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix branches are not shown in Merge Request dropdown when preferred language
- is not English
-merge_request: 20016
-author: Hiroyuki Sato
-type: fixed
diff --git a/changelogs/unreleased/48153-date-selection-dialog-broken-when-creating-a-new-milestone.yml b/changelogs/unreleased/48153-date-selection-dialog-broken-when-creating-a-new-milestone.yml
deleted file mode 100644
index 13ab5b0467d..00000000000
--- a/changelogs/unreleased/48153-date-selection-dialog-broken-when-creating-a-new-milestone.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent browser autocomplete for milestone date fields
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/48246-osw-load-diffs-improvement.yml b/changelogs/unreleased/48246-osw-load-diffs-improvement.yml
new file mode 100644
index 00000000000..c4292ab0d29
--- /dev/null
+++ b/changelogs/unreleased/48246-osw-load-diffs-improvement.yml
@@ -0,0 +1,5 @@
+---
+title: Improve performance when fetching collapsed diffs and commenting in merge requests
+merge_request: 20940
+author:
+type: performance
diff --git a/changelogs/unreleased/48378-avatar-upload.yml b/changelogs/unreleased/48378-avatar-upload.yml
deleted file mode 100644
index 1e359ee72d5..00000000000
--- a/changelogs/unreleased/48378-avatar-upload.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes issue with uploading same image to Profile Avatar twice
-merge_request: 20161
-author: Chirag Bhatia
-type: fixed
diff --git a/changelogs/unreleased/48419-charts-with-long-label-appear-oversized.yml b/changelogs/unreleased/48419-charts-with-long-label-appear-oversized.yml
new file mode 100644
index 00000000000..b3ccbb121f0
--- /dev/null
+++ b/changelogs/unreleased/48419-charts-with-long-label-appear-oversized.yml
@@ -0,0 +1,5 @@
+---
+title: fix height of full-width Metrics charts on large screens
+merge_request: 20866
+author:
+type: fixed
diff --git a/changelogs/unreleased/48456-fix-system-level-labels-admin-ui.yml b/changelogs/unreleased/48456-fix-system-level-labels-admin-ui.yml
new file mode 100644
index 00000000000..c34750a3b88
--- /dev/null
+++ b/changelogs/unreleased/48456-fix-system-level-labels-admin-ui.yml
@@ -0,0 +1,5 @@
+---
+title: Fix the UI for listing system-level labels
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/48461-search-dropdown-hides-shows-when-typing.yml b/changelogs/unreleased/48461-search-dropdown-hides-shows-when-typing.yml
deleted file mode 100644
index 2ebc22dbf8f..00000000000
--- a/changelogs/unreleased/48461-search-dropdown-hides-shows-when-typing.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix loading screen for search autocomplete dropdown
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/48471-sidebar-on-jobs-and-wikis-is-missing-at-small-widths.yml b/changelogs/unreleased/48471-sidebar-on-jobs-and-wikis-is-missing-at-small-widths.yml
deleted file mode 100644
index aaa816516c5..00000000000
--- a/changelogs/unreleased/48471-sidebar-on-jobs-and-wikis-is-missing-at-small-widths.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix sidebar collapse breapoints for job and wiki pages
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/48497-merge-request-refactor-displays-changes-dropdown-incorrectly.yml b/changelogs/unreleased/48497-merge-request-refactor-displays-changes-dropdown-incorrectly.yml
deleted file mode 100644
index 41af2f8cc4f..00000000000
--- a/changelogs/unreleased/48497-merge-request-refactor-displays-changes-dropdown-incorrectly.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed Merge request changes dropdown displays incorrectly
-merge_request: 20237
-author: Constance Okoghenun
-type: fixed
diff --git a/changelogs/unreleased/48515-sql-queries-are-not-shown-from-the-performance-bar-in-safari.yml b/changelogs/unreleased/48515-sql-queries-are-not-shown-from-the-performance-bar-in-safari.yml
deleted file mode 100644
index 65c59dbf31f..00000000000
--- a/changelogs/unreleased/48515-sql-queries-are-not-shown-from-the-performance-bar-in-safari.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix performance bar modal visibility in Safari
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/48528-fix-mr-autocompletion.yml b/changelogs/unreleased/48528-fix-mr-autocompletion.yml
deleted file mode 100644
index ac44f878d1d..00000000000
--- a/changelogs/unreleased/48528-fix-mr-autocompletion.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix broken '!' support to autocomplete MRs in GFM fields
-merge_request: 20204
-author:
-type: fixed
diff --git a/changelogs/unreleased/48537-update-avatar-only-via-api.yml b/changelogs/unreleased/48537-update-avatar-only-via-api.yml
new file mode 100644
index 00000000000..9b3ab946cc1
--- /dev/null
+++ b/changelogs/unreleased/48537-update-avatar-only-via-api.yml
@@ -0,0 +1,5 @@
+---
+title: Allow updating a project's avatar without other params
+merge_request:
+author: Jamie Schembri
+type: fixed
diff --git a/changelogs/unreleased/48542-code-link.yml b/changelogs/unreleased/48542-code-link.yml
new file mode 100644
index 00000000000..8d8d9bf8d74
--- /dev/null
+++ b/changelogs/unreleased/48542-code-link.yml
@@ -0,0 +1,5 @@
+---
+title: Fix link color in markdown code brackets
+merge_request: 20841
+author:
+type: fixed
diff --git a/changelogs/unreleased/48549-markdown-header-code-does-not-have-the-correct-font-size.yml b/changelogs/unreleased/48549-markdown-header-code-does-not-have-the-correct-font-size.yml
deleted file mode 100644
index f01f7f3db55..00000000000
--- a/changelogs/unreleased/48549-markdown-header-code-does-not-have-the-correct-font-size.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: fix size of code blocks in headings
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/48603-merge-request-refactor-title-and-copy-to-clipboard-button-are-behind-the-action-buttons.yml b/changelogs/unreleased/48603-merge-request-refactor-title-and-copy-to-clipboard-button-are-behind-the-action-buttons.yml
deleted file mode 100644
index 792c7814f7e..00000000000
--- a/changelogs/unreleased/48603-merge-request-refactor-title-and-copy-to-clipboard-button-are-behind-the-action-buttons.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix overlapping file title and file actions in MR changes tag
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/48617-promoting-milestone.yml b/changelogs/unreleased/48617-promoting-milestone.yml
new file mode 100644
index 00000000000..7fbc15051cf
--- /dev/null
+++ b/changelogs/unreleased/48617-promoting-milestone.yml
@@ -0,0 +1,5 @@
+---
+title: Escapes milestone and label's names on flash notice when promoting them
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/48636-new-mr-card-styles.yml b/changelogs/unreleased/48636-new-mr-card-styles.yml
new file mode 100644
index 00000000000..94f62e677fb
--- /dev/null
+++ b/changelogs/unreleased/48636-new-mr-card-styles.yml
@@ -0,0 +1,5 @@
+---
+title: Fix new MR card styles
+merge_request: 20822
+author:
+type: fixed
diff --git a/changelogs/unreleased/48657-persist-auto-devops-banner-dismissal-per-user-cookie.yml b/changelogs/unreleased/48657-persist-auto-devops-banner-dismissal-per-user-cookie.yml
new file mode 100644
index 00000000000..7ee018ef679
--- /dev/null
+++ b/changelogs/unreleased/48657-persist-auto-devops-banner-dismissal-per-user-cookie.yml
@@ -0,0 +1,5 @@
+---
+title: Persist 'Auto DevOps' banner dismissal globally
+merge_request: 20540
+author:
+type: other
diff --git a/changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml b/changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml
new file mode 100644
index 00000000000..f298380b920
--- /dev/null
+++ b/changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml
@@ -0,0 +1,5 @@
+---
+title: Add object storage logic to project import
+merge_request: 20773
+author:
+type: added
diff --git a/changelogs/unreleased/48804-redesign-gcp-banner.yml b/changelogs/unreleased/48804-redesign-gcp-banner.yml
new file mode 100644
index 00000000000..729f959badc
--- /dev/null
+++ b/changelogs/unreleased/48804-redesign-gcp-banner.yml
@@ -0,0 +1,5 @@
+---
+title: Redesign GCP offer banner
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/48823-copy-gfm.yml b/changelogs/unreleased/48823-copy-gfm.yml
new file mode 100644
index 00000000000..b6137e2e3f9
--- /dev/null
+++ b/changelogs/unreleased/48823-copy-gfm.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Copy diff file path as GFM is broken
+merge_request: 20725
+author:
+type: fixed
diff --git a/changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml b/changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml
new file mode 100644
index 00000000000..d79b95411aa
--- /dev/null
+++ b/changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml
@@ -0,0 +1,6 @@
+---
+title: Chart versions for applications installed by one click install buttons should
+ be version locked
+merge_request: 20765
+author:
+type: fixed
diff --git a/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml b/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml
new file mode 100644
index 00000000000..0466debea65
--- /dev/null
+++ b/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml
@@ -0,0 +1,5 @@
+---
+title: Disable SAML and Bitbucket if OmniAuth is disabled
+merge_request: 20608
+author:
+type: fixed
diff --git a/changelogs/unreleased/48934.yml b/changelogs/unreleased/48934.yml
new file mode 100644
index 00000000000..8e2e53ed198
--- /dev/null
+++ b/changelogs/unreleased/48934.yml
@@ -0,0 +1,5 @@
+---
+title: Improve danger confirmation modals by focusing input field
+merge_request:
+author: Jamie Schembri
+type: added
diff --git a/changelogs/unreleased/48976-fix-sti-background-migration.yml b/changelogs/unreleased/48976-fix-sti-background-migration.yml
new file mode 100644
index 00000000000..e95536b213c
--- /dev/null
+++ b/changelogs/unreleased/48976-fix-sti-background-migration.yml
@@ -0,0 +1,6 @@
+---
+title: "[Rails5] Fix 'Invalid single-table inheritance type: Group is not a subclass
+ of Gitlab::BackgroundMigration::FixCrossProjectLabelLinks::Namespace'"
+merge_request: 20462
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/49025-docs-kubernetes-tiller.yml b/changelogs/unreleased/49025-docs-kubernetes-tiller.yml
new file mode 100644
index 00000000000..c4f01490cfa
--- /dev/null
+++ b/changelogs/unreleased/49025-docs-kubernetes-tiller.yml
@@ -0,0 +1,5 @@
+---
+title: Update docs of Helm Tiller
+merge_request: 20515
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml b/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml
new file mode 100644
index 00000000000..541b562adac
--- /dev/null
+++ b/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml
@@ -0,0 +1,5 @@
+---
+title: DNS prefetching if asset_host for CDN hosting is set
+merge_request: 20781
+author:
+type: performance
diff --git a/changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml b/changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml
new file mode 100644
index 00000000000..ab320450a1a
--- /dev/null
+++ b/changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml
@@ -0,0 +1,5 @@
+---
+title: Gitaly Servers link into Admin > Overview navigation menu
+merge_request: 20550
+author:
+type: added
diff --git a/changelogs/unreleased/49161-disable-toggle-comments.yml b/changelogs/unreleased/49161-disable-toggle-comments.yml
new file mode 100644
index 00000000000..5ec16191f07
--- /dev/null
+++ b/changelogs/unreleased/49161-disable-toggle-comments.yml
@@ -0,0 +1,5 @@
+---
+title: Disables toggle comments button if diff has no discussions
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml b/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml
new file mode 100644
index 00000000000..c757e55f1cd
--- /dev/null
+++ b/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml
@@ -0,0 +1,5 @@
+---
+title: Sanitize git URL in import errors
+merge_request:
+author: Jamie Schembri
+type: fixed
diff --git a/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml b/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml
new file mode 100644
index 00000000000..f21bd454e84
--- /dev/null
+++ b/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml
@@ -0,0 +1,5 @@
+---
+title: Fix typo in CSS transform property for Memory Graph component
+merge_request: 20650
+author:
+type: fixed
diff --git a/changelogs/unreleased/49324-add-support-for-tar-gz-autodevops-charts.yml b/changelogs/unreleased/49324-add-support-for-tar-gz-autodevops-charts.yml
new file mode 100644
index 00000000000..a87eef3f7f3
--- /dev/null
+++ b/changelogs/unreleased/49324-add-support-for-tar-gz-autodevops-charts.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for tar.gz AUTO_DEVOPS_CHART charts (#49324)
+merge_request: 20691
+author: '@kondi1'
+type: added
diff --git a/changelogs/unreleased/49364-fix-broadcast-margin.yml b/changelogs/unreleased/49364-fix-broadcast-margin.yml
new file mode 100644
index 00000000000..821fb9df1af
--- /dev/null
+++ b/changelogs/unreleased/49364-fix-broadcast-margin.yml
@@ -0,0 +1,5 @@
+---
+title: Fix misalignment of broadcast message on login page
+merge_request: 20794
+author: Robin Naundorf
+type: fixed
diff --git a/changelogs/unreleased/49375-move-help-popover.yml b/changelogs/unreleased/49375-move-help-popover.yml
new file mode 100644
index 00000000000..2547d5768bf
--- /dev/null
+++ b/changelogs/unreleased/49375-move-help-popover.yml
@@ -0,0 +1,5 @@
+---
+title: Moves help_popover component to a common location
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/49499-list-of-projects-not-loading-when-trying-to-create-an-issue-from-a-board-typeerror.yml b/changelogs/unreleased/49499-list-of-projects-not-loading-when-trying-to-create-an-issue-from-a-board-typeerror.yml
new file mode 100644
index 00000000000..043698269e2
--- /dev/null
+++ b/changelogs/unreleased/49499-list-of-projects-not-loading-when-trying-to-create-an-issue-from-a-board-typeerror.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed list of projects not loading in group boards
+merge_request: 20955
+author:
+type: fixed
diff --git a/changelogs/unreleased/49701-sorting-by-name-on-milestones-page-error.yml b/changelogs/unreleased/49701-sorting-by-name-on-milestones-page-error.yml
new file mode 100644
index 00000000000..7eb73110d60
--- /dev/null
+++ b/changelogs/unreleased/49701-sorting-by-name-on-milestones-page-error.yml
@@ -0,0 +1,5 @@
+---
+title: Fix sorting by name on milestones page
+merge_request: 20881
+author:
+type: fixed
diff --git a/changelogs/unreleased/49747-update-poll-2xx.yml b/changelogs/unreleased/49747-update-poll-2xx.yml
new file mode 100644
index 00000000000..359d1b80447
--- /dev/null
+++ b/changelogs/unreleased/49747-update-poll-2xx.yml
@@ -0,0 +1,5 @@
+---
+title: Changes poll.js to keep polling on any 2xx http status code
+merge_request: 20904
+author:
+type: other
diff --git a/changelogs/unreleased/49776-pipeline-job-log-page-uses-too-much-cpu-for-loading-animation.yml b/changelogs/unreleased/49776-pipeline-job-log-page-uses-too-much-cpu-for-loading-animation.yml
new file mode 100644
index 00000000000..96da2436a9f
--- /dev/null
+++ b/changelogs/unreleased/49776-pipeline-job-log-page-uses-too-much-cpu-for-loading-animation.yml
@@ -0,0 +1,5 @@
+---
+title: refactor pipeline job log animation to reduce CPU usage
+merge_request: 20915
+author:
+type: performance
diff --git a/changelogs/unreleased/49830-use-helm-272.yml b/changelogs/unreleased/49830-use-helm-272.yml
new file mode 100644
index 00000000000..f6ecc12dbfa
--- /dev/null
+++ b/changelogs/unreleased/49830-use-helm-272.yml
@@ -0,0 +1,5 @@
+---
+title: Use Helm 2.7.2 for GitLab Managed Apps
+merge_request: 20956
+author:
+type: changed
diff --git a/changelogs/unreleased/49835-increase-width.yml b/changelogs/unreleased/49835-increase-width.yml
new file mode 100644
index 00000000000..f963c0c5e47
--- /dev/null
+++ b/changelogs/unreleased/49835-increase-width.yml
@@ -0,0 +1,5 @@
+---
+title: Increases title column on modal for reports
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/49851-link-to-runners.yml b/changelogs/unreleased/49851-link-to-runners.yml
new file mode 100644
index 00000000000..89fd6853bc8
--- /dev/null
+++ b/changelogs/unreleased/49851-link-to-runners.yml
@@ -0,0 +1,6 @@
+---
+title: Automatically expand runner's settings block when linking to the runner's settings
+ page
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-1.yml b/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-1.yml
new file mode 100644
index 00000000000..ffa4a3bc710
--- /dev/null
+++ b/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-1.yml
@@ -0,0 +1,5 @@
+---
+title: Fix rendering of the context lines in MR diffs page.
+merge_request: 20968
+author:
+type: fixed
diff --git a/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-2.yml b/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-2.yml
new file mode 100644
index 00000000000..42b0e4194f1
--- /dev/null
+++ b/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-2.yml
@@ -0,0 +1,5 @@
+---
+title: Fix autosave and ESC confirmation issues for MR discussions.
+merge_request: 20968
+author:
+type: fixed
diff --git a/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-3.yml b/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-3.yml
new file mode 100644
index 00000000000..29419091d02
--- /dev/null
+++ b/changelogs/unreleased/49854-recover-mr-regression-fixes-safe-3.yml
@@ -0,0 +1,5 @@
+---
+title: Fix navigation to First and Next discussion on MR Changes tab.
+merge_request: 20968
+author:
+type: fixed
diff --git a/changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml b/changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml
new file mode 100644
index 00000000000..30f5002c5b5
--- /dev/null
+++ b/changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml
@@ -0,0 +1,5 @@
+---
+title: fix error caused when using the search bar while unauthenticated
+merge_request: 20970
+author:
+type: fixed
diff --git a/changelogs/unreleased/49899-merge-request-e-mail-link-has-full-url.yml b/changelogs/unreleased/49899-merge-request-e-mail-link-has-full-url.yml
new file mode 100644
index 00000000000..856a7c579f3
--- /dev/null
+++ b/changelogs/unreleased/49899-merge-request-e-mail-link-has-full-url.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure links in notifications footer are not escaped
+merge_request: 21000
+author:
+type: fixed
diff --git a/changelogs/unreleased/49966-improve-junit-fe.yml b/changelogs/unreleased/49966-improve-junit-fe.yml
new file mode 100644
index 00000000000..48971d3bfd6
--- /dev/null
+++ b/changelogs/unreleased/49966-improve-junit-fe.yml
@@ -0,0 +1,5 @@
+---
+title: Renders test reports for resolved failures and resets error state
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/6860-FE-instance-level-project-templates.yml b/changelogs/unreleased/6860-FE-instance-level-project-templates.yml
new file mode 100644
index 00000000000..74e0daee71b
--- /dev/null
+++ b/changelogs/unreleased/6860-FE-instance-level-project-templates.yml
@@ -0,0 +1,5 @@
+---
+title: Update design of project templates
+merge_request: 21012
+author:
+type: changed
diff --git a/changelogs/unreleased/accept-rf3-2822-compliant-addresses.yml b/changelogs/unreleased/accept-rf3-2822-compliant-addresses.yml
new file mode 100644
index 00000000000..97d017613ba
--- /dev/null
+++ b/changelogs/unreleased/accept-rf3-2822-compliant-addresses.yml
@@ -0,0 +1,5 @@
+---
+title: Emails on push recipients now accepts formats like John Doe <johndoe@example.com>
+merge_request:
+author: George Thomas
+type: added
diff --git a/changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml b/changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml
new file mode 100644
index 00000000000..08376014ad7
--- /dev/null
+++ b/changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for daylight savings time to pipleline schedules
+merge_request: 20145
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-homepage-link-to-status-pages.yml b/changelogs/unreleased/add-homepage-link-to-status-pages.yml
new file mode 100644
index 00000000000..0e7375f2061
--- /dev/null
+++ b/changelogs/unreleased/add-homepage-link-to-status-pages.yml
@@ -0,0 +1,5 @@
+---
+title: Add link to homepage on static http status pages (404, 500, etc)
+merge_request: 20898
+author: Jason Funk
+type: added
diff --git a/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml b/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml
new file mode 100644
index 00000000000..4f9a551d13e
--- /dev/null
+++ b/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml
@@ -0,0 +1,5 @@
+---
+title: Add merge request header branch actions left margin
+merge_request: 20643
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/add-missing-index-for-deployments.yml b/changelogs/unreleased/add-missing-index-for-deployments.yml
deleted file mode 100644
index 7863c0ee039..00000000000
--- a/changelogs/unreleased/add-missing-index-for-deployments.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add index on deployable_type/id for deployments
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/add-more-rebase-logging.yml b/changelogs/unreleased/add-more-rebase-logging.yml
deleted file mode 100644
index a7d1c3aa664..00000000000
--- a/changelogs/unreleased/add-more-rebase-logging.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add more detailed logging to githost.log when rebasing
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/add-total-time-flat-printer-for-profiling.yml b/changelogs/unreleased/add-total-time-flat-printer-for-profiling.yml
new file mode 100644
index 00000000000..37a4e31896e
--- /dev/null
+++ b/changelogs/unreleased/add-total-time-flat-printer-for-profiling.yml
@@ -0,0 +1,6 @@
+---
+title: Add a Gitlab::Profiler.print_by_total_time convenience method for profiling
+ from a Rails console
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/api-minimal-access-level.yml b/changelogs/unreleased/api-minimal-access-level.yml
new file mode 100644
index 00000000000..43cab246d69
--- /dev/null
+++ b/changelogs/unreleased/api-minimal-access-level.yml
@@ -0,0 +1,5 @@
+---
+title: Add filter for minimal access level in groups and projects API
+merge_request: 20478
+author: Marko, Peter
+type: added
diff --git a/changelogs/unreleased/artifact-format-v2-with-parser.yml b/changelogs/unreleased/artifact-format-v2-with-parser.yml
new file mode 100644
index 00000000000..e1a779cf6dd
--- /dev/null
+++ b/changelogs/unreleased/artifact-format-v2-with-parser.yml
@@ -0,0 +1,5 @@
+---
+title: JUnit XML Test Summary In MR widget
+merge_request: 20576
+author:
+type: added
diff --git a/changelogs/unreleased/artifact-format-v2.yml b/changelogs/unreleased/artifact-format-v2.yml
new file mode 100644
index 00000000000..e264e0a9fa1
--- /dev/null
+++ b/changelogs/unreleased/artifact-format-v2.yml
@@ -0,0 +1,5 @@
+---
+title: Extend gitlab-ci.yml to request junit.xml test reports
+merge_request: 20390
+author:
+type: added
diff --git a/changelogs/unreleased/author-doc-fix.yml b/changelogs/unreleased/author-doc-fix.yml
deleted file mode 100644
index 83521543239..00000000000
--- a/changelogs/unreleased/author-doc-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix fields for author & assignee in MR API docs.
-merge_request: 19798
-author: gfyoung
-type: fixed
diff --git a/changelogs/unreleased/backstage-gb-stages-position-migration-clean-up.yml b/changelogs/unreleased/backstage-gb-stages-position-migration-clean-up.yml
deleted file mode 100644
index d2ada88870b..00000000000
--- a/changelogs/unreleased/backstage-gb-stages-position-migration-clean-up.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fully migrate pipeline stages position
-merge_request: 19369
-author:
-type: performance
diff --git a/changelogs/unreleased/bjk-48176_ruby_gc.yml b/changelogs/unreleased/bjk-48176_ruby_gc.yml
deleted file mode 100644
index 45c6338df81..00000000000
--- a/changelogs/unreleased/bjk-48176_ruby_gc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cleanup Prometheus ruby metrics
-merge_request: 20039
-author: Ben Kochie
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-add-gemfile-rails5-lock-check.yml b/changelogs/unreleased/blackst0ne-add-gemfile-rails5-lock-check.yml
deleted file mode 100644
index 69d49f3e3e0..00000000000
--- a/changelogs/unreleased/blackst0ne-add-gemfile-rails5-lock-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add CI job to check Gemfile.rails5.lock
-merge_request: 19605
-author: "@blackst0ne"
-type: other
diff --git a/changelogs/unreleased/blackst0ne-bump-grape-path-helpers-gem-to-1-0-5.yml b/changelogs/unreleased/blackst0ne-bump-grape-path-helpers-gem-to-1-0-5.yml
deleted file mode 100644
index 9d975ff81bf..00000000000
--- a/changelogs/unreleased/blackst0ne-bump-grape-path-helpers-gem-to-1-0-5.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump grape-path-helpers to 1.0.5
-merge_request: 19604
-author: "@blackst0ne"
-type: other
diff --git a/changelogs/unreleased/blackst0ne-fix-protect-from-forgery-in-application-controller.yml b/changelogs/unreleased/blackst0ne-fix-protect-from-forgery-in-application-controller.yml
deleted file mode 100644
index da75ea8b09e..00000000000
--- a/changelogs/unreleased/blackst0ne-fix-protect-from-forgery-in-application-controller.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[Rails5] Force the callback run first"
-merge_request: 20055
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml b/changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml
deleted file mode 100644
index e7bb2703b03..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[Rails5] Fix sessions_controller_spec"
-merge_request: 19936
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml b/changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml
deleted file mode 100644
index fad15de2dd5..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[Rails5] Set request.format for artifacts_controller"
-merge_request: 19937
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml b/changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml
deleted file mode 100644
index a83aa03606a..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[Rails5] Explicitly set request.format for blob_controller"
-merge_request: 19876
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-data-store-spec.yml b/changelogs/unreleased/blackst0ne-rails5-fix-data-store-spec.yml
deleted file mode 100644
index 403c3764321..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-fix-data-store-spec.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: '[Rails5] Fix "-1 is not a valid data_store"'
-merge_request: 19917
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml b/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml
deleted file mode 100644
index 1915dff73ab..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[Rails5] Fix optimistic lock value"
-merge_request: 19878
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml b/changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml
deleted file mode 100644
index 7a2b19ad681..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[Rails5] Fix pipeline_schedules_controller_spec"
-merge_request: 19919
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml b/changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml
deleted file mode 100644
index 597b85de26f..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[Rails5] Fix snippets_finder arel queries"
-merge_request: 19796
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-found-new-routes-that-could-cause-conflicts-with-existing-namespaced-routes.yml b/changelogs/unreleased/blackst0ne-rails5-found-new-routes-that-could-cause-conflicts-with-existing-namespaced-routes.yml
deleted file mode 100644
index c8d916af824..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-found-new-routes-that-could-cause-conflicts-with-existing-namespaced-routes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[Rails5] Fix ActionCable '/cable' mountpoint conflict"
-merge_request: 20015
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml b/changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml
deleted file mode 100644
index 92e6ce35941..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: "[Rails5] Invalid single-table inheritance type: Group is not a subclass of
- Namespace"
-merge_request: 19918
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-set-request-format-in--commits-controller.yml b/changelogs/unreleased/blackst0ne-rails5-set-request-format-in--commits-controller.yml
deleted file mode 100644
index 3f8f8fd5d66..00000000000
--- a/changelogs/unreleased/blackst0ne-rails5-set-request-format-in--commits-controller.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[Rails5] Set request.format in commits_controller"
-merge_request: 20023
-author: "@blackst0ne"
-type: fixed
diff --git a/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
new file mode 100644
index 00000000000..69e6b7d815a
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
@@ -0,0 +1,5 @@
+---
+title: Replace 'Sidekiq::Testing.inline!' with 'perform_enqueued_jobs'
+merge_request: 20768
+author: "@blackst0ne"
+type: other
diff --git a/changelogs/unreleased/bvl-fix-maintainer-push-rejected.yml b/changelogs/unreleased/bvl-fix-maintainer-push-rejected.yml
deleted file mode 100644
index 54154ad2449..00000000000
--- a/changelogs/unreleased/bvl-fix-maintainer-push-rejected.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix bug where maintainer would not be allowed to push to forks with merge requests
- that have `Allow maintainer edits` enabled.
-merge_request: 18968
-author:
-type: fixed
diff --git a/changelogs/unreleased/bvl-graphql-nested-merge-request.yml b/changelogs/unreleased/bvl-graphql-nested-merge-request.yml
deleted file mode 100644
index f0f0488d31a..00000000000
--- a/changelogs/unreleased/bvl-graphql-nested-merge-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow querying a single merge request within a project
-merge_request: 19853
-author:
-type: changed
diff --git a/changelogs/unreleased/bvl-graphql-permissions.yml b/changelogs/unreleased/bvl-graphql-permissions.yml
deleted file mode 100644
index 42d5e24bb15..00000000000
--- a/changelogs/unreleased/bvl-graphql-permissions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Expose permissions of the current user on resources in GraphQL'
-merge_request: 20152
-author:
-type: added
diff --git a/changelogs/unreleased/bvl-graphql-wip-mutation.yml b/changelogs/unreleased/bvl-graphql-wip-mutation.yml
new file mode 100644
index 00000000000..00aa1c48677
--- /dev/null
+++ b/changelogs/unreleased/bvl-graphql-wip-mutation.yml
@@ -0,0 +1,5 @@
+---
+title: Add the first mutations for merge requests to GraphQL
+merge_request: 20443
+author:
+type: added
diff --git a/changelogs/unreleased/bvl-user-status-message-35463.yml b/changelogs/unreleased/bvl-user-status-message-35463.yml
new file mode 100644
index 00000000000..c844e7ea0e4
--- /dev/null
+++ b/changelogs/unreleased/bvl-user-status-message-35463.yml
@@ -0,0 +1,5 @@
+---
+title: Users can set a status message and emoji
+merge_request: 20614
+author: niedermyer & davamr
+type: added
diff --git a/changelogs/unreleased/bw-enable-commonmark.yml b/changelogs/unreleased/bw-enable-commonmark.yml
deleted file mode 100644
index 89252e5063d..00000000000
--- a/changelogs/unreleased/bw-enable-commonmark.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use CommonMark syntax and rendering for new Markdown content
-merge_request: 19331
-author:
-type: added
diff --git a/changelogs/unreleased/bw-fix-ee-dashboard.yml b/changelogs/unreleased/bw-fix-ee-dashboard.yml
deleted file mode 100644
index 667181cdf73..00000000000
--- a/changelogs/unreleased/bw-fix-ee-dashboard.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Restore showing Elasticsearch and Geo status on dashboard
-merge_request: 20276
-author:
-type: fixed
diff --git a/changelogs/unreleased/cache-doc-fix.yml b/changelogs/unreleased/cache-doc-fix.yml
deleted file mode 100644
index db4726a92e9..00000000000
--- a/changelogs/unreleased/cache-doc-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Remove incorrect CI doc re: PowerShell'
-merge_request: 19622
-author: gfyoung
-type: fixed
diff --git a/changelogs/unreleased/ce-5024-filename-search.yml b/changelogs/unreleased/ce-5024-filename-search.yml
deleted file mode 100644
index a8bf9b1f802..00000000000
--- a/changelogs/unreleased/ce-5024-filename-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add filename filtering to code search
-merge_request: 19509
-author:
-type: added
diff --git a/changelogs/unreleased/ce-5666-backport.yml b/changelogs/unreleased/ce-5666-backport.yml
new file mode 100644
index 00000000000..344f1a1983f
--- /dev/null
+++ b/changelogs/unreleased/ce-5666-backport.yml
@@ -0,0 +1,5 @@
+---
+title: CE port of "List groups with developer maintainer access on project creation"
+merge_request: 21051
+author:
+type: other
diff --git a/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml b/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml
new file mode 100644
index 00000000000..b76437a8773
--- /dev/null
+++ b/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml
@@ -0,0 +1,5 @@
+---
+title: Tracking the number of repositories and wikis with a cached counter for site-wide statistics
+merge_request: 20413
+author:
+type: performance
diff --git a/changelogs/unreleased/ci-builds-status-index.yml b/changelogs/unreleased/ci-builds-status-index.yml
new file mode 100644
index 00000000000..8b7252f09e5
--- /dev/null
+++ b/changelogs/unreleased/ci-builds-status-index.yml
@@ -0,0 +1,5 @@
+---
+title: Remove redundant ci_builds (status) index
+merge_request: 21070
+author:
+type: performance
diff --git a/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml b/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml
new file mode 100644
index 00000000000..49648cdfcfc
--- /dev/null
+++ b/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml
@@ -0,0 +1,5 @@
+---
+title: Close revert and cherry pick modal on escape keypress
+merge_request: 20341
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/commits_api_with_stats.yml b/changelogs/unreleased/commits_api_with_stats.yml
deleted file mode 100644
index 4357f1a6305..00000000000
--- a/changelogs/unreleased/commits_api_with_stats.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added with_statsoption for GET /projects/:id/repository/commits
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/cr-add-group-milestone-to-dashboard.yml b/changelogs/unreleased/cr-add-group-milestone-to-dashboard.yml
new file mode 100644
index 00000000000..b87a1e5faf7
--- /dev/null
+++ b/changelogs/unreleased/cr-add-group-milestone-to-dashboard.yml
@@ -0,0 +1,5 @@
+---
+title: Adds the ability to view group milestones on the dashboard milestone page.
+merge_request: 20618
+author:
+type: fixed
diff --git a/changelogs/unreleased/cr-add-path-of-group-milestone.yml b/changelogs/unreleased/cr-add-path-of-group-milestone.yml
new file mode 100644
index 00000000000..5ce240110ef
--- /dev/null
+++ b/changelogs/unreleased/cr-add-path-of-group-milestone.yml
@@ -0,0 +1,5 @@
+---
+title: Adds the project and group name to the return type for project and group milestones.
+merge_request: 20890
+author:
+type: changed
diff --git a/changelogs/unreleased/cr-keep-issue-labels.yml b/changelogs/unreleased/cr-keep-issue-labels.yml
deleted file mode 100644
index 051e7faffea..00000000000
--- a/changelogs/unreleased/cr-keep-issue-labels.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Keeps the label on an issue when the issue is moved.
-merge_request: 20036
-author:
-type: fixed
diff --git a/changelogs/unreleased/custom_wiki_sidebar.yml b/changelogs/unreleased/custom_wiki_sidebar.yml
new file mode 100644
index 00000000000..988fccc929c
--- /dev/null
+++ b/changelogs/unreleased/custom_wiki_sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: "Custom Wiki Sidebar Support Issue 14995"
+merge_request:
+author: Josh Sooter
+type: added
diff --git a/changelogs/unreleased/da-port-cte-to-ce.yml b/changelogs/unreleased/da-port-cte-to-ce.yml
deleted file mode 100644
index 6fa759fcf7d..00000000000
--- a/changelogs/unreleased/da-port-cte-to-ce.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Gitlab::SQL:CTE for easily building CTE statements
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/db-configure-after-drop-tables.yml b/changelogs/unreleased/db-configure-after-drop-tables.yml
deleted file mode 100644
index 00844b334fa..00000000000
--- a/changelogs/unreleased/db-configure-after-drop-tables.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes an issue where migrations instead of schema loading were run
-merge_request: 20227
-author:
-type: changed
diff --git a/changelogs/unreleased/dm-blockquote-trailing-whitespace.yml b/changelogs/unreleased/dm-blockquote-trailing-whitespace.yml
deleted file mode 100644
index 98ecdde4f4c..00000000000
--- a/changelogs/unreleased/dm-blockquote-trailing-whitespace.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow trailing whitespace on blockquote fence lines
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/dm-branch-api-can-push.yml b/changelogs/unreleased/dm-branch-api-can-push.yml
deleted file mode 100644
index 3be8962089b..00000000000
--- a/changelogs/unreleased/dm-branch-api-can-push.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Expose whether current user can push into a branch on branches API
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/dm-favicon-asset-host.yml b/changelogs/unreleased/dm-favicon-asset-host.yml
deleted file mode 100644
index c2dc9d765e5..00000000000
--- a/changelogs/unreleased/dm-favicon-asset-host.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Always serve favicon from main GitLab domain so that CI badge can be drawn
- over it
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/dm-label-reference-period.yml b/changelogs/unreleased/dm-label-reference-period.yml
deleted file mode 100644
index 9fdd590641d..00000000000
--- a/changelogs/unreleased/dm-label-reference-period.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Properly detect label reference if followed by period or question mark
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/dz-labels-search.yml b/changelogs/unreleased/dz-labels-search.yml
new file mode 100644
index 00000000000..49c1b6c1a86
--- /dev/null
+++ b/changelogs/unreleased/dz-labels-search.yml
@@ -0,0 +1,5 @@
+---
+title: Search for labels by title or description on project labels page
+merge_request: 20749
+author:
+type: added
diff --git a/changelogs/unreleased/dz-manifest-import.yml b/changelogs/unreleased/dz-manifest-import.yml
new file mode 100644
index 00000000000..b0d29b0869f
--- /dev/null
+++ b/changelogs/unreleased/dz-manifest-import.yml
@@ -0,0 +1,5 @@
+---
+title: Add ability to import multiple repositories by uploading a manifest file
+merge_request: 20304
+author:
+type: added
diff --git a/changelogs/unreleased/existing-gcp-accounts.yml b/changelogs/unreleased/existing-gcp-accounts.yml
deleted file mode 100644
index ce396c70b4a..00000000000
--- a/changelogs/unreleased/existing-gcp-accounts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add back copy for existing gcp accounts within offer banner
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/expose-ci-url.yml b/changelogs/unreleased/expose-ci-url.yml
deleted file mode 100644
index b6ad7d18e0d..00000000000
--- a/changelogs/unreleased/expose-ci-url.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add CI_PIPELINE_URL and CI_JOB_URL
-merge_request: 19618
-author:
-type: added
diff --git a/changelogs/unreleased/feat-add-default-avatar-to-group.yml b/changelogs/unreleased/feat-add-default-avatar-to-group.yml
new file mode 100644
index 00000000000..56d8f2ccd6d
--- /dev/null
+++ b/changelogs/unreleased/feat-add-default-avatar-to-group.yml
@@ -0,0 +1,5 @@
+---
+title: Add default avatar to group
+merge_request: 17271
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/feature-gb-email-delivery-metrics.yml b/changelogs/unreleased/feature-gb-email-delivery-metrics.yml
new file mode 100644
index 00000000000..9d0d08a471d
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-email-delivery-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Add emails delivery Prometheus metrics
+merge_request: 20638
+author:
+type: added
diff --git a/changelogs/unreleased/feature-gb-login-activity-metrics.yml b/changelogs/unreleased/feature-gb-login-activity-metrics.yml
new file mode 100644
index 00000000000..5d687b984eb
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-login-activity-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Add more comprehensive metrics tracking authentication activity
+merge_request: 20668
+author:
+type: added
diff --git a/changelogs/unreleased/feature-oidc-subject-claim.yml b/changelogs/unreleased/feature-oidc-subject-claim.yml
deleted file mode 100644
index e995ca26234..00000000000
--- a/changelogs/unreleased/feature-oidc-subject-claim.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Don't hash user ID in OIDC subject claim
-merge_request: 19784
-author: Markus Koller
-type: changed
diff --git a/changelogs/unreleased/features-show-project-id-on-home-panel.yml b/changelogs/unreleased/features-show-project-id-on-home-panel.yml
new file mode 100644
index 00000000000..f592be07a52
--- /dev/null
+++ b/changelogs/unreleased/features-show-project-id-on-home-panel.yml
@@ -0,0 +1,5 @@
+---
+title: Show Project ID on project home panel
+merge_request: 20305
+author: Tuğçe Nur Taş
+type: added
diff --git a/changelogs/unreleased/fix-boards-issue-highlight.yml b/changelogs/unreleased/fix-boards-issue-highlight.yml
deleted file mode 100644
index 0cc3faa81ca..00000000000
--- a/changelogs/unreleased/fix-boards-issue-highlight.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix boards issue highlight
-merge_request: 20063
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/fix-br-decode.yml b/changelogs/unreleased/fix-br-decode.yml
deleted file mode 100644
index 66ecc3deb35..00000000000
--- a/changelogs/unreleased/fix-br-decode.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: mergeError message has been binded using v-html directive
-merge_request: 19058
-author: Murat Dogan
-type: fixed
diff --git a/changelogs/unreleased/fix-diff-note.yml b/changelogs/unreleased/fix-diff-note.yml
new file mode 100644
index 00000000000..6f10f86b9bc
--- /dev/null
+++ b/changelogs/unreleased/fix-diff-note.yml
@@ -0,0 +1,5 @@
+---
+title: Fix serialization of LegacyDiffNote
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-email-confirmation-addtional-email.yml b/changelogs/unreleased/fix-email-confirmation-addtional-email.yml
new file mode 100644
index 00000000000..56a2efa4d60
--- /dev/null
+++ b/changelogs/unreleased/fix-email-confirmation-addtional-email.yml
@@ -0,0 +1,5 @@
+---
+title: Fix email confirmation bug when user adds additional email to account
+merge_request: 20084
+author: muhammadn
+type: fixed
diff --git a/changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml b/changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml
new file mode 100644
index 00000000000..7e9e8c33a71
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml
@@ -0,0 +1,5 @@
+---
+title: Add missing predefined variable and fix docs
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml b/changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml
new file mode 100644
index 00000000000..80b069c9251
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml
@@ -0,0 +1,5 @@
+---
+title: Fix accessing imported pipeline builds
+merge_request: 20713
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml b/changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml
new file mode 100644
index 00000000000..adf582e34a2
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml
@@ -0,0 +1,5 @@
+---
+title: Limit maximum project build timeout setting to 1 month
+merge_request: 20591
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-groups-api-ordering.yml b/changelogs/unreleased/fix-groups-api-ordering.yml
deleted file mode 100644
index 3a6a7f84356..00000000000
--- a/changelogs/unreleased/fix-groups-api-ordering.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-title: Fixed pagination of groups API
-merge_request: 19665
-author: Marko, Peter
-type: added
diff --git a/changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml b/changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml
new file mode 100644
index 00000000000..d215d034917
--- /dev/null
+++ b/changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml
@@ -0,0 +1,5 @@
+---
+title: Fix label list item container height when there is no label description
+merge_request: 21106
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-multiple-scopes.yml b/changelogs/unreleased/fix-multiple-scopes.yml
new file mode 100644
index 00000000000..24e5172d9a1
--- /dev/null
+++ b/changelogs/unreleased/fix-multiple-scopes.yml
@@ -0,0 +1,5 @@
+---
+title: Support multiple scopes when authing container registry scopes
+merge_request: 20617
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-paragraph-line-height-for-emoji.yml b/changelogs/unreleased/fix-paragraph-line-height-for-emoji.yml
deleted file mode 100644
index 5aaf0fac60e..00000000000
--- a/changelogs/unreleased/fix-paragraph-line-height-for-emoji.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix paragraph line height for emoji
-merge_request: 20137
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-project-api-archived.yml b/changelogs/unreleased/fix-project-api-archived.yml
new file mode 100644
index 00000000000..9d119fd3429
--- /dev/null
+++ b/changelogs/unreleased/fix-project-api-archived.yml
@@ -0,0 +1,5 @@
+---
+title: Fix archived parameter for projects API
+merge_request: 20566
+author: Peter Marko
+type: fixed
diff --git a/changelogs/unreleased/fix-prometheus-updated-status.yml b/changelogs/unreleased/fix-prometheus-updated-status.yml
new file mode 100644
index 00000000000..7261c3429c8
--- /dev/null
+++ b/changelogs/unreleased/fix-prometheus-updated-status.yml
@@ -0,0 +1,5 @@
+---
+title: Fix UI error whereby prometheus application status is updated
+merge_request: 21029
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-search-bar.yml b/changelogs/unreleased/fix-search-bar.yml
new file mode 100644
index 00000000000..d4c0c1efddf
--- /dev/null
+++ b/changelogs/unreleased/fix-search-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Fix search bar text input alignment
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-storage-size-for-artifacts-change.yml b/changelogs/unreleased/fix-storage-size-for-artifacts-change.yml
new file mode 100644
index 00000000000..6a3e1420726
--- /dev/null
+++ b/changelogs/unreleased/fix-storage-size-for-artifacts-change.yml
@@ -0,0 +1,5 @@
+---
+title: Update total storage size when changing size of artifacts
+merge_request: 20697
+author: Peter Marko
+type: fixed
diff --git a/changelogs/unreleased/fix-tooltip-flicker.yml b/changelogs/unreleased/fix-tooltip-flicker.yml
deleted file mode 100644
index c94723d83df..00000000000
--- a/changelogs/unreleased/fix-tooltip-flicker.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix tooltip flickering bug
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml b/changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml
deleted file mode 100644
index 6a4d9b6c8c4..00000000000
--- a/changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disabled Web IDE autocomplete suggestions for Markdown files.
-merge_request:
-author: Isaac Smith
-type: fixed
diff --git a/changelogs/unreleased/fj-37736-improve-performance-post-receive-create-gpg-siganture-worker.yml b/changelogs/unreleased/fj-37736-improve-performance-post-receive-create-gpg-siganture-worker.yml
new file mode 100644
index 00000000000..0b35c5c6786
--- /dev/null
+++ b/changelogs/unreleased/fj-37736-improve-performance-post-receive-create-gpg-siganture-worker.yml
@@ -0,0 +1,5 @@
+---
+title: Performing Commit GPG signature calculation in bulk
+merge_request: 20870
+author:
+type: performance
diff --git a/changelogs/unreleased/fj-46278-apply-doorkeeper-scope-patch.yml b/changelogs/unreleased/fj-46278-apply-doorkeeper-scope-patch.yml
deleted file mode 100644
index 1f4de2cb490..00000000000
--- a/changelogs/unreleased/fj-46278-apply-doorkeeper-scope-patch.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix OAuth Application Authorization screen to appear with each access
-merge_request: 20216
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-46278-enable-doorkeeper-reuse-access-token.yml b/changelogs/unreleased/fj-46278-enable-doorkeeper-reuse-access-token.yml
deleted file mode 100644
index 0994f4de248..00000000000
--- a/changelogs/unreleased/fj-46278-enable-doorkeeper-reuse-access-token.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Enable Doorkeeper option to avoid generating new tokens when users login via
- oauth
-merge_request: 20200
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-48123-fix-gitlab-import.yml b/changelogs/unreleased/fj-48123-fix-gitlab-import.yml
new file mode 100644
index 00000000000..896db2cdcb8
--- /dev/null
+++ b/changelogs/unreleased/fj-48123-fix-gitlab-import.yml
@@ -0,0 +1,5 @@
+---
+title: Fix GitLab project imports not loading due to API timeouts
+merge_request: 20599
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-49014-wiki-search-error.yml b/changelogs/unreleased/fj-49014-wiki-search-error.yml
new file mode 100644
index 00000000000..a76805cb7f9
--- /dev/null
+++ b/changelogs/unreleased/fj-49014-wiki-search-error.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed bug with invalid repository reference using the wiki search
+merge_request: 20722
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-49512-fix-gitlab-git-pages-encoding.yml b/changelogs/unreleased/fj-49512-fix-gitlab-git-pages-encoding.yml
new file mode 100644
index 00000000000..3af90fff3f6
--- /dev/null
+++ b/changelogs/unreleased/fj-49512-fix-gitlab-git-pages-encoding.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent editing and updating wiki pages with non UTF-8 encoding via web interface
+merge_request: 20906
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-49802-bug-api-set-http-headers.yml b/changelogs/unreleased/fj-49802-bug-api-set-http-headers.yml
new file mode 100644
index 00000000000..ba61d378cda
--- /dev/null
+++ b/changelogs/unreleased/fj-49802-bug-api-set-http-headers.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug setting http headers in Files API
+merge_request: 20938
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-bumping-gollum-lib-and-gollum-rugged-adapter.yml b/changelogs/unreleased/fj-bumping-gollum-lib-and-gollum-rugged-adapter.yml
deleted file mode 100644
index 3b4d429707f..00000000000
--- a/changelogs/unreleased/fj-bumping-gollum-lib-and-gollum-rugged-adapter.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed bug that allowed to remove other wiki pages if the title had wildcard characters
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/floating-avarage-commit-numbers.yml b/changelogs/unreleased/floating-avarage-commit-numbers.yml
new file mode 100644
index 00000000000..7f91ab16af4
--- /dev/null
+++ b/changelogs/unreleased/floating-avarage-commit-numbers.yml
@@ -0,0 +1,5 @@
+---
+title: Show one digit after dot in commit_per_day value in charts page.
+merge_request:
+author: msdundar
+type: changed
diff --git a/changelogs/unreleased/frozen-string-app-workers.yml b/changelogs/unreleased/frozen-string-app-workers.yml
deleted file mode 100644
index 48b50cc6ca4..00000000000
--- a/changelogs/unreleased/frozen-string-app-workers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable frozen string in app/workers/*.rb
-merge_request: 19944
-author: gfyoung
-type: other
diff --git a/changelogs/unreleased/frozen-string-danger.yml b/changelogs/unreleased/frozen-string-danger.yml
new file mode 100644
index 00000000000..9910139b8a9
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-danger.yml
@@ -0,0 +1,5 @@
+---
+title: Add Dangerfile for frozen_string_literal
+merge_request: 20767
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml b/changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml
new file mode 100644
index 00000000000..a77f3baeed3
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in rest of app/models/**/*.rb
+merge_request: gfyoung
+author:
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-models-more.yml b/changelogs/unreleased/frozen-string-enable-app-models-more.yml
new file mode 100644
index 00000000000..c0466984134
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-models-more.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string for app/models/**/*.rb
+merge_request: 21001
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-models.yml b/changelogs/unreleased/frozen-string-enable-app-models.yml
new file mode 100644
index 00000000000..4c149ea55ef
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-models.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/models/*.rb
+merge_request: 20851
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml b/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml
new file mode 100644
index 00000000000..5c6b1b1a904
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/presenters and app/policies
+merge_request: 20819
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-serializers.yml b/changelogs/unreleased/frozen-string-enable-app-serializers.yml
new file mode 100644
index 00000000000..40c7b695d39
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-serializers.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/serializers/**/*.rb
+merge_request: 20726
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-services.yml b/changelogs/unreleased/frozen-string-enable-app-services.yml
new file mode 100644
index 00000000000..cfc1f356e3a
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-services.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in apps/uploaders/*.rb
+merge_request: 20401
+author: gfyoung
+type: other
diff --git a/changelogs/unreleased/frozen-string-enable-app-workers-2.yml b/changelogs/unreleased/frozen-string-enable-app-workers-2.yml
deleted file mode 100644
index 81de6899d76..00000000000
--- a/changelogs/unreleased/frozen-string-enable-app-workers-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Finish enabling frozen string for app/workers/*.rb
-merge_request: 20197
-author: gfyoung
-type: other
diff --git a/changelogs/unreleased/frozen-string-enable-apps-services-inner-even-more.yml b/changelogs/unreleased/frozen-string-enable-apps-services-inner-even-more.yml
new file mode 100644
index 00000000000..cee790a07ff
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-apps-services-inner-even-more.yml
@@ -0,0 +1,5 @@
+---
+title: Enable even more frozen string in app/services/**/*.rb
+merge_request: 20702
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml b/changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml
new file mode 100644
index 00000000000..ea962cf8edc
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml
@@ -0,0 +1,5 @@
+---
+title: Enable more frozen string in app/services/**/*.rb
+merge_request: 20677
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml b/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml
new file mode 100644
index 00000000000..16b8ec3908f
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/services/**/*.rb
+merge_request: 20656
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-vestigial.yml b/changelogs/unreleased/frozen-string-vestigial.yml
new file mode 100644
index 00000000000..79e92a19a7a
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-vestigial.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in newly added files to previously processed directories
+merge_request: 20763
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml b/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml
new file mode 100644
index 00000000000..b26eb82b6c9
--- /dev/null
+++ b/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml
@@ -0,0 +1,5 @@
+---
+title: Removes "show all" on reports and adds an actionButtons slot
+merge_request: 20855
+author:
+type: changed
diff --git a/changelogs/unreleased/git-rerere-link-doc-update.yml b/changelogs/unreleased/git-rerere-link-doc-update.yml
new file mode 100644
index 00000000000..06093e8ec13
--- /dev/null
+++ b/changelogs/unreleased/git-rerere-link-doc-update.yml
@@ -0,0 +1,5 @@
+---
+title: Update git rerere link in docs
+merge_request: 21060
+author: gfyoung
+type: other
diff --git a/changelogs/unreleased/gitaly-commit-count-opt-out.yml b/changelogs/unreleased/gitaly-commit-count-opt-out.yml
deleted file mode 100644
index fd8298b1d7b..00000000000
--- a/changelogs/unreleased/gitaly-commit-count-opt-out.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move some Gitaly RPC's to opt-out
-merge_request: 19591
-author:
-type: other
diff --git a/changelogs/unreleased/gitaly-opt-out-branch-tag.yml b/changelogs/unreleased/gitaly-opt-out-branch-tag.yml
deleted file mode 100644
index 750fc863eed..00000000000
--- a/changelogs/unreleased/gitaly-opt-out-branch-tag.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move Gitaly branch/tag/ref RPC's to opt-out
-merge_request: 19644
-author:
-type: other
diff --git a/changelogs/unreleased/hangouts_chat_integration.yml b/changelogs/unreleased/hangouts_chat_integration.yml
new file mode 100644
index 00000000000..bf3484a6d02
--- /dev/null
+++ b/changelogs/unreleased/hangouts_chat_integration.yml
@@ -0,0 +1,5 @@
+---
+title: Add Hangouts Chat integration
+merge_request: 20290
+author: Kukovskii Vladimir
+type: added
diff --git a/changelogs/unreleased/highlight-cluster-settings-message.yml b/changelogs/unreleased/highlight-cluster-settings-message.yml
deleted file mode 100644
index 4e029941c51..00000000000
--- a/changelogs/unreleased/highlight-cluster-settings-message.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Highlight cluster settings message
-merge_request: 19996
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/ide-codesandbox-poc.yml b/changelogs/unreleased/ide-codesandbox-poc.yml
new file mode 100644
index 00000000000..7da1f4e6472
--- /dev/null
+++ b/changelogs/unreleased/ide-codesandbox-poc.yml
@@ -0,0 +1,5 @@
+---
+title: Added live preview for JavaScript projects in the Web IDE
+merge_request: 19764
+author:
+type: added
diff --git a/changelogs/unreleased/ide-commit-actions-update.yml b/changelogs/unreleased/ide-commit-actions-update.yml
deleted file mode 100644
index 35bee94e156..00000000000
--- a/changelogs/unreleased/ide-commit-actions-update.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve Web IDE commit flow
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/ide-delete-entries.yml b/changelogs/unreleased/ide-delete-entries.yml
new file mode 100644
index 00000000000..8cbc0739406
--- /dev/null
+++ b/changelogs/unreleased/ide-delete-entries.yml
@@ -0,0 +1,5 @@
+---
+title: Enabled deletion of files in the Web IDE
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/ide-open-empty-merge-request.yml b/changelogs/unreleased/ide-open-empty-merge-request.yml
new file mode 100644
index 00000000000..05f2de5d31c
--- /dev/null
+++ b/changelogs/unreleased/ide-open-empty-merge-request.yml
@@ -0,0 +1,5 @@
+---
+title: Fix empty merge requests not opening in the Web IDE
+merge_request: 21102
+author:
+type: fixed
diff --git a/changelogs/unreleased/ide-pipeline-icon-open.yml b/changelogs/unreleased/ide-pipeline-icon-open.yml
new file mode 100644
index 00000000000..3a73ff2170f
--- /dev/null
+++ b/changelogs/unreleased/ide-pipeline-icon-open.yml
@@ -0,0 +1,5 @@
+---
+title: Clicking CI icon in Web IDE now opens up pipelines panel
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/ide-rename-files.yml b/changelogs/unreleased/ide-rename-files.yml
new file mode 100644
index 00000000000..c2db284e07c
--- /dev/null
+++ b/changelogs/unreleased/ide-rename-files.yml
@@ -0,0 +1,5 @@
+---
+title: Enable renaming files and folders in Web IDE
+merge_request: 20835
+author:
+type: added
diff --git a/changelogs/unreleased/ide-row-dropdown-design-update.yml b/changelogs/unreleased/ide-row-dropdown-design-update.yml
new file mode 100644
index 00000000000..e0fe64c944e
--- /dev/null
+++ b/changelogs/unreleased/ide-row-dropdown-design-update.yml
@@ -0,0 +1,5 @@
+---
+title: Updated design of new entry dropdown in Web IDE
+merge_request: 20526
+author:
+type: changed
diff --git a/changelogs/unreleased/ide-warn-staged-files.yml b/changelogs/unreleased/ide-warn-staged-files.yml
new file mode 100644
index 00000000000..ae3c4f392c0
--- /dev/null
+++ b/changelogs/unreleased/ide-warn-staged-files.yml
@@ -0,0 +1,5 @@
+---
+title: Warn user when reload IDE with staged changes
+merge_request: 20857
+author:
+type: added
diff --git a/changelogs/unreleased/improve-junit-support-be.yml b/changelogs/unreleased/improve-junit-support-be.yml
new file mode 100644
index 00000000000..db4d47caa7c
--- /dev/null
+++ b/changelogs/unreleased/improve-junit-support-be.yml
@@ -0,0 +1,5 @@
+---
+title: Improve JUnit test reports in merge request widgets
+merge_request: 49966
+author:
+type: fixed
diff --git a/changelogs/unreleased/improve-metadata-access-performance.yml b/changelogs/unreleased/improve-metadata-access-performance.yml
new file mode 100644
index 00000000000..b16fa99dd3b
--- /dev/null
+++ b/changelogs/unreleased/improve-metadata-access-performance.yml
@@ -0,0 +1,5 @@
+---
+title: Access metadata directly from Object Storage
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/issue_43602.yml b/changelogs/unreleased/issue_43602.yml
new file mode 100644
index 00000000000..0482606db0a
--- /dev/null
+++ b/changelogs/unreleased/issue_43602.yml
@@ -0,0 +1,5 @@
+---
+title: Allow multiple JIRA transition ids
+merge_request: 20939
+author:
+type: changed
diff --git a/changelogs/unreleased/issue_44821.yml b/changelogs/unreleased/issue_44821.yml
new file mode 100644
index 00000000000..b1807e069af
--- /dev/null
+++ b/changelogs/unreleased/issue_44821.yml
@@ -0,0 +1,5 @@
+---
+title: Retrieve merge request closing issues from database cache
+merge_request: 20911
+author:
+type: fixed
diff --git a/changelogs/unreleased/issue_47709.yml b/changelogs/unreleased/issue_47709.yml
new file mode 100644
index 00000000000..c3ef55fd692
--- /dev/null
+++ b/changelogs/unreleased/issue_47709.yml
@@ -0,0 +1,5 @@
+---
+title: 'Allow to toggle notifications for issues due soon'
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/issue_47729.yml b/changelogs/unreleased/issue_47729.yml
deleted file mode 100644
index e27972af114..00000000000
--- a/changelogs/unreleased/issue_47729.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix refreshing cache keys for open issues count
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/jprovazn-resource-events.yml b/changelogs/unreleased/jprovazn-resource-events.yml
new file mode 100644
index 00000000000..05643150f16
--- /dev/null
+++ b/changelogs/unreleased/jprovazn-resource-events.yml
@@ -0,0 +1,5 @@
+---
+title: Add new model for tracking label events.
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/jr-48133-web-ide-commit-ellipsis.yml b/changelogs/unreleased/jr-48133-web-ide-commit-ellipsis.yml
deleted file mode 100644
index ac58eaccaaf..00000000000
--- a/changelogs/unreleased/jr-48133-web-ide-commit-ellipsis.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add ellispsis to web ide commit button
-merge_request: 20030
-author:
-type: other
diff --git a/changelogs/unreleased/jr-archive-hook.yml b/changelogs/unreleased/jr-archive-hook.yml
new file mode 100644
index 00000000000..56c13f1370e
--- /dev/null
+++ b/changelogs/unreleased/jr-archive-hook.yml
@@ -0,0 +1,5 @@
+---
+title: Trigger system hooks when project is archived/unarchived
+merge_request: 20995
+author:
+type: added
diff --git a/changelogs/unreleased/jupyter-image.yml b/changelogs/unreleased/jupyter-image.yml
new file mode 100644
index 00000000000..8aeefd603d8
--- /dev/null
+++ b/changelogs/unreleased/jupyter-image.yml
@@ -0,0 +1,5 @@
+---
+title: Rubix, scikit-learn, tensorflow & other useful libraries pre-installed with JupyterHub
+merge_request: 20714
+author: Amit Rathi
+type: changed
diff --git a/changelogs/unreleased/kp-6927-epic-dates-from-milestone.yml b/changelogs/unreleased/kp-6927-epic-dates-from-milestone.yml
new file mode 100644
index 00000000000..c15d73a0c12
--- /dev/null
+++ b/changelogs/unreleased/kp-6927-epic-dates-from-milestone.yml
@@ -0,0 +1,5 @@
+---
+title: Add 'tabindex' attribute support on Icon component to show BS4 popover on trigger type 'focus'
+merge_request: 21066
+author:
+type: other
diff --git a/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml
new file mode 100644
index 00000000000..a2fca4c5b91
--- /dev/null
+++ b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml
@@ -0,0 +1,5 @@
+---
+title: Show decimal place up to single digit in Stacked Progress Bar
+merge_request: 20776
+author:
+type: changed
diff --git a/changelogs/unreleased/leipert-fix-pipelines-view.yml b/changelogs/unreleased/leipert-fix-pipelines-view.yml
new file mode 100644
index 00000000000..7bcc2e84cd2
--- /dev/null
+++ b/changelogs/unreleased/leipert-fix-pipelines-view.yml
@@ -0,0 +1,5 @@
+---
+title: Fix rendering of pipeline failure view when directly navigationg to it
+merge_request: 21043
+author:
+type: fixed
diff --git a/changelogs/unreleased/limit-metrics-content-type.yml b/changelogs/unreleased/limit-metrics-content-type.yml
deleted file mode 100644
index 42cb4347771..00000000000
--- a/changelogs/unreleased/limit-metrics-content-type.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Limit the action suffixes in transaction metrics
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/mk-add-local-project-uploads-cleanup-task.yml b/changelogs/unreleased/mk-add-local-project-uploads-cleanup-task.yml
new file mode 100644
index 00000000000..9d38b353a41
--- /dev/null
+++ b/changelogs/unreleased/mk-add-local-project-uploads-cleanup-task.yml
@@ -0,0 +1,5 @@
+---
+title: Add local project uploads cleanup task
+merge_request: 20863
+author:
+type: added
diff --git a/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml b/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml
new file mode 100644
index 00000000000..8e71377d93f
--- /dev/null
+++ b/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml
@@ -0,0 +1,5 @@
+---
+title: Fix namespace move callback behavior, especially to fix Geo replication of namespace moves during certain exceptions.
+merge_request: 19297
+author:
+type: fixed
diff --git a/changelogs/unreleased/more-group-api-sorting-options.yml b/changelogs/unreleased/more-group-api-sorting-options.yml
deleted file mode 100644
index b29f76a65a9..00000000000
--- a/changelogs/unreleased/more-group-api-sorting-options.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added id sorting option to GET groups and subgroups API
-merge_request: 19665
-author: Marko, Peter
-type: added
diff --git a/changelogs/unreleased/move-boards-modal-empty-state-vue-component.yml b/changelogs/unreleased/move-boards-modal-empty-state-vue-component.yml
deleted file mode 100644
index 54a61d7c914..00000000000
--- a/changelogs/unreleased/move-boards-modal-empty-state-vue-component.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move boards modal EmptyState vue component
-merge_request: 20068
-author: George Tsiolis
-type: performance
diff --git a/changelogs/unreleased/no-multi-assign-enable.yml b/changelogs/unreleased/no-multi-assign-enable.yml
deleted file mode 100644
index bb9c69b18e7..00000000000
--- a/changelogs/unreleased/no-multi-assign-enable.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable no-multi-assignment in JS files
-merge_request: 19808
-author: gfyoung
-type: other
diff --git a/changelogs/unreleased/no-multi-assign-follow-up.yml b/changelogs/unreleased/no-multi-assign-follow-up.yml
deleted file mode 100644
index 817760ff649..00000000000
--- a/changelogs/unreleased/no-multi-assign-follow-up.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve no-multi-assignment fixes after enabling rule
-merge_request: 19915
-author: gfyoung
-type: other
diff --git a/changelogs/unreleased/no-restricted-globals-enable.yml b/changelogs/unreleased/no-restricted-globals-enable.yml
deleted file mode 100644
index 1fa2eac0d03..00000000000
--- a/changelogs/unreleased/no-restricted-globals-enable.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable no-restricted globals in JS files
-merge_request: 19877
-author: gfyoung
-type: other
diff --git a/changelogs/unreleased/osw-delete-non-latest-mr-diff-files-migration.yml b/changelogs/unreleased/osw-delete-non-latest-mr-diff-files-migration.yml
deleted file mode 100644
index e4cbae1a109..00000000000
--- a/changelogs/unreleased/osw-delete-non-latest-mr-diff-files-migration.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Schedule workers to delete non-latest diffs in post-migration
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/osw-delete-non-latest-mr-diff-files-upon-merge.yml b/changelogs/unreleased/osw-delete-non-latest-mr-diff-files-upon-merge.yml
deleted file mode 100644
index 3e752125f3a..00000000000
--- a/changelogs/unreleased/osw-delete-non-latest-mr-diff-files-upon-merge.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Delete non-latest merge request diff files upon merge
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/osw-fix-missing-and-duplicated-milestones-on-list.yml b/changelogs/unreleased/osw-fix-missing-and-duplicated-milestones-on-list.yml
new file mode 100644
index 00000000000..62416b7f87e
--- /dev/null
+++ b/changelogs/unreleased/osw-fix-missing-and-duplicated-milestones-on-list.yml
@@ -0,0 +1,5 @@
+---
+title: Fix missing and duplicates on project milestone listing page
+merge_request: 21058
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-fix-n-plus-1-for-mrs-without-merge-info.yml b/changelogs/unreleased/osw-fix-n-plus-1-for-mrs-without-merge-info.yml
new file mode 100644
index 00000000000..dc8148fa1a5
--- /dev/null
+++ b/changelogs/unreleased/osw-fix-n-plus-1-for-mrs-without-merge-info.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid N+1 on MRs page when metrics merging date cannot be found
+merge_request: 21053
+author:
+type: performance
diff --git a/changelogs/unreleased/osw-mark-as-merged-as-first-post-merge-action.yml b/changelogs/unreleased/osw-mark-as-merged-as-first-post-merge-action.yml
deleted file mode 100644
index 2049afc3d44..00000000000
--- a/changelogs/unreleased/osw-mark-as-merged-as-first-post-merge-action.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Mark MR as merged regardless of errors when closing issues
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/pl-json-gon.yml b/changelogs/unreleased/pl-json-gon.yml
new file mode 100644
index 00000000000..c0f93006c07
--- /dev/null
+++ b/changelogs/unreleased/pl-json-gon.yml
@@ -0,0 +1,5 @@
+---
+title: Don't set gon variables in JSON requests
+merge_request: 21016
+author: Peter Leitzen
+type: performance
diff --git a/changelogs/unreleased/pr-importer-io-extra-error-handling.yml b/changelogs/unreleased/pr-importer-io-extra-error-handling.yml
deleted file mode 100644
index 2f7121b2840..00000000000
--- a/changelogs/unreleased/pr-importer-io-extra-error-handling.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ensure MR diffs always exist in the PR importer
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/prefer-destructuring-fix.yml b/changelogs/unreleased/prefer-destructuring-fix.yml
deleted file mode 100644
index 452e04f553e..00000000000
--- a/changelogs/unreleased/prefer-destructuring-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable prefer-structuring in JS files
-merge_request: 19943
-author: gfyoung
-type: other
diff --git a/changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml b/changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml
new file mode 100644
index 00000000000..6994a238074
--- /dev/null
+++ b/changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml
@@ -0,0 +1,5 @@
+---
+title: Process commits as normal in forks when the upstream project is deleted
+merge_request: 20534
+author:
+type: fixed
diff --git a/changelogs/unreleased/project-visibility-tooltip.yml b/changelogs/unreleased/project-visibility-tooltip.yml
new file mode 100644
index 00000000000..806c93e493a
--- /dev/null
+++ b/changelogs/unreleased/project-visibility-tooltip.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project visibility tooltip
+merge_request: 20535
+author: Jamie Schembri
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-46276.yml b/changelogs/unreleased/rails5-fix-46276.yml
deleted file mode 100644
index cdca91a755d..00000000000
--- a/changelogs/unreleased/rails5-fix-46276.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix format in uploads actions
-merge_request: 19907
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47366.yml b/changelogs/unreleased/rails5-fix-47366.yml
deleted file mode 100644
index 7ea03d2b95e..00000000000
--- a/changelogs/unreleased/rails5-fix-47366.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix expected `issuable.reload.updated_at` to have changed
-merge_request: 19733
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47370.yml b/changelogs/unreleased/rails5-fix-47370.yml
deleted file mode 100644
index 90c19593b7d..00000000000
--- a/changelogs/unreleased/rails5-fix-47370.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use same gem versions for rails5 as for rails4 where possible
-merge_request: 19498
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47804.yml b/changelogs/unreleased/rails5-fix-47804.yml
deleted file mode 100644
index 3332ed3bbaa..00000000000
--- a/changelogs/unreleased/rails5-fix-47804.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix stack level too deep
-merge_request: 19762
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47805.yml b/changelogs/unreleased/rails5-fix-47805.yml
deleted file mode 100644
index 8bd8ad5488c..00000000000
--- a/changelogs/unreleased/rails5-fix-47805.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 'Rails5 ActionController::ParameterMissing: param is missing or the value is
- empty: application_setting'
-merge_request: 19763
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47835.yml b/changelogs/unreleased/rails5-fix-47835.yml
deleted file mode 100644
index fe9cbf1a03a..00000000000
--- a/changelogs/unreleased/rails5-fix-47835.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Rails5 fix no implicit conversion of Hash into String. ActionController::Parameters
- no longer returns an hash in Rails 5
-merge_request: 19792
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47836.yml b/changelogs/unreleased/rails5-fix-47836.yml
deleted file mode 100644
index 2aef2db607a..00000000000
--- a/changelogs/unreleased/rails5-fix-47836.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Rails5 fix passing Group objects array into for_projects_and_groups milestone
- scope
-merge_request: 19863
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47960.yml b/changelogs/unreleased/rails5-fix-47960.yml
deleted file mode 100644
index 2229511ccd6..00000000000
--- a/changelogs/unreleased/rails5-fix-47960.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix update_attribute usage not causing a save
-merge_request: 19881
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48009.yml b/changelogs/unreleased/rails5-fix-48009.yml
deleted file mode 100644
index 7ade9ef2b7d..00000000000
--- a/changelogs/unreleased/rails5-fix-48009.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 update Gemfile.rails5.lock
-merge_request: 19921
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48012.yml b/changelogs/unreleased/rails5-fix-48012.yml
deleted file mode 100644
index 953ccbd8af7..00000000000
--- a/changelogs/unreleased/rails5-fix-48012.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Rails5 fix passing Group objects array into for_projects_and_groups milestone
- scope
-merge_request: 19920
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48104.yml b/changelogs/unreleased/rails5-fix-48104.yml
deleted file mode 100644
index 6cf519ad791..00000000000
--- a/changelogs/unreleased/rails5-fix-48104.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 'Rails5 fix expected: 1 time with arguments: (97, anything, {"squash"=>false})
- received: 0 times'
-merge_request: 20004
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48140.yml b/changelogs/unreleased/rails5-fix-48140.yml
deleted file mode 100644
index a6992803e5a..00000000000
--- a/changelogs/unreleased/rails5-fix-48140.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 'Rails 5 fix Capybara::ElementNotFound: Unable to find visible css #modal-revert-commit
- and expected: "/bar" got: "/foo"'
-merge_request: 20044
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48141.yml b/changelogs/unreleased/rails5-fix-48141.yml
deleted file mode 100644
index 5e2aa23b8fb..00000000000
--- a/changelogs/unreleased/rails5-fix-48141.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 'Rails5 fix expected: 0 times with any arguments received: 1 time with arguments:
- DashboardController'
-merge_request: 20018
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48142.yml b/changelogs/unreleased/rails5-fix-48142.yml
deleted file mode 100644
index bfd95cfbe8b..00000000000
--- a/changelogs/unreleased/rails5-fix-48142.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix Admin::HooksController
-merge_request: 20017
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48430.yml b/changelogs/unreleased/rails5-fix-48430.yml
deleted file mode 100644
index 16495615395..00000000000
--- a/changelogs/unreleased/rails5-fix-48430.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix MySQL milliseconds problem in specs
-merge_request: 20221
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48432.yml b/changelogs/unreleased/rails5-fix-48432.yml
deleted file mode 100644
index 732294447a9..00000000000
--- a/changelogs/unreleased/rails5-fix-48432.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix Mysql comparison failure caused by milliseconds problem
-merge_request: 20222
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48977.yml b/changelogs/unreleased/rails5-fix-48977.yml
new file mode 100644
index 00000000000..bfd86f20e24
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-48977.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix mysql milliseconds problem in specs
+merge_request: 20464
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-db-check.yml b/changelogs/unreleased/rails5-fix-db-check.yml
deleted file mode 100644
index ccac4619ea7..00000000000
--- a/changelogs/unreleased/rails5-fix-db-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix connection execute return integer instead of string
-merge_request: 19901
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml b/changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml
new file mode 100644
index 00000000000..e31768773b1
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix specs duplicate key value violates unique constraint 'index_gpg_signatures_on_commit_sha'
+merge_request: 21119
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-flaky-spec-user-uses-shortcuts.yml b/changelogs/unreleased/rails5-fix-flaky-spec-user-uses-shortcuts.yml
new file mode 100644
index 00000000000..5f2504c604d
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-flaky-spec-user-uses-shortcuts.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails5: fix flaky spec'
+merge_request: 20953
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-mysql-arel-from.yml b/changelogs/unreleased/rails5-fix-mysql-arel-from.yml
deleted file mode 100644
index 9883ff306f1..00000000000
--- a/changelogs/unreleased/rails5-fix-mysql-arel-from.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix arel from in mysql_median_datetime_sql
-merge_request: 20167
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-pages-controller.yml b/changelogs/unreleased/rails5-fix-pages-controller.yml
deleted file mode 100644
index eeb3747c4eb..00000000000
--- a/changelogs/unreleased/rails5-fix-pages-controller.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rails5 fix Projects::PagesController spec
-merge_request: 20007
-author: Jasper Maes
-type: fixed
diff --git a/changelogs/unreleased/rails5-fix-revert-modal-spec.yml b/changelogs/unreleased/rails5-fix-revert-modal-spec.yml
new file mode 100644
index 00000000000..0637e503ca9
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-revert-modal-spec.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix user sees revert modal spec
+merge_request: 20706
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-gpg-permit-concurrent.yml b/changelogs/unreleased/rails5-gpg-permit-concurrent.yml
new file mode 100644
index 00000000000..cf1b0023f86
--- /dev/null
+++ b/changelogs/unreleased/rails5-gpg-permit-concurrent.yml
@@ -0,0 +1,5 @@
+---
+title: Permit concurrent loads in gpg keychain mutex
+merge_request: 20894
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml b/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml
new file mode 100644
index 00000000000..afd9865ee45
--- /dev/null
+++ b/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 mysql fix milliseconds problem in pull request importer spec
+merge_request: 20475
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-mysql-rename-column.yml b/changelogs/unreleased/rails5-mysql-rename-column.yml
new file mode 100644
index 00000000000..cbae9250744
--- /dev/null
+++ b/changelogs/unreleased/rails5-mysql-rename-column.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 MySQL fix rename_column as part of cleanup_concurrent_column_type_change
+merge_request: 20514
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-update-gemfile-lock-2.yml b/changelogs/unreleased/rails5-update-gemfile-lock-2.yml
new file mode 100644
index 00000000000..1f3e9bd2238
--- /dev/null
+++ b/changelogs/unreleased/rails5-update-gemfile-lock-2.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 update Gemfile.rails5.lock
+merge_request: 20858
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-update-gemfile-lock.yml b/changelogs/unreleased/rails5-update-gemfile-lock.yml
new file mode 100644
index 00000000000..58931587fff
--- /dev/null
+++ b/changelogs/unreleased/rails5-update-gemfile-lock.yml
@@ -0,0 +1,5 @@
+---
+title: Update Gemfile.rails5.lock with latest Gemfile.lock changes
+merge_request: 20466
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-update-rouge.yml b/changelogs/unreleased/rails5-update-rouge.yml
new file mode 100644
index 00000000000..1173b3b7e9a
--- /dev/null
+++ b/changelogs/unreleased/rails5-update-rouge.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails5: update Rails5 lock for forgotten gem rouge'
+merge_request: 21010
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/ravlen-deploy-tokens-display-update.yml b/changelogs/unreleased/ravlen-deploy-tokens-display-update.yml
new file mode 100644
index 00000000000..fd5a6521882
--- /dev/null
+++ b/changelogs/unreleased/ravlen-deploy-tokens-display-update.yml
@@ -0,0 +1,5 @@
+---
+title: "Cleans up display of Deploy Tokens to match Personal Access Tokens"
+merge_request: 20578
+author: Marcel Amirault
+type: added \ No newline at end of file
diff --git a/changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml b/changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml
deleted file mode 100644
index 3934381b0cf..00000000000
--- a/changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Invalidate cache with project details when repository is updated
-merge_request: 19774
-author:
-type: fixed
diff --git a/changelogs/unreleased/regen-2fa-codes.yml b/changelogs/unreleased/regen-2fa-codes.yml
new file mode 100644
index 00000000000..596f759df0f
--- /dev/null
+++ b/changelogs/unreleased/regen-2fa-codes.yml
@@ -0,0 +1,5 @@
+---
+title: Added button to regenerate 2FA codes
+merge_request:
+author: Luke Picciau
+type: added
diff --git a/changelogs/unreleased/remove-allocations-gem.yml b/changelogs/unreleased/remove-allocations-gem.yml
deleted file mode 100644
index e809fd26701..00000000000
--- a/changelogs/unreleased/remove-allocations-gem.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove remaining traces of the Allocations Gem
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml b/changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml
deleted file mode 100644
index b86512445d5..00000000000
--- a/changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove the ci_job_request_with_tags_matcher
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/remove-link-label-vertical-alignment-property.yml b/changelogs/unreleased/remove-link-label-vertical-alignment-property.yml
deleted file mode 100644
index 40ec3998b05..00000000000
--- a/changelogs/unreleased/remove-link-label-vertical-alignment-property.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Change label link vertical alignment property
-merge_request: 18777
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/remove-small-container-width.yml b/changelogs/unreleased/remove-small-container-width.yml
deleted file mode 100644
index 1af8aafa87e..00000000000
--- a/changelogs/unreleased/remove-small-container-width.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove small container width
-merge_request: 19893
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml b/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml
new file mode 100644
index 00000000000..8d5ecdfa57e
--- /dev/null
+++ b/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml
@@ -0,0 +1,5 @@
+---
+title: Replace snake case in SCSS variables
+merge_request: 20799
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/replace-snake-case-css-classes.yml b/changelogs/unreleased/replace-snake-case-css-classes.yml
new file mode 100644
index 00000000000..28ec5ee097f
--- /dev/null
+++ b/changelogs/unreleased/replace-snake-case-css-classes.yml
@@ -0,0 +1,5 @@
+---
+title: Replace author_link snake case in stylesheets, specs, and helpers
+merge_request: 20797
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/revert-merge-request-discussion-buttons-padding.yml b/changelogs/unreleased/revert-merge-request-discussion-buttons-padding.yml
deleted file mode 100644
index 9f11dd3dc3f..00000000000
--- a/changelogs/unreleased/revert-merge-request-discussion-buttons-padding.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Revert merge request discussion buttons padding
-merge_request: 20060
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/revert-merge-request-widget-button-height.yml b/changelogs/unreleased/revert-merge-request-widget-button-height.yml
deleted file mode 100644
index 7c400a4a2b2..00000000000
--- a/changelogs/unreleased/revert-merge-request-widget-button-height.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Revert merge request widget button max height
-merge_request: 20175
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/rosulk-patch-12.yml b/changelogs/unreleased/rosulk-patch-12.yml
deleted file mode 100644
index 9637c88d1a4..00000000000
--- a/changelogs/unreleased/rosulk-patch-12.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Flex issue board columns
-merge_request: 19250
-author: Roman Rosluk
-type: changed
diff --git a/changelogs/unreleased/rouge-3-2-0.yml b/changelogs/unreleased/rouge-3-2-0.yml
new file mode 100644
index 00000000000..15ac4cc1e76
--- /dev/null
+++ b/changelogs/unreleased/rouge-3-2-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update to Rouge 3.2.0, including Terraform and Crystal lexer and bug fixes
+merge_request: 20991
+author:
+type: changed
diff --git a/changelogs/unreleased/runner-features.yml b/changelogs/unreleased/runner-features.yml
new file mode 100644
index 00000000000..c5e0fff5a18
--- /dev/null
+++ b/changelogs/unreleased/runner-features.yml
@@ -0,0 +1,5 @@
+---
+title: Verify runner feature set
+merge_request: 20664
+author:
+type: added
diff --git a/changelogs/unreleased/runners-max-timeout-param.yml b/changelogs/unreleased/runners-max-timeout-param.yml
new file mode 100644
index 00000000000..875f805d849
--- /dev/null
+++ b/changelogs/unreleased/runners-max-timeout-param.yml
@@ -0,0 +1,5 @@
+---
+title: Add missing maximum_timeout parameter
+merge_request: 20355
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/safari-scrollbar-bug.yml b/changelogs/unreleased/safari-scrollbar-bug.yml
deleted file mode 100644
index 792a66d1ada..00000000000
--- a/changelogs/unreleased/safari-scrollbar-bug.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove scrollbar in Safari in repo settings page
-merge_request: 19809
-author: gfyoung
-type: fixed
diff --git a/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml b/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml
new file mode 100644
index 00000000000..7bfe1b5778f
--- /dev/null
+++ b/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml
@@ -0,0 +1,5 @@
+---
+title: Include full image URL in webhooks for uploaded images
+merge_request: 18109
+author: Satish Perala
+type: changed
diff --git a/changelogs/unreleased/security-2682-fix-xss-for-markdown-toc.yml b/changelogs/unreleased/security-2682-fix-xss-for-markdown-toc.yml
deleted file mode 100644
index f595678c3c2..00000000000
--- a/changelogs/unreleased/security-2682-fix-xss-for-markdown-toc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix XSS vulnerability for table of content generation
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-fj-bumping-sanitize-gem.yml b/changelogs/unreleased/security-fj-bumping-sanitize-gem.yml
deleted file mode 100644
index bec1033425d..00000000000
--- a/changelogs/unreleased/security-fj-bumping-sanitize-gem.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update sanitize gem to 4.6.5 to fix HTML injection vulnerability
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml b/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml
new file mode 100644
index 00000000000..fabf48acbbc
--- /dev/null
+++ b/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml
@@ -0,0 +1,5 @@
+---
+title: Adding CSRF protection to Hooks test action
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-html_escape_branch_name.yml b/changelogs/unreleased/security-html_escape_branch_name.yml
deleted file mode 100644
index 02d1065348f..00000000000
--- a/changelogs/unreleased/security-html_escape_branch_name.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: HTML escape branch name in project graphs page
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-html_escape_usernames.yml b/changelogs/unreleased/security-html_escape_usernames.yml
deleted file mode 100644
index 7e69e4ae266..00000000000
--- a/changelogs/unreleased/security-html_escape_usernames.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: HTML escape the name of the user in ProjectsHelper#link_to_member
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-ide-branch-name-xss.yml b/changelogs/unreleased/security-ide-branch-name-xss.yml
new file mode 100644
index 00000000000..51742ffa4e9
--- /dev/null
+++ b/changelogs/unreleased/security-ide-branch-name-xss.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed XSS in branch name in Web IDE
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-rd-do-not-show-internal-info-in-public-feed.yml b/changelogs/unreleased/security-rd-do-not-show-internal-info-in-public-feed.yml
deleted file mode 100644
index ff78c162dff..00000000000
--- a/changelogs/unreleased/security-rd-do-not-show-internal-info-in-public-feed.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Don't show events from internal projects for anonymous users in public feed
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/sh-bump-gitaly-0-117.yml b/changelogs/unreleased/sh-bump-gitaly-0-117.yml
new file mode 100644
index 00000000000..90ca86d076b
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-gitaly-0-117.yml
@@ -0,0 +1,5 @@
+---
+title: Bump Gitaly to 0.117.0
+merge_request: 21055
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-bump-haml-5-0-4.yml b/changelogs/unreleased/sh-bump-haml-5-0-4.yml
new file mode 100644
index 00000000000..269b1e55417
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-haml-5-0-4.yml
@@ -0,0 +1,5 @@
+---
+title: Bump haml gem to 5.0.4
+merge_request: 20847
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-bump-rugged-0-27-2.yml b/changelogs/unreleased/sh-bump-rugged-0-27-2.yml
deleted file mode 100644
index 6c519648b51..00000000000
--- a/changelogs/unreleased/sh-bump-rugged-0-27-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump rugged to 0.27.2
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml
new file mode 100644
index 00000000000..b9444440cb9
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml
@@ -0,0 +1,5 @@
+---
+title: Bump nokogiri to 1.8.4 and sanitize to 4.6.6 for performance
+merge_request: 20795
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
new file mode 100644
index 00000000000..897d673e97d
--- /dev/null
+++ b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen strings in remaining lib/banzai/filter/*.rb files
+merge_request: 20777
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-fix-admin-jobs-controller-timing-out.yml b/changelogs/unreleased/sh-fix-admin-jobs-controller-timing-out.yml
new file mode 100644
index 00000000000..e1adebbf076
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-admin-jobs-controller-timing-out.yml
@@ -0,0 +1,5 @@
+---
+title: Fix /admin/jobs failing to load due to statement timeout
+merge_request: 20909
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml b/changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml
new file mode 100644
index 00000000000..3f7044833f1
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Bitbucket Cloud importer omitting replies
+merge_request: 21076
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-47797-ce.yml b/changelogs/unreleased/sh-fix-issue-47797-ce.yml
new file mode 100644
index 00000000000..456d96acacb
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-47797-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Fix handling of annotated tags when Gitaly is not in use
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-49133.yml b/changelogs/unreleased/sh-fix-issue-49133.yml
new file mode 100644
index 00000000000..847220d88b2
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-49133.yml
@@ -0,0 +1,5 @@
+---
+title: Fix symlink vulnerability in project import
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml b/changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml
new file mode 100644
index 00000000000..b7366cf2569
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid process deadlock in popen by consuming input pipes
+merge_request: 20600
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
new file mode 100644
index 00000000000..37b397ea49f
--- /dev/null
+++ b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen strings in lib/banzai/filter/*.rb
+merge_request: 20775
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-handle-colons-in-url-passwords.yml b/changelogs/unreleased/sh-handle-colons-in-url-passwords.yml
new file mode 100644
index 00000000000..7717d0aab37
--- /dev/null
+++ b/changelogs/unreleased/sh-handle-colons-in-url-passwords.yml
@@ -0,0 +1,5 @@
+---
+title: Properly handle colons in URL passwords
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-include-rbtrace.yml b/changelogs/unreleased/sh-include-rbtrace.yml
new file mode 100644
index 00000000000..41f0655e3f8
--- /dev/null
+++ b/changelogs/unreleased/sh-include-rbtrace.yml
@@ -0,0 +1,5 @@
+---
+title: Add rbtrace to Gemfile
+merge_request: 20831
+author:
+type: other
diff --git a/changelogs/unreleased/sh-lfs-fix-content-type.yml b/changelogs/unreleased/sh-lfs-fix-content-type.yml
new file mode 100644
index 00000000000..a839be9b3ae
--- /dev/null
+++ b/changelogs/unreleased/sh-lfs-fix-content-type.yml
@@ -0,0 +1,5 @@
+---
+title: Fix LFS uploads not working with git-lfs 2.5.0
+merge_request: 20923
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-limit-unauthenticated-session-times.yml b/changelogs/unreleased/sh-limit-unauthenticated-session-times.yml
new file mode 100644
index 00000000000..44a46b4115e
--- /dev/null
+++ b/changelogs/unreleased/sh-limit-unauthenticated-session-times.yml
@@ -0,0 +1,5 @@
+---
+title: Limit the TTL for anonymous sessions to 1 hour
+merge_request: 20700
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-normalize-urls.yml b/changelogs/unreleased/sh-normalize-urls.yml
new file mode 100644
index 00000000000..b0d1120e10b
--- /dev/null
+++ b/changelogs/unreleased/sh-normalize-urls.yml
@@ -0,0 +1,5 @@
+---
+title: Escape username and password in UrlSanitizer#full_url
+merge_request: 20684
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-optimize-locks-check-ce.yml b/changelogs/unreleased/sh-optimize-locks-check-ce.yml
deleted file mode 100644
index 933ec9b79bf..00000000000
--- a/changelogs/unreleased/sh-optimize-locks-check-ce.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Eliminate N+1 queries in LFS file locks checks during a push
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-optimize-wiki-empty-check.yml b/changelogs/unreleased/sh-optimize-wiki-empty-check.yml
new file mode 100644
index 00000000000..31ca7497b5a
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-wiki-empty-check.yml
@@ -0,0 +1,5 @@
+---
+title: Optimize ProjectWiki#empty? check
+merge_request: 20573
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-remove-banzai-instrumentation.yml b/changelogs/unreleased/sh-remove-banzai-instrumentation.yml
new file mode 100644
index 00000000000..8bb3cd5942b
--- /dev/null
+++ b/changelogs/unreleased/sh-remove-banzai-instrumentation.yml
@@ -0,0 +1,5 @@
+---
+title: Remove method instrumentation for Banzai filters and reference parsers
+merge_request: 20770
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-simplify-liveness-check.yml b/changelogs/unreleased/sh-simplify-liveness-check.yml
new file mode 100644
index 00000000000..225e3dc1378
--- /dev/null
+++ b/changelogs/unreleased/sh-simplify-liveness-check.yml
@@ -0,0 +1,5 @@
+---
+title: Add /-/health basic health check endpoint
+merge_request: 20456
+author:
+type: added
diff --git a/changelogs/unreleased/sh-support-users-find-by-confirmed-emails.yml b/changelogs/unreleased/sh-support-users-find-by-confirmed-emails.yml
new file mode 100644
index 00000000000..4b0c8117b3e
--- /dev/null
+++ b/changelogs/unreleased/sh-support-users-find-by-confirmed-emails.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for searching users by confirmed e-mails
+merge_request: 20893
+author:
+type: other
diff --git a/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml b/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml
new file mode 100644
index 00000000000..e8c2e11ad31
--- /dev/null
+++ b/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml
@@ -0,0 +1,5 @@
+---
+title: Use limit parameter to retrieve Wikis from Gitaly
+merge_request: 20764
+author:
+type: performance
diff --git a/changelogs/unreleased/stop-dynamic-routable-creation.yml b/changelogs/unreleased/stop-dynamic-routable-creation.yml
new file mode 100644
index 00000000000..8bfcb5b2d11
--- /dev/null
+++ b/changelogs/unreleased/stop-dynamic-routable-creation.yml
@@ -0,0 +1,5 @@
+---
+title: Stop dynamically creating project and namespace routes
+merge_request: 20313
+author:
+type: performance
diff --git a/changelogs/unreleased/straight-comparision-mode.yml b/changelogs/unreleased/straight-comparision-mode.yml
deleted file mode 100644
index 2f6a0c0b54d..00000000000
--- a/changelogs/unreleased/straight-comparision-mode.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow straight diff in Compare API
-merge_request: 20120
-author: Maciej Nowak
-type: added
diff --git a/changelogs/unreleased/tc-reorder-mail-notify-references.yml b/changelogs/unreleased/tc-reorder-mail-notify-references.yml
new file mode 100644
index 00000000000..689afda0259
--- /dev/null
+++ b/changelogs/unreleased/tc-reorder-mail-notify-references.yml
@@ -0,0 +1,5 @@
+---
+title: Put fallback reply-key address first in the References header
+merge_request: 20871
+author:
+type: changed
diff --git a/changelogs/unreleased/tc-repo-check-per-shard.yml b/changelogs/unreleased/tc-repo-check-per-shard.yml
deleted file mode 100644
index 227b6b0b93b..00000000000
--- a/changelogs/unreleased/tc-repo-check-per-shard.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Run repository checks in parallel for each shard
-merge_request: 20179
-author:
-type: added
diff --git a/changelogs/unreleased/text-expander-icon-update.yml b/changelogs/unreleased/text-expander-icon-update.yml
deleted file mode 100644
index be9dc98728f..00000000000
--- a/changelogs/unreleased/text-expander-icon-update.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Updated the icon for expand buttons to ellipsis
-merge_request: 18793
-author: Constance Okoghenun
-type: changed \ No newline at end of file
diff --git a/changelogs/unreleased/todos-visibility-change.yml b/changelogs/unreleased/todos-visibility-change.yml
new file mode 100644
index 00000000000..b7632b94771
--- /dev/null
+++ b/changelogs/unreleased/todos-visibility-change.yml
@@ -0,0 +1,5 @@
+---
+title: Delete todos when user loses access to read the target
+merge_request: 20665
+author:
+type: other
diff --git a/changelogs/unreleased/todos-visibility-migration.yml b/changelogs/unreleased/todos-visibility-migration.yml
new file mode 100644
index 00000000000..651facc4ec8
--- /dev/null
+++ b/changelogs/unreleased/todos-visibility-migration.yml
@@ -0,0 +1,5 @@
+---
+title: Remove todos of users without access to targets migration
+merge_request: 20927
+author:
+type: other
diff --git a/changelogs/unreleased/toggle-password-cluster.yml b/changelogs/unreleased/toggle-password-cluster.yml
new file mode 100644
index 00000000000..1a43c4baa25
--- /dev/null
+++ b/changelogs/unreleased/toggle-password-cluster.yml
@@ -0,0 +1,5 @@
+---
+title: Toggle Show / Hide Button for Kubernetes Password
+merge_request: 20659
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/transfer_project_api_endpoint.yml b/changelogs/unreleased/transfer_project_api_endpoint.yml
deleted file mode 100644
index 60c704c62a0..00000000000
--- a/changelogs/unreleased/transfer_project_api_endpoint.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add transfer project API endpoint
-merge_request: 20122
-author: Aram Visser
-type: added
diff --git a/changelogs/unreleased/tweak-sql-buckets.yml b/changelogs/unreleased/tweak-sql-buckets.yml
new file mode 100644
index 00000000000..00a0f733ee1
--- /dev/null
+++ b/changelogs/unreleased/tweak-sql-buckets.yml
@@ -0,0 +1,5 @@
+---
+title: Add a 10 ms bucket for SQL timings
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/tz-diff-blob-image-viewer.yml b/changelogs/unreleased/tz-diff-blob-image-viewer.yml
deleted file mode 100644
index 81d87bc71f5..00000000000
--- a/changelogs/unreleased/tz-diff-blob-image-viewer.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Web IDE supports now Image + Download Diff Viewing
-merge_request: 18768
-author:
-type: added
diff --git a/changelogs/unreleased/tz-mr-port-memory-fixes.yml b/changelogs/unreleased/tz-mr-port-memory-fixes.yml
new file mode 100644
index 00000000000..61d3c9abf71
--- /dev/null
+++ b/changelogs/unreleased/tz-mr-port-memory-fixes.yml
@@ -0,0 +1,5 @@
+---
+title: Improve performance and memory footprint of Changes tab of Merge Requests
+merge_request: 21028
+author:
+type: performance
diff --git a/changelogs/unreleased/unify-views-search-results.yml b/changelogs/unreleased/unify-views-search-results.yml
deleted file mode 100644
index 81ad3616648..00000000000
--- a/changelogs/unreleased/unify-views-search-results.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Hide project name if searching against a project
-merge_request: 19595
-author:
-type: changed
diff --git a/changelogs/unreleased/update-card-body-style.yml b/changelogs/unreleased/update-card-body-style.yml
new file mode 100644
index 00000000000..d9197c18502
--- /dev/null
+++ b/changelogs/unreleased/update-card-body-style.yml
@@ -0,0 +1,5 @@
+---
+title: Remove background color from card-body style
+merge_request: 20689
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/update-environments-nav-controls.yml b/changelogs/unreleased/update-environments-nav-controls.yml
deleted file mode 100644
index 639dadd0cdf..00000000000
--- a/changelogs/unreleased/update-environments-nav-controls.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update environments nav controls icons
-merge_request: 20199
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/update-external-link-icon-in-header-user-dropdown.yml b/changelogs/unreleased/update-external-link-icon-in-header-user-dropdown.yml
deleted file mode 100644
index ee769f06379..00000000000
--- a/changelogs/unreleased/update-external-link-icon-in-header-user-dropdown.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update external link icon in header user dropdown
-merge_request: 20150
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/update-integrations-external-link-icons.yml b/changelogs/unreleased/update-integrations-external-link-icons.yml
deleted file mode 100644
index 9972744bd00..00000000000
--- a/changelogs/unreleased/update-integrations-external-link-icons.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update integrations external link icons
-merge_request: 20205
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/update-issue-closing-pattern.yml b/changelogs/unreleased/update-issue-closing-pattern.yml
new file mode 100644
index 00000000000..95488adf449
--- /dev/null
+++ b/changelogs/unreleased/update-issue-closing-pattern.yml
@@ -0,0 +1,5 @@
+---
+title: Update issue closing pattern
+merge_request: 20554
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/update-pipeline-icon-in-web-ide-sidebar.yml b/changelogs/unreleased/update-pipeline-icon-in-web-ide-sidebar.yml
deleted file mode 100644
index 3f1f3c643e2..00000000000
--- a/changelogs/unreleased/update-pipeline-icon-in-web-ide-sidebar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update pipeline icon in web ide sidebar
-merge_request: 20058
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/update-specific-runners-help-url.yml b/changelogs/unreleased/update-specific-runners-help-url.yml
new file mode 100644
index 00000000000..0ccbc3b2d65
--- /dev/null
+++ b/changelogs/unreleased/update-specific-runners-help-url.yml
@@ -0,0 +1,5 @@
+---
+title: Update specific runners help URL
+merge_request: 20213
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/upgrade-gitlab-markup.yml b/changelogs/unreleased/upgrade-gitlab-markup.yml
deleted file mode 100644
index 9b0eaa7068d..00000000000
--- a/changelogs/unreleased/upgrade-gitlab-markup.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix extra blank line at start of rendered reStructuredText code block
-merge_request: 19596
-author:
-type: fixed
diff --git a/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml b/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml
new file mode 100644
index 00000000000..39e10121507
--- /dev/null
+++ b/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml
@@ -0,0 +1,5 @@
+---
+title: 'Update hamlit to fix ruby 2.5 incompatibilities, fixes #42045'
+merge_request:
+author: Matthew Dawson
+type: fixed
diff --git a/changelogs/unreleased/use-backup-custom-hooks-gitaly.yml b/changelogs/unreleased/use-backup-custom-hooks-gitaly.yml
deleted file mode 100644
index 4b9766332c3..00000000000
--- a/changelogs/unreleased/use-backup-custom-hooks-gitaly.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: migrate backup rake task to gitaly
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/use-tooltip-component-in-mr-widget-author-time-component.yml b/changelogs/unreleased/use-tooltip-component-in-mr-widget-author-time-component.yml
deleted file mode 100644
index 4ab9b6dadc0..00000000000
--- a/changelogs/unreleased/use-tooltip-component-in-mr-widget-author-time-component.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use Tooltip component in MrWidgetAuthorTime vue comonent
-merge_request: 19635
-author: George Tsiolis
-type: performance
diff --git a/changelogs/unreleased/winh-fix-gpg-regressions.yml b/changelogs/unreleased/winh-fix-gpg-regressions.yml
new file mode 100644
index 00000000000..75d28321259
--- /dev/null
+++ b/changelogs/unreleased/winh-fix-gpg-regressions.yml
@@ -0,0 +1,5 @@
+---
+title: Fix GPG status badge loading regressions
+merge_request: 20987
+author:
+type: fixed
diff --git a/changelogs/unreleased/winh-new-branch-url-encode.yml b/changelogs/unreleased/winh-new-branch-url-encode.yml
deleted file mode 100644
index f3554d0d4a1..00000000000
--- a/changelogs/unreleased/winh-new-branch-url-encode.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix branch name encoding for dropdown on issue page
-merge_request: 19634
-author:
-type: fixed
diff --git a/changelogs/unreleased/winh-restyle-user-status.yml b/changelogs/unreleased/winh-restyle-user-status.yml
new file mode 100644
index 00000000000..90370e87825
--- /dev/null
+++ b/changelogs/unreleased/winh-restyle-user-status.yml
@@ -0,0 +1,5 @@
+---
+title: Restyle status message input on profile settings
+merge_request: 20903
+author:
+type: changed
diff --git a/changelogs/unreleased/winh-stop-all-environments.yml b/changelogs/unreleased/winh-stop-all-environments.yml
new file mode 100644
index 00000000000..6e5f2f506d9
--- /dev/null
+++ b/changelogs/unreleased/winh-stop-all-environments.yml
@@ -0,0 +1,5 @@
+---
+title: Support manually stopping any environment from the UI
+merge_request: 20077
+author:
+type: changed
diff --git a/changelogs/unreleased/winh-tree-view-gpg.yml b/changelogs/unreleased/winh-tree-view-gpg.yml
new file mode 100644
index 00000000000..84d63814a47
--- /dev/null
+++ b/changelogs/unreleased/winh-tree-view-gpg.yml
@@ -0,0 +1,5 @@
+---
+title: Display GPG status on repository and blob pages
+merge_request: 20524
+author:
+type: changed
diff --git a/changelogs/unreleased/winh-upgrade-grape-path-helpers.yml b/changelogs/unreleased/winh-upgrade-grape-path-helpers.yml
new file mode 100644
index 00000000000..62addff1d0f
--- /dev/null
+++ b/changelogs/unreleased/winh-upgrade-grape-path-helpers.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade grape-path-helpers to 1.0.6
+merge_request: 20601
+author:
+type: other
diff --git a/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml b/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml
new file mode 100644
index 00000000000..97fa1592753
--- /dev/null
+++ b/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: Wrap job name on pipeline job sidebar
+merge_request: 20804
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/zj-gitaly-read-write-check.yml b/changelogs/unreleased/zj-gitaly-read-write-check.yml
deleted file mode 100644
index 43951d20e8f..00000000000
--- a/changelogs/unreleased/zj-gitaly-read-write-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Gitaly metrics check for read/writeability
-merge_request: 20022
-author:
-type: other
diff --git a/changelogs/unreleased/zj-remove-git-rake-tasks.yml b/changelogs/unreleased/zj-remove-git-rake-tasks.yml
new file mode 100644
index 00000000000..8c90fc7d0fe
--- /dev/null
+++ b/changelogs/unreleased/zj-remove-git-rake-tasks.yml
@@ -0,0 +1,5 @@
+---
+title: Remove gitlab:user:check_repos, gitlab:check_repo, gitlab:git:prune, gitlab:git:gc, and gitlab:git:repack
+merge_request: 20806
+author:
+type: removed
diff --git a/changelogs/unreleased/zj-repository-languages.yml b/changelogs/unreleased/zj-repository-languages.yml
new file mode 100644
index 00000000000..c42ba60be29
--- /dev/null
+++ b/changelogs/unreleased/zj-repository-languages.yml
@@ -0,0 +1,5 @@
+---
+title: Show repository languages for projects
+merge_request: 19480
+author:
+type: added
diff --git a/config/application.rb b/config/application.rb
index d9483cd806d..76a2c47a750 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../boot', __FILE__)
+require File.expand_path('boot', __dir__)
require 'rails/all'
@@ -43,9 +43,12 @@ module Gitlab
#{config.root}/app/models/members
#{config.root}/app/models/project_services
#{config.root}/app/workers/concerns
+ #{config.root}/app/policies/concerns
#{config.root}/app/services/concerns
#{config.root}/app/serializers/concerns
- #{config.root}/app/finders/concerns])
+ #{config.root}/app/finders/concerns
+ #{config.root}/app/graphql/resolvers/concerns
+ #{config.root}/app/graphql/mutations/concerns])
config.generators.templates.push("#{config.root}/generator_templates")
@@ -130,7 +133,7 @@ module Gitlab
config.assets.precompile << "print.css"
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
- config.assets.precompile << "xterm/xterm.css"
+ config.assets.precompile << "page_bundles/ide.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
config.assets.precompile << "test.css"
@@ -145,11 +148,19 @@ module Gitlab
config.assets.precompile << "icons.json"
config.assets.precompile << "illustrations/*.svg"
+ # Import css for xterm
+ config.assets.paths << "#{config.root}/node_modules/xterm/src/"
+ config.assets.precompile << "xterm.css"
+
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
config.action_view.sanitized_allowed_protocols = %w(smb)
+ # This middleware needs to precede ActiveRecord::QueryCache and other middlewares that
+ # connect to the database.
+ config.middleware.insert_after "Rails::Rack::Logger", "Gitlab::Middleware::BasicHealthCheck"
+
config.middleware.insert_after Warden::Manager, Rack::Attack
# Allow access to GitLab API from other domains
@@ -210,7 +221,7 @@ module Gitlab
next unless name.include?('namespace_project')
define_method(name.sub('namespace_project', 'project')) do |project, *args|
- send(name, project&.namespace, project, *args) # rubocop:disable GitlabSecurity/PublicSend
+ send(name, project&.namespace, project, *args)
end
end
end
diff --git a/config/aws.yml.example b/config/aws.yml.example
deleted file mode 100644
index bb10c3cec7b..00000000000
--- a/config/aws.yml.example
+++ /dev/null
@@ -1,22 +0,0 @@
-# See https://github.com/jnicklas/carrierwave#using-amazon-s3
-# for more options
-# If you change this file in a Merge Request, please also create
-# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
-#
-production:
- access_key_id: AKIA1111111111111UA
- secret_access_key: secret
- bucket: mygitlab.production.us
- region: us-east-1
-
-development:
- access_key_id: AKIA1111111111111UA
- secret_access_key: secret
- bucket: mygitlab.development.us
- region: us-east-1
-
-test:
- access_key_id: AKIA1111111111111UA
- secret_access_key: secret
- bucket: mygitlab.test.us
- region: us-east-1
diff --git a/config/boot.rb b/config/boot.rb
index 84f390f3228..655c54ddb84 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -9,3 +9,8 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../#{gemfile}", __dir__)
# Set up gems listed in the Gemfile.
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
+begin
+ require 'bootsnap/setup'
+rescue LoadError
+ # bootsnap is optional dependency, so if we don't have it it's fine
+end
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index 21c20cd5e93..dce1fc1bc45 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -540,3 +540,33 @@
:why: https://github.com/xtuc/webassemblyjs/blob/master/LICENSE
:versions: []
:when: 2018-06-08 05:30:56.764116000 Z
+- - :approve
+ - "@gitlab-org/gitlab-ui"
+ - :who: Clement Ho
+ :why: Our own library
+ :versions: []
+ :when: 2018-07-17 21:02:54.529227000 Z
+- - :approve
+ - lz-string
+ - :who: Phil Hughes
+ :why: https://github.com/pieroxy/lz-string/blob/master/LICENSE.txt
+ :versions: []
+ :when: 2018-08-03 08:22:44.973457000 Z
+- - :approve
+ - smooshpack
+ - :who: Phil Hughes
+ :why: https://github.com/CompuIves/codesandbox-client/blob/master/packages/sandpack/LICENSE.md
+ :versions: []
+ :when: 2018-08-03 08:24:29.578991000 Z
+- - :approve
+ - codesandbox-import-util-types
+ - :who: Phil Hughes
+ :why: https://github.com/codesandbox-app/codesandbox-importers/blob/master/packages/types/LICENSE
+ :versions: []
+ :when: 2018-08-03 12:22:47.574421000 Z
+- - :approve
+ - codesandbox-import-utils
+ - :who: Phil Hughes
+ :why: https://github.com/codesandbox-app/codesandbox-importers/blob/master/packages/import-utils/LICENSE
+ :versions: []
+ :when: 2018-08-03 12:23:24.083046000 Z
diff --git a/config/environment.rb b/config/environment.rb
index 487a4564b47..5d35937f7c6 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -4,7 +4,7 @@
if %w[1 true].include?(ENV["RAILS5"])
require_relative 'application'
else
- require File.expand_path('../application', __FILE__)
+ require File.expand_path('application', __dir__)
end
# Initialize the rails application
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 45a8c1add3e..23790b84e3c 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -39,7 +39,7 @@ Rails.application.configure do
config.action_mailer.delivery_method = :letter_opener_web
# Don't make a mess when bootstrapping a development environment
config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1')
- config.action_mailer.preview_path = 'spec/mailers/previews'
+ config.action_mailer.preview_path = 'app/mailers/previews'
config.eager_load = false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index e0779112850..561ff57c9fb 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -78,15 +78,15 @@ production: &base
# username_changing_enabled: false # default: true - User can change her username/namespace
## Default theme ID
## 1 - Indigo
- ## 2 - Light Indigo
- ## 3 - Blue
- ## 4 - Light Blue
+ ## 2 - Dark
+ ## 3 - Light
+ ## 4 - Blue
## 5 - Green
- ## 6 - Light Green
- ## 7 - Red
- ## 8 - Light Red
- ## 9 - Dark
- ## 10 - Light
+ ## 6 - Light Indigo
+ ## 7 - Light Blue
+ ## 8 - Light Green
+ ## 9 - Red
+ ## 10 - Light Red
# default_theme: 1 # default: 1
## Automatic issue closing
@@ -160,6 +160,9 @@ production: &base
# aws_access_key_id: AWS_ACCESS_KEY_ID
# aws_secret_access_key: AWS_SECRET_ACCESS_KEY
# region: us-east-1
+ # aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
+ # endpoint: 'https://s3.amazonaws.com' # default: nil - Useful for S3 compliant services such as DigitalOcean Spaces
+
## Git LFS
lfs:
@@ -180,6 +183,7 @@ production: &base
# Use the following options to configure an AWS compatible host
# host: 'localhost' # default: s3.amazonaws.com
# endpoint: 'http://127.0.0.1:9000' # default: nil
+ # aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
# path_style: true # Use 'host/bucket_name/object' instead of 'bucket_name.host/object'
## Uploads (attachments, avatars, etc...)
@@ -197,6 +201,7 @@ production: &base
provider: AWS
aws_access_key_id: AWS_ACCESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
+ aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
region: us-east-1
# host: 'localhost' # default: s3.amazonaws.com
# endpoint: 'http://127.0.0.1:9000' # default: nil
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 550647ae1c6..9ad55e21d11 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -1,4 +1,5 @@
require_relative '../settings'
+require_relative '../object_store_settings'
# Default settings
Settings['ldap'] ||= Settingslogic.new({})
@@ -135,11 +136,12 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *, *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
Settings.gitlab['session_expire_delay'] ||= 10080
+Settings.gitlab['unauthenticated_session_expire_delay'] ||= 1.hour.to_i
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
@@ -178,14 +180,7 @@ Settings.artifacts['storage_path'] = Settings.absolute(Settings.artifacts.values
# Settings.artifact['path'] is deprecated, use `storage_path` instead
Settings.artifacts['path'] = Settings.artifacts['storage_path']
Settings.artifacts['max_size'] ||= 100 # in megabytes
-Settings.artifacts['object_store'] ||= Settingslogic.new({})
-Settings.artifacts['object_store']['enabled'] = false if Settings.artifacts['object_store']['enabled'].nil?
-Settings.artifacts['object_store']['remote_directory'] ||= nil
-Settings.artifacts['object_store']['direct_upload'] = false if Settings.artifacts['object_store']['direct_upload'].nil?
-Settings.artifacts['object_store']['background_upload'] = true if Settings.artifacts['object_store']['background_upload'].nil?
-Settings.artifacts['object_store']['proxy_download'] = false if Settings.artifacts['object_store']['proxy_download'].nil?
-# Convert upload connection settings to use string keys, to make Fog happy
-Settings.artifacts['object_store']['connection']&.deep_stringify_keys!
+Settings.artifacts['object_store'] = ObjectStoreSettings.parse(Settings.artifacts['object_store'])
#
# Registry
@@ -224,14 +219,7 @@ Settings.pages.admin['certificate'] ||= ''
Settings['lfs'] ||= Settingslogic.new({})
Settings.lfs['enabled'] = true if Settings.lfs['enabled'].nil?
Settings.lfs['storage_path'] = Settings.absolute(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects"))
-Settings.lfs['object_store'] ||= Settingslogic.new({})
-Settings.lfs['object_store']['enabled'] = false if Settings.lfs['object_store']['enabled'].nil?
-Settings.lfs['object_store']['remote_directory'] ||= nil
-Settings.lfs['object_store']['direct_upload'] = false if Settings.lfs['object_store']['direct_upload'].nil?
-Settings.lfs['object_store']['background_upload'] = true if Settings.lfs['object_store']['background_upload'].nil?
-Settings.lfs['object_store']['proxy_download'] = false if Settings.lfs['object_store']['proxy_download'].nil?
-# Convert upload connection settings to use string keys, to make Fog happy
-Settings.lfs['object_store']['connection']&.deep_stringify_keys!
+Settings.lfs['object_store'] = ObjectStoreSettings.parse(Settings.lfs['object_store'])
#
# Uploads
@@ -239,14 +227,8 @@ Settings.lfs['object_store']['connection']&.deep_stringify_keys!
Settings['uploads'] ||= Settingslogic.new({})
Settings.uploads['storage_path'] = Settings.absolute(Settings.uploads['storage_path'] || 'public')
Settings.uploads['base_dir'] = Settings.uploads['base_dir'] || 'uploads/-/system'
-Settings.uploads['object_store'] ||= Settingslogic.new({})
-Settings.uploads['object_store']['enabled'] = false if Settings.uploads['object_store']['enabled'].nil?
+Settings.uploads['object_store'] = ObjectStoreSettings.parse(Settings.uploads['object_store'])
Settings.uploads['object_store']['remote_directory'] ||= 'uploads'
-Settings.uploads['object_store']['direct_upload'] = false if Settings.uploads['object_store']['direct_upload'].nil?
-Settings.uploads['object_store']['background_upload'] = true if Settings.uploads['object_store']['background_upload'].nil?
-Settings.uploads['object_store']['proxy_download'] = false if Settings.uploads['object_store']['proxy_download'].nil?
-# Convert upload connection settings to use string keys, to make Fog happy
-Settings.uploads['object_store']['connection']&.deep_stringify_keys!
#
# Mattermost
@@ -318,10 +300,6 @@ Settings.cron_jobs['gitlab_usage_ping_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['gitlab_usage_ping_worker']['cron'] ||= Settings.__send__(:cron_for_usage_ping)
Settings.cron_jobs['gitlab_usage_ping_worker']['job_class'] = 'GitlabUsagePingWorker'
-Settings.cron_jobs['schedule_update_user_activity_worker'] ||= Settingslogic.new({})
-Settings.cron_jobs['schedule_update_user_activity_worker']['cron'] ||= '30 0 * * *'
-Settings.cron_jobs['schedule_update_user_activity_worker']['job_class'] = 'ScheduleUpdateUserActivityWorker'
-
Settings.cron_jobs['remove_old_web_hook_logs_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['remove_old_web_hook_logs_worker']['cron'] ||= '40 0 * * *'
Settings.cron_jobs['remove_old_web_hook_logs_worker']['job_class'] = 'RemoveOldWebHookLogsWorker'
@@ -338,6 +316,10 @@ Settings.cron_jobs['issue_due_scheduler_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['issue_due_scheduler_worker']['cron'] ||= '50 00 * * *'
Settings.cron_jobs['issue_due_scheduler_worker']['job_class'] = 'IssueDueSchedulerWorker'
+Settings.cron_jobs['prune_web_hook_logs_worker'] ||= Settingslogic.new({})
+Settings.cron_jobs['prune_web_hook_logs_worker']['cron'] ||= '0 */1 * * *'
+Settings.cron_jobs['prune_web_hook_logs_worker']['job_class'] = 'PruneWebHookLogsWorker'
+
#
# Sidekiq
#
@@ -436,7 +418,7 @@ Settings['extra'] ||= Settingslogic.new({})
#
Settings['rack_attack'] ||= Settingslogic.new({})
Settings.rack_attack['git_basic_auth'] ||= Settingslogic.new({})
-Settings.rack_attack.git_basic_auth['enabled'] = true if Settings.rack_attack.git_basic_auth['enabled'].nil?
+Settings.rack_attack.git_basic_auth['enabled'] = false if Settings.rack_attack.git_basic_auth['enabled'].nil?
Settings.rack_attack.git_basic_auth['ip_whitelist'] ||= %w{127.0.0.1}
Settings.rack_attack.git_basic_auth['maxretry'] ||= 10
Settings.rack_attack.git_basic_auth['findtime'] ||= 1.minute
diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb
index 8a851b89c56..4d8d35bf6cf 100644
--- a/config/initializers/8_metrics.rb
+++ b/config/initializers/8_metrics.rb
@@ -58,20 +58,6 @@ def instrument_classes(instrumentation)
instrumentation.instrument_instance_methods(const)
end
- # Instruments all Banzai filters and reference parsers
- {
- Filter: Rails.root.join('lib', 'banzai', 'filter', '*.rb'),
- ReferenceParser: Rails.root.join('lib', 'banzai', 'reference_parser', '*.rb')
- }.each do |const_name, path|
- Dir[path].each do |file|
- klass = File.basename(file, File.extname(file)).camelize
- const = Banzai.const_get(const_name).const_get(klass)
-
- instrumentation.instrument_methods(const)
- instrumentation.instrument_instance_methods(const)
- end
- end
-
instrumentation.instrument_methods(Banzai::Renderer)
instrumentation.instrument_methods(Banzai::Querying)
@@ -94,8 +80,6 @@ def instrument_classes(instrumentation)
instrumentation.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker)
- instrumentation.instrument_instance_methods(Rouge::Plugins::CommonMark)
- instrumentation.instrument_instance_methods(Rouge::Plugins::Redcarpet)
instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab)
[:XML, :HTML].each do |namespace|
diff --git a/config/initializers/action_mailer_hooks.rb b/config/initializers/action_mailer_hooks.rb
new file mode 100644
index 00000000000..f1b3c1f8ae8
--- /dev/null
+++ b/config/initializers/action_mailer_hooks.rb
@@ -0,0 +1,12 @@
+unless Gitlab.config.gitlab.email_enabled
+ ActionMailer::Base.register_interceptor(::Gitlab::Email::Hook::DisableEmailInterceptor)
+ ActionMailer::Base.logger = nil
+end
+
+ActionMailer::Base.register_interceptors(
+ ::Gitlab::Email::Hook::AdditionalHeadersInterceptor,
+ ::Gitlab::Email::Hook::EmailTemplateInterceptor,
+ ::Gitlab::Email::Hook::DeliveryMetricsObserver
+)
+
+ActionMailer::Base.register_observer(::Gitlab::Email::Hook::DeliveryMetricsObserver)
diff --git a/config/initializers/active_record_locking.rb b/config/initializers/active_record_locking.rb
index 0861544c5a4..21ff323927b 100644
--- a/config/initializers/active_record_locking.rb
+++ b/config/initializers/active_record_locking.rb
@@ -17,7 +17,7 @@ module ActiveRecord
lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
+ previous_lock_value = send(lock_col).to_i
# This line is added as a patch
previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
@@ -47,7 +47,7 @@ module ActiveRecord
# If something went wrong, revert the version.
rescue Exception
- send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
+ send(lock_col + '=', previous_lock_value)
raise
end
end
diff --git a/config/initializers/active_record_table_definition.rb b/config/initializers/active_record_table_definition.rb
index 8e3a1c7a62f..a71069f27a3 100644
--- a/config/initializers/active_record_table_definition.rb
+++ b/config/initializers/active_record_table_definition.rb
@@ -29,6 +29,11 @@ module ActiveRecord
def datetime_with_timezone(column_name, **options)
column(column_name, :datetime_with_timezone, options)
end
+
+ # Disable timestamp alias to datetime
+ def aliased_types(name, fallback)
+ fallback
+ end
end
end
end
diff --git a/config/initializers/active_record_verbose_query_logs.rb b/config/initializers/active_record_verbose_query_logs.rb
new file mode 100644
index 00000000000..44f86fec7e0
--- /dev/null
+++ b/config/initializers/active_record_verbose_query_logs.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+# This is backport of https://github.com/rails/rails/pull/26815/files
+# Enabled by default for every non-production environment
+
+module ActiveRecord
+ class LogSubscriber
+ module VerboseQueryLogs
+ def debug(progname = nil, &block)
+ return unless super
+
+ log_query_source
+ end
+
+ def log_query_source
+ source_line, line_number = extract_callstack(caller_locations)
+
+ if source_line
+ if defined?(::Rails.root)
+ app_root = "#{::Rails.root}/".freeze
+ source_line = source_line.sub(app_root, "")
+ end
+
+ logger.debug(" ↳ #{source_line}:#{line_number}")
+ end
+ end
+
+ def extract_callstack(callstack)
+ line = callstack.find do |frame|
+ frame.absolute_path && !ignored_callstack(frame.absolute_path)
+ end
+
+ offending_line = line || callstack.first
+ [
+ offending_line.path,
+ offending_line.lineno,
+ offending_line.label
+ ]
+ end
+
+ LOG_SUBSCRIBER_FILE = ActiveRecord::LogSubscriber.method(:logger).source_location.first
+ RAILS_GEM_ROOT = File.expand_path("../../../..", LOG_SUBSCRIBER_FILE) + "/"
+ APP_CONFIG_ROOT = File.expand_path("..", __dir__) + "/"
+
+ def ignored_callstack(path)
+ path.start_with?(APP_CONFIG_ROOT, RAILS_GEM_ROOT, RbConfig::CONFIG["rubylibdir"])
+ end
+ end
+
+ unless Gitlab.rails5?
+ prepend(VerboseQueryLogs) unless Rails.env.production?
+ end
+ end
+end
diff --git a/config/initializers/additional_headers_interceptor.rb b/config/initializers/additional_headers_interceptor.rb
deleted file mode 100644
index b9159e7c06c..00000000000
--- a/config/initializers/additional_headers_interceptor.rb
+++ /dev/null
@@ -1 +0,0 @@
-ActionMailer::Base.register_interceptor(AdditionalEmailHeadersInterceptor)
diff --git a/config/initializers/bootstrap_form.rb b/config/initializers/bootstrap_form.rb
index 11171b38a85..bbc1d83a63f 100644
--- a/config/initializers/bootstrap_form.rb
+++ b/config/initializers/bootstrap_form.rb
@@ -1,6 +1,6 @@
module BootstrapFormBuilderCustomization
def label_class
- "label-light"
+ "label-bold"
end
end
diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb
deleted file mode 100644
index 5cde6cbb0ff..00000000000
--- a/config/initializers/carrierwave.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
-
-aws_file = Rails.root.join('config', 'aws.yml')
-
-if File.exist?(aws_file)
- AWS_CONFIG = YAML.load(File.read(aws_file))[Rails.env]
-
- CarrierWave.configure do |config|
- config.fog_provider = 'fog/aws'
-
- config.fog_credentials = {
- provider: 'AWS', # required
- aws_access_key_id: AWS_CONFIG['access_key_id'], # required
- aws_secret_access_key: AWS_CONFIG['secret_access_key'], # required
- region: AWS_CONFIG['region'], # optional, defaults to 'us-east-1'
- }
-
- # required
- config.fog_directory = AWS_CONFIG['bucket']
-
- # optional, defaults to true
- config.fog_public = false
-
- # optional, defaults to {}
- config.fog_attributes = { 'Cache-Control' => 'max-age=315576000' }
-
- # optional time (in seconds) that authenticated urls will be valid.
- # when fog_public is false and provider is AWS or Google, defaults to 600
- config.fog_authenticated_url_expiration = 1 << 29
- end
-end
diff --git a/config/initializers/carrierwave_monkey_patch.rb b/config/initializers/carrierwave_monkey_patch.rb
deleted file mode 100644
index 7543231cd4f..00000000000
--- a/config/initializers/carrierwave_monkey_patch.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# This fixes the problem https://gitlab.com/gitlab-org/gitlab-ce/issues/46182 that carrierwave eagerly loads upoloading files into memory
-# There is an PR https://github.com/carrierwaveuploader/carrierwave/pull/2314 which has the identical change.
-module CarrierWave
- module Storage
- class Fog < Abstract
- class File
- module MonkeyPatch
- ##
- # Read content of file from service
- #
- # === Returns
- #
- # [String] contents of file
- def read
- file_body = file.body
-
- return if file_body.nil?
- return file_body unless file_body.is_a?(::File)
-
- # Fog::Storage::XXX::File#body could return the source file which was upoloaded to the remote server.
- read_source_file(file_body) if ::File.exist?(file_body.path)
-
- # If the source file doesn't exist, the remote content is read
- @file = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
- file.body
- end
-
- ##
- # Write file to service
- #
- # === Returns
- #
- # [Boolean] true on success or raises error
- def store(new_file)
- if new_file.is_a?(self.class) # rubocop:disable Cop/LineBreakAroundConditionalBlock
- new_file.copy_to(path)
- else
- fog_file = new_file.to_file
- @content_type ||= new_file.content_type # rubocop:disable Gitlab/ModuleWithInstanceVariables
- @file = directory.files.create({ # rubocop:disable Gitlab/ModuleWithInstanceVariables
- :body => fog_file ? fog_file : new_file.read, # rubocop:disable Style/HashSyntax
- :content_type => @content_type, # rubocop:disable Style/HashSyntax,Gitlab/ModuleWithInstanceVariables
- :key => path, # rubocop:disable Style/HashSyntax
- :public => @uploader.fog_public # rubocop:disable Style/HashSyntax,Gitlab/ModuleWithInstanceVariables
- }.merge(@uploader.fog_attributes)) # rubocop:disable Gitlab/ModuleWithInstanceVariables
- fog_file.close if fog_file && !fog_file.closed?
- end
- true
- end
-
- private
-
- def read_source_file(file_body)
- return unless ::File.exist?(file_body.path)
-
- begin
- file_body = ::File.open(file_body.path) if file_body.closed? # Reopen if it's already closed
- file_body.read
- ensure
- file_body.close
- end
- end
- end
-
- prepend MonkeyPatch
- end
- end
- end
-end
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index d051b699102..c41b2db722c 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -219,7 +219,7 @@ Devise.setup do |config|
end
end
- if Gitlab.config.omniauth.enabled
+ if Gitlab::Auth.omniauth_enabled?
Gitlab::OmniauthInitializer.new(config).execute(Gitlab.config.omniauth.providers)
end
end
diff --git a/config/initializers/disable_email_interceptor.rb b/config/initializers/disable_email_interceptor.rb
deleted file mode 100644
index e8770c8d460..00000000000
--- a/config/initializers/disable_email_interceptor.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# Interceptor in lib/disable_email_interceptor.rb
-unless Gitlab.config.gitlab.email_enabled
- ActionMailer::Base.register_interceptor(DisableEmailInterceptor)
- ActionMailer::Base.logger = nil
-end
diff --git a/config/initializers/email_template_interceptor.rb b/config/initializers/email_template_interceptor.rb
deleted file mode 100644
index f195ca9bcd6..00000000000
--- a/config/initializers/email_template_interceptor.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-# Interceptor in lib/email_template_interceptor.rb
-ActionMailer::Base.register_interceptor(EmailTemplateInterceptor)
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index a7fa926a853..ef23ca065c6 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -16,8 +16,3 @@ OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_s
OmniAuth.config.before_request_phase do |env|
Gitlab::RequestForgeryProtection.call(env)
end
-
-if Gitlab.config.omniauth.enabled
- provider_names = Gitlab.config.omniauth.providers.map(&:name)
- Gitlab::Auth.omniauth_setup_providers(provider_names)
-end
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index f6803eb0b5a..6f54bee4713 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -8,6 +8,8 @@ Sidekiq.default_worker_options = { retry: 3 }
enable_json_logs = Gitlab.config.sidekiq.log_format == 'json'
Sidekiq.configure_server do |config|
+ require 'rbtrace' if ENV['ENABLE_RBTRACE']
+
config.redis = queues_config_hash
config.server_middleware do |chain|
diff --git a/config/initializers/warden.rb b/config/initializers/warden.rb
index 8cc36820d3c..33f55069c3e 100644
--- a/config/initializers/warden.rb
+++ b/config/initializers/warden.rb
@@ -1,10 +1,20 @@
Rails.application.configure do |config|
Warden::Manager.after_set_user(scope: :user) do |user, auth, opts|
Gitlab::Auth::UniqueIpsLimiter.limit_user!(user)
- end
- Warden::Manager.before_failure(scope: :user) do |env, opts|
- Gitlab::Auth::BlockedUserTracker.log_if_user_blocked(env)
+ activity = Gitlab::Auth::Activity.new(opts)
+
+ case opts[:event]
+ when :authentication
+ activity.user_authenticated!
+ when :set_user
+ activity.user_authenticated!
+ activity.user_session_override!
+ when :fetch # rubocop:disable Lint/EmptyWhen
+ # We ignore session fetch events
+ else
+ activity.user_session_override!
+ end
end
Warden::Manager.after_authentication(scope: :user) do |user, auth, opts|
@@ -15,7 +25,33 @@ Rails.application.configure do |config|
ActiveSession.set(user, auth.request)
end
+ Warden::Manager.before_failure(scope: :user) do |env, opts|
+ Gitlab::Auth::Activity.new(opts).user_authentication_failed!
+ end
+
Warden::Manager.before_logout(scope: :user) do |user, auth, opts|
- ActiveSession.destroy(user || auth.user, auth.request.session.id)
+ user ||= auth.user
+ activity = Gitlab::Auth::Activity.new(opts)
+ tracker = Gitlab::Auth::BlockedUserTracker.new(user, auth)
+
+ ActiveSession.destroy(user, auth.request.session.id)
+ activity.user_session_destroyed!
+
+ ##
+ # It is possible that `before_logout` event is going to be triggered
+ # multiple times during the request lifecycle. We want to increment
+ # metrics and write logs only once in that case.
+ #
+ # 'warden.auth.*' is our custom hash key that follows usual convention
+ # of naming keys in the Rack env hash.
+ #
+ next if auth.env['warden.auth.user.blocked']
+
+ if user.blocked?
+ activity.user_blocked!
+ tracker.log_activity!
+ end
+
+ auth.env['warden.auth.user.blocked'] = true
end
end
diff --git a/config/object_store_settings.rb b/config/object_store_settings.rb
new file mode 100644
index 00000000000..d85ff394dcc
--- /dev/null
+++ b/config/object_store_settings.rb
@@ -0,0 +1,15 @@
+# Set default values for object_store settings
+class ObjectStoreSettings
+ def self.parse(object_store)
+ object_store ||= Settingslogic.new({})
+ object_store['enabled'] = false if object_store['enabled'].nil?
+ object_store['remote_directory'] ||= nil
+ object_store['direct_upload'] = false if object_store['direct_upload'].nil?
+ object_store['background_upload'] = true if object_store['background_upload'].nil?
+ object_store['proxy_download'] = false if object_store['proxy_download'].nil?
+
+ # Convert upload connection settings to use string keys, to make Fog happy
+ object_store['connection']&.deep_stringify_keys!
+ object_store
+ end
+end
diff --git a/config/routes.rb b/config/routes.rb
index e0a9139b1b4..d16a587c5ee 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -46,6 +46,7 @@ Rails.application.routes.draw do
get 'health_check(/:checks)' => 'health_check#index', as: :health_check
scope path: '-' do
+ # '/-/health' implemented by BasicHealthMiddleware
get 'liveness' => 'health#liveness'
get 'readiness' => 'health#readiness'
post 'storage_check' => 'health#storage_check'
@@ -70,6 +71,8 @@ Rails.application.routes.draw do
get 'ide' => 'ide#index'
get 'ide/*vueroute' => 'ide#index', format: false
+
+ draw :instance_statistics
end
# Koding route
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index ff27ceb50dc..7ee960970f8 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -54,7 +54,7 @@ namespace :admin do
resources :hooks, only: [:index, :create, :edit, :update, :destroy] do
member do
- get :test
+ post :test
end
resources :hook_logs, only: [:show] do
@@ -76,8 +76,6 @@ namespace :admin do
resource :system_info, controller: 'system_info', only: [:show]
resources :requests_profiles, only: [:index, :show], param: :name, constraints: { name: /.+\.html/ }
- get 'conversational_development_index' => 'conversational_development_index#show'
-
resources :projects, only: [:index]
scope(path: 'projects/*namespace_id',
@@ -123,8 +121,6 @@ namespace :admin do
end
end
- resources :cohorts, only: :index
-
resources :jobs, only: :index do
collection do
post :cancel_all
diff --git a/config/routes/group.rb b/config/routes/group.rb
index b09eb3c1b5b..25fbb38ba87 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -55,6 +55,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resources :uploads, only: [:create] do
collection do
get ":secret/:filename", action: :show, as: :show, constraints: { filename: %r{[^/]+} }
+ post :authorize
end
end
diff --git a/config/routes/import.rb b/config/routes/import.rb
index c378253bf15..3998d977c81 100644
--- a/config/routes/import.rb
+++ b/config/routes/import.rb
@@ -24,6 +24,13 @@ namespace :import do
get :jobs
end
+ resource :bitbucket_server, only: [:create, :new], controller: :bitbucket_server do
+ post :configure
+ get :status
+ get :callback
+ get :jobs
+ end
+
resource :google_code, only: [:create, :new], controller: :google_code do
get :status
post :callback
@@ -45,4 +52,10 @@ namespace :import do
resource :gitlab_project, only: [:create, :new] do
post :create
end
+
+ resource :manifest, only: [:create, :new], controller: :manifest do
+ get :status
+ get :jobs
+ post :upload
+ end
end
diff --git a/config/routes/instance_statistics.rb b/config/routes/instance_statistics.rb
new file mode 100644
index 00000000000..824ef47cda3
--- /dev/null
+++ b/config/routes/instance_statistics.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+namespace :instance_statistics do
+ root to: redirect('/-/instance_statistics/conversational_development_index')
+
+ resources :cohorts, only: :index
+ resources :conversational_development_index, only: :index
+end
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 6dfbd7ecd1f..0220e88c819 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -109,6 +109,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
post :assign_related_issues
get :discussions, format: :json
post :rebase
+ get :test_reports
scope constraints: { format: nil }, action: :show do
get :commits, defaults: { tab: 'commits' }
@@ -206,14 +207,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :clusters, except: [:edit, :create] do
collection do
- scope :providers do
- get '/user/new', to: 'clusters/user#new'
- post '/user', to: 'clusters/user#create'
-
- get '/gcp/new', to: 'clusters/gcp#new'
- get '/gcp/login', to: 'clusters/gcp#login'
- post '/gcp', to: 'clusters/gcp#create'
- end
+ post :create_gcp
+ post :create_user
end
member do
@@ -235,6 +230,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
collection do
+ get :metrics, action: :metrics_redirect
get :folder, path: 'folders/*id', constraints: { format: /(html|json)/ }
end
@@ -284,6 +280,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
post :erase
get :trace, defaults: { format: 'json' }
get :raw
+ get :terminal
+ get '/terminal.ws/authorize', to: 'jobs#terminal_websocket_authorize', constraints: { format: nil }
end
resource :artifacts, only: [] do
@@ -304,7 +302,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do
member do
- get :test
+ post :test
end
resources :hook_logs, only: [:show] do
@@ -406,6 +404,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :uploads, only: [:create] do
collection do
get ":secret/:filename", action: :show, as: :show, constraints: { filename: %r{[^/]+} }
+ post :authorize
end
end
diff --git a/config/routes/repository.rb b/config/routes/repository.rb
index e2bf8d6a7ff..d439cb9acbd 100644
--- a/config/routes/repository.rb
+++ b/config/routes/repository.rb
@@ -83,6 +83,7 @@ scope format: false do
get '/raw/*id', to: 'raw#show', as: :raw
get '/blame/*id', to: 'blame#show', as: :blame
+ get '/commits', to: 'commits#commits_root', as: :commits_root
get '/commits/*id/signatures', to: 'commits#signatures', as: :signatures
get '/commits/*id', to: 'commits#show', as: :commits
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 3400142db36..fb7738a5536 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -45,6 +45,7 @@
- [github_import_advance_stage, 1]
- [project_service, 1]
- [delete_user, 1]
+ - [todos_destroyer, 1]
- [delete_merged_branches, 1]
- [authorized_projects, 1]
- [expire_build_instance_artifacts, 1]
@@ -62,7 +63,6 @@
- [default, 1]
- [pages, 1]
- [system_hook_push, 1]
- - [update_user_activity, 1]
- [propagate_service_template, 1]
- [background_migration, 1]
- [gcp_cluster, 1]
@@ -77,4 +77,4 @@
- [repository_remove_remote, 1]
- [create_note_diff_file, 1]
- [delete_diff_files, 1]
-
+ - [detect_repository_languages, 1]
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index 220a0191160..020e9a00d87 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -67,11 +67,11 @@ pid "/home/git/gitlab/tmp/pids/unicorn.pid"
stderr_path "/home/git/gitlab/log/unicorn.stderr.log"
stdout_path "/home/git/gitlab/log/unicorn.stdout.log"
-# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
-# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
+# Save memory by sharing the application code among multiple Unicorn workers
+# with "preload_app true". See:
+# https://www.rubydoc.info/gems/unicorn/5.1.0/Unicorn%2FConfigurator:preload_app
+# https://brandur.org/ruby-memory#copy-on-write
preload_app true
-GC.respond_to?(:copy_on_write_friendly=) and
- GC.copy_on_write_friendly = true
# Enable this flag to have unicorn test client connections by writing the
# beginning of the HTTP headers before calling the application. This
@@ -124,6 +124,10 @@ before_fork do |server, worker|
end
after_fork do |server, worker|
+ # Unicorn clears out signals before it forks, so rbtrace won't work
+ # unless it is enabled after the fork.
+ require 'rbtrace' if ENV['ENABLE_RBTRACE']
+
# per-process listener ports for debugging/admin/migrations
# addr = "127.0.0.1:#{9293 + worker.nr}"
# server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
diff --git a/config/unicorn.rb.example.development b/config/unicorn.rb.example.development
index 0df028648d1..5712549a66d 100644
--- a/config/unicorn.rb.example.development
+++ b/config/unicorn.rb.example.development
@@ -1,7 +1,15 @@
worker_processes 2
timeout 60
+preload_app true
+check_client_connection false
+
before_fork do |server, worker|
+ # the following is highly recommended for Rails + "preload_app true"
+ # as there's no need for the master process to hold a connection
+ defined?(ActiveRecord::Base) and
+ ActiveRecord::Base.connection.disconnect!
+
if /darwin/ =~ RUBY_PLATFORM
require 'fiddle'
@@ -13,3 +21,12 @@ before_fork do |server, worker|
end
end
+after_fork do |server, worker|
+ # Unicorn clears out signals before it forks, so rbtrace won't work
+ # unless it is enabled after the fork.
+ require 'rbtrace' if ENV['ENABLE_RBTRACE']
+
+ # the following is *required* for Rails + "preload_app true",
+ defined?(ActiveRecord::Base) and
+ ActiveRecord::Base.establish_connection
+end
diff --git a/danger/changelog/Dangerfile b/danger/changelog/Dangerfile
new file mode 100644
index 00000000000..a1f94dc6004
--- /dev/null
+++ b/danger/changelog/Dangerfile
@@ -0,0 +1,68 @@
+# rubocop:disable Style/SignalException
+
+require 'yaml'
+
+NO_CHANGELOG_LABELS = %w[backstage Documentation QA test].freeze
+SEE_DOC = "See [the documentation](https://docs.gitlab.com/ce/development/changelog.html).".freeze
+CREATE_CHANGELOG_MESSAGE = <<~MSG.freeze
+You can create one with:
+
+```
+bin/changelog -m %<mr_iid>s "%<mr_title>s"
+```
+
+If your merge request doesn't warrant a CHANGELOG entry,
+consider adding any of the %<labels>s labels.
+#{SEE_DOC}
+MSG
+
+def ee?
+ ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('../../CHANGELOG-EE.md')
+end
+
+def ee_changelog?(changelog_path)
+ changelog_path =~ /unreleased-ee/
+end
+
+def ce_port_changelog?(changelog_path)
+ ee? && !ee_changelog?(changelog_path)
+end
+
+def check_changelog(path)
+ yaml = YAML.safe_load(File.read(path))
+
+ fail "`title` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["title"].nil?
+ fail "`type` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["type"].nil?
+
+ if yaml["merge_request"].nil?
+ message "Consider setting `merge_request` to #{gitlab.mr_json["iid"]} in #{gitlab.html_link(path)}. #{SEE_DOC}"
+ elsif yaml["merge_request"] != gitlab.mr_json["iid"] && !ce_port_changelog?(path)
+ fail "Merge request ID was not set to #{gitlab.mr_json["iid"]}! #{SEE_DOC}"
+ end
+rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias
+ # YAML could not be parsed, fail the build.
+ fail "#{gitlab.html_link(path)} isn't valid YAML! #{SEE_DOC}"
+rescue StandardError => e
+ warn "There was a problem trying to check the Changelog. Exception: #{e.name} - #{e.message}"
+end
+
+def presented_no_changelog_labels
+ NO_CHANGELOG_LABELS.map { |label| "~#{label}" }.join(', ')
+end
+
+changelog_needed = (gitlab.mr_labels & NO_CHANGELOG_LABELS).empty?
+changelog_found = git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} }
+
+if git.modified_files.include?("CHANGELOG.md")
+ fail "**CHANGELOG.md was edited.** Please remove the additions and create a CHANGELOG entry.\n\n" +
+ format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels)
+end
+
+if changelog_needed
+ if changelog_found
+ check_changelog(changelog_found)
+ else
+ warn "**[CHANGELOG missing](https://docs.gitlab.com/ce/development/changelog.html).**\n\n" +
+ format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels)
+ end
+end
diff --git a/danger/changes_size/Dangerfile b/danger/changes_size/Dangerfile
new file mode 100644
index 00000000000..8251d0d5cbf
--- /dev/null
+++ b/danger/changes_size/Dangerfile
@@ -0,0 +1,17 @@
+# FIXME: git.info_for_file raises the following error
+# /usr/local/bundle/gems/git-1.4.0/lib/git/lib.rb:956:in `command': (Danger::DSLError)
+# [!] Invalid `Dangerfile` file:
+# [!] Invalid `Dangerfile` file: git '--git-dir=/builds/gitlab-org/gitlab-ce/.git' '--work-tree=/builds/gitlab-org/gitlab-ce' cat-file '-t' '' 2>&1:fatal: Not a valid object name
+# This seems to be the same as https://github.com/danger/danger/issues/535.
+
+# locale_files_updated = git.modified_files.select { |path| path.start_with?('locale') }
+# locale_files_updated.each do |locale_file_updated|
+# git_stats = git.info_for_file(locale_file_updated)
+# message "Git stats for #{locale_file_updated}: #{git_stats[:insertions]} insertions, #{git_stats[:deletions]} insertions"
+# end
+
+if git.lines_of_code > 2_000
+ warn "This merge request is definitely too big (more than #{git.lines_of_code} lines changed), please split it into multiple merge requests."
+elsif git.lines_of_code > 500
+ warn "This merge request is quite big (more than #{git.lines_of_code} lines changed), please consider splitting it into multiple merge requests."
+end
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
new file mode 100644
index 00000000000..ad5f1c1e0f3
--- /dev/null
+++ b/danger/database/Dangerfile
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+# All the files/directories that should be reviewed by the DB team.
+DB_FILES = [
+ 'db/',
+ 'app/models/project_authorization.rb',
+ 'app/services/users/refresh_authorized_projects_service.rb',
+ 'lib/gitlab/background_migration.rb',
+ 'lib/gitlab/background_migration/',
+ 'lib/gitlab/database.rb',
+ 'lib/gitlab/database/',
+ 'lib/gitlab/github_import.rb',
+ 'lib/gitlab/github_import/',
+ 'lib/gitlab/sql/',
+ 'rubocop/cop/migration',
+ 'ee/db/',
+ 'ee/lib/gitlab/database/'
+].freeze
+
+SCHEMA_NOT_UPDATED_MESSAGE = <<~MSG
+**New %<migrations>s added but %<schema>s wasn't updated.**
+
+Usually, when adding new %<migrations>s, %<schema>s should be
+updated too (unless the migration isn't changing the DB schema
+and isn't the most recent one).
+MSG
+
+def database_paths_requiring_review(files)
+ to_review = []
+
+ files.each do |file|
+ review = DB_FILES.any? do |pattern|
+ file.start_with?(pattern)
+ end
+
+ to_review << file if review
+ end
+
+ to_review
+end
+
+all_files = git.added_files + git.modified_files
+
+non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/schema\.rb}).empty?
+geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/schema\.rb}).empty?
+
+non_geo_migration_created = !git.added_files.grep(%r{\A(db/(post_)?migrate)/}).empty?
+geo_migration_created = !git.added_files.grep(%r{\Aee/db/geo/(post_)?migrate/}).empty?
+
+if non_geo_migration_created && !non_geo_db_schema_updated
+ warn format(SCHEMA_NOT_UPDATED_MESSAGE, migrations: 'migrations', schema: gitlab.html_link("db/schema.rb"))
+end
+
+if geo_migration_created && !geo_db_schema_updated
+ warn format(SCHEMA_NOT_UPDATED_MESSAGE, migrations: 'Geo migrations', schema: gitlab.html_link("ee/db/geo/schema.rb"))
+end
+
+db_paths_to_review = database_paths_requiring_review(all_files)
+
+unless db_paths_to_review.empty?
+ message 'This merge request adds or changes files that require a ' \
+ 'review from the Database team.'
+
+ markdown(<<~MARKDOWN.strip)
+## Database Review
+
+The following files require a review from the Database team:
+
+* #{db_paths_to_review.map { |path| "`#{path}`" }.join("\n* ")}
+
+To make sure these changes are reviewed, take the following steps:
+
+1. Edit your merge request, and add `gl-database` to the list of Group
+ approvers.
+1. Mention `@gl-database` in a separate comment, and explain what needs to be
+ reviewed by the team. Please don't mention the team until your changes are
+ ready for review.
+ MARKDOWN
+
+ unless gitlab.mr_labels.include?('database')
+ warn 'This merge request is missing the ~database label.'
+ end
+end
diff --git a/danger/frozen_string/Dangerfile b/danger/frozen_string/Dangerfile
new file mode 100644
index 00000000000..b9687ef6b83
--- /dev/null
+++ b/danger/frozen_string/Dangerfile
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+FILE_EXTENSION = ".rb"
+MAGIC_COMMENT = "# frozen_string_literal: true"
+
+def get_files_with_no_magic_comment(files)
+ files.select do |file|
+ file.end_with?(FILE_EXTENSION) &&
+ !File.open(file, &:gets)&.start_with?(MAGIC_COMMENT)
+ end
+end
+
+files_to_fix = get_files_with_no_magic_comment(git.added_files)
+
+if files_to_fix.any?
+ warn 'This merge request adds files that do not enforce frozen string literal. ' \
+ 'See https://gitlab.com/gitlab-org/gitlab-ce/issues/47424 for more information.'
+
+ markdown(<<~MARKDOWN)
+ ## Enable Frozen String Literal
+
+ The following files should have `#{MAGIC_COMMENT}` on the first line:
+
+ * #{files_to_fix.map { |path| "`#{path}`" }.join("\n* ")}
+ MARKDOWN
+end
diff --git a/danger/gemfile/Dangerfile b/danger/gemfile/Dangerfile
new file mode 100644
index 00000000000..8ef4a464fe4
--- /dev/null
+++ b/danger/gemfile/Dangerfile
@@ -0,0 +1,24 @@
+GEMFILE_LOCK_NOT_UPDATED_MESSAGE = <<~MSG.freeze
+**%<gemfile>s was updated but %<gemfile_lock>s wasn't updated.**
+
+Usually, when %<gemfile>s is updated, you should run
+```
+bundle install && \
+ BUNDLE_GEMFILE=Gemfile.rails5 bundle install
+```
+
+or
+
+```
+bundle update <the-added-or-updated-gem>
+```
+
+and commit the %<gemfile_lock>s changes.
+MSG
+
+gemfile_modified = git.modified_files.include?("Gemfile")
+gemfile_lock_modified = git.modified_files.include?("Gemfile.lock")
+
+if gemfile_modified && !gemfile_lock_modified
+ warn format(GEMFILE_LOCK_NOT_UPDATED_MESSAGE, gemfile: gitlab.html_link("Gemfile"), gemfile_lock: gitlab.html_link("Gemfile.lock"))
+end
diff --git a/danger/metadata/Dangerfile b/danger/metadata/Dangerfile
new file mode 100644
index 00000000000..3cfaa04e01b
--- /dev/null
+++ b/danger/metadata/Dangerfile
@@ -0,0 +1,25 @@
+# rubocop:disable Style/SignalException
+
+if gitlab.mr_body.size < 5
+ fail "Please provide a proper merge request description."
+end
+
+if gitlab.mr_labels.empty?
+ fail "Please add labels to this merge request."
+end
+
+unless gitlab.mr_json["assignee"]
+ warn "This merge request does not have any assignee yet. Setting an assignee clarifies who needs to take action on the merge request at any given time."
+end
+
+has_milestone = !gitlab.mr_json["milestone"].nil?
+
+unless has_milestone
+ warn "This merge request does not refer to an existing milestone.", sticky: false
+end
+
+has_pick_into_stable_label = gitlab.mr_labels.find { |label| label.start_with?('Pick into') }
+
+if gitlab.branch_for_base != "master" && !has_pick_into_stable_label
+ warn "Most of the time, all merge requests should target `master`. Otherwise, please set the relevant `Pick into X.Y` label."
+end
diff --git a/danger/specs/Dangerfile b/danger/specs/Dangerfile
new file mode 100644
index 00000000000..97188df8785
--- /dev/null
+++ b/danger/specs/Dangerfile
@@ -0,0 +1,18 @@
+NO_SPECS_LABELS = %w[backstage Documentation QA].freeze
+NO_NEW_SPEC_MESSAGE = <<~MSG.freeze
+You've made some app changes, but didn't add any tests.
+That's OK as long as you're refactoring existing code,
+but please consider adding any of the %<labels>s labels.
+MSG
+
+def presented_no_changelog_labels
+ NO_SPECS_LABELS.map { |label| "~#{label}" }.join(', ')
+end
+
+has_app_changes = !git.modified_files.grep(%r{\A(ee/)?(app|lib|db/(geo/)?(post_)?migrate)/}).empty?
+has_spec_changes = !git.modified_files.grep(%r{\A(ee/)?spec/}).empty?
+new_specs_needed = (gitlab.mr_labels & NO_SPECS_LABELS).empty?
+
+if has_app_changes && !has_spec_changes && new_specs_needed
+ warn format(NO_NEW_SPEC_MESSAGE, labels: presented_no_changelog_labels), sticky: false
+end
diff --git a/db/fixtures/development/12_snippets.rb b/db/fixtures/development/12_snippets.rb
index 4f3bdba043d..a9f4069a0f8 100644
--- a/db/fixtures/development/12_snippets.rb
+++ b/db/fixtures/development/12_snippets.rb
@@ -17,7 +17,7 @@ class Member < ActiveRecord::Base
scope :guests, -> { where(access_level: GUEST) }
scope :reporters, -> { where(access_level: REPORTER) }
scope :developers, -> { where(access_level: DEVELOPER) }
- scope :masters, -> { where(access_level: MASTER) }
+ scope :maintainers, -> { where(access_level: MAINTAINER) }
scope :owners, -> { where(access_level: OWNER) }
delegate :name, :username, :email, to: :user, prefix: true
diff --git a/db/fixtures/development/19_environments.rb b/db/fixtures/development/19_environments.rb
index 00a14f458d1..3e227928a29 100644
--- a/db/fixtures/development/19_environments.rb
+++ b/db/fixtures/development/19_environments.rb
@@ -30,14 +30,14 @@ class Gitlab::Seeder::Environments
def create_merge_request_review_deployments!
@project
.merge_requests
- .select { |mr| mr.source_branch.match(/\p{Alnum}+/) }
+ .select { |mr| mr.source_branch.match?(/[a-zA-Z0-9]+/) }
.sample(4)
.each do |merge_request|
next unless merge_request.diff_head_sha
create_deployment!(
merge_request.source_project,
- "review/#{merge_request.source_branch.gsub(/[^a-zA-Z0-9]/, '')}",
+ "review/#{merge_request.source_branch.gsub(/[^a-zA-Z0-9]+/, '')}",
merge_request.source_branch,
merge_request.diff_head_sha
)
diff --git a/db/fixtures/development/20_nested_groups.rb b/db/fixtures/development/20_nested_groups.rb
index 2bc78e120a5..3d95e243f8a 100644
--- a/db/fixtures/development/20_nested_groups.rb
+++ b/db/fixtures/development/20_nested_groups.rb
@@ -1,30 +1,5 @@
require './spec/support/sidekiq'
-def create_group_with_parents(user, full_path)
- parent_path = nil
- group = nil
-
- until full_path.blank?
- path, _, full_path = full_path.partition('/')
-
- if parent_path
- parent = Group.find_by_full_path(parent_path)
-
- parent_path += '/'
- parent_path += path
-
- group = Groups::CreateService.new(user, path: path, parent_id: parent.id).execute
- else
- parent_path = path
-
- group = Group.find_by_full_path(parent_path) ||
- Groups::CreateService.new(user, path: path).execute
- end
- end
-
- group
-end
-
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
flag = 'SEED_NESTED_GROUPS'
@@ -48,7 +23,8 @@ Sidekiq::Testing.inline! do
full_path = url.sub('https://android.googlesource.com/', '')
full_path = full_path.sub(/\.git\z/, '')
full_path, _, project_path = full_path.rpartition('/')
- group = Group.find_by_full_path(full_path) || create_group_with_parents(user, full_path)
+ group = Group.find_by_full_path(full_path) ||
+ Groups::NestedCreateService.new(user, group_path: full_path).execute
params = {
import_url: url,
diff --git a/db/fixtures/development/23_spam_logs.rb b/db/fixtures/development/23_spam_logs.rb
new file mode 100644
index 00000000000..81cc13e6b2d
--- /dev/null
+++ b/db/fixtures/development/23_spam_logs.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Db
+ module Fixtures
+ module Development
+ class SpamLog
+ def self.seed
+ Gitlab::Seeder.quiet do
+ (::SpamLog.default_per_page + 3).times do |i|
+ ::SpamLog.create(
+ user: self.random_user,
+ user_agent: FFaker::Lorem.sentence,
+ source_ip: FFaker::Internet.ip_v4_address,
+ title: FFaker::Lorem.sentence,
+ description: FFaker::Lorem.paragraph,
+ via_api: FFaker::Boolean.random,
+ submitted_as_ham: FFaker::Boolean.random,
+ recaptcha_verified: FFaker::Boolean.random)
+ print '.'
+ end
+ end
+ end
+
+ def self.random_user
+ User.find(User.pluck(:id).sample)
+ end
+ end
+ end
+ end
+end
+
+Db::Fixtures::Development::SpamLog.seed
diff --git a/db/migrate/20140313092127_init_schema.rb b/db/migrate/20140313092127_init_schema.rb
index 895298ed4ed..29fb386ad76 100644
--- a/db/migrate/20140313092127_init_schema.rb
+++ b/db/migrate/20140313092127_init_schema.rb
@@ -1,6 +1,7 @@
class InitSchema < ActiveRecord::Migration
DOWNTIME = true
+ # rubocop:disable Metrics/AbcSize
def up
create_table "broadcast_messages", force: :cascade do |t|
t.text "message", null: false
@@ -157,9 +158,9 @@ class InitSchema < ActiveRecord::Migration
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree
add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree
- add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
+ add_index "notes", %w[noteable_id noteable_type], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
- add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
+ add_index "notes", %w[project_id noteable_type], name: "index_notes_on_project_id_and_noteable_type", using: :btree
add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
create_table "project_group_links", force: :cascade do |t|
t.integer "project_id", null: false
@@ -241,7 +242,7 @@ class InitSchema < ActiveRecord::Migration
t.datetime "created_at"
end
add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
- add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
+ add_index "taggings", %w[taggable_id taggable_type context], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "tags", force: :cascade do |t|
t.string "name"
end
@@ -292,7 +293,7 @@ class InitSchema < ActiveRecord::Migration
add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
- add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
+ add_index "users", %w[extern_uid provider], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
add_index "users", ["name"], name: "index_users_on_name", using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_index "users", ["username"], name: "index_users_on_username", using: :btree
diff --git a/db/migrate/20140407135544_fix_namespaces.rb b/db/migrate/20140407135544_fix_namespaces.rb
index 0026ce645a6..3d2ca5c13c4 100644
--- a/db/migrate/20140407135544_fix_namespaces.rb
+++ b/db/migrate/20140407135544_fix_namespaces.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FixNamespaces < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/migrate/20140415124820_limits_to_mysql.rb b/db/migrate/20140415124820_limits_to_mysql.rb
index c712423bcd1..3f6e62617c5 100644
--- a/db/migrate/20140415124820_limits_to_mysql.rb
+++ b/db/migrate/20140415124820_limits_to_mysql.rb
@@ -1,2 +1 @@
-# rubocop:disable all
require_relative 'limits_to_mysql'
diff --git a/db/migrate/20140729145339_migrate_project_tags.rb b/db/migrate/20140729145339_migrate_project_tags.rb
index ac46847f3e6..5760e4bfeaa 100644
--- a/db/migrate/20140729145339_migrate_project_tags.rb
+++ b/db/migrate/20140729145339_migrate_project_tags.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateProjectTags < ActiveRecord::Migration
def up
ActsAsTaggableOn::Tagging.where(taggable_type: 'Project', context: 'labels').update_all(context: 'tags')
diff --git a/db/migrate/20141121133009_add_timestamps_to_members.rb b/db/migrate/20141121133009_add_timestamps_to_members.rb
index 68f164cd35d..ef6d4dedf32 100644
--- a/db/migrate/20141121133009_add_timestamps_to_members.rb
+++ b/db/migrate/20141121133009_add_timestamps_to_members.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# In 20140914145549_migrate_to_new_members_model.rb we forgot to set the
# created_at and updated_at times for new records in the 'members' table. This
# became a problem after commit c8e78d972a5a628870eefca0f2ccea0199c55bda which
diff --git a/db/migrate/20141223135007_add_import_data_to_project_table.rb b/db/migrate/20141223135007_add_import_data_to_project_table.rb
index 9c8a483e4d5..5db78f94cc9 100644
--- a/db/migrate/20141223135007_add_import_data_to_project_table.rb
+++ b/db/migrate/20141223135007_add_import_data_to_project_table.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImportDataToProjectTable < ActiveRecord::Migration
def change
add_column :projects, :import_type, :string
diff --git a/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb b/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb
index 10e6549c729..aa179ce3a4d 100644
--- a/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb
+++ b/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddHomePageUrlForApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :home_page_url, :string
diff --git a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
index e083973615a..c28ba3197ac 100644
--- a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
+++ b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddGitlabAccessTokenToUser < ActiveRecord::Migration
def change
add_column :users, :gitlab_access_token, :string
diff --git a/db/migrate/20150206222854_add_notification_email_to_user.rb b/db/migrate/20150206222854_add_notification_email_to_user.rb
index ebae092cac8..ab80f7e582f 100644
--- a/db/migrate/20150206222854_add_notification_email_to_user.rb
+++ b/db/migrate/20150206222854_add_notification_email_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddNotificationEmailToUser < ActiveRecord::Migration
def up
add_column :users, :notification_email, :string
diff --git a/db/migrate/20150211174341_allow_null_in_services_project_id.rb b/db/migrate/20150211174341_allow_null_in_services_project_id.rb
index fea95c79adf..68f02812791 100644
--- a/db/migrate/20150211174341_allow_null_in_services_project_id.rb
+++ b/db/migrate/20150211174341_allow_null_in_services_project_id.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AllowNullInServicesProjectId < ActiveRecord::Migration
def change
change_column :services, :project_id, :integer, null: true
diff --git a/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb
index 78e9fd0c3a9..23ac1b399ec 100644
--- a/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb
+++ b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddBitbucketAccessTokenAndSecretToUser < ActiveRecord::Migration
def change
add_column :users, :bitbucket_access_token, :string
diff --git a/db/migrate/20150223022001_set_missing_last_activity_at.rb b/db/migrate/20150223022001_set_missing_last_activity_at.rb
index 300381ad65b..3f6d4d83474 100644
--- a/db/migrate/20150223022001_set_missing_last_activity_at.rb
+++ b/db/migrate/20150223022001_set_missing_last_activity_at.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class SetMissingLastActivityAt < ActiveRecord::Migration
def up
execute "UPDATE projects SET last_activity_at = updated_at WHERE last_activity_at IS NULL"
diff --git a/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb b/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb
index 7d8d65ef2ee..494c3033bff 100644
--- a/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb
+++ b/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRestrictedVisibilityLevelsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :restricted_visibility_levels, :text
diff --git a/db/migrate/20150320234437_add_location_to_user.rb b/db/migrate/20150320234437_add_location_to_user.rb
index df046570361..32731d37d75 100644
--- a/db/migrate/20150320234437_add_location_to_user.rb
+++ b/db/migrate/20150320234437_add_location_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddLocationToUser < ActiveRecord::Migration
def change
add_column :users, :location, :string
diff --git a/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb b/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb
index 9f8b6f4bd59..42dc8173e46 100644
--- a/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb
+++ b/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class SetIncorrectAssigneeIdToNull < ActiveRecord::Migration
def up
execute "UPDATE issues SET assignee_id = NULL WHERE assignee_id = -1"
diff --git a/db/migrate/20150327150017_add_import_data_to_project.rb b/db/migrate/20150327150017_add_import_data_to_project.rb
index 67b1554dfd1..12c00339eec 100644
--- a/db/migrate/20150327150017_add_import_data_to_project.rb
+++ b/db/migrate/20150327150017_add_import_data_to_project.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImportDataToProject < ActiveRecord::Migration
def change
add_column :projects, :import_data, :text
diff --git a/db/migrate/20150327223628_add_devise_two_factor_to_users.rb b/db/migrate/20150327223628_add_devise_two_factor_to_users.rb
index eccb0123e77..11b026ee8f3 100644
--- a/db/migrate/20150327223628_add_devise_two_factor_to_users.rb
+++ b/db/migrate/20150327223628_add_devise_two_factor_to_users.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDeviseTwoFactorToUsers < ActiveRecord::Migration
def change
add_column :users, :encrypted_otp_secret, :string
diff --git a/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb b/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb
index 4c56a2fb78b..1d161674a9a 100644
--- a/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb
+++ b/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMaxAttachmentSizeToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :max_attachment_size, :integer, default: 10, null: false
diff --git a/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb b/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb
index fdb6d72917e..913958db7c5 100644
--- a/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb
+++ b/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDeviseTwoFactorBackupableToUsers < ActiveRecord::Migration
def change
add_column :users, :otp_backup_codes, :text
diff --git a/db/migrate/20150411000035_fix_identities.rb b/db/migrate/20150411000035_fix_identities.rb
index a10fcc001f4..d9051f9fffd 100644
--- a/db/migrate/20150411000035_fix_identities.rb
+++ b/db/migrate/20150411000035_fix_identities.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FixIdentities < ActiveRecord::Migration
def up
# Up until now, legacy 'ldap' references in the database were charitably
diff --git a/db/migrate/20150411180045_rename_buildbox_service.rb b/db/migrate/20150411180045_rename_buildbox_service.rb
index 9f3b25c3971..5a0b5d07e50 100644
--- a/db/migrate/20150411180045_rename_buildbox_service.rb
+++ b/db/migrate/20150411180045_rename_buildbox_service.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RenameBuildboxService < ActiveRecord::Migration
def up
execute "UPDATE services SET type = 'BuildkiteService' WHERE type = 'BuildboxService';"
diff --git a/db/migrate/20150417121913_create_project_import_data.rb b/db/migrate/20150417121913_create_project_import_data.rb
index fc357cbacc8..c78f5fde85e 100644
--- a/db/migrate/20150417121913_create_project_import_data.rb
+++ b/db/migrate/20150417121913_create_project_import_data.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class CreateProjectImportData < ActiveRecord::Migration
def change
create_table :project_import_data do |t|
diff --git a/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
index 129ce4d04af..50a9b2439e0 100644
--- a/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
+++ b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDefaultProjectVisibililtyToApplicationSettings < ActiveRecord::Migration
def up
add_column :application_settings, :default_project_visibility, :integer
diff --git a/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
index 8f352414ffd..281c88d2a7d 100644
--- a/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This migration is a duplicate of 20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
# It shold be applied before the index additions to ensure that `name` is case sensitive.
diff --git a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
index 88829b87711..71f2d7f4330 100644
--- a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This migration comes from acts_as_taggable_on_engine (originally 4)
class AddMissingTaggableIndex < ActiveRecord::Migration
def self.up
diff --git a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
index 642c4745321..bfb06bc7cda 100644
--- a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This migration comes from acts_as_taggable_on_engine (originally 5)
# This migration is added to circumvent issue #623 and have special characters
# work properly
diff --git a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
index dd13def4176..8f1b0cc8935 100644
--- a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
+++ b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDefaultSnippetVisibilityToAppSettings < ActiveRecord::Migration
def up
add_column :application_settings, :default_snippet_visibility, :integer
diff --git a/db/migrate/20150429002313_remove_abandoned_group_members_records.rb b/db/migrate/20150429002313_remove_abandoned_group_members_records.rb
index d2c7f3c442e..244637e1c4a 100644
--- a/db/migrate/20150429002313_remove_abandoned_group_members_records.rb
+++ b/db/migrate/20150429002313_remove_abandoned_group_members_records.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveAbandonedGroupMembersRecords < ActiveRecord::Migration
def up
execute("DELETE FROM members WHERE type = 'GroupMember' AND source_id NOT IN(\
diff --git a/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb b/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb
index b63ea9aec7a..184e2653610 100644
--- a/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb
+++ b/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRestrictedSignupDomainsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :restricted_signup_domains, :text
diff --git a/db/migrate/20150509180749_convert_legacy_reference_notes.rb b/db/migrate/20150509180749_convert_legacy_reference_notes.rb
index cd8bf90108d..b02605489be 100644
--- a/db/migrate/20150509180749_convert_legacy_reference_notes.rb
+++ b/db/migrate/20150509180749_convert_legacy_reference_notes.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# Convert legacy Markdown-emphasized notes to the current, non-emphasized format
#
# _mentioned in 54f7727c850972f0401c1312a7c4a6a380de5666_
diff --git a/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
index 9b02eda56ab..6a78294f0b2 100644
--- a/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
+++ b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddUserOauthApplicationsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :user_oauth_applications, :bool, default: true
diff --git a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
index 1f5cf1fe5f1..61ff0af41f4 100644
--- a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
+++ b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration
def change
unless column_exists?(:application_settings, :session_expire_delay)
diff --git a/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb b/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb
index da0fd457a34..8eed8678b2f 100644
--- a/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb
+++ b/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDefaultOtpRequiredForLoginValue < ActiveRecord::Migration
def up
execute %q{UPDATE users SET otp_required_for_login = FALSE WHERE otp_required_for_login IS NULL}
diff --git a/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb b/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb
index be30e881c74..78d45c7f96b 100644
--- a/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb
+++ b/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddUpdatedByToIssuablesAndNotes < ActiveRecord::Migration
def change
add_column :notes, :updated_by_id, :integer
diff --git a/db/migrate/20150818213832_add_sent_notifications.rb b/db/migrate/20150818213832_add_sent_notifications.rb
index fa0c3ce0acf..43e8d6a1a82 100644
--- a/db/migrate/20150818213832_add_sent_notifications.rb
+++ b/db/migrate/20150818213832_add_sent_notifications.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSentNotifications < ActiveRecord::Migration
def change
create_table :sent_notifications do |t|
diff --git a/db/migrate/20150915001905_enable_ssl_verification_by_default.rb b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb
index 3f070139418..6e924262a13 100644
--- a/db/migrate/20150915001905_enable_ssl_verification_by_default.rb
+++ b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class EnableSslVerificationByDefault < ActiveRecord::Migration
def change
change_column :web_hooks, :enable_ssl_verification, :boolean, default: true
diff --git a/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb
index ea2ab6e4093..90ce6c2db3d 100644
--- a/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb
+++ b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class EnableSslVerificationForWebHooks < ActiveRecord::Migration
def up
execute("UPDATE web_hooks SET enable_ssl_verification = true")
diff --git a/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb
index a504f25b1be..37a27f11935 100644
--- a/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb
+++ b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddHelpPageTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :help_page_text, :text
diff --git a/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb
index c9b6e035122..6cf668a170e 100644
--- a/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb
+++ b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddCiEnabledToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :ci_enabled, :boolean, null: false, default: true
diff --git a/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb
index e1818b566d7..0aad6fe5e6e 100644
--- a/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb
+++ b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveInvalidMilestonesFromMergeRequests < ActiveRecord::Migration
def up
execute("UPDATE merge_requests SET milestone_id = NULL where milestone_id NOT IN (SELECT id FROM milestones)")
diff --git a/db/migrate/20150920010715_add_consumed_timestep_to_users.rb b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb
index e6975f5b9fe..c8438b3f6aa 100644
--- a/db/migrate/20150920010715_add_consumed_timestep_to_users.rb
+++ b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddConsumedTimestepToUsers < ActiveRecord::Migration
def change
add_column :users, :consumed_timestep, :integer
diff --git a/db/migrate/20150920161119_add_line_code_to_sent_notification.rb b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb
index 1bcb06e4bda..d9af4e71751 100644
--- a/db/migrate/20150920161119_add_line_code_to_sent_notification.rb
+++ b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddLineCodeToSentNotification < ActiveRecord::Migration
def change
add_column :sent_notifications, :line_code, :string
diff --git a/db/migrate/20150924125150_add_project_id_to_ci_commit.rb b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb
index 905332b7dc7..1a761fe0f86 100644
--- a/db/migrate/20150924125150_add_project_id_to_ci_commit.rb
+++ b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddProjectIdToCiCommit < ActiveRecord::Migration
def up
add_column :ci_commits, :gl_project_id, :integer
diff --git a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb
index fb0e0ba1fa5..2be57b6062e 100644
--- a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb
+++ b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateProjectIdForCiCommits < ActiveRecord::Migration
def up
subquery = 'SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = ci_commits.project_id'
diff --git a/db/migrate/20150930001110_merge_request_error_field.rb b/db/migrate/20150930001110_merge_request_error_field.rb
index 71a8ae3938a..c2ee498ef3f 100644
--- a/db/migrate/20150930001110_merge_request_error_field.rb
+++ b/db/migrate/20150930001110_merge_request_error_field.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MergeRequestErrorField < ActiveRecord::Migration
def up
add_column :merge_requests, :merge_error, :string
diff --git a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb
index 229c9942b50..8d47dac6441 100644
--- a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb
+++ b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddNullToNameForCiProjects < ActiveRecord::Migration
def up
change_column_null :ci_projects, :name, true
diff --git a/db/migrate/20151002112914_add_stage_idx_to_builds.rb b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
index 4297ba0e7c8..68a745ffef4 100644
--- a/db/migrate/20151002112914_add_stage_idx_to_builds.rb
+++ b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddStageIdxToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :stage_idx, :integer
diff --git a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
index 3c0fcf6c45d..e3d2ac1cea5 100644
--- a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
+++ b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRefAndTagToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :tag, :boolean
diff --git a/db/migrate/20151005075649_add_user_id_to_build.rb b/db/migrate/20151005075649_add_user_id_to_build.rb
index be9d403e002..0f4b92b8b79 100644
--- a/db/migrate/20151005075649_add_user_id_to_build.rb
+++ b/db/migrate/20151005075649_add_user_id_to_build.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddUserIdToBuild < ActiveRecord::Migration
def change
add_column :ci_builds, :user_id, :integer
diff --git a/db/migrate/20151008143519_add_admin_notification_email_setting.rb b/db/migrate/20151008143519_add_admin_notification_email_setting.rb
index f48ec9aa4a6..0bb581efe2c 100644
--- a/db/migrate/20151008143519_add_admin_notification_email_setting.rb
+++ b/db/migrate/20151008143519_add_admin_notification_email_setting.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddAdminNotificationEmailSetting < ActiveRecord::Migration
def change
add_column :application_settings, :admin_notification_email, :string
diff --git a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
index a54ac9d57a4..5a299f7b26d 100644
--- a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
+++ b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddArtifactsFileToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :artifacts_file, :text
diff --git a/db/migrate/20151019111551_fix_build_tags.rb b/db/migrate/20151019111551_fix_build_tags.rb
index 8c05acfc190..299a24b0a7c 100644
--- a/db/migrate/20151019111551_fix_build_tags.rb
+++ b/db/migrate/20151019111551_fix_build_tags.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FixBuildTags < ActiveRecord::Migration
def up
execute("UPDATE taggings SET taggable_type='CommitStatus' WHERE taggable_type='Ci::Build'")
diff --git a/db/migrate/20151019111703_fail_build_without_names.rb b/db/migrate/20151019111703_fail_build_without_names.rb
index 362e31eb435..dcdb5d1b25d 100644
--- a/db/migrate/20151019111703_fail_build_without_names.rb
+++ b/db/migrate/20151019111703_fail_build_without_names.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FailBuildWithoutNames < ActiveRecord::Migration
def up
execute("UPDATE ci_builds SET status='failed' WHERE name IS NULL AND status='pending'")
diff --git a/db/migrate/20151020173516_ci_limits_to_mysql.rb b/db/migrate/20151020173516_ci_limits_to_mysql.rb
index 5314611cbcd..9bb960082f5 100644
--- a/db/migrate/20151020173516_ci_limits_to_mysql.rb
+++ b/db/migrate/20151020173516_ci_limits_to_mysql.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class CiLimitsToMysql < ActiveRecord::Migration
def change
return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
diff --git a/db/migrate/20151023112551_fail_build_with_empty_name.rb b/db/migrate/20151023112551_fail_build_with_empty_name.rb
index 0666dfeaef4..41c0f0649cd 100644
--- a/db/migrate/20151023112551_fail_build_with_empty_name.rb
+++ b/db/migrate/20151023112551_fail_build_with_empty_name.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FailBuildWithEmptyName < ActiveRecord::Migration
def up
execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'")
diff --git a/db/migrate/20151023144219_remove_satellites.rb b/db/migrate/20151023144219_remove_satellites.rb
index 98fe0bd7d1d..e73f300028a 100644
--- a/db/migrate/20151023144219_remove_satellites.rb
+++ b/db/migrate/20151023144219_remove_satellites.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
require 'fileutils'
class RemoveSatellites < ActiveRecord::Migration
diff --git a/db/migrate/20151103133339_add_shared_runners_setting.rb b/db/migrate/20151103133339_add_shared_runners_setting.rb
index b5b34d4ca61..4231dfd5c2e 100644
--- a/db/migrate/20151103133339_add_shared_runners_setting.rb
+++ b/db/migrate/20151103133339_add_shared_runners_setting.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSharedRunnersSetting < ActiveRecord::Migration
def up
add_column :application_settings, :shared_runners_enabled, :boolean, default: true, null: false
diff --git a/db/migrate/20151104105513_add_file_to_lfs_objects.rb b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
index 4e46ae8101c..7c57f3f0df6 100644
--- a/db/migrate/20151104105513_add_file_to_lfs_objects.rb
+++ b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddFileToLfsObjects < ActiveRecord::Migration
def change
add_column :lfs_objects, :file, :string
diff --git a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
index 25106ace7e9..01d8c0f043e 100644
--- a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
+++ b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMaxArtifactsSizeToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :max_artifacts_size, :integer, default: 100, null: false
diff --git a/db/migrate/20151110125604_add_import_error_to_project.rb b/db/migrate/20151110125604_add_import_error_to_project.rb
index 793358c305e..7fc990f8d0a 100644
--- a/db/migrate/20151110125604_add_import_error_to_project.rb
+++ b/db/migrate/20151110125604_add_import_error_to_project.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImportErrorToProject < ActiveRecord::Migration
def change
add_column :projects, :import_error, :text
diff --git a/db/migrate/20151201203948_raise_hook_url_limit.rb b/db/migrate/20151201203948_raise_hook_url_limit.rb
index c490b7ace0f..98a7fca6f6f 100644
--- a/db/migrate/20151201203948_raise_hook_url_limit.rb
+++ b/db/migrate/20151201203948_raise_hook_url_limit.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RaiseHookUrlLimit < ActiveRecord::Migration
def change
change_column :web_hooks, :url, :string, limit: 2000
diff --git a/db/migrate/20151209144329_migrate_ci_web_hooks.rb b/db/migrate/20151209144329_migrate_ci_web_hooks.rb
index 62a6d334f04..e1e4729f821 100644
--- a/db/migrate/20151209144329_migrate_ci_web_hooks.rb
+++ b/db/migrate/20151209144329_migrate_ci_web_hooks.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiWebHooks < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb
index 5de7b205fb1..e1d92f0157e 100644
--- a/db/migrate/20151209145909_migrate_ci_emails.rb
+++ b/db/migrate/20151209145909_migrate_ci_emails.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiEmails < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20151210030143_add_unlock_token_to_user.rb b/db/migrate/20151210030143_add_unlock_token_to_user.rb
index d23c648f782..0ea66ba65df 100644
--- a/db/migrate/20151210030143_add_unlock_token_to_user.rb
+++ b/db/migrate/20151210030143_add_unlock_token_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddUnlockTokenToUser < ActiveRecord::Migration
def change
add_column :users, :unlock_token, :string
diff --git a/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb
index 92c7b5befd2..00f88180e46 100644
--- a/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb
+++ b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRunnersRegistrationTokenToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :runners_registration_token, :string
diff --git a/db/migrate/20151210125232_migrate_ci_slack_service.rb b/db/migrate/20151210125232_migrate_ci_slack_service.rb
index fff130b7b10..e6dca4c0008 100644
--- a/db/migrate/20151210125232_migrate_ci_slack_service.rb
+++ b/db/migrate/20151210125232_migrate_ci_slack_service.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiSlackService < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
index 824f6f84195..72fcebf2959 100644
--- a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
+++ b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiHipChatService < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20151210125929_add_project_id_to_ci.rb b/db/migrate/20151210125929_add_project_id_to_ci.rb
index b5de64b82ca..84273591fa2 100644
--- a/db/migrate/20151210125929_add_project_id_to_ci.rb
+++ b/db/migrate/20151210125929_add_project_id_to_ci.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddProjectIdToCi < ActiveRecord::Migration
def change
add_column :ci_builds, :gl_project_id, :integer
diff --git a/db/migrate/20151210125930_migrate_ci_to_project.rb b/db/migrate/20151210125930_migrate_ci_to_project.rb
index bb6d74ae212..c32c7feb193 100644
--- a/db/migrate/20151210125930_migrate_ci_to_project.rb
+++ b/db/migrate/20151210125930_migrate_ci_to_project.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiToProject < ActiveRecord::Migration
def up
migrate_project_id_for_table('ci_runner_projects')
diff --git a/db/migrate/20151218154042_add_tfa_to_application_settings.rb b/db/migrate/20151218154042_add_tfa_to_application_settings.rb
index afdaf76b917..dd95db775c5 100644
--- a/db/migrate/20151218154042_add_tfa_to_application_settings.rb
+++ b/db/migrate/20151218154042_add_tfa_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddTfaToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
diff --git a/db/migrate/20151221234414_add_tfa_additional_fields.rb b/db/migrate/20151221234414_add_tfa_additional_fields.rb
index c3e4aaa606a..c16df47932f 100644
--- a/db/migrate/20151221234414_add_tfa_additional_fields.rb
+++ b/db/migrate/20151221234414_add_tfa_additional_fields.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddTfaAdditionalFields < ActiveRecord::Migration
def change
change_table :users do |t|
diff --git a/db/migrate/20151224123230_rename_emojis.rb b/db/migrate/20151224123230_rename_emojis.rb
index 2c24f3beeea..62d921dfdcc 100644
--- a/db/migrate/20151224123230_rename_emojis.rb
+++ b/db/migrate/20151224123230_rename_emojis.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# Migration type: online without errors (works on previous version and new one)
class RenameEmojis < ActiveRecord::Migration
def up
diff --git a/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb
index e0dd19b2b06..259fd0248d2 100644
--- a/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb
+++ b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRecaptchaToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
diff --git a/db/migrate/20151229102248_influxdb_udp_port_setting.rb b/db/migrate/20151229102248_influxdb_udp_port_setting.rb
index 3e1bfd43899..ae0499f936d 100644
--- a/db/migrate/20151229102248_influxdb_udp_port_setting.rb
+++ b/db/migrate/20151229102248_influxdb_udp_port_setting.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class InfluxdbUdpPortSetting < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_port, :integer, default: 8089
diff --git a/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
index 4fcca06d905..6c282fc5039 100644
--- a/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
+++ b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddArtifactsMetadataToCiBuild < ActiveRecord::Migration
def change
add_column :ci_builds, :artifacts_metadata, :text
diff --git a/db/migrate/20151231152326_add_akismet_to_application_settings.rb b/db/migrate/20151231152326_add_akismet_to_application_settings.rb
index 7b0fab6f557..3f52c758f9a 100644
--- a/db/migrate/20151231152326_add_akismet_to_application_settings.rb
+++ b/db/migrate/20151231152326_add_akismet_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddAkismetToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
diff --git a/db/migrate/20160113111034_add_metrics_sample_interval.rb b/db/migrate/20160113111034_add_metrics_sample_interval.rb
index c1041da818c..b741f5d2c75 100644
--- a/db/migrate/20160113111034_add_metrics_sample_interval.rb
+++ b/db/migrate/20160113111034_add_metrics_sample_interval.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMetricsSampleInterval < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_sample_interval, :integer,
diff --git a/db/migrate/20160118155830_add_sentry_to_application_settings.rb b/db/migrate/20160118155830_add_sentry_to_application_settings.rb
index a6f715263ef..fa7ff9d9228 100644
--- a/db/migrate/20160118155830_add_sentry_to_application_settings.rb
+++ b/db/migrate/20160118155830_add_sentry_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSentryToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
diff --git a/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb b/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb
index 3837208f81e..d6c6aa4a4e8 100644
--- a/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb
+++ b/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddBaseCommitShaToMergeRequestDiffs < ActiveRecord::Migration
def change
add_column :merge_request_diffs, :base_commit_sha, :string
diff --git a/db/migrate/20160128233227_change_lfs_objects_size_column.rb b/db/migrate/20160128233227_change_lfs_objects_size_column.rb
index 645c0cdb192..e7fd1f71777 100644
--- a/db/migrate/20160128233227_change_lfs_objects_size_column.rb
+++ b/db/migrate/20160128233227_change_lfs_objects_size_column.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class ChangeLfsObjectsSizeColumn < ActiveRecord::Migration
def change
change_column :lfs_objects, :size, :integer, limit: 8
diff --git a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
index b10c0602e24..d3ea956952e 100644
--- a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
+++ b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveDotAtomPathEndingOfProjects < ActiveRecord::Migration
include Gitlab::ShellAdapter
diff --git a/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb b/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb
index 332b5a756e8..f0d94226514 100644
--- a/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb
+++ b/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMergeCommitShaToMergeRequests < ActiveRecord::Migration
def change
add_column :merge_requests, :merge_commit_sha, :string
diff --git a/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
index 11b6ff31000..f996ae74dca 100644
--- a/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
+++ b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRealSizeToMergeRequestDiffs < ActiveRecord::Migration
def change
add_column :merge_request_diffs, :real_size, :string
diff --git a/db/migrate/20160217100506_add_description_to_label.rb b/db/migrate/20160217100506_add_description_to_label.rb
index af5af167470..eed6d1f236a 100644
--- a/db/migrate/20160217100506_add_description_to_label.rb
+++ b/db/migrate/20160217100506_add_description_to_label.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDescriptionToLabel < ActiveRecord::Migration
def change
add_column :labels, :description, :string
diff --git a/db/migrate/20160217174422_add_note_to_tasks.rb b/db/migrate/20160217174422_add_note_to_tasks.rb
index a9a2b77e423..da5cb2e05db 100644
--- a/db/migrate/20160217174422_add_note_to_tasks.rb
+++ b/db/migrate/20160217174422_add_note_to_tasks.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddNoteToTasks < ActiveRecord::Migration
def change
add_reference :tasks, :note, index: true
diff --git a/db/migrate/20160220123949_rename_tasks_to_todos.rb b/db/migrate/20160220123949_rename_tasks_to_todos.rb
index f16b37537f3..30c10d27146 100644
--- a/db/migrate/20160220123949_rename_tasks_to_todos.rb
+++ b/db/migrate/20160220123949_rename_tasks_to_todos.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RenameTasksToTodos < ActiveRecord::Migration
def change
rename_table :tasks, :todos
diff --git a/db/migrate/20160229193553_add_main_language_to_repository.rb b/db/migrate/20160229193553_add_main_language_to_repository.rb
index ad5167b4c93..b5446c6a447 100644
--- a/db/migrate/20160229193553_add_main_language_to_repository.rb
+++ b/db/migrate/20160229193553_add_main_language_to_repository.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMainLanguageToRepository < ActiveRecord::Migration
def change
add_column :projects, :main_language, :string
diff --git a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb
index 1f400566f9f..ffcd64266e3 100644
--- a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb
+++ b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImportCredentialsToProjectImportData < ActiveRecord::Migration
def change
add_column :project_import_data, :encrypted_credentials, :text
diff --git a/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb b/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb
index 10f2b8cc56a..49e787d9a9a 100644
--- a/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb
+++ b/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class DisallowBlankLineCodeOnNote < ActiveRecord::Migration
def up
execute("UPDATE notes SET line_code = NULL WHERE line_code = ''")
diff --git a/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
index 65e0e61c78f..6871b3920df 100644
--- a/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
+++ b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class ChangeTargetIdToNullOnTodos < ActiveRecord::Migration
def change
change_column_null :todos, :target_id, true
diff --git a/db/migrate/20160317092222_add_moved_to_to_issue.rb b/db/migrate/20160317092222_add_moved_to_to_issue.rb
index 9dde668ddff..2bf549d7ecd 100644
--- a/db/migrate/20160317092222_add_moved_to_to_issue.rb
+++ b/db/migrate/20160317092222_add_moved_to_to_issue.rb
@@ -1,6 +1,5 @@
-# rubocop:disable all
class AddMovedToToIssue < ActiveRecord::Migration
def change
- add_reference :issues, :moved_to, references: :issues
+ add_reference :issues, :moved_to, references: :issues # rubocop:disable Migration/AddReference
end
end
diff --git a/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
index a9a851cfe63..1fff9759d1e 100644
--- a/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
+++ b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveTodosForDeletedIssues < ActiveRecord::Migration
def up
execute <<-SQL
diff --git a/db/migrate/20160328115649_migrate_new_notification_setting.rb b/db/migrate/20160328115649_migrate_new_notification_setting.rb
index eb6b7d07219..3c81b2c37bf 100644
--- a/db/migrate/20160328115649_migrate_new_notification_setting.rb
+++ b/db/migrate/20160328115649_migrate_new_notification_setting.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This migration will create one row of NotificationSetting for each Member row
# It can take long time on big instances.
#
diff --git a/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
index b15af79b9b5..54cea964ff2 100644
--- a/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
+++ b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveTodosForDeletedMergeRequests < ActiveRecord::Migration
def up
execute <<-SQL
diff --git a/db/migrate/20160407120251_add_images_enabled_for_project.rb b/db/migrate/20160407120251_add_images_enabled_for_project.rb
index fcffc98b47a..47f0ca8e8de 100644
--- a/db/migrate/20160407120251_add_images_enabled_for_project.rb
+++ b/db/migrate/20160407120251_add_images_enabled_for_project.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImagesEnabledForProject < ActiveRecord::Migration
def change
add_column :projects, :container_registry_enabled, :boolean
diff --git a/db/migrate/20160413115152_add_token_to_web_hooks.rb b/db/migrate/20160413115152_add_token_to_web_hooks.rb
index 628b1d51b30..f04225068cd 100644
--- a/db/migrate/20160413115152_add_token_to_web_hooks.rb
+++ b/db/migrate/20160413115152_add_token_to_web_hooks.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddTokenToWebHooks < ActiveRecord::Migration
def change
add_column :web_hooks, :token, :string
diff --git a/db/migrate/20160415062917_create_personal_access_tokens.rb b/db/migrate/20160415062917_create_personal_access_tokens.rb
index c7b49870bf7..94650026994 100644
--- a/db/migrate/20160415062917_create_personal_access_tokens.rb
+++ b/db/migrate/20160415062917_create_personal_access_tokens.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class CreatePersonalAccessTokens < ActiveRecord::Migration
def change
diff --git a/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb
index b53b9bc6c3d..d493044c67b 100644
--- a/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb
+++ b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSharedRunnersTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :shared_runners_text, :text
diff --git a/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb b/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb
index 95ee03611d9..50f159a80b1 100644
--- a/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb
+++ b/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class ConvertAwardNoteToEmojiAward < ActiveRecord::Migration
disable_ddl_transaction!
diff --git a/db/migrate/20160419120017_add_metrics_packet_size.rb b/db/migrate/20160419120017_add_metrics_packet_size.rb
index c759427c590..78c163d62ac 100644
--- a/db/migrate/20160419120017_add_metrics_packet_size.rb
+++ b/db/migrate/20160419120017_add_metrics_packet_size.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMetricsPacketSize < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_packet_size, :integer, default: 1
diff --git a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
index bf50616656c..facd33875ba 100644
--- a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
+++ b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDisabledOauthSignInSourcesToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :disabled_oauth_sign_in_sources, :text
diff --git a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb
index c60892a6279..84e5e4eabe2 100644
--- a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb
+++ b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRunUntaggedToCiRunner < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
diff --git a/db/migrate/20160508215820_add_type_to_notes.rb b/db/migrate/20160508215820_add_type_to_notes.rb
index c1d07c9363f..58944d4e651 100644
--- a/db/migrate/20160508215820_add_type_to_notes.rb
+++ b/db/migrate/20160508215820_add_type_to_notes.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddTypeToNotes < ActiveRecord::Migration
def change
add_column :notes, :type, :string
diff --git a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
index b6a5bea79b6..9d729fec189 100644
--- a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
+++ b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddHealthCheckAccessTokenToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :health_check_access_token, :string
diff --git a/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb b/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb
index 3e26be7c09c..7910120b4e0 100644
--- a/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb
+++ b/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveNotificationSettingsForDeletedProjects < ActiveRecord::Migration
def up
execute <<-SQL
diff --git a/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb b/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb
index d811fd5271e..e21376bd571 100644
--- a/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb
+++ b/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This is ONLINE migration
class AddContainerRegistryTokenExpireDelayToApplicationSettings < ActiveRecord::Migration
diff --git a/db/migrate/20160603180330_remove_duplicated_notification_settings.rb b/db/migrate/20160603180330_remove_duplicated_notification_settings.rb
index 4f4f58b1619..fe1c863b5b9 100644
--- a/db/migrate/20160603180330_remove_duplicated_notification_settings.rb
+++ b/db/migrate/20160603180330_remove_duplicated_notification_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveDuplicatedNotificationSettings < ActiveRecord::Migration
def up
duplicates = exec_query(%Q{
diff --git a/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb b/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb
index 3c5d2ad910e..89826fb96cb 100644
--- a/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb
+++ b/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddAfterSignUpTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :after_sign_up_text, :text
diff --git a/db/migrate/20160610204157_add_deployments.rb b/db/migrate/20160610204157_add_deployments.rb
index 0e7e6e747a3..0ee0b1f5a86 100644
--- a/db/migrate/20160610204157_add_deployments.rb
+++ b/db/migrate/20160610204157_add_deployments.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable Migration/Datetime
class AddDeployments < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160610204158_add_environments.rb b/db/migrate/20160610204158_add_environments.rb
index 699cee2b246..534a73a5fb6 100644
--- a/db/migrate/20160610204158_add_environments.rb
+++ b/db/migrate/20160610204158_add_environments.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable Migration/Datetime
class AddEnvironments < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb b/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb
index 013904b3f4f..d0e6d8d1ea1 100644
--- a/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb
+++ b/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb
@@ -1,6 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-# rubocop:disable all
class AddEnabledGitAccessProtocolsToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160616102642_remove_duplicated_keys.rb b/db/migrate/20160616102642_remove_duplicated_keys.rb
index 180a75e0998..5e41cc53e32 100644
--- a/db/migrate/20160616102642_remove_duplicated_keys.rb
+++ b/db/migrate/20160616102642_remove_duplicated_keys.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveDuplicatedKeys < ActiveRecord::Migration
def up
select_all("SELECT fingerprint FROM #{quote_table_name(:keys)} GROUP BY fingerprint HAVING COUNT(*) > 1").each do |row|
diff --git a/db/migrate/20160705054938_add_protected_branches_push_access.rb b/db/migrate/20160705054938_add_protected_branches_push_access.rb
index 97aaaf9d2c8..de3aefcb1fb 100644
--- a/db/migrate/20160705054938_add_protected_branches_push_access.rb
+++ b/db/migrate/20160705054938_add_protected_branches_push_access.rb
@@ -9,7 +9,7 @@ class AddProtectedBranchesPushAccess < ActiveRecord::Migration
create_table :protected_branch_push_access_levels do |t|
t.references :protected_branch, index: { name: "index_protected_branch_push_access" }, foreign_key: true, null: false
- # Gitlab::Access::MASTER == 40
+ # Gitlab::Access::MAINTAINER == 40
t.integer :access_level, default: 40, null: false
t.timestamps null: false
diff --git a/db/migrate/20160705054952_add_protected_branches_merge_access.rb b/db/migrate/20160705054952_add_protected_branches_merge_access.rb
index 51a52a5ac17..9b18a2061b3 100644
--- a/db/migrate/20160705054952_add_protected_branches_merge_access.rb
+++ b/db/migrate/20160705054952_add_protected_branches_merge_access.rb
@@ -9,7 +9,7 @@ class AddProtectedBranchesMergeAccess < ActiveRecord::Migration
create_table :protected_branch_merge_access_levels do |t|
t.references :protected_branch, index: { name: "index_protected_branch_merge_access" }, foreign_key: true, null: false
- # Gitlab::Access::MASTER == 40
+ # Gitlab::Access::MAINTAINER == 40
t.integer :access_level, default: 40, null: false
t.timestamps null: false
diff --git a/db/migrate/20160824124900_add_table_issue_metrics.rb b/db/migrate/20160824124900_add_table_issue_metrics.rb
index 30d35ef1db2..49be8bc949b 100644
--- a/db/migrate/20160824124900_add_table_issue_metrics.rb
+++ b/db/migrate/20160824124900_add_table_issue_metrics.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class AddTableIssueMetrics < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160825052008_add_table_merge_request_metrics.rb b/db/migrate/20160825052008_add_table_merge_request_metrics.rb
index 56b39634dfd..3c9dcc08190 100644
--- a/db/migrate/20160825052008_add_table_merge_request_metrics.rb
+++ b/db/migrate/20160825052008_add_table_merge_request_metrics.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class AddTableMergeRequestMetrics < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20161113184239_create_user_chat_names_table.rb b/db/migrate/20161113184239_create_user_chat_names_table.rb
index 62ccb599f2e..7bead07fd76 100644
--- a/db/migrate/20161113184239_create_user_chat_names_table.rb
+++ b/db/migrate/20161113184239_create_user_chat_names_table.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class CreateUserChatNamesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
index dc16d5c5169..1eb6a8fa5df 100644
--- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
+++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
@@ -1,9 +1,19 @@
-# See http://doc.gitlab.com/ce/development/migration_style_guide.html
-# for more information on how to write migrations for GitLab.
-
class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
+ class Repository
+ attr_reader :storage
+
+ def initialize(storage, relative_path)
+ @storage = storage
+ @relative_path = relative_path
+ end
+
+ def gitaly_repository
+ Gitaly::Repository.new(storage_name: @storage, relative_path: @relative_path)
+ end
+ end
+
class Project < ActiveRecord::Base
def self.find_including_path(id)
select("projects.*, CONCAT(namespaces.path, '/', projects.path) AS path_with_namespace")
@@ -11,19 +21,12 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration
.find_by(id: id)
end
- def repository_storage_path
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
- end
- end
-
- def repository_path
- # TODO: review if the change from Legacy storage needs to reflect here as well.
- File.join(repository_storage_path, read_attribute(:path_with_namespace) + '.git')
+ def commit(rev)
+ Gitlab::GitalyClient::CommitService.new(repository).find_commit(rev)
end
def repository
- @repository ||= Rugged::Repository.new(repository_path)
+ @repository ||= Repository.new(repository_storage, read_attribute(:path_with_namespace) + '.git')
end
end
@@ -42,22 +45,19 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration
next unless project
- begin
- commit = project.repository.lookup(payload['args'][2])
- rescue Rugged::OdbError
- next
- end
+ commit = project.commit(payload['args'][2])
+ next unless commit
hash = {
- id: commit.oid,
- message: encode(commit.message),
- parent_ids: commit.parent_ids,
- authored_date: commit.author[:time],
- author_name: encode(commit.author[:name]),
- author_email: encode(commit.author[:email]),
- committed_date: commit.committer[:time],
- committer_email: encode(commit.committer[:email]),
- committer_name: encode(commit.committer[:name])
+ id: commit.id,
+ message: encode(commit.body),
+ parent_ids: commit.parent_ids.to_a,
+ authored_date: Time.at(commit.author.date.seconds).utc,
+ author_name: encode(commit.author.name),
+ author_email: encode(commit.author.email),
+ committed_date: Time.at(commit.committer.date.seconds).utc,
+ committer_email: encode(commit.committer.email),
+ committer_name: encode(commit.committer.name)
}
payload['args'][2] = hash
diff --git a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
index 133435523e1..db10426b483 100644
--- a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
+++ b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
@@ -1,11 +1,7 @@
-# See http://doc.gitlab.com/ce/development/migration_style_guide.html
-# for more information on how to write migrations for GitLab.
-
class RemoveDotGitFromUsernames < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::ShellAdapter
- # Set this constant to true if this migration requires downtime.
DOWNTIME = false
def up
@@ -64,16 +60,14 @@ class RemoveDotGitFromUsernames < ActiveRecord::Migration
# we rename suffix instead of removing it
path = path.sub(/\.git\z/, '_git')
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- check_routes(path.dup, 0, path)
- end
+ check_routes(path.dup, 0, path)
end
def check_routes(base, counter, path)
route_exists = route_exists?(path)
- Gitlab.config.repositories.storages.each do |shard, storage|
- if route_exists || path_exists?(shard, storage.legacy_disk_path)
+ Gitlab.config.repositories.storages.each do |shard, _storage|
+ if route_exists || path_exists?(shard, path)
counter += 1
path = "#{base}#{counter}"
diff --git a/db/migrate/20170130221926_create_uploads.rb b/db/migrate/20170130221926_create_uploads.rb
index 4d9fa0bb692..6f06c5dd840 100644
--- a/db/migrate/20170130221926_create_uploads.rb
+++ b/db/migrate/20170130221926_create_uploads.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
class CreateUploads < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170222143317_drop_ci_projects.rb b/db/migrate/20170222143317_drop_ci_projects.rb
index 9973e53501c..4db8658f36f 100644
--- a/db/migrate/20170222143317_drop_ci_projects.rb
+++ b/db/migrate/20170222143317_drop_ci_projects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
class DropCiProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb b/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb
index 0535c2ddaf2..ee802ab34ca 100644
--- a/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb
+++ b/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb
@@ -1,5 +1,4 @@
# rubocop:disable Migration/RemoveColumn
-# rubocop:disable Migration/Datetime
class RemoveUnusedCiTablesAndColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170329095907_create_ci_trigger_schedules.rb b/db/migrate/20170329095907_create_ci_trigger_schedules.rb
index 06a2010db23..cfcfa27ebb5 100644
--- a/db/migrate/20170329095907_create_ci_trigger_schedules.rb
+++ b/db/migrate/20170329095907_create_ci_trigger_schedules.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
class CreateCiTriggerSchedules < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb
index 84635fa39b9..42c90103262 100644
--- a/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb
+++ b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable RemoveIndex
class RemoveIndexForUsersCurrentSignInAt < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170425112128_create_pipeline_schedules_table.rb b/db/migrate/20170425112128_create_pipeline_schedules_table.rb
index 4f9c56a1ad8..bd15b9eef19 100644
--- a/db/migrate/20170425112128_create_pipeline_schedules_table.rb
+++ b/db/migrate/20170425112128_create_pipeline_schedules_table.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class CreatePipelineSchedulesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb b/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb
index 008a94d8334..1c5d4997d40 100644
--- a/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb
+++ b/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb
@@ -1,2 +1 @@
-# rubocop:disable all
require_relative 'markdown_cache_limits_to_mysql'
diff --git a/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb b/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb
index 470c3b8166c..f858611d24b 100644
--- a/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb
+++ b/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb
@@ -6,6 +6,7 @@ class RenameUsersLdapEmailToExternalEmail < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :users, :ldap_email, :external_email
end
diff --git a/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb b/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb
index ad540b1e509..0f1e937545b 100644
--- a/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb
+++ b/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb
@@ -10,6 +10,7 @@ class AddTimezoneToIssuesClosedAt < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateLargeTable
change_column_type_concurrently(:issues, :closed_at, :datetime_with_timezone)
end
diff --git a/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb b/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb
index 5a36dec6a9a..36a85b61968 100644
--- a/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb
+++ b/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb
@@ -25,6 +25,7 @@ class MigrateRemainingIssuesClosedAt < ActiveRecord::Migration
# Due to some EE merge problems some environments may not have the
# "closed_at_for_type_change" column. If this is the case we have no
# other option than to migrate the data _right now_.
+ # rubocop:disable Migration/UpdateLargeTable
change_column_type_concurrently(:issues, :closed_at, :datetime_with_timezone)
cleanup_concurrent_column_type_change(:issues, :closed_at)
end
diff --git a/db/migrate/20180214093516_create_badges.rb b/db/migrate/20180214093516_create_badges.rb
index 6559f834484..a1d77328f77 100644
--- a/db/migrate/20180214093516_create_badges.rb
+++ b/db/migrate/20180214093516_create_badges.rb
@@ -12,6 +12,7 @@ class CreateBadges < ActiveRecord::Migration
t.timestamps_with_timezone null: false
end
+ # rubocop:disable Migration/AddConcurrentForeignKey
add_foreign_key :badges, :namespaces, column: :group_id, on_delete: :cascade
end
end
diff --git a/db/migrate/20180227182112_add_group_id_to_boards_ce.rb b/db/migrate/20180227182112_add_group_id_to_boards_ce.rb
index f54dd8d7687..5b2691b3a00 100644
--- a/db/migrate/20180227182112_add_group_id_to_boards_ce.rb
+++ b/db/migrate/20180227182112_add_group_id_to_boards_ce.rb
@@ -8,6 +8,7 @@ class AddGroupIdToBoardsCe < ActiveRecord::Migration
def up
return if group_id_exists?
+ # rubocop:disable Migration/AddConcurrentForeignKey
add_column :boards, :group_id, :integer
add_foreign_key :boards, :namespaces, column: :group_id, on_delete: :cascade
add_concurrent_index :boards, :group_id
@@ -18,6 +19,7 @@ class AddGroupIdToBoardsCe < ActiveRecord::Migration
def down
return unless group_id_exists?
+ # rubocop:disable Migration/RemoveIndex
remove_foreign_key :boards, column: :group_id
remove_index :boards, :group_id if index_exists? :boards, :group_id
remove_column :boards, :group_id
diff --git a/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb b/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb
index 8298979e96a..e6cec39e61f 100644
--- a/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb
+++ b/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb
@@ -8,6 +8,7 @@ class EnsureForeignKeysOnClustersApplications < ActiveRecord::Migration
disable_ddl_transaction!
+ # rubocop:disable Cop/InBatches
def up
existing = Clusters::Cluster
.joins(:application_ingress)
diff --git a/db/migrate/20180309160427_add_partial_indexes_on_todos.rb b/db/migrate/20180309160427_add_partial_indexes_on_todos.rb
index 18a5c69df1b..671fa743cec 100644
--- a/db/migrate/20180309160427_add_partial_indexes_on_todos.rb
+++ b/db/migrate/20180309160427_add_partial_indexes_on_todos.rb
@@ -7,15 +7,16 @@ class AddPartialIndexesOnTodos < ActiveRecord::Migration
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
- disable_ddl_transaction!
+ disable_ddl_transaction!
+
+ INDEX_NAME_PENDING = "index_todos_on_user_id_and_id_pending"
+ INDEX_NAME_DONE = "index_todos_on_user_id_and_id_done"
- INDEX_NAME_PENDING="index_todos_on_user_id_and_id_pending"
- INDEX_NAME_DONE="index_todos_on_user_id_and_id_done"
-
def up
unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_PENDING)
add_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING)
end
+
unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_DONE)
add_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE)
end
@@ -24,5 +25,5 @@ class AddPartialIndexesOnTodos < ActiveRecord::Migration
def down
remove_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING)
remove_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE)
- end
+ end
end
diff --git a/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb b/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb
index 007cbebaf1b..e852d50b25e 100644
--- a/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb
+++ b/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb
@@ -6,6 +6,7 @@ class RenameUsersRssTokenToFeedToken < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :users, :rss_token, :feed_token
end
diff --git a/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb b/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb
index 582b89a3948..58de795472a 100644
--- a/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb
+++ b/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb
@@ -26,6 +26,7 @@ class AddIndexConstraintsToInternalIdTable < ActiveRecord::Migration
end
private
+
def replace_index(table, columns, name:)
temporary_name = "#{name}_old"
diff --git a/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb b/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb
index 1084ca14a34..ac91624c3d5 100644
--- a/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb
+++ b/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb
@@ -10,6 +10,7 @@ class AddIndexToCiJobArtifactsFileStore < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :ci_job_artifacts, :file_store if index_exists?(:ci_job_artifacts, :file_store)
end
end
diff --git a/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb b/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb
index 306cd737771..f5afdb0e4e6 100644
--- a/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb
+++ b/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb
@@ -4,6 +4,7 @@ class AddEnforceTermsToApplicationSettings < ActiveRecord::Migration
DOWNTIME = false
def change
+ # rubocop:disable Migration/SaferBooleanColumn
add_column :application_settings, :enforce_terms, :boolean, default: false
end
end
diff --git a/db/migrate/20180425075446_create_term_agreements.rb b/db/migrate/20180425075446_create_term_agreements.rb
index 22a9d7b574d..1fa2c8dd3be 100644
--- a/db/migrate/20180425075446_create_term_agreements.rb
+++ b/db/migrate/20180425075446_create_term_agreements.rb
@@ -21,6 +21,7 @@ class CreateTermAgreements < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :term_agreements, name: 'term_agreements_unique_index'
drop_table :term_agreements
diff --git a/db/migrate/20180503131624_create_remote_mirrors.rb b/db/migrate/20180503131624_create_remote_mirrors.rb
index 7800186455f..249882f8613 100644
--- a/db/migrate/20180503131624_create_remote_mirrors.rb
+++ b/db/migrate/20180503131624_create_remote_mirrors.rb
@@ -23,6 +23,7 @@ class CreateRemoteMirrors < ActiveRecord::Migration
t.string :encrypted_credentials_iv
t.string :encrypted_credentials_salt
+ # rubocop:disable Migration/Timestamps
t.timestamps null: false
end
end
diff --git a/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb b/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb
index 4c4e576d49f..9e55690bd33 100644
--- a/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb
+++ b/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb
@@ -14,6 +14,7 @@ class AddIndexToNamespacesRunnersToken < ActiveRecord::Migration
def down
if index_exists?(:namespaces, :runners_token, unique: true)
+ # rubocop:disable Migration/RemoveIndex
remove_index :namespaces, :runners_token
end
end
diff --git a/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb b/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb
index 17570269b2e..b59b941c815 100644
--- a/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb
+++ b/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb
@@ -11,6 +11,7 @@ class AddIndexesToProjectMirrorData < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :project_mirror_data, :jid if index_exists? :project_mirror_data, :jid
remove_index :project_mirror_data, :status if index_exists? :project_mirror_data, :status
end
diff --git a/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb b/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb
index 9a9decffdab..4af42b4fb29 100644
--- a/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb
+++ b/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb
@@ -10,6 +10,7 @@ class AddIndexesToRemoteMirror < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :remote_mirrors, :last_successful_update_at if index_exists? :remote_mirrors, :last_successful_update_at
end
end
diff --git a/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb b/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb
index 580f56007c7..f3ed20fd243 100644
--- a/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb
+++ b/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb
@@ -10,6 +10,7 @@ class AddIndexOnCiRunnersRunnerType < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :ci_runners, :runner_type
end
end
diff --git a/db/migrate/20180515005612_add_squash_to_merge_requests.rb b/db/migrate/20180515005612_add_squash_to_merge_requests.rb
index f526b45bd4b..fd85e968acd 100644
--- a/db/migrate/20180515005612_add_squash_to_merge_requests.rb
+++ b/db/migrate/20180515005612_add_squash_to_merge_requests.rb
@@ -9,6 +9,7 @@ class AddSquashToMergeRequests < ActiveRecord::Migration
def up
unless column_exists?(:merge_requests, :squash)
+ # rubocop:disable Migration/UpdateLargeTable
add_column_with_default :merge_requests, :squash, :boolean, default: false, allow_null: false
end
end
diff --git a/db/migrate/20180515121227_create_notes_diff_files.rb b/db/migrate/20180515121227_create_notes_diff_files.rb
index 7108bc1a64b..efcd3bb9c7e 100644
--- a/db/migrate/20180515121227_create_notes_diff_files.rb
+++ b/db/migrate/20180515121227_create_notes_diff_files.rb
@@ -16,6 +16,7 @@ class CreateNotesDiffFiles < ActiveRecord::Migration
t.text :old_path, null: false
end
+ # rubocop:disable Migration/AddConcurrentForeignKey
add_foreign_key :note_diff_files, :notes, column: :diff_note_id, on_delete: :cascade
end
end
diff --git a/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb b/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb
index df84898003f..08ce8cc3094 100644
--- a/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb
+++ b/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb
@@ -1,2 +1 @@
-# rubocop:disable all
require_relative 'gpg_keys_limits_to_mysql'
diff --git a/db/migrate/20180529093006_ensure_remote_mirror_columns.rb b/db/migrate/20180529093006_ensure_remote_mirror_columns.rb
index 290416cb61c..22e9482cb1d 100644
--- a/db/migrate/20180529093006_ensure_remote_mirror_columns.rb
+++ b/db/migrate/20180529093006_ensure_remote_mirror_columns.rb
@@ -6,6 +6,7 @@ class EnsureRemoteMirrorColumns < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/Datetime
add_column :remote_mirrors, :last_update_started_at, :datetime unless column_exists?(:remote_mirrors, :last_update_started_at)
add_column :remote_mirrors, :remote_name, :string unless column_exists?(:remote_mirrors, :remote_name)
diff --git a/db/migrate/20180531185349_add_repository_languages.rb b/db/migrate/20180531185349_add_repository_languages.rb
new file mode 100644
index 00000000000..bfcfb618c87
--- /dev/null
+++ b/db/migrate/20180531185349_add_repository_languages.rb
@@ -0,0 +1,28 @@
+class AddRepositoryLanguages < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ create_table(:programming_languages) do |t|
+ t.string :name, null: false
+ t.string :color, null: false
+ t.datetime_with_timezone :created_at, null: false
+ end
+
+ create_table(:repository_languages, id: false) do |t|
+ t.references :project, null: false, foreign_key: { on_delete: :cascade }
+ t.references :programming_language, null: false
+ t.float :share, null: false
+ end
+
+ add_index :programming_languages, :name, unique: true
+ add_index :repository_languages, [:project_id, :programming_language_id],
+ unique: true, name: "index_repository_languages_on_project_and_languages_id"
+ end
+
+ def down
+ drop_table :repository_languages
+ drop_table :programming_languages
+ end
+end
diff --git a/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb b/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb
index d0dcacc5b66..dbbbcd1f622 100644
--- a/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb
+++ b/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb
@@ -4,13 +4,13 @@ class ChangeDefaultValueForDsaKeyRestriction < ActiveRecord::Migration
def up
change_column :application_settings, :dsa_key_restriction, :integer, null: false,
- default: -1
+ default: -1
execute("UPDATE application_settings SET dsa_key_restriction = -1")
end
def down
change_column :application_settings, :dsa_key_restriction, :integer, null: false,
- default: 0
+ default: 0
end
end
diff --git a/db/migrate/20180608091413_add_group_to_todos.rb b/db/migrate/20180608091413_add_group_to_todos.rb
new file mode 100644
index 00000000000..20ba4849057
--- /dev/null
+++ b/db/migrate/20180608091413_add_group_to_todos.rb
@@ -0,0 +1,36 @@
+class AddGroupToTodos < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class Todo < ActiveRecord::Base
+ self.table_name = 'todos'
+
+ include ::EachBatch
+ end
+
+ def up
+ add_column(:todos, :group_id, :integer) unless group_id_exists?
+ add_concurrent_foreign_key :todos, :namespaces, column: :group_id, on_delete: :cascade
+ add_concurrent_index :todos, :group_id
+
+ change_column_null :todos, :project_id, true
+ end
+
+ def down
+ remove_foreign_key_without_error(:todos, column: :group_id)
+ remove_concurrent_index(:todos, :group_id)
+ remove_column(:todos, :group_id) if group_id_exists?
+
+ Todo.where(project_id: nil).each_batch { |batch| batch.delete_all }
+ change_column_null :todos, :project_id, false
+ end
+
+ private
+
+ def group_id_exists?
+ column_exists?(:todos, :group_id)
+ end
+end
diff --git a/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb b/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb
index dcbbef9bd4a..36f2a593fbe 100644
--- a/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb
+++ b/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb
@@ -11,6 +11,7 @@ class RenameMergeRequestsAllowCollaboration < ActiveRecord::Migration
def up
if column_exists?(:merge_requests, :allow_collaboration)
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
end
end
diff --git a/db/migrate/20180612103626_add_columns_for_helm_tiller_certificates.rb b/db/migrate/20180612103626_add_columns_for_helm_tiller_certificates.rb
new file mode 100644
index 00000000000..57cea18abcd
--- /dev/null
+++ b/db/migrate/20180612103626_add_columns_for_helm_tiller_certificates.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+class AddColumnsForHelmTillerCertificates < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :clusters_applications_helm, :encrypted_ca_key, :text
+ add_column :clusters_applications_helm, :encrypted_ca_key_iv, :text
+ add_column :clusters_applications_helm, :ca_cert, :text
+ end
+end
diff --git a/db/migrate/20180613081317_create_ci_builds_runner_session.rb b/db/migrate/20180613081317_create_ci_builds_runner_session.rb
new file mode 100644
index 00000000000..e550c07b9ab
--- /dev/null
+++ b/db/migrate/20180613081317_create_ci_builds_runner_session.rb
@@ -0,0 +1,21 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class CreateCiBuildsRunnerSession < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def change
+ create_table :ci_builds_runner_session, id: :bigserial do |t|
+ t.integer :build_id, null: false
+ t.string :url, null: false
+ t.string :certificate
+ t.string :authorization
+
+ t.foreign_key :ci_builds, column: :build_id, on_delete: :cascade
+ t.index :build_id, unique: true
+ end
+ end
+end
diff --git a/db/migrate/20180625113853_create_import_export_uploads.rb b/db/migrate/20180625113853_create_import_export_uploads.rb
new file mode 100644
index 00000000000..be42304b0ae
--- /dev/null
+++ b/db/migrate/20180625113853_create_import_export_uploads.rb
@@ -0,0 +1,16 @@
+class CreateImportExportUploads < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ create_table :import_export_uploads do |t|
+ t.datetime_with_timezone :updated_at, null: false
+
+ t.references :project, index: true, foreign_key: { on_delete: :cascade }, unique: true
+
+ t.text :import_file
+ t.text :export_file
+ end
+
+ add_index :import_export_uploads, :updated_at
+ end
+end
diff --git a/db/migrate/20180628124813_alter_web_hook_logs_indexes.rb b/db/migrate/20180628124813_alter_web_hook_logs_indexes.rb
new file mode 100644
index 00000000000..1878e76811d
--- /dev/null
+++ b/db/migrate/20180628124813_alter_web_hook_logs_indexes.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AlterWebHookLogsIndexes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ # "created_at" comes first so the Sidekiq worker pruning old webhook logs can
+ # use a composite index index.
+ #
+ # We leave the old standalone index on "web_hook_id" in place so future code
+ # that doesn't care about "created_at" can still use that index.
+ COLUMNS_TO_INDEX = %i[created_at web_hook_id]
+
+ def up
+ add_concurrent_index(:web_hook_logs, COLUMNS_TO_INDEX)
+ end
+
+ def down
+ remove_concurrent_index(:web_hook_logs, COLUMNS_TO_INDEX)
+ end
+end
diff --git a/db/migrate/20180629153018_create_site_statistics.rb b/db/migrate/20180629153018_create_site_statistics.rb
new file mode 100644
index 00000000000..085ce1ba64b
--- /dev/null
+++ b/db/migrate/20180629153018_create_site_statistics.rb
@@ -0,0 +1,18 @@
+class CreateSiteStatistics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ create_table :site_statistics do |t|
+ t.integer :repositories_count, default: 0, null: false
+ t.integer :wikis_count, default: 0, null: false
+ end
+
+ execute('INSERT INTO site_statistics (id) VALUES(1)')
+ end
+
+ def down
+ drop_table :site_statistics
+ end
+end
diff --git a/db/migrate/20180702124358_remove_orphaned_routes.rb b/db/migrate/20180702124358_remove_orphaned_routes.rb
new file mode 100644
index 00000000000..6f6e289ba87
--- /dev/null
+++ b/db/migrate/20180702124358_remove_orphaned_routes.rb
@@ -0,0 +1,49 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveOrphanedRoutes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class Route < ActiveRecord::Base
+ self.table_name = 'routes'
+ include EachBatch
+
+ def self.orphaned_namespace_routes
+ where(source_type: 'Namespace')
+ .where('NOT EXISTS ( SELECT 1 FROM namespaces WHERE namespaces.id = routes.source_id )')
+ end
+
+ def self.orphaned_project_routes
+ where(source_type: 'Project')
+ .where('NOT EXISTS ( SELECT 1 FROM projects WHERE projects.id = routes.source_id )')
+ end
+ end
+
+ def up
+ # Some of these queries can take up to 10 seconds to run on GitLab.com,
+ # which is pretty close to our 15 second statement timeout. To ensure a
+ # smooth deployment procedure we disable the statement timeouts for this
+ # migration, just in case.
+ disable_statement_timeout
+
+ # On GitLab.com there are around 4000 orphaned project routes, and around
+ # 150 orphaned namespace routes.
+ [
+ Route.orphaned_project_routes,
+ Route.orphaned_namespace_routes
+ ].each do |relation|
+ relation.each_batch(of: 1_000) do |batch|
+ batch.delete_all
+ end
+ end
+ end
+
+ def down
+ # There is no way to restore orphaned routes, and this doesn't make any
+ # sense anyway.
+ end
+end
diff --git a/db/migrate/20180702134423_generate_missing_routes.rb b/db/migrate/20180702134423_generate_missing_routes.rb
new file mode 100644
index 00000000000..994725f9bd1
--- /dev/null
+++ b/db/migrate/20180702134423_generate_missing_routes.rb
@@ -0,0 +1,143 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+# This migration generates missing routes for any projects and namespaces that
+# don't already have a route.
+#
+# On GitLab.com this would insert 611 project routes, and 0 namespace routes.
+# The exact number could vary per instance, so we take care of both just in
+# case.
+class GenerateMissingRoutes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class User < ActiveRecord::Base
+ self.table_name = 'users'
+ end
+
+ class Route < ActiveRecord::Base
+ self.table_name = 'routes'
+ end
+
+ module Routable
+ def build_full_path
+ if parent && path
+ parent.build_full_path + '/' + path
+ else
+ path
+ end
+ end
+
+ def build_full_name
+ if parent && name
+ parent.human_name + ' / ' + name
+ else
+ name
+ end
+ end
+
+ def human_name
+ build_full_name
+ end
+
+ def attributes_for_insert
+ time = Time.zone.now
+
+ {
+ # We can't use "self.class.name" here as that would include the
+ # migration namespace.
+ source_type: source_type_for_route,
+ source_id: id,
+ created_at: time,
+ updated_at: time,
+ name: build_full_name,
+
+ # The route path might already be taken. Instead of trying to generate a
+ # new unique name on every conflict, we just append the row ID to the
+ # route path.
+ path: "#{build_full_path}-#{id}"
+ }
+ end
+ end
+
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+
+ include EachBatch
+ include GenerateMissingRoutes::Routable
+
+ belongs_to :namespace, class_name: 'GenerateMissingRoutes::Namespace'
+
+ has_one :route,
+ as: :source,
+ inverse_of: :source,
+ class_name: 'GenerateMissingRoutes::Route'
+
+ alias_method :parent, :namespace
+ alias_attribute :parent_id, :namespace_id
+
+ def self.without_routes
+ where(
+ 'NOT EXISTS (
+ SELECT 1
+ FROM routes
+ WHERE source_type = ?
+ AND source_id = projects.id
+ )',
+ 'Project'
+ )
+ end
+
+ def source_type_for_route
+ 'Project'
+ end
+ end
+
+ class Namespace < ActiveRecord::Base
+ self.table_name = 'namespaces'
+
+ include EachBatch
+ include GenerateMissingRoutes::Routable
+
+ belongs_to :parent, class_name: 'GenerateMissingRoutes::Namespace'
+ belongs_to :owner, class_name: 'GenerateMissingRoutes::User'
+
+ has_one :route,
+ as: :source,
+ inverse_of: :source,
+ class_name: 'GenerateMissingRoutes::Route'
+
+ def self.without_routes
+ where(
+ 'NOT EXISTS (
+ SELECT 1
+ FROM routes
+ WHERE source_type = ?
+ AND source_id = namespaces.id
+ )',
+ 'Namespace'
+ )
+ end
+
+ def source_type_for_route
+ 'Namespace'
+ end
+ end
+
+ def up
+ [Namespace, Project].each do |model|
+ model.without_routes.each_batch(of: 100) do |batch|
+ rows = batch.map(&:attributes_for_insert)
+
+ Gitlab::Database.bulk_insert(:routes, rows)
+ end
+ end
+ end
+
+ def down
+ # Removing routes we previously generated makes no sense.
+ end
+end
diff --git a/db/migrate/20180704204006_add_hide_third_party_offers_to_application_settings.rb b/db/migrate/20180704204006_add_hide_third_party_offers_to_application_settings.rb
new file mode 100644
index 00000000000..6631c5d1b6c
--- /dev/null
+++ b/db/migrate/20180704204006_add_hide_third_party_offers_to_application_settings.rb
@@ -0,0 +1,18 @@
+class AddHideThirdPartyOffersToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default(:application_settings, :hide_third_party_offers,
+ :boolean,
+ default: false,
+ allow_null: false)
+ end
+
+ def down
+ remove_column(:application_settings, :hide_third_party_offers)
+ end
+end
diff --git a/db/migrate/20180705160945_add_file_format_to_ci_job_artifacts.rb b/db/migrate/20180705160945_add_file_format_to_ci_job_artifacts.rb
new file mode 100644
index 00000000000..63c188693f3
--- /dev/null
+++ b/db/migrate/20180705160945_add_file_format_to_ci_job_artifacts.rb
@@ -0,0 +1,7 @@
+class AddFileFormatToCiJobArtifacts < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :ci_job_artifacts, :file_format, :integer, limit: 2
+ end
+end
diff --git a/db/migrate/20180710162338_add_foreign_key_from_notification_settings_to_users.rb b/db/migrate/20180710162338_add_foreign_key_from_notification_settings_to_users.rb
new file mode 100644
index 00000000000..91656f194e5
--- /dev/null
+++ b/db/migrate/20180710162338_add_foreign_key_from_notification_settings_to_users.rb
@@ -0,0 +1,30 @@
+class AddForeignKeyFromNotificationSettingsToUsers < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ class NotificationSetting < ActiveRecord::Base
+ self.table_name = 'notification_settings'
+
+ include EachBatch
+ end
+
+ class User < ActiveRecord::Base
+ self.table_name = 'users'
+ end
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ NotificationSetting.each_batch(of: 1000) do |batch|
+ batch.where('NOT EXISTS (?)', User.select(1).where('users.id = notification_settings.user_id'))
+ .delete_all
+ end
+
+ add_concurrent_foreign_key(:notification_settings, :users, column: :user_id, on_delete: :cascade)
+ end
+
+ def down
+ remove_foreign_key(:notification_settings, column: :user_id)
+ end
+end
diff --git a/db/migrate/20180713092803_create_user_statuses.rb b/db/migrate/20180713092803_create_user_statuses.rb
new file mode 100644
index 00000000000..cbe21b89ad9
--- /dev/null
+++ b/db/migrate/20180713092803_create_user_statuses.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class CreateUserStatuses < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :user_statuses, id: false, primary_key: :user_id do |t|
+ t.references :user,
+ foreign_key: { on_delete: :cascade },
+ null: false,
+ primary_key: true
+ t.integer :cached_markdown_version, limit: 4
+ t.string :emoji, null: false, default: 'speech_balloon'
+ t.string :message, limit: 100
+ t.string :message_html
+ end
+ end
+end
diff --git a/db/migrate/20180717125853_remove_restricted_todos.rb b/db/migrate/20180717125853_remove_restricted_todos.rb
new file mode 100644
index 00000000000..fdf43921a73
--- /dev/null
+++ b/db/migrate/20180717125853_remove_restricted_todos.rb
@@ -0,0 +1,31 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+# frozen_string_literal: true
+
+class RemoveRestrictedTodos < ActiveRecord::Migration
+ DOWNTIME = false
+ disable_ddl_transaction!
+
+ MIGRATION = 'RemoveRestrictedTodos'.freeze
+ BATCH_SIZE = 1000
+ DELAY_INTERVAL = 5.minutes.to_i
+
+ class Project < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'projects'
+ end
+
+ def up
+ Project.where('EXISTS (SELECT 1 FROM todos WHERE todos.project_id = projects.id)')
+ .each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, range)
+ end
+ end
+
+ def down
+ # nothing to do
+ end
+end
diff --git a/db/migrate/20180718005113_add_instance_statistics_visibility_to_application_setting.rb b/db/migrate/20180718005113_add_instance_statistics_visibility_to_application_setting.rb
new file mode 100644
index 00000000000..4b6c1f74346
--- /dev/null
+++ b/db/migrate/20180718005113_add_instance_statistics_visibility_to_application_setting.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddInstanceStatisticsVisibilityToApplicationSetting < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default(:application_settings, :instance_statistics_visibility_private,
+ :boolean,
+ default: false,
+ allow_null: false)
+ end
+
+ def down
+ remove_column(:application_settings, :instance_statistics_visibility_private)
+ end
+end
diff --git a/db/migrate/20180722103201_add_private_profile_to_users.rb b/db/migrate/20180722103201_add_private_profile_to_users.rb
new file mode 100644
index 00000000000..4f7ef1322d8
--- /dev/null
+++ b/db/migrate/20180722103201_add_private_profile_to_users.rb
@@ -0,0 +1,10 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddPrivateProfileToUsers < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :users, :private_profile, :boolean
+ end
+end
diff --git a/db/migrate/20180723135214_add_web_ide_client_side_preview_enabled_to_application_settings.rb b/db/migrate/20180723135214_add_web_ide_client_side_preview_enabled_to_application_settings.rb
new file mode 100644
index 00000000000..1ebb91da00c
--- /dev/null
+++ b/db/migrate/20180723135214_add_web_ide_client_side_preview_enabled_to_application_settings.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddWebIdeClientSidePreviewEnabledToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default(:application_settings, :web_ide_clientside_preview_enabled,
+ :boolean,
+ default: false,
+ allow_null: false)
+ end
+
+ def down
+ remove_column(:application_settings, :web_ide_clientside_preview_enabled)
+ end
+end
diff --git a/db/migrate/20180726172057_create_resource_label_events.rb b/db/migrate/20180726172057_create_resource_label_events.rb
new file mode 100644
index 00000000000..2ef7078d898
--- /dev/null
+++ b/db/migrate/20180726172057_create_resource_label_events.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class CreateResourceLabelEvents < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :resource_label_events, id: :bigserial do |t|
+ t.integer :action, null: false
+ t.references :issue, null: true, index: true, foreign_key: { on_delete: :cascade }
+ t.references :merge_request, null: true, index: true, foreign_key: { on_delete: :cascade }
+ t.references :label, index: true, foreign_key: { on_delete: :nullify }
+ t.references :user, index: true, foreign_key: { on_delete: :nullify }
+ t.datetime_with_timezone :created_at, null: false
+ end
+ end
+end
diff --git a/db/migrate/20180807153545_remove_redundant_status_index_on_ci_builds.rb b/db/migrate/20180807153545_remove_redundant_status_index_on_ci_builds.rb
new file mode 100644
index 00000000000..f4f7cb6f8ca
--- /dev/null
+++ b/db/migrate/20180807153545_remove_redundant_status_index_on_ci_builds.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RemoveRedundantStatusIndexOnCiBuilds < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index :ci_builds, :status
+ end
+
+ def down
+ add_concurrent_index :ci_builds, :status
+ end
+end
diff --git a/db/migrate/gpg_keys_limits_to_mysql.rb b/db/migrate/gpg_keys_limits_to_mysql.rb
index 780340d0564..38729320d8c 100644
--- a/db/migrate/gpg_keys_limits_to_mysql.rb
+++ b/db/migrate/gpg_keys_limits_to_mysql.rb
@@ -1,5 +1,4 @@
class IncreaseMysqlTextLimitForGpgKeys < ActiveRecord::Migration
-
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
diff --git a/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb b/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb
index e1771912c3c..9fd23aae1e5 100644
--- a/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb
+++ b/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb
@@ -4,6 +4,6 @@ class LimitsCiBuildTraceChunksRawDataForMysql < ActiveRecord::Migration
# Mysql needs MEDIUMTEXT type (up to 16MB) rather than TEXT (up to 64KB)
# Because 'raw_data' is always capped by Ci::BuildTraceChunk::CHUNK_SIZE, which is 128KB
- change_column :ci_build_trace_chunks, :raw_data, :binary, limit: 16.megabytes - 1 #MEDIUMTEXT
+ change_column :ci_build_trace_chunks, :raw_data, :binary, limit: 16.megabytes - 1 # MEDIUMTEXT
end
end
diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb
index 8f8d8f27410..7507a4bb431 100644
--- a/db/migrate/limits_to_mysql.rb
+++ b/db/migrate/limits_to_mysql.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class LimitsToMysql < ActiveRecord::Migration
def up
return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
diff --git a/db/optional_migrations/composite_primary_keys.rb b/db/optional_migrations/composite_primary_keys.rb
index 0fd3fca52dd..d45705021b0 100644
--- a/db/optional_migrations/composite_primary_keys.rb
+++ b/db/optional_migrations/composite_primary_keys.rb
@@ -21,8 +21,8 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration
Index.new(:merge_request_diff_commits, 'index_merge_request_diff_commits_on_mr_diff_id_and_order', %i(merge_request_diff_id relative_order)),
Index.new(:project_authorizations, 'index_project_authorizations_on_user_id_project_id_access_level', %i(user_id project_id access_level)),
Index.new(:push_event_payloads, 'index_push_event_payloads_on_event_id', %i(event_id)),
- Index.new(:schema_migrations, 'unique_schema_migrations', %(version)),
- ]
+ Index.new(:schema_migrations, 'unique_schema_migrations', %(version))
+ ].freeze
disable_ddl_transaction!
@@ -45,6 +45,7 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration
end
private
+
def add_primary_key(index)
execute "ALTER TABLE #{index.table} ADD PRIMARY KEY USING INDEX #{index.name}"
end
@@ -60,4 +61,3 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration
execute "ALTER TABLE #{index.table} DROP CONSTRAINT IF EXISTS #{temp_index_name}"
end
end
-
diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
index d322844e2fd..017c58477ac 100644
--- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb
+++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
@@ -124,7 +124,7 @@ class RenameReservedProjectNames < ActiveRecord::Migration
def rename_project_row(project, path)
project.respond_to?(:update_attributes) &&
- project.update_attributes(path: path) &&
+ project.update(path: path) &&
project.respond_to?(:rename_repo)
end
end
diff --git a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
index 6a49450cc50..3e8ccfdb899 100644
--- a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
+++ b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
@@ -66,7 +66,7 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration
def rename_project_row(project, path)
project.respond_to?(:update_attributes) &&
- project.update_attributes(path: path) &&
+ project.update(path: path) &&
project.respond_to?(:rename_repo)
end
end
diff --git a/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb b/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb
index 159b533eaaa..24750c58ef0 100644
--- a/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb
+++ b/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
class DropCiTriggerSchedulesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb b/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb
index 15edb402b86..a70e3985005 100644
--- a/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb
+++ b/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb
@@ -10,6 +10,7 @@ class CleanupUsersLdapEmailRename < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :users, :external_email, :ldap_email
end
end
diff --git a/db/post_migrate/20170711145558_migrate_stages_statuses.rb b/db/post_migrate/20170711145558_migrate_stages_statuses.rb
index aeb900354db..65755c0e824 100644
--- a/db/post_migrate/20170711145558_migrate_stages_statuses.rb
+++ b/db/post_migrate/20170711145558_migrate_stages_statuses.rb
@@ -28,6 +28,7 @@ class MigrateStagesStatuses < ActiveRecord::Migration
def down
disable_statement_timeout
+ # rubocop:disable Migration/UpdateLargeTable
update_column_in_batches(:ci_stages, :status, nil)
end
end
diff --git a/db/post_migrate/20170830150306_drop_events_for_migration_table.rb b/db/post_migrate/20170830150306_drop_events_for_migration_table.rb
index 763ee9a810d..69a612ead40 100644
--- a/db/post_migrate/20170830150306_drop_events_for_migration_table.rb
+++ b/db/post_migrate/20170830150306_drop_events_for_migration_table.rb
@@ -18,7 +18,6 @@ class DropEventsForMigrationTable < ActiveRecord::Migration
end
end
- # rubocop: disable Migration/Datetime
def down
create_table :events_for_migration do |t|
t.string :target_type, index: true
diff --git a/db/post_migrate/20171106154015_remove_issues_branch_name.rb b/db/post_migrate/20171106154015_remove_issues_branch_name.rb
index 162b6bafab4..3d08225c96d 100644
--- a/db/post_migrate/20171106154015_remove_issues_branch_name.rb
+++ b/db/post_migrate/20171106154015_remove_issues_branch_name.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/RemoveColumn
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
diff --git a/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb b/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb
index 88dd8f89ba6..53f376f216b 100644
--- a/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb
+++ b/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb
@@ -13,6 +13,7 @@ class CleanupAddTimezoneToIssuesClosedAt < ActiveRecord::Migration
end
# rubocop:disable Migration/Datetime
+ # rubocop:disable Migration/UpdateLargeTable
def down
change_column_type_concurrently(:issues, :closed_at, :datetime)
end
diff --git a/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb b/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb
index fce1829c982..980f76e7d57 100644
--- a/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb
+++ b/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable GitlabSecurity/SqlInjection
class SchedulePopulateMergeRequestMetricsWithEventsData < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb b/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb
index 9addd36dca6..8c8dbb1a043 100644
--- a/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb
+++ b/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb
@@ -43,8 +43,6 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration
end
end
- private
-
class PostgresStrategy < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
@@ -79,6 +77,7 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration
end
private
+
def insert_missing_records
iteration = 0
records = 0
diff --git a/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb b/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb
index bff83379087..3d77ff921c7 100644
--- a/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb
+++ b/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb
@@ -8,6 +8,7 @@ class CleanupUsersRssTokenRename < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :users, :feed_token, :rss_token
end
end
diff --git a/db/post_migrate/20180424151928_fill_file_store.rb b/db/post_migrate/20180424151928_fill_file_store.rb
index b41feb233be..03d54dab250 100644
--- a/db/post_migrate/20180424151928_fill_file_store.rb
+++ b/db/post_migrate/20180424151928_fill_file_store.rb
@@ -38,7 +38,7 @@ class FillFileStore < ActiveRecord::Migration
def up
# NOTE: Schedule background migrations that fill 'NULL' value by '1'(ObjectStorage::Store::LOCAL) on `file_store`, `store` columns
- #
+ #
# Here are the target columns
# - ci_job_artifacts.file_store
# - lfs_objects.file_store
diff --git a/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb b/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb
index 38af5aae924..0e6ec46e5f0 100644
--- a/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb
+++ b/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb
@@ -9,6 +9,7 @@ class BackfillRunnerTypeForCiRunnersPostMigrate < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateColumnInBatches
update_column_in_batches(:ci_runners, :runner_type, INSTANCE_RUNNER_TYPE) do |table, query|
query.where(table[:is_shared].eq(true)).where(table[:runner_type].eq(nil))
end
diff --git a/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb b/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb
index e39cd33c414..08d7d64a2c5 100644
--- a/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb
+++ b/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb
@@ -34,5 +34,4 @@ class MigrateImportAttributesDataFromProjectsToProjectMirrorData < ActiveRecord:
queue_background_migration_jobs_by_range_at_intervals(import_state, DOWN_MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
end
-
end
diff --git a/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb b/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb
index d9d9e93f5a3..fb9616f0c07 100644
--- a/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb
+++ b/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb
@@ -8,6 +8,8 @@ class SetMinimalProjectBuildTimeout < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateLargeTable
+ # rubocop:disable Migration/UpdateColumnInBatches
update_column_in_batches(:projects, :build_timeout, MINIMUM_TIMEOUT) do |table, query|
query.where(table[:build_timeout].lt(MINIMUM_TIMEOUT))
end
diff --git a/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb b/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb
index 7301bcf2c6c..7eca7394f5f 100644
--- a/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb
+++ b/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb
@@ -11,6 +11,7 @@ class CleanupMergeRequestsAllowMaintainerToPushRename < ActiveRecord::Migration
def down
if column_exists?(:merge_requests, :allow_collaboration)
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
end
end
diff --git a/db/post_migrate/20180619121030_enqueue_delete_diff_files_workers.rb b/db/post_migrate/20180619121030_enqueue_delete_diff_files_workers.rb
index 5fb3d545624..c4d2f5f61a0 100644
--- a/db/post_migrate/20180619121030_enqueue_delete_diff_files_workers.rb
+++ b/db/post_migrate/20180619121030_enqueue_delete_diff_files_workers.rb
@@ -1,65 +1,21 @@
class EnqueueDeleteDiffFilesWorkers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
- class MergeRequestDiff < ActiveRecord::Base
- self.table_name = 'merge_request_diffs'
-
- belongs_to :merge_request
-
- include EachBatch
- end
-
DOWNTIME = false
- BATCH_SIZE = 1000
- MIGRATION = 'DeleteDiffFiles'
- DELAY_INTERVAL = 8.minutes
+ SCHEDULER = 'ScheduleDiffFilesDeletion'.freeze
TMP_INDEX = 'tmp_partial_diff_id_with_files_index'.freeze
disable_ddl_transaction!
def up
- # We add temporary index, to make iteration over batches more performant.
- # Conditional here is to avoid the need of doing that in a separate
- # migration file to make this operation idempotent.
- #
unless index_exists_by_name?(:merge_request_diffs, TMP_INDEX)
add_concurrent_index(:merge_request_diffs, :id, where: "(state NOT IN ('without_files', 'empty'))", name: TMP_INDEX)
end
+ BackgroundMigrationWorker.perform_async(SCHEDULER)
- diffs_with_files = MergeRequestDiff.where.not(state: ['without_files', 'empty'])
-
- # explain (analyze, buffers) example for the iteration:
- #
- # Index Only Scan using tmp_index_20013 on merge_request_diffs (cost=0.43..1630.19 rows=60567 width=4) (actual time=0.047..9.572 rows=56976 loops=1)
- # Index Cond: ((id >= 764586) AND (id < 835298))
- # Heap Fetches: 8
- # Buffers: shared hit=18188
- # Planning time: 0.752 ms
- # Execution time: 12.430 ms
- #
- diffs_with_files.each_batch(of: BATCH_SIZE) do |relation, outer_index|
- ids = relation.pluck(:id)
-
- ids.each_with_index do |diff_id, inner_index|
- # This will give some space between batches of workers.
- interval = DELAY_INTERVAL * outer_index + inner_index.minutes
-
- # A single `merge_request_diff` can be associated with way too many
- # `merge_request_diff_files`. It's better to avoid batching these and
- # schedule one at a time.
- #
- # Considering roughly 6M jobs, this should take ~30 days to process all
- # of them.
- #
- BackgroundMigrationWorker.perform_in(interval, MIGRATION, [diff_id])
- end
- end
-
- # We remove temporary index, because it is not required during standard
- # operations and runtime.
- #
- remove_concurrent_index_by_name(:merge_request_diffs, TMP_INDEX)
+ # We don't remove the index since it's going to be used on DeleteDiffFiles
+ # worker. We should remove it in an upcoming release.
end
def down
diff --git a/db/post_migrate/20180629191052_add_partial_index_to_projects_for_last_repository_check_at.rb b/db/post_migrate/20180629191052_add_partial_index_to_projects_for_last_repository_check_at.rb
new file mode 100644
index 00000000000..a701d3678db
--- /dev/null
+++ b/db/post_migrate/20180629191052_add_partial_index_to_projects_for_last_repository_check_at.rb
@@ -0,0 +1,18 @@
+class AddPartialIndexToProjectsForLastRepositoryCheckAt < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ INDEX_NAME = "index_projects_on_last_repository_check_at"
+
+ def up
+ add_concurrent_index(:projects, :last_repository_check_at, where: "last_repository_check_at IS NOT NULL", name: INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index(:projects, :last_repository_check_at, where: "last_repository_check_at IS NOT NULL", name: INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20180702120647_enqueue_fix_cross_project_label_links.rb b/db/post_migrate/20180702120647_enqueue_fix_cross_project_label_links.rb
new file mode 100644
index 00000000000..59aa41adede
--- /dev/null
+++ b/db/post_migrate/20180702120647_enqueue_fix_cross_project_label_links.rb
@@ -0,0 +1,30 @@
+class EnqueueFixCrossProjectLabelLinks < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ BATCH_SIZE = 100
+ MIGRATION = 'FixCrossProjectLabelLinks'
+ DELAY_INTERVAL = 5.minutes
+
+ disable_ddl_transaction!
+
+ class Label < ActiveRecord::Base
+ self.table_name = 'labels'
+ end
+
+ class Namespace < ActiveRecord::Base
+ self.table_name = 'namespaces'
+
+ include ::EachBatch
+
+ default_scope { where(type: 'Group', id: Label.where(type: 'GroupLabel').select('distinct group_id')) }
+ end
+
+ def up
+ queue_background_migration_jobs_by_range_at_intervals(Namespace, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
+ end
+
+ def down
+ # noop
+ end
+end
diff --git a/db/post_migrate/20180704145007_update_project_indexes.rb b/db/post_migrate/20180704145007_update_project_indexes.rb
new file mode 100644
index 00000000000..0e2601ad4fa
--- /dev/null
+++ b/db/post_migrate/20180704145007_update_project_indexes.rb
@@ -0,0 +1,23 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class UpdateProjectIndexes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ NEW_INDEX_NAME = 'idx_project_repository_check_partial'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:projects,
+ [:repository_storage, :created_at],
+ name: NEW_INDEX_NAME,
+ where: 'last_repository_check_at IS NULL'
+ )
+ end
+
+ def down
+ remove_concurrent_index_by_name(:projects, NEW_INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20180706223200_populate_site_statistics.rb b/db/post_migrate/20180706223200_populate_site_statistics.rb
new file mode 100644
index 00000000000..e78e9eb900a
--- /dev/null
+++ b/db/post_migrate/20180706223200_populate_site_statistics.rb
@@ -0,0 +1,25 @@
+class PopulateSiteStatistics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ transaction do
+ execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967
+
+ execute("UPDATE site_statistics SET repositories_count = (SELECT COUNT(*) FROM projects)")
+ end
+
+ transaction do
+ execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967
+
+ execute("UPDATE site_statistics SET wikis_count = (SELECT COUNT(*) FROM project_features WHERE wiki_access_level != 0)")
+ end
+ end
+
+ def down
+ # No downside in keeping the counter up-to-date
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 0112fc726d4..f1d8f4df3b7 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180626125654) do
+ActiveRecord::Schema.define(version: 20180807153545) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -167,6 +167,9 @@ ActiveRecord::Schema.define(version: 20180626125654) do
t.boolean "allow_local_requests_from_hooks_and_services", default: false, null: false
t.boolean "enforce_terms", default: false
t.boolean "mirror_available", default: true, null: false
+ t.boolean "hide_third_party_offers", default: false, null: false
+ t.boolean "instance_statistics_visibility_private", default: false, null: false
+ t.boolean "web_ide_clientside_preview_enabled", default: false, null: false
end
create_table "audit_events", force: :cascade do |t|
@@ -343,7 +346,6 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "ci_builds", ["stage_id", "stage_idx"], name: "tmp_build_stage_position_index", where: "(stage_idx IS NOT NULL)", using: :btree
add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree
add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree
- add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree
add_index "ci_builds", ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree
add_index "ci_builds", ["updated_at"], name: "index_ci_builds_on_updated_at", using: :btree
add_index "ci_builds", ["user_id"], name: "index_ci_builds_on_user_id", using: :btree
@@ -358,6 +360,15 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "ci_builds_metadata", ["build_id"], name: "index_ci_builds_metadata_on_build_id", unique: true, using: :btree
add_index "ci_builds_metadata", ["project_id"], name: "index_ci_builds_metadata_on_project_id", using: :btree
+ create_table "ci_builds_runner_session", id: :bigserial, force: :cascade do |t|
+ t.integer "build_id", null: false
+ t.string "url", null: false
+ t.string "certificate"
+ t.string "authorization"
+ end
+
+ add_index "ci_builds_runner_session", ["build_id"], name: "index_ci_builds_runner_session_on_build_id", unique: true, using: :btree
+
create_table "ci_group_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
@@ -383,6 +394,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
t.datetime_with_timezone "expire_at"
t.string "file"
t.binary "file_sha256"
+ t.integer "file_format", limit: 2
end
add_index "ci_job_artifacts", ["expire_at", "job_id"], name: "index_ci_job_artifacts_on_expire_at_and_job_id", using: :btree
@@ -625,6 +637,9 @@ ActiveRecord::Schema.define(version: 20180626125654) do
t.integer "status", null: false
t.string "version", null: false
t.text "status_reason"
+ t.text "encrypted_ca_key"
+ t.text "encrypted_ca_key_iv"
+ t.text "ca_cert"
end
create_table "clusters_applications_ingress", force: :cascade do |t|
@@ -940,6 +955,16 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree
+ create_table "import_export_uploads", force: :cascade do |t|
+ t.datetime_with_timezone "updated_at", null: false
+ t.integer "project_id"
+ t.text "import_file"
+ t.text "export_file"
+ end
+
+ add_index "import_export_uploads", ["project_id"], name: "index_import_export_uploads_on_project_id", using: :btree
+ add_index "import_export_uploads", ["updated_at"], name: "index_import_export_uploads_on_updated_at", using: :btree
+
create_table "internal_ids", id: :bigserial, force: :cascade do |t|
t.integer "project_id"
t.integer "usage", null: false
@@ -1480,6 +1505,14 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
add_index "personal_access_tokens", ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree
+ create_table "programming_languages", force: :cascade do |t|
+ t.string "name", null: false
+ t.string "color", null: false
+ t.datetime_with_timezone "created_at", null: false
+ end
+
+ add_index "programming_languages", ["name"], name: "index_programming_languages_on_name", unique: true, using: :btree
+
create_table "project_authorizations", id: false, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "project_id", null: false
@@ -1646,6 +1679,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
add_index "projects", ["id"], name: "index_projects_on_id_partial_for_visibility", unique: true, where: "(visibility_level = ANY (ARRAY[10, 20]))", using: :btree
add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
+ add_index "projects", ["last_repository_check_at"], name: "index_projects_on_last_repository_check_at", where: "(last_repository_check_at IS NOT NULL)", using: :btree
add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree
add_index "projects", ["last_repository_updated_at"], name: "index_projects_on_last_repository_updated_at", using: :btree
add_index "projects", ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
@@ -1653,6 +1687,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "projects", ["path"], name: "index_projects_on_path", using: :btree
add_index "projects", ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
add_index "projects", ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree
+ add_index "projects", ["repository_storage", "created_at"], name: "idx_project_repository_check_partial", where: "(last_repository_check_at IS NULL)", using: :btree
add_index "projects", ["repository_storage"], name: "index_projects_on_repository_storage", using: :btree
add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree
add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree
@@ -1764,6 +1799,28 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "remote_mirrors", ["last_successful_update_at"], name: "index_remote_mirrors_on_last_successful_update_at", using: :btree
add_index "remote_mirrors", ["project_id"], name: "index_remote_mirrors_on_project_id", using: :btree
+ create_table "repository_languages", id: false, force: :cascade do |t|
+ t.integer "project_id", null: false
+ t.integer "programming_language_id", null: false
+ t.float "share", null: false
+ end
+
+ add_index "repository_languages", ["project_id", "programming_language_id"], name: "index_repository_languages_on_project_and_languages_id", unique: true, using: :btree
+
+ create_table "resource_label_events", id: :bigserial, force: :cascade do |t|
+ t.integer "action", null: false
+ t.integer "issue_id"
+ t.integer "merge_request_id"
+ t.integer "label_id"
+ t.integer "user_id"
+ t.datetime_with_timezone "created_at", null: false
+ end
+
+ add_index "resource_label_events", ["issue_id"], name: "index_resource_label_events_on_issue_id", using: :btree
+ add_index "resource_label_events", ["label_id"], name: "index_resource_label_events_on_label_id", using: :btree
+ add_index "resource_label_events", ["merge_request_id"], name: "index_resource_label_events_on_merge_request_id", using: :btree
+ add_index "resource_label_events", ["user_id"], name: "index_resource_label_events_on_user_id", using: :btree
+
create_table "routes", force: :cascade do |t|
t.integer "source_id", null: false
t.string "source_type", null: false
@@ -1819,6 +1876,11 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
add_index "services", ["template"], name: "index_services_on_template", using: :btree
+ create_table "site_statistics", force: :cascade do |t|
+ t.integer "repositories_count", default: 0, null: false
+ t.integer "wikis_count", default: 0, null: false
+ end
+
create_table "snippets", force: :cascade do |t|
t.string "title"
t.text "content"
@@ -1929,7 +1991,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
create_table "todos", force: :cascade do |t|
t.integer "user_id", null: false
- t.integer "project_id", null: false
+ t.integer "project_id"
t.integer "target_id"
t.string "target_type", null: false
t.integer "author_id", null: false
@@ -1939,10 +2001,12 @@ ActiveRecord::Schema.define(version: 20180626125654) do
t.datetime "updated_at"
t.integer "note_id"
t.string "commit_id"
+ t.integer "group_id"
end
add_index "todos", ["author_id"], name: "index_todos_on_author_id", using: :btree
add_index "todos", ["commit_id"], name: "index_todos_on_commit_id", using: :btree
+ add_index "todos", ["group_id"], name: "index_todos_on_group_id", using: :btree
add_index "todos", ["note_id"], name: "index_todos_on_note_id", using: :btree
add_index "todos", ["project_id"], name: "index_todos_on_project_id", using: :btree
add_index "todos", ["target_type", "target_id"], name: "index_todos_on_target_type_and_target_id", using: :btree
@@ -2026,6 +2090,13 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_index "user_interacted_projects", ["project_id", "user_id"], name: "index_user_interacted_projects_on_project_id_and_user_id", unique: true, using: :btree
add_index "user_interacted_projects", ["user_id"], name: "index_user_interacted_projects_on_user_id", using: :btree
+ create_table "user_statuses", primary_key: "user_id", force: :cascade do |t|
+ t.integer "cached_markdown_version"
+ t.string "emoji", default: "speech_balloon", null: false
+ t.string "message", limit: 100
+ t.string "message_html"
+ end
+
create_table "user_synced_attributes_metadata", force: :cascade do |t|
t.boolean "name_synced", default: false
t.boolean "email_synced", default: false
@@ -2102,6 +2173,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
t.integer "theme_id", limit: 2
t.integer "accepted_term_id"
t.string "feed_token"
+ t.boolean "private_profile"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
@@ -2144,6 +2216,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
t.datetime "updated_at", null: false
end
+ add_index "web_hook_logs", ["created_at", "web_hook_id"], name: "index_web_hook_logs_on_created_at_and_web_hook_id", using: :btree
add_index "web_hook_logs", ["web_hook_id"], name: "index_web_hook_logs_on_web_hook_id", using: :btree
create_table "web_hooks", force: :cascade do |t|
@@ -2187,6 +2260,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_foreign_key "ci_builds", "projects", name: "fk_befce0568a", on_delete: :cascade
add_foreign_key "ci_builds_metadata", "ci_builds", column: "build_id", on_delete: :cascade
add_foreign_key "ci_builds_metadata", "projects", on_delete: :cascade
+ add_foreign_key "ci_builds_runner_session", "ci_builds", column: "build_id", on_delete: :cascade
add_foreign_key "ci_group_variables", "namespaces", column: "group_id", name: "fk_33ae4d58d8", on_delete: :cascade
add_foreign_key "ci_job_artifacts", "ci_builds", column: "job_id", on_delete: :cascade
add_foreign_key "ci_job_artifacts", "projects", on_delete: :cascade
@@ -2238,6 +2312,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_foreign_key "gpg_signatures", "gpg_keys", on_delete: :nullify
add_foreign_key "gpg_signatures", "projects", on_delete: :cascade
add_foreign_key "group_custom_attributes", "namespaces", column: "group_id", on_delete: :cascade
+ add_foreign_key "import_export_uploads", "projects", on_delete: :cascade
add_foreign_key "internal_ids", "namespaces", name: "fk_162941d509", on_delete: :cascade
add_foreign_key "internal_ids", "projects", on_delete: :cascade
add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade
@@ -2280,6 +2355,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_foreign_key "milestones", "projects", name: "fk_9bd0a0c791", on_delete: :cascade
add_foreign_key "note_diff_files", "notes", column: "diff_note_id", on_delete: :cascade
add_foreign_key "notes", "projects", name: "fk_99e097b079", on_delete: :cascade
+ add_foreign_key "notification_settings", "users", name: "fk_0c95e91db7", on_delete: :cascade
add_foreign_key "oauth_openid_requests", "oauth_access_grants", column: "access_grant_id", name: "fk_oauth_openid_requests_oauth_access_grants_access_grant_id"
add_foreign_key "pages_domains", "projects", name: "fk_ea2f6dfc6f", on_delete: :cascade
add_foreign_key "personal_access_tokens", "users"
@@ -2305,6 +2381,11 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_foreign_key "push_event_payloads", "events", name: "fk_36c74129da", on_delete: :cascade
add_foreign_key "releases", "projects", name: "fk_47fe2a0596", on_delete: :cascade
add_foreign_key "remote_mirrors", "projects", on_delete: :cascade
+ add_foreign_key "repository_languages", "projects", on_delete: :cascade
+ add_foreign_key "resource_label_events", "issues", on_delete: :cascade
+ add_foreign_key "resource_label_events", "labels", on_delete: :nullify
+ add_foreign_key "resource_label_events", "merge_requests", on_delete: :cascade
+ add_foreign_key "resource_label_events", "users", on_delete: :nullify
add_foreign_key "services", "projects", name: "fk_71cce407f9", on_delete: :cascade
add_foreign_key "snippets", "projects", name: "fk_be41fd4bb7", on_delete: :cascade
add_foreign_key "subscriptions", "projects", on_delete: :cascade
@@ -2313,6 +2394,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_foreign_key "term_agreements", "users", on_delete: :cascade
add_foreign_key "timelogs", "issues", name: "fk_timelogs_issues_issue_id", on_delete: :cascade
add_foreign_key "timelogs", "merge_requests", name: "fk_timelogs_merge_requests_merge_request_id", on_delete: :cascade
+ add_foreign_key "todos", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "todos", "notes", name: "fk_91d1f47b13", on_delete: :cascade
add_foreign_key "todos", "projects", name: "fk_45054f9c45", on_delete: :cascade
add_foreign_key "todos", "users", column: "author_id", name: "fk_ccf0373936", on_delete: :cascade
@@ -2323,6 +2405,7 @@ ActiveRecord::Schema.define(version: 20180626125654) do
add_foreign_key "user_custom_attributes", "users", on_delete: :cascade
add_foreign_key "user_interacted_projects", "projects", name: "fk_722ceba4f7", on_delete: :cascade
add_foreign_key "user_interacted_projects", "users", name: "fk_0894651f08", on_delete: :cascade
+ add_foreign_key "user_statuses", "users", on_delete: :cascade
add_foreign_key "user_synced_attributes_metadata", "users", on_delete: :cascade
add_foreign_key "users", "application_setting_terms", column: "accepted_term_id", name: "fk_789cd90b35", on_delete: :cascade
add_foreign_key "users_star_projects", "projects", name: "fk_22cd27ddfc", on_delete: :cascade
diff --git a/doc/README.md b/doc/README.md
index fee920f2012..a814c787f94 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -177,7 +177,8 @@ instant how code changes impact your production environment.
- [Prometheus metrics](user/project/integrations/prometheus_library/metrics.md): Let Prometheus collect metrics from various services, like Kubernetes, NGINX, NGINX ingress controller, HAProxy, and Amazon Cloud Watch.
- [GitLab Performance Monitoring](administration/monitoring/performance/index.md): Use InfluxDB and Grafana to monitor the performance of your GitLab instance (will be eventually replaced by Prometheus).
- [Health check](user/admin_area/monitoring/health_check.md): GitLab provides liveness and readiness probes to indicate service health and reachability to required services.
-- [GitLab Cycle Analytics](user/project/cycle_analytics.md): Cycle Analytics measures the time it takes to go from an [idea to production](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab) for each project you have.
+- [GitLab Cycle Analytics](user/project/cycle_analytics.md): Cycle Analytics measures the time it takes to go from an
+ [idea to production](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab) for each project you have.
## Getting started with GitLab
@@ -228,7 +229,7 @@ straight away.
### GitLab self-hosted
-With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/products/): Core, Starter, Premium, and Ultimate.
+With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/pricing/): Core, Starter, Premium, and Ultimate.
Every feature available in Core is also available in Starter, Premium, and Ultimate.
Starter features are also available in Premium and Ultimate, and Premium features are also
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index d9a61aea6ef..6d7e408d41b 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -63,7 +63,7 @@ Gitaly network traffic is unencrypted so you should use a firewall to
restrict access to your Gitaly server.
Below we describe how to configure a Gitaly server at address
-`gitaly.internal:9999` with secret token `abc123secret`. We assume
+`gitaly.internal:8075` with secret token `abc123secret`. We assume
your GitLab installation has two repository storages, `default` and
`storage1`.
@@ -101,18 +101,42 @@ documentation on configuring Gitaly
authentication](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/configuration/README.md#authentication)
.
-In most or all cases the storage paths below end in `/repositories`. Check the
+>
+**NOTE:** In most or all cases the storage paths below end in `/repositories` which is
+different than `path` in `git_data_dirs` of Omnibus installations. Check the
directory layout on your Gitaly server to be sure.
Omnibus installations:
```ruby
# /etc/gitlab/gitlab.rb
-gitaly['listen_addr'] = '0.0.0.0:9999'
+
+# Avoid running unnecessary services on the gitaly server
+postgresql['enable'] = false
+redis['enable'] = false
+nginx['enable'] = false
+prometheus['enable'] = false
+unicorn['enable'] = false
+sidekiq['enable'] = false
+gitlab_workhorse['enable'] = false
+
+# Prevent database connections during 'gitlab-ctl reconfigure'
+gitlab_rails['rake_cache_clear'] = false
+gitlab_rails['auto_migrate'] = false
+
+# Configure the gitlab-shell API callback URL. Without this, `git push` will
+# fail. This can be your 'front door' GitLab URL or an internal load
+# balancer.
+gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'
+
+# Make Gitaly accept connections on all network interfaces. You must use
+# firewalls to restrict access to this address/port.
+gitaly['listen_addr'] = "0.0.0.0:8075"
gitaly['auth_token'] = 'abc123secret'
+
gitaly['storage'] = [
- { 'name' => 'default', 'path' => '/path/to/default/repositories' },
- { 'name' => 'storage1', 'path' => '/path/to/storage1/repositories' },
+ { 'name' => 'default', 'path' => '/mnt/gitlab/default/repositories' },
+ { 'name' => 'storage1', 'path' => '/mnt/gitlab/storage1/repositories' },
]
```
@@ -120,18 +144,18 @@ Source installations:
```toml
# /home/git/gitaly/config.toml
-listen_addr = '0.0.0.0:9999'
+listen_addr = '0.0.0.0:8075'
[auth]
token = 'abc123secret'
[[storage]
name = 'default'
-path = '/path/to/default/repositories'
+path = '/mnt/gitlab/default/repositories'
[[storage]]
name = 'storage1'
-path = '/path/to/storage1/repositories'
+path = '/mnt/gitlab/storage1/repositories'
```
Again, reconfigure (Omnibus) or restart (source).
@@ -146,7 +170,7 @@ server from reaching the Gitaly server then all Gitaly requests will
fail.
We assume that your Gitaly server can be reached at
-`gitaly.internal:9999` from your GitLab server, and that your GitLab
+`gitaly.internal:8075` from your GitLab server, and that your GitLab
NFS shares are mounted at `/mnt/gitlab/default` and
`/mnt/gitlab/storage1` respectively.
@@ -155,8 +179,8 @@ Omnibus installations:
```ruby
# /etc/gitlab/gitlab.rb
git_data_dirs({
- 'default' => { 'path' => '/mnt/gitlab/default', 'gitaly_address' => 'tcp://gitlab.internal:9999' },
- 'storage1' => { 'path' => '/mnt/gitlab/storage1', 'gitaly_address' => 'tcp://gitlab.internal:9999' },
+ 'default' => { 'path' => '/mnt/gitlab/default', 'gitaly_address' => 'tcp://gitaly.internal:8075' },
+ 'storage1' => { 'path' => '/mnt/gitlab/storage1', 'gitaly_address' => 'tcp://gitaly.internal:8075' },
})
gitlab_rails['gitaly_token'] = 'abc123secret'
@@ -171,10 +195,10 @@ gitlab:
storages:
default:
path: /mnt/gitlab/default/repositories
- gitaly_address: tcp://gitlab.internal:9999
+ gitaly_address: tcp://gitaly.internal:8075
storage1:
path: /mnt/gitlab/storage1/repositories
- gitaly_address: tcp://gitlab.internal:9999
+ gitaly_address: tcp://gitaly.internal:8075
gitaly:
token: 'abc123secret'
diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md
index 0d9c10687f2..637d44d2823 100644
--- a/doc/administration/high_availability/gitlab.md
+++ b/doc/administration/high_availability/gitlab.md
@@ -122,6 +122,11 @@ need some extra configuration.
from running on upgrade. Only the primary GitLab application server should
handle migrations.
+1. **Optional** Configure host keys. Copy all contents(primary and public keys) inside `/etc/ssh/` on
+ the primary application server to `/etc/ssh` on all secondary servers. This
+ prevents false man-in-the-middle-attack alerts when accessing servers in your
+ High Availability cluster behind a load balancer.
+
1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
## Troubleshooting
diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md
index 87e96b71dd4..387c3fb6a5b 100644
--- a/doc/administration/high_availability/nfs.md
+++ b/doc/administration/high_availability/nfs.md
@@ -39,23 +39,11 @@ 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
-use-case. There are several issues that can cause problems. For these reasons
-GitLab does not recommend using EFS with GitLab.
-
-- EFS bases allowed IOPS on volume size. The larger the volume, the more IOPS
- are allocated. For smaller volumes, users may experience decent performance
- for a period of time due to 'Burst Credits'. Over a period of weeks to months
- credits may run out and performance will bottom out.
-- To keep "Burst Credits" available, it may be necessary to provision more space
- with 'dummy data'. However, this may get expensive.
-- Another option to maintain "Burst Credits" is to use FS Cache on the server so
- that AWS doesn't always have to go into EFS to access files.
-- For larger volumes, allocated IOPS may not be the problem. Workloads where
- many small files are written in a serialized manner are not well-suited for EFS.
- EBS with an NFS server on top will perform much better.
-
-In addition, avoid storing GitLab log files (e.g. those in `/var/log/gitlab`)
-because this will also affect performance. We recommend that the log files be
+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.
+
+If you do choose to use EFS, avoid storing GitLab log files (e.g. those in `/var/log/gitlab`)
+there because this will also affect performance. We recommend that the log files be
stored on a local volume.
For more details on another person's experience with EFS, see
diff --git a/doc/administration/img/raketasks/check_repos_output.png b/doc/administration/img/raketasks/check_repos_output.png
deleted file mode 100644
index 7fda2ba0c0f..00000000000
--- a/doc/administration/img/raketasks/check_repos_output.png
+++ /dev/null
Binary files differ
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 0e65f9a9963..112d14652af 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -11,7 +11,7 @@ Regular users don't have access to GitLab administration tools and settings.
GitLab has two product distributions: the open source
[GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce),
and the open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee),
-available through [different subscriptions](https://about.gitlab.com/products/).
+available through [different subscriptions](https://about.gitlab.com/pricing/).
You can [install GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/),
but the features you'll have access to depend on the subscription you choose
@@ -45,6 +45,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Environment variables](environment_variables.md): Supported environment variables that can be used to override their defaults values in order to configure GitLab.
- [Plugins](plugins.md): With custom plugins, GitLab administrators can introduce custom integrations without modifying GitLab's source code.
- [Enforcing Terms of Service](../user/admin_area/settings/terms.md)
+- [Third party offers](../user/admin_area/settings/third_party_offers.md)
#### Customizing GitLab's appearance
@@ -105,6 +106,7 @@ created in snippets, wikis, and repos.
- [Gitaly](gitaly/index.md): Configuring Gitaly, GitLab's Git repository storage service.
- [Default labels](../user/admin_area/labels.html): Create labels that will be 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](https://docs.gitlab.com/ee/user/admin_area/custom_project_templates.html): Configure a set of projects to be used as custom templates when creating a new project. **[PREMIUM ONLY]**
### Repository settings
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index e59ab5a72e1..8c55c8c4298 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -88,13 +88,12 @@ _The artifacts are stored by default in
### Using object storage
>**Notes:**
-- [Introduced][ee-1762] in [GitLab Premium][eep] 9.4.
-- Since version 9.5, artifacts are [browsable], when object storage is enabled.
- 9.4 lacks this feature.
-> Available in [GitLab Premium](https://about.gitlab.com/products/) and
-[GitLab.com Silver](https://about.gitlab.com/gitlab-com/).
-> Since version 10.6, available in [GitLab CE](https://about.gitlab.com/products/)
-> Since version 11.0, we support direct_upload to S3.
+- [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1762) in
+ [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
+- Since version 9.5, artifacts are [browsable](../user/project/pipelines/job_artifacts.md#browsing-artifacts),
+ when object storage is enabled. 9.4 lacks this feature.
+- Since version 10.6, available in [GitLab Core](https://about.gitlab.com/pricing/)
+- Since version 11.0, we support `direct_upload` to S3.
If you don't want to use the local disk where GitLab is installed to store the
artifacts, you can use an object storage like AWS S3 instead.
@@ -123,6 +122,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
| `provider` | Always `AWS` for compatible hosts | AWS |
| `aws_access_key_id` | AWS credentials, or compatible | |
| `aws_secret_access_key` | AWS credentials, or compatible | |
+| `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 |
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md
index a5cd2b642dc..24d1a3fd151 100644
--- a/doc/administration/job_traces.md
+++ b/doc/administration/job_traces.md
@@ -1,15 +1,29 @@
# Job traces (logs)
-By default, all job traces (logs) are saved to `/var/opt/gitlab/gitlab-ci/builds`
-and `/home/git/gitlab/builds` for Omnibus packages and installations from source
-respectively. The job logs are organized by year and month (for example, `2017_03`),
-and then by project ID.
+Job traces are sent by GitLab Runner while it's processing a job. You can see
+traces in job pages, pipelines, email notifications, etc.
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.
-## Changing the job traces location
+## Data flow
+
+In general, there are two states in job traces: "live trace" and "archived trace".
+In the following table you can see the phases a trace goes through.
+
+| Phase | State | Condition | Data flow | Stored path |
+| ----- | ----- | --------- | --------- | ----------- |
+| 1: patching | Live trace | When a job is running | GitLab Runner => Unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`|
+| 2: overwriting | Live trace | When a job is finished | GitLab Runner => Unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`|
+| 3: archiving | Archived trace | After a job is finished | Sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`|
+| 4: uploading | Archived trace | After a trace is archived | Sidekiq moves archived trace to [object storage](#uploading-traces-to-object-storage) (if configured) |`#{bucket_name}/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`|
+
+The `ROOT_PATH` varies per your environment. For Omnibus GitLab it
+would be `/var/opt/gitlab/gitlab-ci`, whereas for installations from source
+it would be `/home/git/gitlab`.
+
+## Changing the job traces local location
To change the location where the job logs will be stored, follow the steps below.
@@ -41,97 +55,110 @@ To change the location where the job logs will be stored, follow the steps below
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab"
+## Uploading traces to object storage
+
+An archived trace is considered as a [job artifact](job_artifacts.md).
+Therefore, when you [set up an object storage](job_artifacts.md#object-storage-settings),
+job traces are automatically migrated to it along with the other job artifacts.
+
+See [Data flow](#data-flow) to learn about the process.
+
## New live trace architecture
> [Introduced][ce-18169] in GitLab 10.4.
+> [Announced as General availability][ce-46097] in GitLab 11.0.
-> **Notes**:
-- This feature is still Beta, which could impact GitLab.com/on-premises instances, and in the worst case scenario, traces will be lost.
-- This feature is still being discussed in [an issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46097) for the performance improvements.
-- This feature is off by default. Please check below how to enable/disable this featrue.
+NOTE: **Note:**
+This feature is off by default. Check below how to [enable/disable](#enabling-live-trace) it.
-**What is "live trace"?**
+By combining the process with object storage settings, we can completely bypass
+the local file storage. This is a useful option if GitLab is installed as
+cloud-native, for example on Kubernetes.
-Job trace that is sent by runner while jobs are running. You can see live trace in job pages UI.
-The live traces are archived once job finishes.
+The data flow is the same as described in the [data flow section](#data-flow)
+with one change: _the stored path of the first two phases is different_. This new live
+trace architecture stores chunks of traces 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. Once the full chunk is sent, it is flushed a persistent store, either object storage(temporary directory) or database.
+After a while, the data in Redis and a persitent store will be archived to [object storage](#uploading-traces-to-object-storage).
-**What is new architecture?**
+The data are stored in the following Redis namespace: `Gitlab::Redis::SharedState`.
-So far, when GitLab Runner sends a job trace to GitLab-Rails, traces have been saved to file storage as text files.
-This was a problem for [Cloud Native-compatible GitLab application](https://gitlab.com/gitlab-com/migration/issues/23) where GitLab had to rely on File Storage.
+Here is the detailed data flow:
-This new live trace architecture stores chunks of traces in Redis and database instead of file storage.
-Redis is used as first-class storage, and it stores up-to 128kB. Once the full chunk is sent it will be flushed to database. Afterwhile, the data in Redis and database will be archived to ObjectStorage.
+1. GitLab Runner picks a job from GitLab
+1. GitLab Runner sends a piece of trace to GitLab
+1. GitLab appends the data to Redis
+1. Once the data in Redis reach 128KB, the data is flushed to a persistent store (object storage or the database).
+1. The above steps are repeated until the job is finished.
+1. Once the job is finished, GitLab schedules a Sidekiq worker to archive the trace.
+1. The Sidekiq worker archives the trace to object storage and cleans up the trace
+ in Redis and a persistent store (object storage or the database).
-Here is the detailed data flow.
+### Enabling live trace
-1. GitLab Runner picks a job from GitLab-Rails
-1. GitLab Runner sends a piece of trace to GitLab-Rails
-1. GitLab-Rails appends the data to Redis
-1. If the data in Redis is fulfilled 128kB, the data is flushed to Database.
-1. 2.~4. is continued until the job is finished
-1. Once the job is finished, GitLab-Rails schedules a sidekiq worker to archive the trace
-1. The sidekiq worker archives the trace to Object Storage, and cleanup the trace in Redis and Database
+The following commands are to be issues in a Rails console:
-**How to check if it's on or off?**
+```sh
+# Omnibus GitLab
+gitlab-rails console
+
+# Installation from source
+cd /home/git/gitlab
+sudo -u git -H bin/rails console RAILS_ENV=production
+```
+
+**To check if live trace is enabled:**
```ruby
Feature.enabled?('ci_enable_live_trace')
```
-**How to enable?**
+**To enable live trace:**
```ruby
Feature.enable('ci_enable_live_trace')
```
->**Note:**
-The transition period will be handled gracefully. Upcoming traces will be generated with the new architecture, and on-going live traces will stay with the legacy architecture (i.e. on-going live traces won't be re-generated forcibly with the new architecture).
+NOTE: **Note:**
+The transition period will be handled gracefully. Upcoming traces will be
+generated with the new architecture, and on-going live traces will stay with the
+legacy architecture, which means that on-going live traces won't be forcibly
+re-generated with the new architecture.
-**How to disable?**
+**To disable live trace:**
```ruby
Feature.disable('ci_enable_live_trace')
```
->**Note:**
-The transition period will be handled gracefully. Upcoming traces will be generated with the legacy architecture, and on-going live traces will stay with the new architecture (i.e. on-going live traces won't be re-generated forcibly with the legacy architecture).
-
-**Redis namespace:**
-
-`Gitlab::Redis::SharedState`
-
-**Potential impact:**
+NOTE: **Note:**
+The transition period will be handled gracefully. Upcoming traces will be generated
+with the legacy architecture, and on-going live traces will stay with the new
+architecture, which means that on-going live traces won't be forcibly re-generated
+with the legacy architecture.
-- This feature could incur data loss:
- - Case 1: When all data in Redis are accidentally flushed.
- - On-going live traces could be recovered by re-sending traces (This is supported by all versions of GitLab Runner)
- - Finished jobs which has not archived live traces will lose the last part (~128kB) of trace data.
- - Case 2: When sidekiq workers failed to archive (e.g. There was a bug that prevents archiving process, Sidekiq inconsistancy, etc):
- - Currently all trace data in Redis will be deleted after one week. If the sidekiq workers can't finish by the expiry date, the part of trace data will be lost.
-- This feature could consume all memory on Redis instance. If the number of jobs is 1000, 128MB (128kB * 1000) is consumed.
-- This feature could pressure Database replication lag. `INSERT` are generated to indicate that we have trace chunk. `UPDATE` with 128kB of data is issued once we receive multiple chunks.
-- and so on
+### Potential implications
-**How to test?**
+In some cases, having data stored on Redis could incur data loss:
-We're currently evaluating this feature on dev.gitalb.org or staging.gitlab.com to verify this features. Here is the list of tests/measurements.
+1. **Case 1: When all data in Redis are accidentally flushed**
+ - On going live traces could be recovered by re-sending traces (this is
+ supported by all versions of the GitLab Runner).
+ - Finished jobs which have not archived live traces will lose the last part
+ (~128KB) of trace data.
-- Features:
- - Live traces should be visible on job pages
- - Archived traces should be visible on job pages
- - Live traces should be archived to Object storage
- - Live traces should be cleaned up after archived
- - etc
-- Performance:
- - Schedule 1000~10000 jobs and let GitLab-runners process concurrently. Measure memoery presssure, IO load, etc.
- - etc
-- Failover:
- - Simulate Redis outage
- - etc
+1. **Case 2: When Sidekiq workers fail to archive (e.g., there was a bug that
+ prevents archiving process, Sidekiq inconsistency, etc.)**
+ - Currently all trace data in Redis will be deleted after one week. If the
+ Sidekiq workers can't finish by the expiry date, the part of trace data will be lost.
-**How to verify the correctnesss?**
+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.
-- TBD
+Also, it could pressure the database replication lag. `INSERT`s are generated to
+indicate that we have trace chunk. `UPDATE`s with 128KB of data is issued once we
+receive multiple chunks.
-[ce-18169]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169 \ No newline at end of file
+[ce-18169]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169
+[ce-46097]: https://gitlab.com/gitlab-org/gitlab-ce/issues/46097
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index cea6764df41..7bc92ae77ee 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -22,7 +22,7 @@ collect metrics from this endpoint. We recommend setting up another Prometheus
server, because the embedded server configuration is overwritten once every
[reconfigure of GitLab][reconfigure]. In the future this will not be required.
-## Metrics available
+## Unicorn Metrics available
The following metrics are available:
@@ -48,6 +48,8 @@ The following metrics are available:
| filesystem_circuitbreaker_latency_seconds | Gauge | 9.5 | Time spent validating if a storage is accessible |
| filesystem_circuitbreaker | Gauge | 9.5 | Whether or not the circuit for a certain shard is broken or not |
| circuitbreaker_storage_check_duration_seconds | Histogram | 10.3 | Time a single storage probe took |
+| failed_login_captcha_total | Gauge | 11.0 | Counter of failed CAPTCHA attempts during login |
+| successful_login_captcha_total | Gauge | 11.0 | Counter of successful CAPTCHA attempts during login |
### Ruby metrics
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index 89331238ce4..eada7b19dcd 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -1,5 +1,10 @@
# Fast lookup of authorized SSH keys in the database
+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 drop-in replacement.
+
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1631) in
> [GitLab Starter](https://about.gitlab.com/gitlab-ee) 9.3.
>
diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md
index 5655b7efec6..e9cad99c4b0 100644
--- a/doc/administration/operations/index.md
+++ b/doc/administration/operations/index.md
@@ -14,4 +14,7 @@ that to prioritize important jobs.
- [Sidekiq MemoryKiller](sidekiq_memory_killer.md): Configure Sidekiq MemoryKiller
to restart Sidekiq.
- [Unicorn](unicorn.md): Understand Unicorn and unicorn-worker-killer.
-- [Speed up SSH operations](fast_ssh_key_lookup.md): Authorize SSH users via a fast, indexed lookup to the GitLab database.
+- Speed up SSH operations by [Authorizing SSH users via a fast,
+indexed lookup to the GitLab database](fast_ssh_key_lookup.md), and/or
+by [doing away with user SSH keys stored on GitLab entirely in favor
+of SSH certificates](ssh_certificates.md).
diff --git a/doc/administration/operations/ssh_certificates.md b/doc/administration/operations/ssh_certificates.md
new file mode 100644
index 00000000000..8968afba01b
--- /dev/null
+++ b/doc/administration/operations/ssh_certificates.md
@@ -0,0 +1,165 @@
+# User lookup via OpenSSH's AuthorizedPrincipalsCommand
+
+> [Available in](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19911) GitLab
+> Community Edition 11.2.
+
+GitLab's default SSH authentication 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 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
+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)
+to compile an up-to-date version.
+
+## Why use OpenSSH certificates?
+
+By using OpenSSH certificates all the information about what user on
+GitLab owns the key is encoded in the key itself, and OpenSSH itself
+guarantees that users can't fake this, since they'd need to have
+access to the private CA signing key.
+
+When correctly set up, this does away with the requirement of
+uploading user SSH keys to GitLab entirely.
+
+## Setting up SSH certificate lookup via GitLab Shell
+
+How to fully setup SSH certificates is outside the scope of this
+document. See [OpenSSH's
+PROTOCOL.certkeys](https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD)
+for how it works, and e.g. [RedHat's documentation about
+it](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/sec-using_openssh_certificate_authentication).
+
+We assume that you already have SSH certificates set up, and have
+added the `TrustedUserCAKeys` of your CA to your `sshd_config`, e.g.:
+
+```
+TrustedUserCAKeys /etc/security/mycompany_user_ca.pub
+```
+
+Usually `TrustedUserCAKeys` would not be scoped under a `Match User
+git` in such a setup, since it would also be used for system logins to
+the GitLab server itself, but your setup may vary. If the CA is only
+used for GitLab consider putting this in the `Match User git` section
+(described below).
+
+The SSH certificates being issued by that CA **MUST** have a "key id"
+corresponding to that user's username on GitLab, e.g. (some output
+omitted for brevity):
+
+```
+$ ssh-add -L | grep cert | ssh-keygen -L -f -
+(stdin):1:
+ Type: ssh-rsa-cert-v01@openssh.com user certificate
+ Public key: RSA-CERT SHA256:[...]
+ Signing CA: RSA SHA256:[...]
+ Key ID: "aearnfjord"
+ Serial: 8289829611021396489
+ Valid: from 2018-07-18T09:49:00 to 2018-07-19T09:50:34
+ Principals:
+ sshUsers
+ [...]
+ [...]
+```
+
+Technically that's not strictly true, e.g. it could be
+`prod-aearnfjord` if it's a SSH certificate you'd normally log in to
+servers as the `prod-aearnfjord` user, but then you must specify your
+own `AuthorizedPrincipalsCommand` to do that mapping instead of using
+our provided default.
+
+The important part is that the `AuthorizedPrincipalsCommand` must be
+able to map from the "key id" to a GitLab username in some way, the
+default command we ship assumes there's a 1=1 mapping between the two,
+since the whole point of this is to allow us to extract a GitLab
+username from the key itself, instead of relying on something like the
+default public key to username mapping.
+
+Then, in your `sshd_config` set up `AuthorizedPrincipalsCommand` for
+the `git` user. Hopefully you can use the default one shipped with
+GitLab:
+
+```
+Match User git
+ AuthorizedPrincipalsCommandUser root
+ AuthorizedPrincipalsCommand /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell-authorized-principals-check %i sshUsers
+```
+
+This command will emit output that looks something like:
+
+```
+command="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell username-{KEY_ID}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty {PRINCIPAL}
+```
+
+Where `{KEY_ID}` is the `%i` argument passed to the script
+(e.g. `aeanfjord`), and `{PRINCIPAL}` is the principal passed to it
+(e.g. `sshUsers`).
+
+You will need to customize the `sshUsers` part of that. It should be
+some principal that's guaranteed to be part of the key for all users
+who can log in to GitLab, or you must provide a list of principals,
+one of which is going to be present for the user, e.g.:
+
+```
+ [...]
+ AuthorizedPrincipalsCommand /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell-authorized-principals-check %i sshUsers windowsUsers
+```
+
+## Principals and security
+
+You can supply as many principals as you want, these will be turned
+into multiple lines of `authorized_keys` output, as described in the
+`AuthorizedPrincipalsFile` documentation in `sshd_config(5)`.
+
+Normally when using the `AuthorizedKeysCommand` with OpenSSH the
+principal is some "group" that's allowed to log into that
+server. However with GitLab it's only used to appease OpenSSH's
+requirement for it, we effectively only care about the "key id" being
+correct. Once that's extracted GitLab will enforce its own ACLs for
+that user (e.g. what projects the user can access).
+
+So it's OK to e.g. be overly generous in what you accept, since if the
+user e.g. has no access to GitLab at all it'll just error out with a
+message about this being an invalid user.
+
+## Interaction with the `authorized_keys` file
+
+SSH certificates can be used in conjunction with the `authorized_keys`
+file, and if setup as configured above the `authorized_keys` file will
+still serve as a fallback.
+
+This is because if the `AuthorizedPrincipalsCommand` can't
+authenticate the user, OpenSSH will fall back on
+`~/.ssh/authorized_keys` (or the `AuthorizedKeysCommand`).
+
+Therefore there may still be a reason to use the ["Fast lookup of
+authorized SSH keys in the database"](fast_ssh_key_lookup.html) method
+in conjunction with this. Since you'll be using SSH certificates for
+all your normal users, and relying on the `~/.ssh/authorized_keys`
+fallback for deploy keys, if you make use of those.
+
+But you may find that there's no reason to do that, since all your
+normal users will use the fast `AuthorizedPrincipalsCommand` path, and
+only automated deployment key access will fall back on
+`~/.ssh/authorized_keys`, or that you have a lot more keys for normal
+users (especially if they're renewed) than you have deploy keys.
+
+## Other security caveats
+
+Users can still bypass SSH certificate authentication by manually
+uploading an SSH public key to their profile, relying on the
+`~/.ssh/authorized_keys` fallback to authenticate it. There's
+currently no feature to prevent this, [but there's an open request for
+adding it](https://gitlab.com/gitlab-org/gitlab-ce/issues/49218).
+
+Such a restriction can currently be hacked in by e.g. providing a
+custom `AuthorizedKeysCommand` which checks if the discovered key-ID
+returned from `gitlab-shell-authorized-keys-check` is a deploy key or
+not (all non-deploy keys should be refused).
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 9b1297ca4ba..eefa86f8e42 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -49,8 +49,8 @@ supporting custom domains a secondary IP is not needed.
Before proceeding with the Pages configuration, you will need to:
-1. Have a separate domain under which the GitLab Pages will be served. In this
- document we assume that to be `example.io`.
+1. Have an exclusive root domain for serving GitLab Pages. Note that you cannot
+ use a subdomain of your GitLab's instance domain.
1. Configure a **wildcard DNS record**.
1. (Optional) Have a **wildcard certificate** for that domain if you decide to
serve Pages under HTTPS.
@@ -124,11 +124,6 @@ The Pages daemon doesn't listen to the outside world.
```
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
- ```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
Watch the [video tutorial][video-admin] for this configuration.
@@ -161,11 +156,6 @@ outside world.
respectively.
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
- ```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
## Advanced configuration
@@ -203,11 +193,6 @@ world. Custom domains are supported, but no TLS.
listens on. If you don't have IPv6, you can omit the IPv6 address.
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
- ```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
### Custom domains with TLS support
@@ -241,11 +226,6 @@ world. Custom domains and TLS are supported.
listens on. If you don't have IPv6, you can omit the IPv6 address.
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
- ```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
### Custom domain verification
@@ -259,6 +239,23 @@ verification requirement. Navigate to `Admin area âž” Settings` and uncheck
**Require users to prove ownership of custom domains** in the Pages section.
This setting is enabled by default.
+## Activate verbose logging for daemon
+
+Verbose logging was [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2533) in
+Omnibus GitLab 11.1.
+
+Follow the steps below to configure verbose logging of GitLab Pages daemon.
+
+1. By default the daemon only logs with `INFO` level.
+ If you wish to make it log events with level `DEBUG` you must configure this in
+ `/etc/gitlab/gitlab.rb`:
+
+ ```shell
+ gitlab_pages['log_verbose'] = true
+ ```
+
+1. [Reconfigure GitLab][reconfigure]
+
## Change storage path
Follow the steps below to change the default path where GitLab Pages' contents
@@ -273,11 +270,29 @@ are stored.
```
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
+
+## Configure listener for reverse proxy requests
+
+Follow the steps below to configure the proxy listener of GitLab Pages. [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2533) in
+Omnibus GitLab 11.1.
+
+1. By default the listener is configured to listen for requests on `localhost:8090`.
+
+ If you wish to disable it you must configure this in
+ `/etc/gitlab/gitlab.rb`:
```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
+ gitlab_pages['listen_proxy'] = nil
+ ```
+
+ If you wish to make it listen on a different port you must configure this also in
+ `/etc/gitlab/gitlab.rb`:
+
+ ```shell
+ gitlab_pages['listen_proxy'] = "localhost:10080"
+ ```
+
+1. [Reconfigure GitLab][reconfigure]
## Set maximum pages size
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index 2649bf61d74..0ca1d77f1d0 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -1,4 +1,4 @@
-# Check Rake Tasks
+# Integrity Check Rake Task
## Repository Integrity
@@ -28,14 +28,8 @@ exactly which repositories are causing the trouble.
### Check all GitLab repositories
->**Note:**
->
-> - `gitlab:repo:check` has been deprecated in favor of `gitlab:git:fsck`
-> - [Deprecated][ce-15931] in GitLab 10.4.
-> - `gitlab:repo:check` will be removed in the future. [Removal issue][ce-41699]
-
This task loops through all repositories on the GitLab server and runs the
-3 integrity checks described previously.
+integrity check described previously.
**Omnibus Installation**
@@ -49,33 +43,6 @@ sudo gitlab-rake gitlab:git:fsck
sudo -u git -H bundle exec rake gitlab:git:fsck RAILS_ENV=production
```
-### Check repositories for a specific user
-
-This task checks all repositories that a specific user has access to. This is important
-because sometimes you know which user is experiencing trouble but you don't know
-which project might be the cause.
-
-If the rake task is executed without brackets at the end, you will be prompted
-to enter a username.
-
-**Omnibus Installation**
-
-```bash
-sudo gitlab-rake gitlab:user:check_repos
-sudo gitlab-rake gitlab:user:check_repos[<username>]
-```
-
-**Source Installation**
-
-```bash
-sudo -u git -H bundle exec rake gitlab:user:check_repos RAILS_ENV=production
-sudo -u git -H bundle exec rake gitlab:user:check_repos[<username>] RAILS_ENV=production
-```
-
-Example output:
-
-![gitlab:user:check_repos output](../img/raketasks/check_repos_output.png)
-
## Uploaded Files Integrity
Various types of files can be uploaded to a GitLab installation by users.
@@ -121,6 +88,45 @@ sudo gitlab-rake gitlab:lfs:check BATCH=100 ID_FROM=50 ID_TO=250
sudo gitlab-rake gitlab:uploads:check BATCH=100 ID_FROM=50 ID_TO=250
```
+Example output:
+
+```
+$ sudo gitlab-rake gitlab:uploads:check
+Checking integrity of Uploads
+- 1..1350: Failures: 0
+- 1351..2743: Failures: 0
+- 2745..4349: Failures: 2
+- 4357..5762: Failures: 1
+- 5764..7140: Failures: 2
+- 7142..8651: Failures: 0
+- 8653..10134: Failures: 0
+- 10135..11773: Failures: 0
+- 11777..13315: Failures: 0
+Done!
+```
+
+Example verbose output:
+
+```
+$ sudo gitlab-rake gitlab:uploads:check VERBOSE=1
+Checking integrity of Uploads
+- 1..1350: Failures: 0
+- 1351..2743: Failures: 0
+- 2745..4349: Failures: 2
+ - Upload: 3573: #<Errno::ENOENT: No such file or directory @ rb_sysopen - /opt/gitlab/embedded/service/gitlab-rails/public/uploads/user-foo/project-bar/7a77cc52947bfe188adeff42f890bb77/image.png>
+ - Upload: 3580: #<Errno::ENOENT: No such file or directory @ rb_sysopen - /opt/gitlab/embedded/service/gitlab-rails/public/uploads/user-foo/project-bar/2840ba1ba3b2ecfa3478a7b161375f8a/pug.png>
+- 4357..5762: Failures: 1
+ - Upload: 4636: #<Google::Apis::ServerError: Server error>
+- 5764..7140: Failures: 2
+ - Upload: 5812: #<NoMethodError: undefined method `hashed_storage?' for nil:NilClass>
+ - Upload: 5837: #<NoMethodError: undefined method `hashed_storage?' for nil:NilClass>
+- 7142..8651: Failures: 0
+- 8653..10134: Failures: 0
+- 10135..11773: Failures: 0
+- 11777..13315: Failures: 0
+Done!
+```
+
## LDAP Check
The LDAP check Rake task will test the bind_dn and password credentials
@@ -128,5 +134,4 @@ The LDAP check Rake task will test the bind_dn and password credentials
executed as part of the `gitlab:check` task, but can run independently.
See [LDAP Rake Tasks - LDAP Check](ldap.md#check) for details.
-[ce-15931]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15931
-[ce-41699]: https://gitlab.com/gitlab-org/gitlab-ce/issues/41699
+[git-fsck]: https://git-scm.com/docs/git-fsck
diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md
index ecc4ac6b29b..7bd765a35e0 100644
--- a/doc/administration/raketasks/project_import_export.md
+++ b/doc/administration/raketasks/project_import_export.md
@@ -30,5 +30,12 @@ sudo gitlab-rake gitlab:import_export:data
bundle exec rake gitlab:import_export:data RAILS_ENV=production
```
+In order to enable Object Storage on the Export, you can use the [feature flag][feature-flags]:
+
+```
+import_export_object_storage
+```
+
[ce-3050]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3050
+[feature-flags]: https://docs.gitlab.com/ee/api/features.html
[tmp]: ../../development/shared_files.md
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 087fe729b28..88221db78f1 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -73,7 +73,7 @@ by another folder with the next 2 characters. They are both stored in a special
### How to migrate to Hashed Storage
In GitLab, go to **Admin > Settings**, find the **Repository Storage** section
-and select "_Create new projects using hashed storage paths_".
+and select "_Use hashed storage paths for newly created and renamed projects_".
To migrate your existing projects to the new storage type, check the specific
[rake tasks].
diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md
index 83a714810c1..2902af8c782 100644
--- a/doc/administration/troubleshooting/debug.md
+++ b/doc/administration/troubleshooting/debug.md
@@ -66,6 +66,24 @@ On CentOS:
sudo yum install gdb
```
+### rbtrace
+
+GitLab 11.2 ships with [rbtrace](https://github.com/tmm1/rbtrace), which
+allows you to trace Ruby code, view all running threads, take memory dumps,
+and more. However, this is not enabled by default. To enable it, define the
+`ENABLE_RBTRACE` variable to the environment. For example, in Omnibus:
+
+```ruby
+gitlab_rails['env'] = {"ENABLE_RBTRACE" => "1"}
+```
+
+Then reconfigure the system and restart Unicorn and Sidekiq. To run this
+in Omnibus, run as root:
+
+```ruby
+/opt/gitlab/embedded/bin/ruby /opt/gitlab/embedded/bin/rbtrace
+```
+
## Common Problems
Many of the tips to diagnose issues below apply to many different situations. We'll use one
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index 7f0bd8f04e3..77e73b23021 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -52,6 +52,7 @@ _The uploads are stored by default in
>**Notes:**
- [Introduced][ee-3867] in [GitLab Enterprise Edition Premium][eep] 10.5.
+- Since version 11.1, we support direct_upload to S3.
If you don't want to use the local disk where GitLab is installed to store the
uploads, you can use an object storage provider like AWS S3 instead.
@@ -65,7 +66,7 @@ For source installations the following settings are nested under `uploads:` and
|---------|-------------|---------|
| `enabled` | Enable/disable object storage | `false` |
| `remote_directory` | The bucket name where Uploads will be stored| |
-| `direct_upload` | Set to true to enable direct upload of Uploads without the need of local shared storage. Option may be removed once we decide to support only single storage for all files. This is beta option as it uses inefficient way of uploading data (via Unicorn). The accelerated uploads gonna be implemented in future releases | `false` |
+| `direct_upload` | Set to true to enable direct upload of Uploads 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 | |
@@ -79,6 +80,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
| `provider` | Always `AWS` for compatible hosts | AWS |
| `aws_access_key_id` | AWS credentials, or compatible | |
| `aws_secret_access_key` | AWS credentials, or compatible | |
+| `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 |
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
@@ -140,12 +142,6 @@ These task complies with the `BATCH` environment variable to process uploads in
gitlab-rake "gitlab:uploads:migrate[FileUploader, MergeRequest]"
```
- Currently this has to be executed manually and it will allow you to
- migrate the existing uploads to the object storage, but all new
- uploads will still be stored on the local disk. In the future
- you will be given an option to define a default storage for all
- new files.
-
---
**In installations from source:**
@@ -198,12 +194,6 @@ _The uploads are stored by default in
```
- Currently this has to be executed manually and it will allow you to
- migrate the existing uploads to the object storage, but all new
- uploads will still be stored on the local disk. In the future
- you will be given an option to define a default storage for all
- new files.
-
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab"
[eep]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition Premium"
diff --git a/doc/api/README.md b/doc/api/README.md
index 6267618d3bc..45e926d3b6b 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -388,7 +388,7 @@ For example, `/` is represented by `%2F`:
GET /api/v4/projects/diaspora%2Fdiaspora
```
-## Branches & tags name encoding
+## Branches and tags name encoding
If your branch or tag contains a `/`, make sure the branch/tag name is
URL-encoded.
@@ -399,6 +399,49 @@ For example, `/` is represented by `%2F`:
GET /api/v4/projects/1/branches/my%2Fbranch/commits
```
+## Encoding API parameters of `array` and `hash` types
+
+We can call the API with `array` and `hash` types parameters as shown below:
+
+### `array`
+
+`import_sources` is a parameter of type `array`:
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
+-d "import_sources[]=github" \
+-d "import_sources[]=bitbucket" \
+"https://gitlab.example.com/api/v4/some_endpoint
+```
+
+### `hash`
+
+`override_params` is a parameter of type `hash`:
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
+--form "namespace=email" \
+--form "path=impapi" \
+--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
+```
+
+### Array of hashes
+
+`variables` is a parameter of type `array` containing hash key/value pairs `[{ 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }]`:
+
+```bash
+curl --globoff --request POST --header "PRIVATE-TOKEN: ********************" \
+"https://gitlab.example.com/api/v4/projects/169/pipeline?ref=master&variables[][key]=VAR1&variables[][value]=hello&variables[][key]=VAR2&variables[][value]=world"
+
+curl --request POST --header "PRIVATE-TOKEN: ********************" \
+--header "Content-Type: application/json" \
+--data '{ "ref": "master", "variables": [ {"key": "VAR1", "value": "hello"}, {"key": "VAR2", "value": "world"} ] }' \
+"https://gitlab.example.com/api/v4/projects/169/pipeline"
+```
+
## `id` vs `iid`
When you work with the API, you may notice two similar fields in API entities:
diff --git a/doc/api/groups.md b/doc/api/groups.md
index a48905f2f15..87be36cc815 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -10,13 +10,14 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups owned by the current user |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
```
GET /groups
@@ -94,13 +95,14 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group |
| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups owned by the current user |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
```
GET /groups/:id/subgroups
@@ -147,6 +149,8 @@ Parameters:
| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
| `owned` | boolean | no | Limit by projects owned by the current user |
| `starred` | boolean | no | Limit by projects starred by the current user |
+| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
+| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
Example response:
@@ -208,6 +212,7 @@ 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`). |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4
@@ -359,6 +364,30 @@ Example response:
}
```
+When adding the parameter `with_projects=false`, projects will not be returned.
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4?with_projects=false
+```
+
+Example response:
+
+```json
+{
+ "id": 4,
+ "name": "Twitter",
+ "path": "twitter",
+ "description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.",
+ "visibility": "public",
+ "avatar_url": null,
+ "web_url": "https://gitlab.example.com/groups/twitter",
+ "request_access_enabled": false,
+ "full_name": "Twitter",
+ "full_path": "twitter",
+ "parent_id": null
+}
+```
+
## New group
Creates a new project group. Available only for users who can create groups.
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 5613cb6d915..103eaa5655f 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -37,7 +37,7 @@ GET /issues?my_reaction_emoji=star
| ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
-| `milestone` | string | no | The milestone title |
+| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
@@ -151,7 +151,7 @@ GET /groups/:id/issues?my_reaction_emoji=star
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
-| `milestone` | string | no | The milestone title |
+| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
@@ -265,7 +265,7 @@ GET /projects/:id/issues?my_reaction_emoji=star
| `iids[]` | Array[integer] | no | Return only the milestone having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
-| `milestone` | string | no | The milestone title |
+| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
@@ -463,6 +463,7 @@ POST /projects/:id/issues
| 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 |
+| `iid` | integer/string | no | The internal ID of the project's issue (requires admin or project owner rights) |
| `title` | string | yes | The title of an issue |
| `description` | string | no | The description of an issue |
| `confidential` | boolean | no | Set an issue to be confidential. Default is `false`. |
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index 0fbfc7cf0fd..9a950097675 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -32,15 +32,12 @@ Example of response
"title": "Test the CI integration."
},
"coverage": null,
- "created_at": "2015-12-24T15:51:21.802Z",
- "artifacts_file": {
- "filename": "artifacts.zip",
- "size": 1000
- },
- "finished_at": "2015-12-24T17:54:27.895Z",
- "artifacts_expire_at": "2016-01-23T17:54:27.895Z"
- "id": 7,
- "name": "teaspoon",
+ "created_at": "2015-12-24T15:51:21.727Z",
+ "artifacts_file": null,
+ "finished_at": "2015-12-24T17:54:24.921Z",
+ "artifacts_expire_at": "2016-01-23T17:54:24.921Z",
+ "id": 6,
+ "name": "rspec:other",
"pipeline": {
"id": 6,
"ref": "master",
@@ -50,9 +47,10 @@ Example of response
"ref": "master",
"runner": null,
"stage": "test",
- "started_at": "2015-12-24T17:54:27.722Z",
+ "started_at": "2015-12-24T17:54:24.729Z",
"status": "failed",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/6",
"user": {
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"bio": null,
@@ -79,12 +77,15 @@ Example of response
"title": "Test the CI integration."
},
"coverage": null,
- "created_at": "2015-12-24T15:51:21.727Z",
- "artifacts_file": null,
- "finished_at": "2015-12-24T17:54:24.921Z",
- "artifacts_expire_at": "2016-01-23T17:54:24.921Z",
- "id": 6,
- "name": "rspec:other",
+ "created_at": "2015-12-24T15:51:21.802Z",
+ "artifacts_file": {
+ "filename": "artifacts.zip",
+ "size": 1000
+ },
+ "finished_at": "2015-12-24T17:54:27.895Z",
+ "artifacts_expire_at": "2016-01-23T17:54:27.895Z",
+ "id": 7,
+ "name": "teaspoon",
"pipeline": {
"id": 6,
"ref": "master",
@@ -94,9 +95,10 @@ Example of response
"ref": "master",
"runner": null,
"stage": "test",
- "started_at": "2015-12-24T17:54:24.729Z",
+ "started_at": "2015-12-24T17:54:27.722Z",
"status": "failed",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/7",
"user": {
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"bio": null,
@@ -148,15 +150,12 @@ Example of response
"title": "Test the CI integration."
},
"coverage": null,
- "created_at": "2015-12-24T15:51:21.802Z",
- "artifacts_file": {
- "filename": "artifacts.zip",
- "size": 1000
- },
- "finished_at": "2015-12-24T17:54:27.895Z",
- "artifacts_expire_at": "2016-01-23T17:54:27.895Z"
- "id": 7,
- "name": "teaspoon",
+ "created_at": "2015-12-24T15:51:21.727Z",
+ "artifacts_file": null,
+ "finished_at": "2015-12-24T17:54:24.921Z",
+ "artifacts_expire_at": "2016-01-23T17:54:24.921Z",
+ "id": 6,
+ "name": "rspec:other",
"pipeline": {
"id": 6,
"ref": "master",
@@ -166,9 +165,10 @@ Example of response
"ref": "master",
"runner": null,
"stage": "test",
- "started_at": "2015-12-24T17:54:27.722Z",
+ "started_at": "2015-12-24T17:54:24.729Z",
"status": "failed",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/6",
"user": {
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"bio": null,
@@ -195,12 +195,15 @@ Example of response
"title": "Test the CI integration."
},
"coverage": null,
- "created_at": "2015-12-24T15:51:21.727Z",
- "artifacts_file": null,
- "finished_at": "2015-12-24T17:54:24.921Z",
- "artifacts_expire_at": "2016-01-23T17:54:24.921Z"
- "id": 6,
- "name": "rspec:other",
+ "created_at": "2015-12-24T15:51:21.802Z",
+ "artifacts_file": {
+ "filename": "artifacts.zip",
+ "size": 1000
+ },
+ "finished_at": "2015-12-24T17:54:27.895Z",
+ "artifacts_expire_at": "2016-01-23T17:54:27.895Z",
+ "id": 7,
+ "name": "teaspoon",
"pipeline": {
"id": 6,
"ref": "master",
@@ -210,9 +213,10 @@ Example of response
"ref": "master",
"runner": null,
"stage": "test",
- "started_at": "2015-12-24T17:54:24.729Z",
+ "started_at": "2015-12-24T17:54:27.722Z",
"status": "failed",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/7",
"user": {
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"bio": null,
@@ -280,6 +284,7 @@ Example of response
"started_at": "2015-12-24T17:54:30.733Z",
"status": "failed",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/8",
"user": {
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"bio": null,
@@ -455,7 +460,7 @@ Example of response
"created_at": "2016-01-11T10:13:33.506Z",
"artifacts_file": null,
"finished_at": "2016-01-11T10:14:09.526Z",
- "id": 69,
+ "id": 42,
"name": "rubocop",
"ref": "master",
"runner": null,
@@ -463,6 +468,7 @@ Example of response
"started_at": null,
"status": "canceled",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/42",
"user": null
}
```
@@ -501,7 +507,7 @@ Example of response
"created_at": "2016-01-11T10:13:33.506Z",
"artifacts_file": null,
"finished_at": null,
- "id": 69,
+ "id": 42,
"name": "rubocop",
"ref": "master",
"runner": null,
@@ -509,6 +515,7 @@ Example of response
"started_at": null,
"status": "pending",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/42",
"user": null
}
```
@@ -549,7 +556,7 @@ Example of response
},
"coverage": null,
"download_url": null,
- "id": 69,
+ "id": 42,
"name": "rubocop",
"ref": "master",
"runner": null,
@@ -559,6 +566,7 @@ Example of response
"finished_at": "2016-01-11T10:15:10.506Z",
"status": "failed",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/42",
"user": null
}
```
@@ -599,7 +607,7 @@ Example response:
},
"coverage": null,
"download_url": null,
- "id": 69,
+ "id": 42,
"name": "rubocop",
"ref": "master",
"runner": null,
@@ -609,6 +617,7 @@ Example response:
"finished_at": "2016-01-11T10:15:10.506Z",
"status": "failed",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/42",
"user": null
}
```
@@ -647,7 +656,7 @@ Example of response
"created_at": "2016-01-11T10:13:33.506Z",
"artifacts_file": null,
"finished_at": null,
- "id": 69,
+ "id": 42,
"name": "rubocop",
"ref": "master",
"runner": null,
@@ -655,6 +664,7 @@ Example of response
"started_at": null,
"status": "started",
"tag": false,
+ "web_url": "https://example.com/foo/bar/-/jobs/42",
"user": null
}
```
diff --git a/doc/api/members.md b/doc/api/members.md
index 8ebe464c359..7b228b92594 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -40,7 +40,9 @@ Example response:
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
"access_level": 30
},
{
@@ -48,7 +50,65 @@ Example response:
"username": "john_doe",
"name": "John Doe",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
+ }
+]
+```
+
+## List all members of a group or project including inherited members
+
+Gets a list of group or project members viewable by the authenticated user, including inherited members through ancestor groups.
+
+```
+GET /groups/:id/members/all
+GET /projects/:id/members/all
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `query` | string | no | A query string to search for members |
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/members/all
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/members/all
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 1,
+ "username": "raymond_smith",
+ "name": "Raymond Smith",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
+ },
+ {
+ "id": 2,
+ "username": "john_doe",
+ "name": "John Doe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
+ },
+ {
+ "id": 3,
+ "username": "foo_bar",
+ "name": "Foo bar",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-11-22T14:13:35Z",
"access_level": 30
}
]
@@ -81,7 +141,8 @@ Example response:
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
"access_level": 30,
"expires_at": null
}
@@ -116,7 +177,9 @@ Example response:
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
"access_level": 30
}
```
@@ -150,7 +213,9 @@ Example response:
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
"access_level": 40
}
```
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index da74045b702..58d05a70d05 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -11,15 +11,10 @@ default it returns only merge requests created by the current user. To
get all merge requests, use parameter `scope=all`.
The `state` parameter can be used to get only merge requests with a
-given state (`opened`, `closed`, or `merged`) or all of them (`all`).
+given state (`opened`, `closed`, `locked`, or `merged`) or all of them (`all`). It should be noted that when searching by `locked` it will mostly return no results as it is a short-lived, transitional state.
The pagination parameters `page` and `per_page` can be used to
restrict the list of merge requests.
-**Note**: the `changes_count` value in the response is a string, not an
-integer. This is because when an MR has too many changes to display and store,
-it will be capped at 1,000. In that case, the API will return the string
-`"1000+"` for the changes count.
-
```
GET /merge_requests
GET /merge_requests?state=opened
@@ -35,7 +30,7 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, or `merged` |
+| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
| `milestone` | string | no | Return merge requests for a specific milestone |
@@ -104,7 +99,6 @@ Parameters:
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
"should_remove_source_branch": true,
"force_remove_source_branch": false,
"squash": false,
@@ -122,7 +116,7 @@ Parameters:
## List project merge requests
Get all merge requests for this project.
-The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
+The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, `locked`, or `merged`) or all of them (`all`).
The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
```
@@ -144,10 +138,6 @@ will be the same. In the case of a merge request from a fork,
`target_project_id` and `project_id` will be the same and
`source_project_id` will be the fork project's ID.
-**Note**: the `changes_count` value in the response is a string, not an
-integer. This is because when an MR has too many changes to display and store,
-it will be capped at 1,000. In that case, the API will return the string
-`"1000+"` for the changes count.
Parameters:
@@ -155,7 +145,7 @@ Parameters:
| ------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `id` | integer | yes | The ID of a project |
| `iids[]` | Array[integer] | no | Return the request having the given `iid` |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, or `merged` |
+| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
| `milestone` | string | no | Return merge requests for a specific milestone |
@@ -224,7 +214,6 @@ Parameters:
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
"should_remove_source_branch": true,
"force_remove_source_branch": false,
"squash": false,
@@ -243,7 +232,7 @@ Parameters:
## List group merge requests
Get all merge requests for this group and its subgroups.
-The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
+The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, `locked`, or `merged`) or all of them (`all`).
The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
```
@@ -262,7 +251,7 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `id` | integer | yes | The ID of a group |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, or `merged` |
+| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
| `order_by` | string | no | Return merge requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return merge requests sorted in `asc` or `desc` order. Default is `desc` |
| `milestone` | string | no | Return merge requests for a specific milestone |
@@ -331,7 +320,6 @@ Parameters:
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
- "changes_count": "1",
"should_remove_source_branch": true,
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1",
@@ -350,6 +338,11 @@ Parameters:
Shows information about a single merge request.
+**Note**: the `changes_count` value in the response is a string, not an
+integer. This is because when an MR has too many changes to display and store,
+it will be capped at 1,000. In that case, the API will return the string
+`"1000+"` for the changes count.
+
```
GET /projects/:id/merge_requests/:merge_request_iid
```
@@ -358,6 +351,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The internal ID of the merge request
+- `render_html` (optional) - If `true` response includes rendered HTML for title and description
```json
{
diff --git a/doc/api/notes.md b/doc/api/notes.md
index d29c5b94915..c271d46688f 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -218,6 +218,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
+- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/snippet/11/notes?body=note
@@ -340,6 +341,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
+- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
### Modify existing merge request note
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index ebae68fe389..574be52801c 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -33,13 +33,15 @@ Example of response
"id": 47,
"status": "pending",
"ref": "new-pipeline",
- "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a"
+ "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
+ "web_url": "https://example.com/foo/bar/pipelines/47"
},
{
"id": 48,
"status": "pending",
"ref": "new-pipeline",
- "sha": "eb94b618fb5865b26e80fdd8ae531b7a63ad851a"
+ "sha": "eb94b618fb5865b26e80fdd8ae531b7a63ad851a",
+ "web_url": "https://example.com/foo/bar/pipelines/48"
}
]
```
@@ -86,7 +88,8 @@ Example of response
"finished_at": "2016-08-11T11:32:35.145Z",
"committed_at": null,
"duration": null,
- "coverage": "30.0"
+ "coverage": "30.0",
+ "web_url": "https://example.com/foo/bar/pipelines/46"
}
```
@@ -133,7 +136,8 @@ Example of response
"finished_at": null,
"committed_at": null,
"duration": null,
- "coverage": null
+ "coverage": null,
+ "web_url": "https://example.com/foo/bar/pipelines/61"
}
```
@@ -151,7 +155,7 @@ POST /projects/:id/pipelines/:pipeline_id/retry
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/retry"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/retry"
```
Response:
@@ -179,7 +183,8 @@ Response:
"finished_at": "2016-08-11T11:32:35.145Z",
"committed_at": null,
"duration": null,
- "coverage": null
+ "coverage": null,
+ "web_url": "https://example.com/foo/bar/pipelines/46"
}
```
@@ -197,7 +202,7 @@ POST /projects/:id/pipelines/:pipeline_id/cancel
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/cancel"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/cancel"
```
Response:
@@ -225,7 +230,8 @@ Response:
"finished_at": "2016-08-11T11:32:35.145Z",
"committed_at": null,
"duration": null,
- "coverage": null
+ "coverage": null,
+ "web_url": "https://example.com/foo/bar/pipelines/46"
}
```
diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md
index 085437c801a..83e405141f1 100644
--- a/doc/api/project_import_export.md
+++ b/doc/api/project_import_export.md
@@ -28,8 +28,11 @@ POST /projects/:id/export
| `upload[url]` | string | yes | The URL to upload the project |
| `upload[http_method]` | string | no | The HTTP method to upload the exported project. Only `PUT` and `POST` methods allowed. Default is `PUT` |
+
```console
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/export --data "description=FooBar&upload[http_method]=PUT&upload[url]=https://example-bucket.s3.eu-west-3.amazonaws.com/backup?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIMBJHN2O62W8IELQ%2F20180312%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20180312T110328Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=8413facb20ff33a49a147a0b4abcff4c8487cc33ee1f7e450c46e8f695569dbd"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/export \
+ --data "upload[http_method]=PUT" \
+ --data-urlencode "upload[url]=https://example-bucket.s3.eu-west-3.amazonaws.com/backup?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIMBJHN2O62W8IELQ%2F20180312%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20180312T110328Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=8413facb20ff33a49a147a0b4abcff4c8487cc33ee1f7e450c46e8f695569dbd"
```
```json
@@ -125,6 +128,29 @@ by `@`. For example:
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "path=api-project" --form "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import
```
+cURL doesn't support posting a file from a remote server. Importing a project from a remote server can be accomplished through something like the following:
+
+```python
+import requests
+import urllib
+import json
+import sys
+
+s3_file = urllib.urlopen(presigned_url)
+
+url = 'https://gitlab.example.com/api/v4/projects/import'
+files = {'file': s3_file}
+data = {
+ "path": "example-project",
+ "namespace": "example-group"
+}
+headers = {
+ 'Private-Token': "9koXpg98eAheJpvBs5tK"
+}
+
+requests.post(url, headers=headers, data=data, files=files)
+```
+
```json
{
"id": 1,
diff --git a/doc/api/projects.md b/doc/api/projects.md
index b4599fdc97e..f360b49c293 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -34,7 +34,7 @@ There are currently three options for `merge_method` to choose from:
## List all projects
Get a list of all visible projects across GitLab for the authenticated user.
-When accessed without authentication, only public projects are returned.
+When accessed without authentication, only public projects with "simple" fields are returned.
```
GET /projects
@@ -47,14 +47,52 @@ GET /projects
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
-| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+| `wiki_checksum_failed` | boolean | no | Limit projects where the wiki checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
+| `repository_checksum_failed` | boolean | no | Limit projects where the repository checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
+
+When `simple=true` or the user is unauthenticated this returns something like:
+
+```json
+[
+ {
+ "id": 4,
+ "description": null,
+ "default_branch": "master",
+ "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
+ "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
+ "web_url": "http://example.com/diaspora/diaspora-client",
+ "readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",
+ "tag_list": [
+ "example",
+ "disapora client"
+ ],
+ "name": "Diaspora Client",
+ "name_with_namespace": "Diaspora / Diaspora Client",
+ "path": "diaspora-client",
+ "path_with_namespace": "diaspora/diaspora-client",
+ "created_at": "2013-09-30T13:46:02Z",
+ "last_activity_at": "2013-09-30T13:46:02Z",
+ "forks_count": 0,
+ "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",
+ "star_count": 0,
+ },
+ {
+ "id": 6,
+ "description": null,
+ "default_branch": "master",
+...
+```
+
+When the user is authenticated and `simple` is not set this returns something like:
```json
[
@@ -235,14 +273,15 @@ GET /users/:user_id/projects
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
-| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
```json
[
@@ -511,6 +550,47 @@ GET /projects/:id
}
```
+If the project is a fork, and you provide a valid token to authenticate, the
+`forked_from_project` field will appear in the response.
+
+```json
+{
+ "id":3,
+
+ ...
+
+ "forked_from_project":{
+ "id":13083,
+ "description":"GitLab Community Edition",
+ "name":"GitLab Community Edition",
+ "name_with_namespace":"GitLab.org / GitLab Community Edition",
+ "path":"gitlab-ce",
+ "path_with_namespace":"gitlab-org/gitlab-ce",
+ "created_at":"2013-09-26T06:02:36.000Z",
+ "default_branch":"master",
+ "tag_list":[],
+ "ssh_url_to_repo":"git@gitlab.com:gitlab-org/gitlab-ce.git",
+ "http_url_to_repo":"https://gitlab.com/gitlab-org/gitlab-ce.git",
+ "web_url":"https://gitlab.com/gitlab-org/gitlab-ce",
+ "avatar_url":"https://assets.gitlab-static.net/uploads/-/system/project/avatar/13083/logo-extra-whitespace.png",
+ "star_count":3812,
+ "forks_count":3561,
+ "last_activity_at":"2018-01-02T11:40:26.570Z",
+ "namespace": {
+ "id": 72,
+ "name": "GitLab.org",
+ "path": "gitlab-org",
+ "kind": "group",
+ "full_path": "gitlab-org",
+ "parent_id": null
+ }
+ }
+
+ ...
+
+}
+```
+
## Get project users
Get the users list of a project.
@@ -690,14 +770,15 @@ GET /projects/:id/forks
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
-| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/forks"
@@ -1441,3 +1522,6 @@ GET /projects/:id/snapshot
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `wiki` | boolean | no | Whether to download the wiki, rather than project, repository |
+
+[eep]: https://about.gitlab.com/pricing/ "Available only in GitLab Premium"
+[ee-6137]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137
diff --git a/doc/api/services.md b/doc/api/services.md
index aeb48ccc36c..efa173180bb 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -443,6 +443,54 @@ Get Gemnasium service settings for a project.
GET /projects/:id/services/gemnasium
```
+## Hangouts Chat
+
+Google GSuite team collaboration tool.
+
+>**Note:** This service was [introduced in v11.2](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20290)
+
+### Create/Edit Hangouts Chat service
+
+Set Hangouts Chat service for a project.
+
+```
+PUT /projects/:id/services/hangouts_chat
+```
+
+>**Note:** Specific event parameters (e.g. `push_events` flag) were [introduced in v10.4][11435]
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces... |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | Send notifications only for the default branch |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Hangouts Chat service
+
+Delete Hangouts Chat service for a project.
+
+```
+DELETE /projects/:id/services/hangouts_chat
+```
+
+### Get Hangouts Chat service settings
+
+Get Hangouts Chat service settings for a project.
+
+```
+GET /projects/:id/services/hangouts_chat
+```
+
## HipChat
Private group chat and IM
diff --git a/doc/api/settings.md b/doc/api/settings.md
index e6b207d8746..68fc56b1fa3 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -55,7 +55,8 @@ Example response:
"ed25519_key_restriction": 0,
"enforce_terms": true,
"terms": "Hello world!",
- "performance_bar_allowed_group_id": 42
+ "performance_bar_allowed_group_id": 42,
+ "instance_statistics_visibility_private": false
}
```
@@ -105,7 +106,7 @@ PUT /application/settings
| `housekeeping_gc_period` | integer | no | Number of Git pushes after which 'git gc' is run. |
| `housekeeping_incremental_repack_period` | integer | no | 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 gitlab google_code fogbugz git gitlab_project |
+| `import_sources` | Array of strings | no | Sources to allow project import from, possible values: "github bitbucket gitlab google_code fogbugz git gitlab_project manifest |
| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
| `max_artifacts_size` | integer | no | Maximum artifacts size in MB |
@@ -159,6 +160,7 @@ PUT /application/settings
| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |
| `enforce_terms` | boolean | no | Enforce application ToS to all users |
| `terms` | text | yes (if `enforce_terms` is true) | Markdown content for the ToS |
+| `instance_statistics_visibility_private` | boolean | no | When set to `true` Instance statistics will only be available to admins |
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/application/settings?signup_enabled=false&default_project_visibility=internal
@@ -203,6 +205,7 @@ Example response:
"ed25519_key_restriction": 0,
"enforce_terms": true,
"terms": "Hello world!",
- "performance_bar_allowed_group_id": 42
+ "performance_bar_allowed_group_id": 42,
+ "instance_statistics_visibility_private": false
}
```
diff --git a/doc/api/todos.md b/doc/api/todos.md
index 27e623007cc..0843e4eedc6 100644
--- a/doc/api/todos.md
+++ b/doc/api/todos.md
@@ -18,6 +18,7 @@ Parameters:
| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, `approval_required`, `unmergeable` or `directly_addressed`. |
| `author_id` | integer | no | The ID of an author |
| `project_id` | integer | no | The ID of a project |
+| `group_id` | integer | no | The ID of a group |
| `state` | string | no | The state of the todo. Can be either `pending` or `done` |
| `type` | string | no | The type of a todo. Can be either `Issue` or `MergeRequest` |
diff --git a/doc/api/users.md b/doc/api/users.md
index ca5afa04687..a8858468cab 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -33,6 +33,20 @@ GET /users
]
```
+You can also search for users by email or username with: `/users?search=John`
+
+In addition, you can lookup users by username:
+
+```
+GET /users?username=:username
+```
+
+For example:
+
+```
+GET /users?username=jack_smith
+```
+
In addition, you can filter users based on states eg. `blocked`, `active`
This works only to filter users who are `blocked` or `active`.
It does not support `active=false` or `blocked=false`.
@@ -91,7 +105,8 @@ GET /users
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
},
{
"id": 2,
@@ -121,26 +136,13 @@ GET /users
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
}
]
```
-You can search for users by email or username with: `/users?search=John`
-
-In addition, you can lookup users by username:
-
-```
-GET /users?username=:username
-```
-
-For example:
-
-```
-GET /users?username=jack_smith
-```
-
-You can also lookup users by external UID and provider:
+You can lookup users by external UID and provider:
```
GET /users?extern_uid=:extern_uid&provider=:provider
@@ -248,7 +250,8 @@ Parameters:
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
}
```
@@ -288,6 +291,7 @@ Parameters:
- `skip_confirmation` (optional) - Skip confirmation - true or false (default)
- `external` (optional) - Flags the user as external - true or false(default)
- `avatar` (optional) - Image file for user's avatar
+- `private_profile` (optional) - User's profile is private - true or false
## User modification
@@ -318,6 +322,7 @@ Parameters:
- `skip_reconfirmation` (optional) - Skip reconfirmation - true or false (default)
- `external` (optional) - Flags the user as external - true or false(default)
- `avatar` (optional) - Image file for user's avatar
+- `private_profile` (optional) - User's profile is private - true or false
On password update, user will be forced to change it upon next login.
Note, at the moment this method does only return a `404` error,
@@ -382,7 +387,8 @@ GET /user
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
}
```
@@ -429,7 +435,85 @@ GET /user
"can_create_group": true,
"can_create_project": true,
"two_factor_enabled": true,
- "external": false
+ "external": false,
+ "private_profile": false
+}
+```
+
+## User status
+
+Get the status of the currently signed in user.
+
+```
+GET /user/status
+```
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/user/status"
+```
+
+Example response:
+
+```json
+{
+ "emoji":"coffee",
+ "message":"I crave coffee :coffee:",
+ "message_html": "I crave coffee <gl-emoji title=\"hot beverage\" data-name=\"coffee\" data-unicode-version=\"4.0\">☕</gl-emoji>"
+}
+```
+
+## Get the status of a user
+
+Get the status of a user.
+
+```
+GET /users/:id_or_username/status
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id_or_username` | string | yes | The id or username of the user to get a status of |
+
+```bash
+curl "https://gitlab.example.com/users/janedoe/status"
+```
+
+Example response:
+
+```json
+{
+ "emoji":"coffee",
+ "message":"I crave coffee :coffee:",
+ "message_html": "I crave coffee <gl-emoji title=\"hot beverage\" data-name=\"coffee\" data-unicode-version=\"4.0\">☕</gl-emoji>"
+}
+```
+
+## Set user status
+
+Set the status of the current user.
+
+```
+PUT /user/status
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `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][gemojione-index]. |
+| `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.
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "emoji=coffee" --data "emoji=I crave coffee" https://gitlab.example.com/api/v4/user/status
+```
+
+Example responses
+
+```json
+{
+ "emoji":"coffee",
+ "message":"I crave coffee",
+ "message_html": "I crave coffee"
}
```
@@ -1160,3 +1244,5 @@ Example response:
```
Please note that `last_activity_at` is deprecated, please use `last_activity_on`.
+
+[gemojione-index]: https://github.com/jonathanwiesel/gemojione/blob/master/config/index.json
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index 07b144f6ddd..63338ff632c 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -134,9 +134,20 @@ In order to do that, follow the steps:
```yaml
image: docker:stable
- # When using dind, it's wise to use the overlayfs driver for
- # improved performance.
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.
+ #
+ # 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
+ #
+ # Note that if you're using Kubernetes executor, the variable should be set to
+ # tcp://localhost:2375 because of how Kubernetes executor connects services
+ # to the job container
+ DOCKER_HOST: tcp://docker:2375/
+ # When using dind, it's wise to use the overlayfs driver for
+ # improved performance.
DOCKER_DRIVER: overlay2
services:
@@ -293,6 +304,7 @@ services:
variables:
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
+ DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
before_script:
@@ -391,6 +403,9 @@ could look like:
image: docker:stable
services:
- docker:dind
+ variables:
+ DOCKER_HOST: tcp://docker:2375
+ DOCKER_DRIVER: overlay2
stage: build
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.example.com
@@ -410,7 +425,9 @@ services:
- docker:dind
variables:
- IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
+ DOCKER_HOST: tcp://docker:2375
+ DOCKER_DRIVER: overlay2
+ IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
@@ -423,8 +440,10 @@ build:
```
Here, `$CI_REGISTRY_IMAGE` would be resolved to the address of the registry tied
-to this project, and `$CI_COMMIT_REF_NAME` would be resolved to the branch or
-tag name for this particular job. We also declare our own variable, `$IMAGE_TAG`,
+to this project. Since `$CI_COMMIT_REF_NAME` resolves to the branch or tag name,
+and your branch-name can contain forward slashes (e.g., feature/my-feature), it is
+safer to use `$CI_COMMIT_REF_SLUG` as the image tag. This is due to that image tags
+cannot contain forward slashes. We also declare our own variable, `$IMAGE_TAG`,
combining the two to save us some typing in the `script` section.
Here's a more elaborate example that splits up the tasks into 4 pipeline stages,
@@ -445,7 +464,9 @@ stages:
- deploy
variables:
- CONTAINER_TEST_IMAGE: registry.example.com/my-group/my-project/my-image:$CI_COMMIT_REF_NAME
+ DOCKER_HOST: tcp://docker:2375
+ DOCKER_DRIVER: overlay2
+ CONTAINER_TEST_IMAGE: registry.example.com/my-group/my-project/my-image:$CI_COMMIT_REF_SLUG
CONTAINER_RELEASE_IMAGE: registry.example.com/my-group/my-project/my-image:latest
before_script:
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index aa31e172641..811f4d1f07a 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -43,9 +43,9 @@ There's also a collection of repositories with [example projects](https://gitlab
- [Using `dpl` as deployment tool](deployment/README.md)
- [The `.gitlab-ci.yml` file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
-## Code quality analysis
+## Code Quality analysis
-[Analyze code quality with the Code Climate CLI](code_climate.md).
+**(Starter)** [Analyze your project's Code Quality](code_quality.md).
## Static Application Security Testing (SAST)
diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md
index cc19e090964..b34637efc8d 100644
--- a/doc/ci/examples/code_climate.md
+++ b/doc/ci/examples/code_climate.md
@@ -1,49 +1,6 @@
-# Analyze project code quality with Code Climate CLI
+---
+redirect_from: 'https://docs.gitlab.com/ee/ci/examples/code_climate.html'
+redirect_to: code_quality.md
+---
-This example shows how to run [Code Climate CLI][cli] on your code by using
-GitLab CI and Docker.
-
-First, you need GitLab Runner with [docker-in-docker executor][dind].
-
-Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `code_quality`:
-
-```yaml
-code_quality:
- image: docker:stable
- variables:
- DOCKER_DRIVER: overlay2
- allow_failure: true
- services:
- - docker:stable-dind
- script:
- - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- - docker run
- --env SOURCE_CODE="$PWD"
- --volume "$PWD":/code
- --volume /var/run/docker.sock:/var/run/docker.sock
- "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
- artifacts:
- paths: [gl-code-quality-report.json]
-```
-
-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 an artifact that you can later download and analyze.
-
-TIP: **Tip:**
-Starting with [GitLab Starter][ee] 9.3, this information will
-be automatically extracted and shown right in the merge request widget. To do
-so, the CI/CD job must be named `code_quality` and the artifact path must be
-`gl-code-quality-report.json`.
-[Learn more on code quality diffs in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
-
-CAUTION: **Caution:**
-Code Quality was previously using `codeclimate` and `codequality` for job name and
-`codeclimate.json` for the artifact name. While these old names
-are still maintained they have been deprecated with GitLab 11.0 and may be removed
-in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
-configuration to reflect that change.
-
-[cli]: https://github.com/codeclimate/codeclimate
-[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
-[ee]: https://about.gitlab.com/products/
+This document was moved to [another location](code_quality.md).
diff --git a/doc/ci/examples/code_quality.md b/doc/ci/examples/code_quality.md
new file mode 100644
index 00000000000..2a7040ecdeb
--- /dev/null
+++ b/doc/ci/examples/code_quality.md
@@ -0,0 +1,49 @@
+# Analyze your project's Code Quality
+
+This example shows how to run Code Quality on your code by using GitLab CI/CD
+and Docker.
+
+First, you need GitLab Runner with [docker-in-docker executor][dind].
+
+Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `code_quality`:
+
+```yaml
+code_quality:
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+ - docker run
+ --env SOURCE_CODE="$PWD"
+ --volume "$PWD":/code
+ --volume /var/run/docker.sock:/var/run/docker.sock
+ "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
+ artifacts:
+ paths: [gl-code-quality-report.json]
+```
+
+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 an artifact that you can later download and analyze.
+
+TIP: **Tip:**
+Starting with [GitLab Starter][ee] 9.3, this information will
+be automatically extracted and shown right in the merge request widget. To do
+so, the CI/CD job must be named `code_quality` and the artifact path must be
+`gl-code-quality-report.json`.
+[Learn more on Code Quality in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html).
+
+CAUTION: **Caution:**
+Code Quality was previously using `codeclimate` and `codequality` for job name and
+`codeclimate.json` for the artifact name. While these old names
+are still maintained they have been deprecated with GitLab 11.0 and may be removed
+in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
+configuration to reflect that change.
+
+[cli]: https://github.com/codeclimate/codeclimate
+[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
+[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/ci/examples/container_scanning.md b/doc/ci/examples/container_scanning.md
index 92ff90507ee..0f79f7d1b17 100644
--- a/doc/ci/examples/container_scanning.md
+++ b/doc/ci/examples/container_scanning.md
@@ -57,10 +57,10 @@ so, the CI/CD job must be named `container_scanning` and the artifact path must
[Learn more on container scanning results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/container_scanning.html).
CAUTION: **Caution:**
-Container Scanning was previously using `sast:container` for job name and
+Before GitLab 11.0, Container Scanning was previously using `sast:container` for job name and
`gl-sast-container-report.json` for the artifact name. While these old names
-are still maintained they have been deprecated with GitLab 11.0 and may be removed
+are still maintained, they have been deprecated with GitLab 11.0 and may be removed
in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
configuration to reflect that change.
-[ee]: https://about.gitlab.com/products/
+[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/ci/examples/dast.md b/doc/ci/examples/dast.md
index a8720f0b7ea..ff20f0b3b5e 100644
--- a/doc/ci/examples/dast.md
+++ b/doc/ci/examples/dast.md
@@ -60,4 +60,4 @@ so, the CI job must be named `dast` and the artifact path must be
`gl-dast-report.json`.
[Learn more about DAST results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
-[ee]: https://about.gitlab.com/products/
+[ee]: https://about.gitlab.com/pricing/
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 a433cd5a5dd..087b317ab73 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
@@ -46,7 +46,7 @@ This project has three jobs:
## Store API keys
-You'll need to create two variables in `Project > Variables`:
+You'll need to create two variables in `Settings > CI/CD > Variables` on your GitLab project settings:
1. `HEROKU_STAGING_API_KEY` - Heroku API key used to deploy staging app,
2. `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app.
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index c507036aa6a..c213b096a14 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -219,7 +219,7 @@ removed with one of the future versions of GitLab. You are advised to
[ee-2017]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2017
[ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229
-[ee]: https://about.gitlab.com/products/
+[ee]: https://about.gitlab.com/pricing/
[variables]: ../variables/README.md
[predef]: ../variables/README.md#predefined-variables-environment-variables
[registry]: ../../user/project/container_registry.md
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index a3da6515a19..115e6e390c9 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -47,6 +47,7 @@ future GitLab releases.**
| **CI_COMMIT_REF_NAME** | 9.0 | all | The branch or tag name for which project is built |
| **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_BEFORE_SHA** | 11.2 | all | The previous latest commit present on a branch before a push request. |
| **CI_COMMIT_TAG** | 9.0 | 0.5 | The commit tag name. Present only when building tags. |
| **CI_COMMIT_MESSAGE** | 10.8 | all | The full commit message. |
| **CI_COMMIT_TITLE** | 10.8 | all | The title of the commit - the full first line of the message |
@@ -63,8 +64,8 @@ future GitLab releases.**
| **CI_JOB_MANUAL** | 8.12 | all | The flag to indicate that job was manually started |
| **CI_JOB_NAME** | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` |
| **CI_JOB_STAGE** | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
-| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the GitLab Container Registry |
-| **CI_JOB_URL** | 11.0 | 0.5 | Job details URL |
+| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the [GitLab Container Registry][registry] and downloading [dependent repositories][dependent-repositories] |
+| **CI_JOB_URL** | 11.1 | 0.5 | Job details URL |
| **CI_REPOSITORY_URL** | 9.0 | all | The URL to clone the Git repository |
| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
@@ -82,7 +83,7 @@ future GitLab releases.**
| **CI_PROJECT_NAMESPACE** | 8.10 | 0.5 | The project namespace (username or groupname) that is currently being built |
| **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name |
| **CI_PROJECT_PATH_SLUG** | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
-| **CI_PIPELINE_URL** | 11.0 | 0.5 | Pipeline details URL |
+| **CI_PIPELINE_URL** | 11.1 | 0.5 | Pipeline details URL |
| **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP 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 |
@@ -118,6 +119,7 @@ future GitLab releases.**
| `CI_BUILD_ID` | `CI_JOB_ID` |
| `CI_BUILD_REF` | `CI_COMMIT_SHA` |
| `CI_BUILD_TAG` | `CI_COMMIT_TAG` |
+| `CI_BUILD_BEFORE_SHA` | `CI_COMMIT_BEFORE_SHA` |
| `CI_BUILD_REF_NAME` | `CI_COMMIT_REF_NAME` |
| `CI_BUILD_REF_SLUG` | `CI_COMMIT_REF_SLUG` |
| `CI_BUILD_NAME` | `CI_JOB_NAME` |
@@ -553,7 +555,7 @@ Below you can find supported syntax reference:
`/pattern/i` to make a pattern case-insensitive.
[ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 "Simple protection of CI variables"
-[eep]: https://about.gitlab.com/products/ "Available only in GitLab Premium"
+[eep]: https://about.gitlab.com/pricing/ "Available only in GitLab Premium"
[envs]: ../environments.md
[protected branches]: ../../user/project/protected_branches.md
[protected tags]: ../../user/project/protected_tags.md
@@ -564,3 +566,5 @@ Below you can find supported syntax reference:
[subgroups]: ../../user/group/subgroups/index.md
[builds-policies]: ../yaml/README.md#only-and-except-complex
[gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token
+[registry]: ../../user/project/container_registry.md
+[dependent-repositories]: ../../user/project/new_ci_build_permissions_model.md#dependent-repositories \ No newline at end of file
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 096b64eb881..95d705d3a3d 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -962,8 +962,8 @@ the binaries directory:
```yaml
job:
- artifacts:
- name: "$CI_COMMIT_REF_NAME"
+ artifacts:
+ name: "$CI_COMMIT_REF_NAME"
paths:
- binaries/
```
@@ -1411,43 +1411,6 @@ variables:
You can set it globally or per-job in the [`variables`](#variables) section.
-### Custom build directories
-
-> [Introduced][gitlab-runner-876] in Gitlab Runner 11.1
-
-NOTE: **Note:**
-This can only be used when `custom_build_dir` is set to true in the [Runner's
-configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html).
-
-By default, GitLab Runner clones the repository in the `/builds` directory,
-but sometimes your project might require to have the code in a specific
-directory, like the GO projects for example. In that case, you can specify
-the `CI_PROJECT_DIR` variable to tell the Runner in which directory to clone
-the repository:
-
-```yml
-image: golang:1.10-alpine3.7
-
-variables:
- CI_PROJECT_DIR: /go/src/gitlab.com/namespace/project-name
-
-stages:
- - test
-
-dir:
- stage: test
- script:
- - pwd # /go/src/gitlab.com/namespace/project-name
-```
-
-The following executors may use this feature only when
-[concurrent](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section)
-is set to `1`:
-
-- `shell`
-- `ssh`
-- `docker`, `docker+machine` when the job's working directory is mounted as a host volume.
-
## Special YAML features
It's possible to use special YAML features like anchors (`&`), aliases (`*`)
@@ -1641,6 +1604,5 @@ CI with various languages.
[ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983
[ce-7447]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7447
[ce-12909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12909
-[gitlab-runner-876]: https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/876
[schedules]: ../../user/project/pipelines/schedules.md
[variables-expressions]: ../variables/README.md#variables-expressions
diff --git a/doc/development/README.md b/doc/development/README.md
index 5d6fed5bc72..fed3903c771 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -40,6 +40,7 @@ description: 'Learn how to contribute to GitLab.'
- [View sent emails or preview mailers](emails.md)
- [Shell commands](shell_commands.md) in the GitLab codebase
- [`Gemfile` guidelines](gemfile.md)
+- [Pry debugging](pry_debugging.md)
- [Sidekiq debugging](sidekiq_debugging.md)
- [Gotchas](gotchas.md) to avoid
- [Avoid modules with instance variables](module_with_instance_variables.md) if possible
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 33f078b0a63..6c6e198a7c3 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -54,6 +54,94 @@ a new presenter specifically for GraphQL.
The presenter is initialized using the object resolved by a field, and
the context.
+### Connection Types
+
+GraphQL uses [cursor based
+pagination](https://graphql.org/learn/pagination/#pagination-and-edges)
+to expose collections of items. This provides the clients with a lot
+of flexibility while also allowing the backend to use different
+pagination models.
+
+To expose a collection of resources we can use a connection type. This wraps the array with default pagination fields. For example a query for project-pipelines could look like this:
+
+```
+query($project_path: ID!) {
+ project(fullPath: $project_path) {
+ pipelines(first: 2) {
+ pageInfo {
+ hasNextPage
+ hasPreviousPage
+ }
+ edges {
+ cursor
+ node {
+ id
+ status
+ }
+ }
+ }
+ }
+}
+```
+
+This would return the first 2 pipelines of a project and related
+pagination info., ordered by descending ID. The returned data would
+look like this:
+
+```json
+{
+ "data": {
+ "project": {
+ "pipelines": {
+ "pageInfo": {
+ "hasNextPage": true,
+ "hasPreviousPage": false
+ },
+ "edges": [
+ {
+ "cursor": "Nzc=",
+ "node": {
+ "id": "77",
+ "status": "FAILED"
+ }
+ },
+ {
+ "cursor": "Njc=",
+ "node": {
+ "id": "67",
+ "status": "FAILED"
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+```
+
+To get the next page, the cursor of the last known element could be
+passed:
+
+```
+query($project_path: ID!) {
+ project(fullPath: $project_path) {
+ pipelines(first: 2, after: "Njc=") {
+ pageInfo {
+ hasNextPage
+ hasPreviousPage
+ }
+ edges {
+ cursor
+ node {
+ id
+ status
+ }
+ }
+ }
+ }
+}
+```
+
### Exposing permissions for a type
To expose permissions the current user has on a resource, you can call
@@ -113,6 +201,148 @@ lot of dependant objects.
To limit the amount of queries performed, we can use `BatchLoader`.
+## Mutations
+
+Mutations are used to change any stored values, or to trigger
+actions. In the same way a GET-request should not modify data, we
+cannot modify data in a regular GraphQL-query. We can however in a
+mutation.
+
+### Fields
+
+In the most common situations, a mutation would return 2 fields:
+
+- The resource being modified
+- A list of errors explaining why the action could not be
+ performed. If the mutation succeeded, this list would be empty.
+
+By inheriting any new mutations from `Mutations::BaseMutation` the
+`errors` field is automatically added. A `clientMutationId` field is
+also added, this can be used by the client to identify the result of a
+single mutation when multiple are performed within a single request.
+
+### Building Mutations
+
+Mutations live in `app/graphql/mutations` ideally grouped per
+resources they are mutating, similar to our services. They should
+inherit `Mutations::BaseMutation`. The fields defined on the mutation
+will be returned as the result of the mutation.
+
+Always provide a consistent GraphQL-name to the mutation, this name is
+used to generate the input types and the field the mutation is mounted
+on. The name should look like `<Resource being modified><Mutation
+class name>`, for example the `Mutations::MergeRequests::SetWip`
+mutation has GraphQL name `MergeRequestSetWip`.
+
+Arguments required by the mutation can be defined as arguments
+required for a field. These will be wrapped up in an input type for
+the mutation. For example, the `Mutations::MergeRequests::SetWip`
+with GraphQL-name `MergeRequestSetWip` defines these arguments:
+
+```ruby
+argument :project_path, GraphQL::ID_TYPE,
+ required: true,
+ description: "The project the merge request to mutate is in"
+
+argument :iid, GraphQL::ID_TYPE,
+ required: true,
+ description: "The iid of the merge request to mutate"
+
+argument :wip,
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: <<~DESC
+ Whether or not to set the merge request as a WIP.
+ If not passed, the value will be toggled.
+ DESC
+```
+
+This would automatically generate an input type called
+`MergeRequestSetWipInput` with the 3 arguments we specified and the
+`clientMutationId`.
+
+These arguments are then passed to the `resolve` method of a mutation
+as keyword arguments. From here, we can call the service that will
+modify the resource.
+
+The `resolve` method should then return a hash with the same field
+names as defined on the mutation and an `errors` array. For example,
+the `Mutations::MergeRequests::SetWip` defines a `merge_request`
+field:
+
+```ruby
+field :merge_request,
+ Types::MergeRequestType,
+ null: true,
+ description: "The merge request after mutation"
+```
+
+This means that the hash returned from `resolve` in this mutation
+should look like this:
+
+```ruby
+{
+ # The merge request modified, this will be wrapped in the type
+ # defined on the field
+ merge_request: merge_request,
+ # An array if strings if the mutation failed after authorization
+ errors: merge_request.errors.full_messages
+}
+```
+
+To make the mutation available it should 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
+GraphQL-name of the mutation:
+
+```ruby
+module Types
+ class MutationType < BaseObject
+ include Gitlab::Graphql::MountMutation
+
+ graphql_name "Mutation"
+
+ mount_mutation Mutations::MergeRequests::SetWip
+ end
+end
+```
+
+Will generate a field called `mergeRequestSetWip` that
+`Mutations::MergeRequests::SetWip` to be resolved.
+
+### Authorizing resources
+
+To authorize resources inside a mutation, we can include the
+`Gitlab::Graphql::Authorize::AuthorizeResource` concern in the
+mutation.
+
+This allows us to provide the required abilities on the mutation like
+this:
+
+```ruby
+module Mutations
+ module MergeRequests
+ class SetWip < Base
+ graphql_name 'MergeRequestSetWip'
+
+ authorize :update_merge_request
+ end
+ end
+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
+object on the mutation. This would allow you to use the
+`authorized_find!` and `authorized_find!` helper methods.
+
+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
+correctly rendered to the clients.
+
## Testing
_full stack_ tests for a graphql query or mutation live in
@@ -124,3 +354,35 @@ be used to test if the query renders valid results.
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.
+
+To test GraphQL mutation requests, `GraphqlHelpers` provides 2
+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 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 params, like a GraphQL
+client would do.
+
+To access the response of a mutation, the `graphql_mutation_response`
+helper is available.
+
+Using these helpers, we can build specs like this:
+
+```ruby
+let(:mutation) do
+ graphql_mutation(
+ :merge_request_set_wip,
+ project_path: 'gitlab-org/gitlab-ce',
+ iid: '1',
+ wip: true
+ )
+end
+
+it 'returns a successfull response' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(graphql_mutation_response(:merge_request_set_wip)['errors']).to be_empty
+end
+```
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 31117b5e723..3e417a44ec1 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -2,7 +2,7 @@
## Software delivery
-There are two software distributions of GitLab: the open source [Community Edition](https://gitlab.com/gitlab-org/gitlab-ce/) (CE), and the open core [Enterprise Edition](https://gitlab.com/gitlab-org/gitlab-ee/) (EE). GitLab is available under [different subscriptions](https://about.gitlab.com/products/).
+There are two software distributions of GitLab: the open source [Community Edition](https://gitlab.com/gitlab-org/gitlab-ce/) (CE), and the open core [Enterprise Edition](https://gitlab.com/gitlab-org/gitlab-ee/) (EE). GitLab is available under [different subscriptions](https://about.gitlab.com/pricing/).
New versions of GitLab are released in stable branches and the master branch is for bleeding edge development.
@@ -197,4 +197,4 @@ Note: It is recommended to log into the `git` user using `sudo -i -u git` or `su
## GitLab.com
-We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/handbook/infrastructure/production-architecture/) but this is probably over the top unless you have millions of users.
+We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/) but this is probably over the top unless you have millions of users.
diff --git a/doc/development/automatic_ce_ee_merge.md b/doc/development/automatic_ce_ee_merge.md
index a85e5b1b1cc..8d41503f874 100644
--- a/doc/development/automatic_ce_ee_merge.md
+++ b/doc/development/automatic_ce_ee_merge.md
@@ -100,7 +100,7 @@ Notes:
number of times you have to resolve conflicts.
- Please remember to
[always have your EE merge request merged before the CE version](#always-merge-ee-merge-requests-before-their-ce-counterparts).
-- You can use [`git rerere`](https://git-scm.com/blog/2010/03/08/rerere.html)
+- You can use [`git rerere`](https://git-scm.com/docs/git-rerere)
to avoid resolving the same conflicts multiple times.
### Cherry-picking from CE to EE
diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md
index 46c5baddb9c..f0d5af9fcb5 100644
--- a/doc/development/background_migrations.md
+++ b/doc/development/background_migrations.md
@@ -5,6 +5,9 @@ otherwise take a very long time (hours, days, years, etc) to complete. For
example, you can use background migrations to migrate data so that instead of
storing data in a single JSON column the data is stored in a separate table.
+If the database cluster is considered to be in an unhealthy state, background
+migrations automatically reschedule themselves for a later point in time.
+
## When To Use Background Migrations
>**Note:**
@@ -296,10 +299,18 @@ for more details.
## Best practices
+1. Make sure to know how much data you're dealing with
1. Make sure that background migration jobs are idempotent.
1. Make sure that tests you write are not false positives.
1. Make sure that if the data being migrated is critical and cannot be lost, the
clean-up migration also checks the final state of the data before completing.
+1. Make sure to know how much time it'll take to run all scheduled migrations
+1. When migrating many columns, make sure it won't generate too many
+ dead tuples in the process (you may need to directly query the number of dead tuples
+ and adjust the scheduling according to this piece of data)
+1. Make sure to discuss the numbers with a database specialist, the migration may add
+ more pressure on DB than you expect (measure on staging,
+ or ask someone to measure on production)
[migrations-readme]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/migrations/README.md
[issue-rspec-hooks]: https://gitlab.com/gitlab-org/gitlab-ce/issues/35351
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
new file mode 100644
index 00000000000..d4cce79b067
--- /dev/null
+++ b/doc/development/contributing/design.md
@@ -0,0 +1,70 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Implement design & UI elements](#implement-design--ui-elements)
+- [Style guides](#style-guides)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Implement design & UI elements
+
+For guidance on UX implementation at GitLab, please refer to our [Design System](https://design.gitlab.com/).
+
+The UX team uses labels to manage their workflow.
+
+The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
+To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook.
+
+Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue.
+
+The UX team has a special type label called ~"design artifact". This label indicates that the final output
+for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone.
+Any issue labeled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is
+needed until the solution has been decided.
+
+~"design artifact" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone.
+
+To prevent the misunderstanding that a feature will be be delivered in the
+assigned milestone, when only UX design is planned for that milestone, the
+Product Manager should create a separate issue for the ~"design artifact",
+assign the ~UX, ~"design artifact" and ~"Deliverable" labels, add a milestone
+and use a title that makes it clear that the scheduled issue is design only
+(e.g. `Design exploration for XYZ`).
+
+When the ~"design artifact" issue has been completed, the UXer removes the ~UX
+label, adds the ~"UX ready" label and closes the issue. This indicates the
+design artifact is complete. The UXer will also copy the designs to related
+issues for implementation in an upcoming milestone.
+
+## Style guides
+
+1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
+ Important sections include [Source Code Layout][rss-source] and
+ [Naming][rss-naming]. Use:
+ - multi-line method chaining style **Option A**: dot `.` on the second line
+ - string literal quoting style **Option A**: single quoted by default
+1. [Rails](https://github.com/bbatsov/rails-style-guide)
+1. [Newlines styleguide][newlines-styleguide]
+1. [Testing][testing]
+1. [JavaScript styleguide][js-styleguide]
+1. [SCSS styleguide][scss-styleguide]
+1. [Shell commands](../shell_commands.md) created by GitLab
+ contributors to enhance security
+1. [Database Migrations](../migration_style_guide.md)
+1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
+1. [Documentation styleguide](https://docs.gitlab.com/ee/development/documentation/styleguide.html)
+1. Interface text should be written subjectively instead of objectively. It
+ should be the GitLab core team addressing a person. It should be written in
+ present time and never use past tense (has been/was). For example instead
+ of _prohibited this user from being saved due to the following errors:_ the
+ text should be _sorry, we could not create your account because:_
+1. Code should be written in [US English][us-english]
+
+This is also the style used by linting tools such as
+[RuboCop](https://github.com/bbatsov/rubocop),
+[PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
+
+---
+
+[Return to Contributing documentation](index.md)
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
new file mode 100644
index 00000000000..64f5a2c8022
--- /dev/null
+++ b/doc/development/contributing/index.md
@@ -0,0 +1,246 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Contribute to GitLab](#contribute-to-gitlab)
+- [Security vulnerability disclosure](#security-vulnerability-disclosure)
+- [Code of conduct](#code-of-conduct)
+- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
+- [Helping others](#helping-others)
+- [I want to contribute!](#i-want-to-contribute)
+- [Contribution Flow](#contribution-flow)
+- [Workflow labels](#workflow-labels)
+ - [Type labels](#type-labels)
+ - [Subject labels](#subject-labels)
+ - [Team labels](#team-labels)
+ - [Milestone labels](#milestone-labels)
+ - [Bug Priority labels](#bug-priority-labels)
+ - [Bug Severity labels](#bug-severity-labels)
+ - [Severity impact guidance](#severity-impact-guidance)
+ - [Label for community contributors](#label-for-community-contributors)
+- [Implement design & UI elements](#implement-design--ui-elements)
+- [Issue tracker](#issue-tracker)
+ - [Issue triaging](#issue-triaging)
+ - [Feature proposals](#feature-proposals)
+ - [Issue tracker guidelines](#issue-tracker-guidelines)
+ - [Issue weight](#issue-weight)
+ - [Regression issues](#regression-issues)
+ - [Technical and UX debt](#technical-and-ux-debt)
+ - [Stewardship](#stewardship)
+- [Merge requests](#merge-requests)
+ - [Merge request guidelines](#merge-request-guidelines)
+ - [Contribution acceptance criteria](#contribution-acceptance-criteria)
+- [Definition of done](#definition-of-done)
+- [Style guides](#style-guides)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Contribute to GitLab
+
+For a first-time step-by-step guide to the contribution process, see
+["Contributing to GitLab"](https://about.gitlab.com/contributing/).
+
+Thank you for your interest in contributing to GitLab. This guide details how
+to contribute to GitLab in a way that is efficient for everyone.
+
+Looking for something to work on? Look for issues with the label [Accepting Merge Requests](#i-want-to-contribute).
+
+GitLab comes into two flavors, GitLab Community Edition (CE) our free and open
+source edition, and GitLab Enterprise Edition (EE) which is our commercial
+edition. Throughout this guide you will see references to CE and EE for
+abbreviation.
+
+If you have read this guide and want to know how the GitLab [core team]
+operates please see [the GitLab contributing process](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/PROCESS.md).
+
+- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
+
+## Security vulnerability disclosure
+
+Please report suspected security vulnerabilities in private to
+`support@gitlab.com`, also see the
+[disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/).
+Please do **NOT** create publicly viewable issues for suspected security
+vulnerabilities.
+
+## 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/).
+
+## Closing policy for issues and merge requests
+
+GitLab is a popular open source project and the capacity to deal with issues
+and merge requests is limited. Out of respect for our volunteers, issues and
+merge requests not in line with the guidelines listed in this document may be
+closed without notice.
+
+Please treat our volunteers with courtesy and respect, it will go a long way
+towards getting your issue resolved.
+
+Issues and merge requests should be in English and contain appropriate language
+for audiences of all ages.
+
+If a contributor is no longer actively working on a submitted merge request
+we can decide that the merge request will be finished by one of our
+[Merge request coaches][team] or close the merge request. We make this decision
+based on how important the change is for our product vision. If a Merge request
+coach is going to finish the merge request we assign the
+~"coach will finish" label.
+
+## Helping others
+
+Please help other GitLab users when you can.
+The methods people will use to seek help can be found on the [getting help page][getting-help].
+
+Sign up for the mailing list, answer GitLab questions on StackOverflow or
+respond in the IRC channel. You can also sign up on [CodeTriage][codetriage] to help with
+the remaining issues on the GitHub issue tracker.
+
+## I want to contribute!
+
+If you want to contribute to GitLab [issues with the label `Accepting Merge Requests` and small weight][accepting-mrs-weight]
+is a great place to start. Issues with a lower weight (1 or 2) are deemed
+suitable for beginners. These issues will be of reasonable size and challenge,
+for anyone to start contributing to GitLab. If you have any questions or need help visit [Getting Help](https://about.gitlab.com/getting-help/#discussion) to
+learn how to communicate with GitLab. If you're looking for a Gitter or Slack channel
+please consider we favor
+[asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real time communication. Thanks for your contribution!
+
+## Contribution Flow
+
+When contributing to GitLab, your merge request is subject to review by merge request maintainers of a particular specialty.
+
+When you submit code to GitLab, we really want it to get merged, but there will be times when it will not be merged.
+
+When maintainers are reading through a merge request they may request guidance from other maintainers. If merge request maintainers conclude that the code should not be merged, our reasons will be fully disclosed. If it has been decided that the code quality is not up to GitLab’s standards, the merge request maintainer will refer the author to our docs and code style guides, and provide some guidance.
+
+Sometimes style guides will be followed but the code will lack structural integrity, or the maintainer will have reservations about the code’s overall quality. When there is a reservation the maintainer will inform the author and provide some guidance. The author may then choose to update the merge request. Once the merge request has been updated and reassigned to the maintainer, they will review the code again. Once the code has been resubmitted any number of times, the maintainer may choose to close the merge request with a summary of why it will not be merged, as well as some guidance. If the merge request is closed the maintainer will be open to discussion as to how to improve the code so it can be approved in the future.
+
+GitLab will do its best to review community contributions as quickly as possible. Specially appointed developers review community contributions daily. You may take a look at the [team page](https://about.gitlab.com/team/) for the merge request coach who specializes in the type of code you have written and mention them in the merge request. For example, if you have written some JavaScript in your code then you should mention the frontend merge request coach. If your code has multiple disciplines you may mention multiple merge request coaches.
+
+GitLab receives a lot of community contributions, so if your code has not been reviewed within 4 days of its initial submission feel free to re-mention the appropriate merge request coach.
+
+When submitting code to GitLab, you may feel that your contribution requires the aid of an external library. If your code includes an external library please provide a link to the library, as well as reasons for including it.
+
+When your code contains more than 500 changes, any major breaking changes, or an external library, `@mention` a maintainer in the merge request. If you are not sure who to mention, the reviewer will add one early in the merge request process.
+
+## Workflow labels
+
+This [documentation](issue_workflow.md) outlines the current workflow labels.
+
+### Type labels
+
+This [documentation](issue_workflow.md) outlines the current type labels.
+
+### Subject labels
+
+This [documentation](issue_workflow.md) outlines the current subject labels.
+
+### Team labels
+
+This [documentation](issue_workflow.md) outlines the current team labels.
+
+### Milestone labels
+
+This [documentation](issue_workflow.md) outlines the current milestone labels.
+
+### Bug Priority labels
+
+This [documentation](issue_workflow.md) outlines the current bug priority labels.
+
+### Bug Severity labels
+
+This [documentation](issue_workflow.md) outlines the current severity labels.
+
+#### Severity impact guidance
+
+This [documentation](issue_workflow.md) outlines the current severity impact guidance.
+
+### Label for community contributors
+
+This [documentation](issue_workflow.md) outlines the current policy regarding community contributor issues.
+
+## Implement design & UI elements
+
+This [documentation](design.md) outlines the current design and UI guidelines.
+
+## Issue tracker
+
+This [documentation](issue_workflow.md) outlines the issue tracker process.
+
+### Issue triaging
+
+This [documentation](issue_workflow.md) outlines the current issue triaging process.
+
+### Feature proposals
+
+This [documentation](issue_workflow.md) outlines the feature proposal process.
+
+### Issue tracker guidelines
+
+This [documentation](issue_workflow.md) outlines the issue tracker guidelines.
+
+### Issue weight
+
+This [documentation](issue_workflow.md) outlines the issue weight guidelines.
+
+### Regression issues
+
+This [documentation](issue_workflow.md) outlines the regression issue process.
+
+### Technical and UX debt
+
+This [documentation](issue_workflow.md) about technical and UX debt has been moved.
+
+### Stewardship
+
+This [documentation](issue_workflow.md) outlines the stewardship process.
+
+## Merge requests
+
+This [documentation](merge_request_workflow.md) outlines the current merge request process.
+
+### Merge request guidelines
+
+This [documentation](merge_request_workflow.md) outlines the current merge request guidelines.
+
+### Contribution acceptance criteria
+
+This [documentation](merge_request_workflow.md) outlines the current acceptance criteria for contributions.
+
+## Definition of done
+
+This [documentation](merge_request_workflow.md) outlines the definition of done.
+
+## Style guides
+This [documentation](design.md) outlines the current style guidelines.
+
+---
+
+[Return to Development documentation](../README.md)
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
new file mode 100644
index 00000000000..6a334e9b17d
--- /dev/null
+++ b/doc/development/contributing/issue_workflow.md
@@ -0,0 +1,357 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Workflow labels](#workflow-labels)
+ - [Type labels](#type-labels)
+ - [Subject labels](#subject-labels)
+ - [Team labels](#team-labels)
+ - [Release Scoping labels](#release-scoping-labels)
+ - [Priority labels](#priority-labels)
+ - [Severity labels](#severity-labels)
+ - [Severity impact guidance](#severity-impact-guidance)
+ - [Label for community contributors](#label-for-community-contributors)
+ - [Issue triaging](#issue-triaging)
+ - [Feature proposals](#feature-proposals)
+ - [Issue tracker guidelines](#issue-tracker-guidelines)
+ - [Issue weight](#issue-weight)
+ - [Regression issues](#regression-issues)
+ - [Technical and UX debt](#technical-and-ux-debt)
+ - [Stewardship](#stewardship)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Workflow labels
+
+To allow for asynchronous issue handling, we use [milestones][milestones-page]
+and [labels][labels-page]. Leads and product managers handle most of the
+scheduling into milestones. Labelling is a task for everyone.
+
+Most issues will have labels for at least one of the following:
+
+- Type: ~"feature proposal", ~bug, ~customer, etc.
+- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
+- Team: ~"CI/CD", ~Plan, ~Manage, ~Quality, etc.
+- Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release"
+- Priority: ~P1, ~P2, ~P3, ~P4
+- Severity: ~S1, ~S2, ~S3, ~S4
+
+All labels, their meaning and priority are defined on the
+[labels page][labels-page].
+
+If you come across an issue that has none of these, and you're allowed to set
+labels, you can _always_ add the team and type, and often also the subject.
+
+[milestones-page]: https://gitlab.com/gitlab-org/gitlab-ce/milestones
+[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels
+
+### Type labels
+
+Type labels are very important. They define what kind of issue this is. Every
+issue should have one or more.
+
+Examples of type labels are ~"feature proposal", ~bug, ~customer, ~security,
+and ~"direction".
+
+A number of type labels have a priority assigned to them, which automatically
+makes them float to the top, depending on their importance.
+
+Type labels are always lowercase, and can have any color, besides blue (which is
+already reserved for subject labels).
+
+The descriptions on the [labels page][labels-page] explain what falls under each type label.
+
+### Subject labels
+
+Subject labels are labels that define what area or feature of GitLab this issue
+hits. They are not always necessary, but very convenient.
+
+Examples of subject labels are ~wiki, ~ldap, ~api,
+~issues, ~"merge requests", ~labels, and ~"container registry".
+
+If you are an expert in a particular area, it makes it easier to find issues to
+work on. You can also subscribe to those labels to receive an email each time an
+issue is labeled with a subject label corresponding to your expertise.
+
+Subject labels are always all-lowercase.
+
+### Team labels
+
+Team labels specify what team is responsible for this issue.
+Assigning a team label makes sure issues get the attention of the appropriate
+people.
+
+The current team labels are:
+
+- ~Configuration
+- ~"CI/CD"
+- ~Create
+- ~Distribution
+- ~Documentation
+- ~Geo
+- ~Gitaly
+- ~Manage
+- ~Monitoring
+- ~Plan
+- ~Quality
+- ~Release
+- ~Secure
+- ~UX
+
+The descriptions on the [labels page][labels-page] explain what falls under the
+responsibility of each team.
+
+Within those team labels, we also have the ~backend and ~frontend labels to
+indicate if an issue needs backend work, frontend work, or both.
+
+Team labels are always capitalized so that they show up as the first label for
+any issue.
+
+### Release Scoping labels
+
+Release Scoping labels help us clearly communicate expectations of the work for the
+release. There are three levels of Release Scoping labels:
+
+- ~Deliverable: Issues that are expected to be delivered in the current
+ milestone.
+- ~Stretch: Issues that are a stretch goal for delivering in the current
+ milestone. If these issues are not done in the current release, they will
+ strongly be considered for the next release.
+- ~"Next Patch Release": Issues to put in the next patch release. Work on these
+ first, and add the "Pick Into X" label to the merge request, along with the
+ appropriate milestone.
+
+Each issue scheduled for the current milestone should be labeled ~Deliverable
+or ~"Stretch". Any open issue for a previous milestone should be labeled
+~"Next Patch Release", or otherwise rescheduled to a different milestone.
+
+### Priority labels
+
+Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
+If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
+This label documents the planned timeline & urgency which is used to measure against our actual SLA on delivering ~bug fixes.
+
+| Label | Meaning | Estimate time to fix |
+|-------|-----------------|------------------------------------------------------------------|
+| ~P1 | Urgent Priority | The current release + potentially immediate hotfix to GitLab.com |
+| ~P2 | High Priority | The next release |
+| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) |
+| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) |
+
+### Severity labels
+
+Severity labels help us clearly communicate the impact of a ~bug on users.
+
+| Label | Meaning | Impact on Functionality | Example |
+|-------|-------------------|-------------------------------------------------------|---------|
+| ~S1 | Blocker | Outage, broken feature with no workaround | Unable to create an issue. Data corruption/loss. Security breach. |
+| ~S2 | Critical Severity | Broken Feature, workaround too complex & unacceptable | Can push commits, but only via the command line. |
+| ~S3 | Major Severity | Broken Feature, workaround acceptable | Can create merge requests only from the Merge Requests page, not through the Issue. |
+| ~S4 | Low Severity | Functionality inconvenience or cosmetic issue | Label colors are incorrect / not being displayed. |
+
+#### Severity impact guidance
+
+Severity levels can be applied further depending on the facet of the impact; e.g. Affected customers, GitLab.com availability, performance and etc. The below is a guideline.
+
+| Severity | Affected Customers/Users | GitLab.com Availability | Performance Degradation |
+|----------|---------------------------------------------------------------------|----------------------------------------------------|------------------------------|
+| ~S1 | >50% users affected (possible company extinction level event) | Significant impact on all of GitLab.com | |
+| ~S2 | Many users or multiple paid customers affected (but not apocalyptic)| Significant impact on large portions of GitLab.com | Degradation is guaranteed to occur in the near future |
+| ~S3 | A few users or a single paid customer affected | Limited impact on important portions of GitLab.com | Degradation is likely to occur in the near future |
+| ~S4 | No paid users/customer affected, or expected to in the near future | Minor impact on on GitLab.com | Degradation _may_ occur but it's not likely |
+
+### Label for community contributors
+
+Issues that are beneficial to our users, 'nice to haves', that we currently do
+not have the capacity for or want to give the priority to, are labeled as
+~"Accepting Merge Requests", so the community can make a contribution.
+
+Community contributors can submit merge requests for any issue they want, but
+the ~"Accepting Merge Requests" label has a special meaning. It points to
+changes that:
+
+1. We already agreed on,
+1. Are well-defined,
+1. Are likely to get accepted by a maintainer.
+
+We want to avoid a situation when a contributor picks an
+~"Accepting Merge Requests" issue and then their merge request gets closed,
+because we realize that it does not fit our vision, or we want to solve it in a
+different way.
+
+We add the ~"Accepting Merge Requests" label to:
+
+- Low priority ~bug issues (i.e. we do not add it to the bugs that we want to
+solve in the ~"Next Patch Release")
+- Small ~"feature proposal"
+- Small ~"technical debt" issues
+
+After adding the ~"Accepting Merge Requests" label, we try to estimate the
+[weight](#issue-weight) of the issue. We use issue weight to let contributors
+know how difficult the issue is. Additionally:
+
+- We advertise ["Accepting Merge Requests" issues with weight < 5][up-for-grabs]
+ as suitable for people that have never contributed to GitLab before on the
+ [Up For Grabs campaign](http://up-for-grabs.net)
+- We encourage people that have never contributed to any open source project to
+ look for ["Accepting Merge Requests" issues with a weight of 1][firt-timers]
+
+If you've decided that you would like to work on an issue, please @-mention
+the [appropriate product manager](https://about.gitlab.com/handbook/product/#who-to-talk-to-for-what)
+as soon as possible. The product manager will then pull in appropriate GitLab team
+members to further discuss scope, design, and technical considerations. This will
+ensure that that your contribution is aligned with the GitLab product and minimize
+any rework and delay in getting it merged into master.
+
+GitLab team members who apply the ~"Accepting Merge Requests" label to an issue
+should update the issue description with a responsible product manager, inviting
+any potential community contributor to @-mention per above.
+
+[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened
+[firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1
+
+
+### Issue triaging
+
+Our issue triage policies are [described in our handbook]. You are very welcome
+to help the GitLab team triage issues. We also organize [issue bash events] once
+every quarter.
+
+The most important thing is making sure valid issues receive feedback from the
+development team. Therefore the priority is mentioning developers that can help
+on those issues. Please select someone with relevant experience from the
+[GitLab team][team]. If there is nobody mentioned with that expertise look in
+the commit history for the affected files to find someone.
+
+We also use [GitLab Triage] to automate some triaging policies. This is
+currently setup as a [scheduled pipeline] running on [quality/triage-ops]
+project.
+
+[described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
+[issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
+[GitLab Triage]: https://gitlab.com/gitlab-org/gitlab-triage
+[scheduled pipeline]: https://gitlab.com/gitlab-org/quality/triage-ops/pipeline_schedules/10512/edit
+[quality/triage-ops]: https://gitlab.com/gitlab-org/quality/triage-ops
+
+### Feature proposals
+
+To create a feature proposal for CE, open an issue on the
+[issue tracker of CE][ce-tracker].
+
+For feature proposals for EE, open an issue on the
+[issue tracker of EE][ee-tracker].
+
+In order to help track the feature proposals, we have created a
+[`feature proposal`][fpl] label. For the time being, users that are not members
+of the project cannot add labels. You can instead ask one of the [core team]
+members to add the label ~"feature proposal" to the issue or add the following
+code snippet right after your description in a new line: `~"feature proposal"`.
+
+Please keep feature proposals as small and simple as possible, complex ones
+might be edited to make them small and simple.
+
+Please submit Feature Proposals using the ['Feature Proposal' issue template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker.
+
+For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
+be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
+need to ask one of the [core team] members to add the label, if you do not have permissions to do it by yourself.
+
+If you want to create something yourself, consider opening an issue first to
+discuss whether it is interesting to include this in GitLab.
+
+### Issue tracker guidelines
+
+**[Search the issue tracker][ce-tracker]** for similar entries before
+submitting your own, there's a good chance somebody else had the same issue or
+feature proposal. Show your support with an award emoji and/or join the
+discussion.
+
+Please submit bugs using the ['Bug' issue template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/issue_templates/Bug.md) provided on the issue tracker.
+The text in the parenthesis is there to help you with what to include. Omit it
+when submitting the actual issue. You can copy-paste it and then edit as you
+see fit.
+
+### Issue weight
+
+Issue weight allows us to get an idea of the amount of work required to solve
+one or multiple issues. This makes it possible to schedule work more accurately.
+
+You are encouraged to set the weight of any issue. Following the guidelines
+below will make it easy to manage this, without unnecessary overhead.
+
+1. Set weight for any issue at the earliest possible convenience
+1. If you don't agree with a set weight, discuss with other developers until
+consensus is reached about the weight
+1. Issue weights are an abstract measurement of complexity of the issue. Do not
+relate issue weight directly to time. This is called [anchoring](https://en.wikipedia.org/wiki/Anchoring)
+and something you want to avoid.
+1. Something that has a weight of 1 (or no weight) is really small and simple.
+Something that is 9 is rewriting a large fundamental part of GitLab,
+which might lead to many hard problems to solve. Changing some text in GitLab
+is probably 1, adding a new Git Hook maybe 4 or 5, big features 7-9.
+1. If something is very large, it should probably be split up in multiple
+issues or chunks. You can simply not set the weight of a parent issue and set
+weights to children issues.
+
+### Regression issues
+
+Every monthly release has a corresponding issue on the CE issue tracker to keep
+track of functionality broken by that release and any fixes that need to be
+included in a patch release (see [8.3 Regressions] as an example).
+
+As outlined in the issue description, the intended workflow is to post one note
+with a reference to an issue describing the regression, and then to update that
+note with a reference to the merge request that fixes it as it becomes available.
+
+If you're a contributor who doesn't have the required permissions to update
+other users' notes, please post a new note with a reference to both the issue
+and the merge request.
+
+The release manager will [update the notes] in the regression issue as fixes are
+addressed.
+
+[8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-ce/issues/4127
+[update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue
+
+### 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][ce-tracker].
+For user experience improvements, we use the ~"UX debt" label.
+
+These labels should be added to issues that describe things that can be improved,
+shortcuts that have been taken, features that need additional attention, and all
+other things that have been left behind due to high velocity of development.
+For example, code that needs refactoring should use the ~"technical debt" label,
+user experience refinements should use the ~"UX debt" label.
+
+Everyone can create an issue, though you may need to ask for adding a specific
+label, if you do not have permissions to do it by yourself. Additional labels
+can be combined with these labels, to make it easier to schedule
+the improvements for a release.
+
+Issues tagged with these labels have the same priority like issues
+that describe a new feature to be introduced in GitLab, and should be scheduled
+for a release by the appropriate person.
+
+Make sure to mention the merge request that the ~"technical debt" issue or
+~"UX debt" issue is associated with in the description of the issue.
+
+### Stewardship
+
+For issues related to the open source stewardship of GitLab,
+there is the ~"stewardship" label.
+
+This label is to be used for issues in which the stewardship of GitLab
+is a topic of discussion. For instance if GitLab Inc. is planning to add
+features from GitLab EE to GitLab CE, related issues would be labelled with
+~"stewardship".
+
+A recent example of this was the issue for
+[bringing the time tracking API to GitLab CE][time-tracking-issue].
+
+[time-tracking-issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/25517#note_20019084
+
+---
+
+[Return to Contributing documentation](index.md)
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
new file mode 100644
index 00000000000..9b1da4e7bc1
--- /dev/null
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -0,0 +1,191 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Merge requests](#merge-requests)
+ - [Merge request guidelines](#merge-request-guidelines)
+ - [Contribution acceptance criteria](#contribution-acceptance-criteria)
+- [Definition of done](#definition-of-done)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Merge requests
+
+We welcome merge requests with fixes and improvements to GitLab code, tests,
+and/or documentation. The issues that are specifically suitable for
+community contributions are listed with the label
+[`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce]
+and [EE][accepting-mrs-ee], but you are free to contribute to any other issue
+you want.
+
+Please note that if an issue is marked for the current milestone either before
+or while you are working on it, a team member may take over the merge request
+in order to ensure the work is finished before the release date.
+
+If you want to add a new feature that is not labeled it is best to first create
+a feedback issue (if there isn't one already) and leave a comment asking for it
+to be marked as `Accepting Merge Requests`. Please include screenshots or
+wireframes if the feature will also change the UI.
+
+Merge requests should be opened at [GitLab.com][gitlab-mr-tracker].
+
+If you are new to GitLab development (or web development in general), see the
+[I want to contribute!](#i-want-to-contribute) section to get you started with
+some potentially easy issues.
+
+To start with GitLab development download the [GitLab Development Kit][gdk] and
+see the [Development section](../README.md) for some guidelines.
+
+### Merge request guidelines
+
+If you can, please submit a merge request with the fix or improvements
+including tests. If you don't know how to fix the issue but can write a test
+that exposes the issue we will accept that as well. In general bug fixes that
+include a regression test are merged quickly while new features without proper
+tests are least likely to receive timely feedback. The workflow to make a merge
+request is as follows:
+
+1. Fork the project into your personal space on GitLab.com
+1. Create a feature branch, branch away from `master`
+1. Write [tests](https://docs.gitlab.com/ee/development/rake_tasks.html#run-tests) and code
+1. [Generate a changelog entry with `bin/changelog`][changelog]
+1. If you are writing documentation, make sure to follow the
+ [documentation guidelines][doc-guidelines]
+1. If you have multiple commits please combine them into a few logically
+ organized commits by [squashing them][git-squash]
+1. Push the commit(s) to your fork
+1. Submit a merge request (MR) to the `master` branch
+ 1. Your merge request needs at least 1 approval but feel free to require more.
+ For instance if you're touching backend and frontend code, it's a good idea
+ to require 2 approvals: 1 from a backend maintainer and 1 from a frontend
+ maintainer
+ 1. You don't have to select any approvers, but you can if you really want
+ specific people to approve your merge request
+1. The MR title should describe the change you want to make
+1. The MR description should give a motive for your change and the method you
+ used to achieve it.
+ 1. If you are contributing code, fill in the template already provided in the
+ "Description" field.
+ 1. If you are contributing documentation, choose `Documentation` from the
+ "Choose a template" menu and fill in the template.
+ 1. Mention the issue(s) your merge request solves, using the `Solves #XXX` or
+ `Closes #XXX` syntax to auto-close the issue(s) once the merge request will
+ be merged.
+1. If you're allowed to, set a relevant milestone and labels
+1. If the MR changes the UI it should include *Before* and *After* screenshots
+1. If the MR changes CSS classes please include the list of affected pages,
+ `grep css-class ./app -R`
+1. Be prepared to answer questions and incorporate feedback even if requests
+ for this arrive weeks or months after your MR submission
+ 1. If a discussion has been addressed, select the "Resolve discussion" button
+ beneath it to mark it resolved.
+1. If your MR touches code that executes shell commands, reads or opens files or
+ handles paths to files on disk, make sure it adheres to the
+ [shell command guidelines](../shell_commands.md)
+1. If your code creates new files on disk please read the
+ [shared files guidelines](../shared_files.md).
+1. When writing commit messages please follow
+ [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
+ [guidelines](http://chris.beams.io/posts/git-commit/).
+1. If your merge request adds one or more migrations, make sure to execute all
+ migrations on a fresh database before the MR is reviewed. If the review leads
+ to large changes in the MR, do this again once the review is complete.
+1. For more complex migrations, write tests.
+1. Merge requests **must** adhere to the [merge request performance
+ guidelines](../merge_request_performance_guidelines.md).
+1. For tests that use Capybara or PhantomJS, see this [article on how
+ to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
+
+Please keep the change in a single MR **as small as possible**. If you want to
+contribute a large feature think very hard what the minimum viable change is.
+Can you split the functionality? Can you only submit the backend/API code? Can
+you start with a very simple UI? Can you do part of the refactor? The increased
+reviewability of small MRs that leads to higher code quality is more important
+to us than having a minimal commit log. The smaller an MR is the more likely it
+is it will be merged (quickly). After that you can send more MRs to enhance it.
+The ['How to get faster PR reviews' document of Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md) also has some great points regarding this.
+
+For examples of feedback on merge requests please look at already
+[closed merge requests][closed-merge-requests]. If you would like quick feedback
+on your merge request feel free to mention someone from the [core team] or one
+of the [Merge request coaches][team].
+Please ensure that your merge request meets the contribution acceptance criteria.
+
+When having your code reviewed and when reviewing merge requests please take the
+[code review guidelines](../code_review.md) into account.
+
+### Contribution acceptance criteria
+
+1. The change is as small as possible
+1. Include proper tests and make all tests pass (unless it contains a test
+ exposing a bug in existing code). Every new class should have corresponding
+ unit tests, even if the class is exercised at a higher level, such as a feature test.
+1. If you suspect a failing CI build is unrelated to your contribution, you may
+ try and restart the failing CI job or ask a developer to fix the
+ aforementioned failing test
+1. Your MR initially contains a single commit (please use `git rebase -i` to
+ squash commits)
+1. Your changes can merge without problems (if not please rebase if you're the
+ only one working on your feature branch, otherwise, merge `master`)
+1. Does not break any existing functionality
+1. Fixes one specific issue or implements one specific feature (do not combine
+ things, send separate merge requests if needed)
+1. Migrations should do only one thing (e.g., either create a table, move data
+ to a new table or remove an old table) to aid retrying on failure
+1. Keeps the GitLab code base clean and well structured
+1. Contains functionality we think other users will benefit from too
+1. Doesn't add configuration options or settings options since they complicate
+ making and testing future changes
+1. Changes do not adversely degrade performance.
+ - Avoid repeated polling of endpoints that require a significant amount of overhead
+ - Check for N+1 queries via the SQL log or [`QueryRecorder`](https://docs.gitlab.com/ce/development/mer ge_request_performance_guidelines.html)
+ - Avoid repeated access of filesystem
+1. If you need polling to support real-time features, please use
+ [polling with ETag caching][polling-etag].
+1. Changes after submitting the merge request should be in separate commits
+ (no squashing).
+1. It conforms to the [style guides](#style-guides) and the following:
+ - If your change touches a line that does not follow the style, modify the
+ entire line to follow it. This prevents linting tools from generating warnings.
+ - Don't touch neighbouring lines. As an exception, automatic mass
+ refactoring modifications may leave style non-compliant.
+1. If the merge request adds any new libraries (gems, JavaScript libraries,
+ etc.), they should conform to our [Licensing guidelines][license-finder-doc].
+ See the instructions in that document for help if your MR fails the
+ "license-finder" test with a "Dependencies that need approval" error.
+1. The merge request meets the [definition of done](#definition-of-done).
+
+## Definition of done
+
+If you contribute to GitLab please know that changes involve more than just
+code. We have the following [definition of done][definition-of-done]. Please ensure you support
+the feature you contribute through all of these steps.
+
+1. Description explaining the relevancy (see following item)
+1. Working and clean code that is commented where needed
+1. [Unit, integration, and system tests][testing] that pass on the CI server
+1. Performance/scalability implications have been considered, addressed, and tested
+1. [Documented][doc-guidelines] in the `/doc` directory
+1. [Changelog entry added][changelog], if necessary
+1. Reviewed and any concerns are addressed
+1. Merged by a project maintainer
+1. Added to the release blog article, if relevant
+1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/), if relevant
+1. Community questions answered
+1. Answers to questions radiated (in docs/wiki/support etc.)
+
+If you add a dependency in GitLab (such as an operating system package) please
+consider updating the following and note the applicability of each in your
+merge request:
+
+1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/
+1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md
+1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool
+1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies
+1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit
+1. Test suite https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/prepare_build.sh
+1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab
+
+---
+
+[Return to Contributing documentation](index.md)
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index a315cfdc116..ad49c77aac8 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -219,28 +219,26 @@ below.
- Every piece of documentation that comes with a new feature should declare the
GitLab version that feature got introduced. Right below the heading add a
- note:
+ blockquote:
```md
> Introduced in GitLab 8.3.
```
-- Whenever possible, every feature should have a link to the MR, issue, or epic that introduced it.
- The above note would be then transformed to:
+- Whenever possible, every feature should have a link to the issue, MR or epic
+ (in that order) that introduced it. The above quote would be then transformed to:
```md
- > [Introduced][ce-1242] in GitLab 8.3.
+ > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/1242) in GitLab 8.3.
```
- , where the [link identifier](#links) is named after the repository (CE) and
- the MR number.
-
- If the feature is only available in GitLab Enterprise Edition, don't forget to mention
the [paid tier](https://about.gitlab.com/handbook/marketing/product-marketing/#tiers)
the feature is available in:
```md
- > [Introduced][ee-1234] in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
+ > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/1242)
+ in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
```
### Product badges
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 7f061d06da8..32de741c9fe 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -5,7 +5,7 @@
- **Write documentation.**: Add documentation to the `doc/` directory. Describe
the feature and include screenshots, if applicable.
- **Submit a MR to the `www-gitlab-com` project.**: Add the new feature to the
- [EE features list][ee-features-list].
+ [EE features list](https://about.gitlab.com/features/).
## Act as CE when unlicensed
diff --git a/doc/development/emails.md b/doc/development/emails.md
index 73cac82caf0..35ada35babe 100644
--- a/doc/development/emails.md
+++ b/doc/development/emails.md
@@ -10,12 +10,12 @@ To view rendered emails "sent" in your development instance, visit
Rails provides a way to preview our mailer templates in HTML and plaintext using
dummy data.
-The previews live in [`spec/mailers/previews`][previews] and can be viewed at
+The previews live in [`app/mailers/previews`][previews] and can be viewed at
[`/rails/mailers`](http://localhost:3000/rails/mailers).
See the [Rails guides] for more info.
-[previews]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/spec/mailers/previews
+[previews]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/mailers/previews
[Rails guides]: http://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails
## Incoming email
diff --git a/doc/development/fe_guide/development_process.md b/doc/development/fe_guide/development_process.md
index d240dbe8b02..905668eef58 100644
--- a/doc/development/fe_guide/development_process.md
+++ b/doc/development/fe_guide/development_process.md
@@ -1,6 +1,6 @@
# Frontend Development Process
-You can find more about the organization of the frontend team in the [handbook](https://about.gitlab.com/handbook/frontend/).
+You can find more about the organization of the frontend team in the [handbook](https://about.gitlab.com/handbook/engineering/frontend/).
## Development Checklist
@@ -34,7 +34,7 @@ Please use your best judgement when to use it and please contribute new points t
- [ ] **Cookie Mode** Think about hiding the feature behind a cookie flag if the implementation is on top of existing features
- [ ] **New route** Are you refactoring something big then you might consider adding a new route where you implement the new feature and when finished delete the current route and rename the new one. (for example 'merge_request' and 'new_merge_request')
- [ ] **Setup** Is there any specific setup needed for your implementation (for example a kubernetes cluster)? Then let everyone know if it is not already mentioned where they can find documentation (if it doesn't exist - create it)
-- [ ] **Security** Are there any new security relevant implementations? Then please contact the security team for an app security review. If you are not sure ask our [domain expert](https://about.gitlab.com/handbook/frontend/#frontend-domain-experts)
+- [ ] **Security** Are there any new security relevant implementations? Then please contact the security team for an app security review. If you are not sure ask our [domain expert](https://about.gitlab.com/handbook/engineering/frontend/#frontend-domain-experts)
#### During development
@@ -51,7 +51,7 @@ Please use your best judgement when to use it and please contribute new points t
- [ ] **Performance** Have you checked performance? For example do the same thing with 500 comments instead of 1. Document the tests and possible findings in the MR so a reviewer can directly see it.
- [ ] Have you tested with a variety of our [supported browsers](../../install/requirements.md#supported-web-browsers)? You can use [browserstack](https://www.browserstack.com/) to be able to access a wide variety of browsers and operating systems.
- [ ] Did you check the mobile view?
-- [ ] Check the built webpack bundle (For the report run `WEBPACK_REPORT=true gdk run`, then open `webpack-report/index.html`) if we have unnecessary bloat due to wrong references, including libraries multiple times, etc.. If you need help contact the webpack [domain expert](https://about.gitlab.com/handbook/frontend/#frontend-domain-experts)
+- [ ] Check the built webpack bundle (For the report run `WEBPACK_REPORT=true gdk run`, then open `webpack-report/index.html`) if we have unnecessary bloat due to wrong references, including libraries multiple times, etc.. If you need help contact the webpack [domain expert](https://about.gitlab.com/handbook/engineering/frontend/#frontend-domain-experts)
- [ ] **Tests** Not only greenfield tests - Test also all bad cases that come to your mind.
- [ ] If you have multiple MR's then also smoke test against the final merge.
- [ ] Are there any big changes on how and especially how frequently we use the API then let production know about it
diff --git a/doc/development/fe_guide/img/vue_arch.png b/doc/development/fe_guide/img/vue_arch.png
deleted file mode 100644
index a67706c7c1e..00000000000
--- a/doc/development/fe_guide/img/vue_arch.png
+++ /dev/null
Binary files differ
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
index 1320efaf767..da836a0e82e 100644
--- a/doc/development/fe_guide/performance.md
+++ b/doc/development/fe_guide/performance.md
@@ -15,7 +15,7 @@ Use the following rules when creating realtime solutions.
Use that as your polling interval. This way it is [easy for 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 `4XX` or `5XX` should disable polling as well.
+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
@@ -25,15 +25,15 @@ controlled by the server.
### 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,
+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.
* 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 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 will search for lazy images and load them if needed.
But in general it should be handled automatically through a `MutationObserver` in the lazy loading function.
### Animations
@@ -97,19 +97,19 @@ bundle and included on the page.
```javascript
import initMyWidget from './my_widget';
-
+
document.addEventListener('DOMContentLoaded', () => {
initMyWidget();
});
```
-- **Supporting Module Placement:**
+- **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
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
shared directory at the closest common parent directory for the entry
points that import it. For example, if `my_widget.js` is imported within
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index e31ee087358..f6cbd11042c 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -2,27 +2,24 @@
To get started with Vue, read through [their documentation][vue-docs].
-## Vue architecture
+## Examples
-All new features built with Vue.js must follow a [Flux architecture][flux].
-The main goal we are trying to achieve is to have only one data flow and only one data entry.
-In order to achieve this goal, you can either use [vuex](#vuex) or use the [store pattern][state-management], explained below:
+What is described in the following sections can be found in these examples:
-Each Vue bundle needs a Store - where we keep all the data -, a Service - that we use to communicate with the server - and a main Vue component.
+- web ide: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/ide/stores
+- security products: https://gitlab.com/gitlab-org/gitlab-ee/tree/master/ee/app/assets/javascripts/vue_shared/security_reports
+- registry: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/registry/stores
-Think of the Main Vue Component as the entry point of your application. This is the only smart
-component that should exist in each Vue feature.
-This component is responsible for:
-1. Calling the Service to get data from the server
-1. Calling the Store to store the data received
-1. Mounting all the other components
+## Vue architecture
-![Vue Architecture](img/vue_arch.png)
+All new features built with Vue.js must follow a [Flux architecture][flux].
+The main goal we are trying to achieve is to have only one data flow and only one data entry.
+In order to achieve this goal we use [vuex](#vuex).
You can also read about this architecture in vue docs about [state management][state-management]
and about [one way data flow][one-way-data-flow].
-### Components, Stores and Services
+### Components and Store
In some features implemented with Vue.js, like the [issue board][issue-boards]
or [environments table][environments-table]
@@ -33,10 +30,8 @@ new_feature
├── components
│ └── component.vue
│ └── ...
-├── stores
+├── store
│ └── new_feature_store.js
-├── services # only when not using vuex
-│ └── new_feature_service.js
├── index.js
```
_For consistency purposes, we recommend you to follow the same structure._
@@ -125,217 +120,6 @@ You can read more about components in Vue.js site, [Component System][component-
#### Vuex
Check this [page](vuex.md) for more details.
-#### Flux like state management
-The Store is a class that allows us to manage the state in a single
-source of truth. It is not aware of the service or the components.
-
-The concept we are trying to follow is better explained by Vue documentation
-itself, please read this guide: [State Management][state-management]
-
-### A folder for the Service
-
-**If you are using Vuex you won't need this step**
-
-The Service is a class used only to communicate with the server.
-It does not store or manipulate any data. It is not aware of the store or the components.
-We use [axios][axios] to communicate with the server.
-Refer to [axios](axios.md) for more details.
-
-Axios instance should only be imported in the service file.
-
-```javascript
-import axios from '~/lib/utils/axios_utils';
-```
-
-### End Result
-
-The following example shows an application:
-
-```javascript
-// store.js
-export default class Store {
-
- /**
- * This is where we will iniatialize the state of our data.
- * Usually in a small SPA you don't need any options when starting the store.
- * In that case you do need guarantee it's an Object and it's documented.
- *
- * @param {Object} options
- */
- constructor(options) {
- this.options = options;
-
- // Create a state object to handle all our data in the same place
- this.todos = [];
- }
-
- setTodos(todos = []) {
- this.todos = todos;
- }
-
- addTodo(todo) {
- this.todos.push(todo);
- }
-
- removeTodo(todoID) {
- const state = this.todos;
-
- const newState = state.filter((element) => {element.id !== todoID});
-
- this.todos = newState;
- }
-}
-
-// service.js
-import axios from '~/lib/utils/axios_utils'
-
-export default class Service {
- constructor(options) {
- this.todos = axios.create({
- baseURL: endpoint.todosEndpoint
- });
-
- }
-
- getTodos() {
- return this.todos.get();
- }
-
- addTodo(todo) {
- return this.todos.put(todo);
- }
-}
-// todo_component.vue
-<script>
-export default {
- props: {
- data: {
- type: Object,
- required: true,
- },
- },
-};
-</script>
-<template>
- <div>
- <h1>
- Title: {{data.title}}
- </h1>
- <p>
- {{data.text}}
- </p>
- </div>
-</template>
-
-// todos_main_component.vue
-<script>
-import Store from 'store';
-import Service from 'service';
-import TodoComponent from 'todoComponent';
-export default {
- components: {
- todo: TodoComponent,
- },
- /**
- * Although most data belongs in the store, each component it's own state.
- * We want to show a loading spinner while we are fetching the todos, this state belong
- * in the component.
- *
- * We need to access the store methods through all methods of our component.
- * We need to access the state of our store.
- */
- data() {
- const store = new Store();
-
- return {
- isLoading: false,
- store: store,
- todos: store.todos,
- };
- },
-
- created() {
- this.service = new Service('/todos');
-
- this.getTodos();
- },
-
- methods: {
- getTodos() {
- this.isLoading = true;
-
- this.service
- .getTodos()
- .then(response => {
- this.store.setTodos(response);
- this.isLoading = false;
- })
- .catch(() => {
- this.isLoading = false;
- // Show an error
- });
- },
-
- addTodo(event) {
- this.service
- .addTodo({
- title: 'New entry',
- text: `You clicked on ${event.target.tagName}`,
- })
- .then(response => {
- this.store.addTodo(response);
- })
- .catch(() => {
- // Show an error
- });
- },
- },
-};
-</script>
-<template>
- <div class="container">
- <div v-if="isLoading">
- <i
- class="fa fa-spin fa-spinner"
- aria-hidden="true" />
- </div>
-
- <div
- v-if="!isLoading"
- class="js-todo-list">
- <template v-for='todo in todos'>
- <todo :data="todo" />
- </template>
-
- <button
- @click="addTodo"
- class="js-add-todo">
- Add Todo
- </button>
- </div>
- <div>
-</template>
-
-// index.js
-import todoComponent from 'todos_main_component.vue';
-
-new Vue({
- el: '.js-todo-app',
- components: {
- todoComponent,
- },
- render: createElement => createElement('todo-component' {
- props: {
- someProp: [],
- }
- }),
-});
-
-```
-
-The [issue boards service][issue-boards-service]
-is a good example of this pattern.
-
## Style guide
Please refer to the Vue section of our [style guide](style_guide_js.md#vue-js)
@@ -425,7 +209,7 @@ There is a helper in `spec/javascripts/helpers/vue_mount_component_helper.js` th
```javascript
import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper.js'
+import mountComponent from 'spec/helpers/vue_mount_component_helper'
import component from 'component.vue'
const Component = Vue.extend(component);
@@ -446,6 +230,5 @@ need to test the rendered output. [Vue][vue-test] guide's to unit test show us e
[state-management]: https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch
[one-way-data-flow]: https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow
[vue-test]: https://vuejs.org/v2/guide/unit-testing.html
-[issue-boards-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/boards/services/board_service.js.es6
[flux]: https://facebook.github.io/flux
[axios]: https://github.com/axios/axios
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 4ba9958e2c6..6323275426f 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -174,6 +174,8 @@ For example use `%{created_at}` in Ruby but `%{createdAt}` in JavaScript.
# => When size == 2: 'There are 2 mice.'
```
+ Avoid using `%d` or count variables in sigular strings. This allows more natural translation in some languages.
+
- In JavaScript:
```js
@@ -279,7 +281,7 @@ Now that the new content is marked for translation, we need to update the PO
files with the following command:
```sh
-bin/rake gettext:find
+bin/rake gettext:regenerate
```
This command will update the `locale/gitlab.pot` file with the newly externalized
@@ -290,16 +292,6 @@ file in. Once the changes are on master, they will be picked up by
If there are merge conflicts in the `gitlab.pot` file, you can delete the file
and regenerate it using the same command. Confirm that you are not deleting any strings accidentally by looking over the diff.
-The command also updates the translation files for each language: `locale/*/gitlab.po`
-These changes can be discarded, the language files will be updated by Crowdin
-automatically.
-
-Discard all of them at once like this:
-
-```sh
-git checkout locale/*/gitlab.po
-```
-
### Validating PO files
To make sure we keep our translation files up to date, there's a linter that is
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index 9d0d7348df9..ad5f6b2ecf6 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -11,9 +11,11 @@ are very appreciative of the work done by translators and proofreaders!
- Chinese Traditional
- Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
- Weizhe Ding - [GitLab](https://gitlab.com/d.weizhe), [Crowdin](https://crowdin.com/profile/d.weizhe)
+ - Yi-Jyun Pan - [GitLab](https://gitlab.com/pan93412), [Crowdin](https://crowdin.com/profile/pan93412)
- Chinese Traditional, Hong Kong
- Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
- Dutch
+ - Emily Hendle - [GitLab](https://gitlab.com/pundachan), [Crowdin](https://crowdin.com/profile/pandachan)
- Esperanto
- French
- Davy Defaud - [GitLab](https://gitlab.com/DevDef), [Crowdin](https://crowdin.com/profile/DevDef)
diff --git a/doc/development/licensing.md b/doc/development/licensing.md
index c06bc0d4731..ddaf636a742 100644
--- a/doc/development/licensing.md
+++ b/doc/development/licensing.md
@@ -60,7 +60,7 @@ Libraries with the following licenses are acceptable for use:
## Unacceptable Licenses
-Libraries with the following licenses are unacceptable for use:
+Libraries with the following licenses require legal approval for use:
- [GNU GPL][GPL] (version 1, [version 2][GPLv2], [version 3][GPLv3], or any future versions): GPL-licensed libraries cannot be linked to from non-GPL projects.
- [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects.
@@ -68,6 +68,26 @@ Libraries with the following licenses are unacceptable for use:
- [Facebook BSD + PATENTS][Facebook]: is a 3-clause BSD license with a patent grant that has been deemed [Category X][x-list] by the Apache foundation.
- [WTFPL][WTFPL]: is a public domain dedication [rejected by the OSI (3.2)][WTFPL-OSI]. Also has a strong language which is not in accordance with our diversity policy.
+## GPL Cooperation Commitment
+
+Before filing or continuing to prosecute any legal proceeding or claim (other than a Defensive Action) arising from termination of a Covered License, GitLab commits to extend to the person or entity (“youâ€) accused of violating the Covered License the following provisions regarding cure and reinstatement, taken from GPL version 3. As used here, the term ‘this License’ refers to the specific Covered License being enforced.
+
+However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
+
+GitLab intends this Commitment to be irrevocable, and binding and enforceable against GitLab and assignees of or successors to GitLab’s copyrights.
+
+GitLab may modify this Commitment by publishing a new edition on this page or a successor location.
+
+Definitions
+
+‘Covered License’ means the GNU General Public License, version 2 (GPLv2), the GNU Lesser General Public License, version 2.1 (LGPLv2.1), or the GNU Library General Public License, version 2 (LGPLv2), all as published by the Free Software Foundation.
+
+‘Defensive Action’ means a legal proceeding or claim that GitLab brings against you in response to a prior proceeding or claim initiated by you or your affiliate.
+
+GitLab means GitLab Inc. and its affiliates and subsidiaries.
+
## Requesting Approval for Licenses
Libraries that are not listed in the [Acceptable Licenses][Acceptable-Licenses] or [Unacceptable Licenses][Unacceptable-Licenses] list can be submitted to the legal team for review. Please email `legal@gitlab.com` with the details. After a decision has been made, the original requestor is responsible for updating this document.
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index a211effdfa7..6f31e5b82e5 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -182,6 +182,34 @@ class MyMigration < ActiveRecord::Migration
end
```
+## Adding foreign-key constraints
+
+When adding a foreign-key constraint to either an existing or new
+column remember to also add a index on the column.
+
+This is _required_ if the foreign-key constraint specifies
+`ON DELETE CASCADE` or `ON DELETE SET NULL` behavior. On a cascading
+delete, the [corresponding record needs to be retrieved using an
+index](https://www.cybertec-postgresql.com/en/postgresql-indexes-and-foreign-keys/)
+(otherwise, we'd need to scan the whole table) for subsequent update or
+deletion.
+
+Here's an example where we add a new column with a foreign key
+constraint. Note it includes `index: true` to create an index for it.
+
+```ruby
+class Migration < ActiveRecord::Migration
+
+ def change
+ add_reference :model, :other_model, index: true, foreign_key: { on_delete: :cascade }
+ end
+end
+```
+
+When adding a foreign-key constraint to an existing column, we
+have to employ `add_concurrent_foreign_key` and `add_concurrent_index`
+instead of `add_reference`.
+
## Adding Columns With Default Values
When adding columns with default values you must use the method
diff --git a/doc/development/performance.md b/doc/development/performance.md
index c4162a05b77..6b4cb6d72d1 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -347,13 +347,7 @@ def expire_first_branch_cache
end
```
-## Anti-Patterns
-
-This is a collection of [anti-patterns][anti-pattern] that should be avoided
-unless these changes have a measurable, significant and positive impact on
-production environments.
-
-### String Freezing
+## 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 this will only allocate the
@@ -365,17 +359,38 @@ only once and re-used. For example, on Ruby 2.3 this will only allocate the
end
```
-Blindly adding a `.freeze` call to every String is an anti-pattern that should
-be avoided unless one can prove (using production data) the call actually has a
-positive impact on performance.
+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.
+
+Strings will be frozen by default in Ruby 3.0. To prepare our code base for
+this eventuality, it's a good practice to add the following header to all
+Ruby files:
+
+```ruby
+# frozen_string_literal: true
+```
+
+This may cause test failures in the code that expects to be able to manipulate
+strings. Instead of using `dup`, use the unary plus to get an unfrozen string:
+
+```ruby
+test = +"hello"
+test += " world"
+```
+
+## Anti-Patterns
-This feature of Ruby wasn't really meant to make things faster directly, instead
-it was meant to reduce the number of allocations. 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 is a collection of [anti-patterns][anti-pattern] that should be avoided
+unless these changes have a measurable, significant and positive impact on
+production environments.
-Another common flavour of this is to not only freeze a String, but also assign
-it to a constant, for example:
+### 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
+impact on runtime performance, and as such, using a constant instead of
+referencing an object directly may even slow code down. For example:
```ruby
SOME_CONSTANT = 'foo'.freeze
@@ -393,13 +408,6 @@ there's nothing stopping somebody from doing this elsewhere in the code:
SOME_CONSTANT = 'bar'
```
-### 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
-impact on runtime performance, and as such, using a constant instead of
-referencing an object directly may even slow code down.
-
[#15607]: https://gitlab.com/gitlab-org/gitlab-ce/issues/15607
[yorickpeterse]: https://gitlab.com/yorickpeterse
[anti-pattern]: https://en.wikipedia.org/wiki/Anti-pattern
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index 11878b4009b..0ca8bb67a77 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -42,6 +42,36 @@ Passing a `logger:` keyword argument to `Gitlab::Profiler.profile` will send
ActiveRecord and ActionController log output to that logger. Further options are
documented with the method source.
+There is also a RubyProf printer available:
+`Gitlab::Profiler::TotalTimeFlatPrinter`. This acts like
+`RubyProf::FlatPrinter`, but its `min_percent` option works on the method's
+total time, not its self time. (This is because we often spend most of our time
+in library code, but this comes from calls in our application.) It also offers a
+`max_percent` option to help filter out outer calls that aren't useful (like
+`ActionDispatch::Integration::Session#process`).
+
+There is a convenience method for using this,
+`Gitlab::Profiler.print_by_total_time`:
+
+```ruby
+result = Gitlab::Profiler.profile('/my-user')
+Gitlab::Profiler.print_by_total_time(result, max_percent: 60, min_percent: 2)
+# Measure Mode: wall_time
+# Thread ID: 70005223698240
+# Fiber ID: 70004894952580
+# Total: 1.768912
+# Sort by: total_time
+#
+# %self total self wait child calls name
+# 0.00 1.017 0.000 0.000 1.017 14 *ActionView::Helpers::RenderingHelper#render
+# 0.00 1.017 0.000 0.000 1.017 14 *ActionView::Renderer#render_partial
+# 0.00 1.017 0.000 0.000 1.017 14 *ActionView::PartialRenderer#render
+# 0.00 1.007 0.000 0.000 1.007 14 *ActionView::PartialRenderer#render_partial
+# 0.00 0.930 0.000 0.000 0.930 14 Hamlit::TemplateHandler#call
+# 0.00 0.928 0.000 0.000 0.928 14 Temple::Engine#call
+# 0.02 0.865 0.000 0.000 0.864 638 *Enumerable#inject
+```
+
[GitLab-Profiler](https://gitlab.com/gitlab-com/gitlab-profiler) is a project
that builds on this to add some additional niceties, such as allowing
configuration with a single Yaml file for multiple URLs, and uploading of the
diff --git a/doc/development/pry_debugging.md b/doc/development/pry_debugging.md
new file mode 100644
index 00000000000..de5e1323e6a
--- /dev/null
+++ b/doc/development/pry_debugging.md
@@ -0,0 +1,130 @@
+# Pry debugging
+
+## Invoking pry debugging
+
+To invoke the debugger, place `binding.pry` somewhere in your
+code. When the Ruby interpreter hits that code, execution will stop,
+and you can type in commands to debug the state of the program
+
+## `byebug` vs `binding.pry`
+
+`byebug` has a very similar interface as `gdb`, but `byebug` does not
+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.
+
+## `byebug`
+
+Check out [the docs](https://github.com/deivid-rodriguez/byebug) for the full list of commands.
+
+You can start the Pry REPL with the `pry` command.
+
+## `pry`
+
+There are **a lot** of features present in `pry`, too much to cover in
+this document, so for the full documentation head over to the [Pry wiki](https://github.com/pry/pry/wiki).
+
+Below are a few features definitely worth checking out, also run
+`help` in a pry session to see what else you can do.
+
+### State navigation
+
+With the [state navigation](https://github.com/pry/pry/wiki/State-navigation)
+you can move around in the code to discover methods and such:
+
+```ruby
+# Change context
+[1] pry(main)> cd Pry
+[2] pry(Pry):1>
+
+# Print methods
+[2] pry(Pry):1> ls -m
+
+# Find a method
+[3] pry(Pry):1> find-method to_yaml
+```
+
+### Source browsing
+
+You [look at the source code](https://github.com/pry/pry/wiki/Source-browsing)
+from your `pry` session:
+
+```ruby
+[1] pry(main)> $ Array#first
+# The above is equivalent to
+[2] pry(main)> cd Array
+[3] pry(Array):1> show-source first
+```
+
+`$` is an alias for `show-source`.
+
+### Documentation browsing
+
+Similar to source browsing, is [Documentation browsing](https://github.com/pry/pry/wiki/Documentation-browsing).
+
+```ruby
+[1] pry(main)> show-doc Array#first
+```
+
+`?` is an alias for `show-doc`.
+
+### Command history
+
+With <kdb>Ctrl+R</kbd> you can search your [command history](https://github.com/pry/pry/wiki/History).
+
+## Stepping
+
+To step through the code, you can use the following commands:
+
+- `break`: Manage breakpoints.
+- `step`: Step execution into the next line or method. Takes an
+ optional numeric argument to step multiple times.
+- `next`: Step over to the next line within the same frame. Also takes
+ an optional numeric argument to step multiple lines.
+- `finish`: Execute until current stack frame returns.
+- `continue`: Continue program execution and end the Pry session.
+
+## Callstack navigation
+
+You also can move around in the callstack with these commands:
+
+- `backtrace`: Shows the current stack. You can use the numbers on the
+ left side with the frame command to navigate the stack.
+- `up`: Moves the stack frame up. Takes an optional numeric argument
+ to move multiple frames.
+- `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.
+
+## Short commands
+
+When you use `binding.pry` instead of `byebug`, the short commands
+like `s`, `n`, `f`, and `c` do not work. To reinstall them, add this
+to `~/.pryrc`:
+
+```ruby
+if defined?(PryByebug)
+ Pry.commands.alias_command 's', 'step'
+ Pry.commands.alias_command 'n', 'next'
+ Pry.commands.alias_command 'f', 'finish'
+ Pry.commands.alias_command 'c', 'continue'
+end
+```
+
+## Repeat last command
+
+You can repeat the last command by just hitting the <kbd>Enter</kbd>
+key (e.g., with `step` or`next`), if you place the following snippet
+in your `~/.pryrc`:
+
+```ruby
+Pry::Commands.command /^$/, "repeat last command" do
+ _pry_.run_command Pry.history.to_a.last
+end
+```
+
+`byebug` supports this out-of-the-box.
diff --git a/doc/development/sql.md b/doc/development/sql.md
index 974b1d99dff..e1e1d31a85f 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -243,3 +243,45 @@ WHERE EXISTS (
```
[gin-index]: http://www.postgresql.org/docs/current/static/gin.html
+
+## `.find_or_create_by` is not atomic
+
+The inherent pattern with methods like `.find_or_create_by` and
+`.first_or_create` and others is that they are not atomic. This means,
+it first runs a `SELECT`, and if there are no results an `INSERT` is
+performed. With concurrent processes in mind, there is a race condition
+which may lead to trying to insert two similar records. This may not be
+desired, or may cause one of the queries to fail due to a constraint
+violation, for example.
+
+Using transactions does not solve this problem.
+
+The following pattern should be used to avoid the problem:
+
+```ruby
+Project.transaction do
+ begin
+ User.find_or_create_by(username: "foo")
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+end
+```
+
+If the above block is run inside a transaction and hits the race
+condition, the transaction is aborted and we cannot simply retry (any
+further queries inside the aborted transaction are going to fail). We
+can employ [nested transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions)
+here to only rollback the "inner transaction". Note that `requires_new: true` is required here.
+
+```ruby
+Project.transaction do
+ begin
+ User.transaction(requires_new: true) do
+ User.find_or_create_by(username: "foo")
+ end
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+end
+```
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index 1a926a660f1..acbfa1850b4 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -101,7 +101,7 @@ CHROME_HEADLESS=0 bundle exec rspec some_spec.rb
The test will go by quickly, but this will give you an idea of what's happening.
-You can also add `byebug` or `binding.pry` to pause execution and step through
+You can also add `byebug` or `binding.pry` to pause execution and [step through](../pry_debugging.md#stepping)
the test.
#### Screenshots
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 3b2b9c8c947..f8993653aec 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -172,6 +172,10 @@ object which can be treated like any other jasmine spy object.
Further documentation on the babel rewire pluign API can be found on
[its repository Readme doc](https://github.com/speedskater/babel-plugin-rewire#babel-plugin-rewire).
+#### Waiting in tests
+
+If you cannot avoid using [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) in tests, please use the [Jasmine mock clock](https://jasmine.github.io/api/2.9/Clock.html).
+
### Vue.js unit tests
See this [section][vue-test].
diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md
index 070efdc15b5..d5afa544372 100644
--- a/doc/development/ux_guide/copy.md
+++ b/doc/development/ux_guide/copy.md
@@ -192,7 +192,7 @@ Portions of this page are modifications based on work created and shared by the
[material design]: https://material.io/guidelines/
[features]: https://about.gitlab.com/features/ "GitLab features page"
-[products]: https://about.gitlab.com/products/ "GitLab products page"
+[products]: https://about.gitlab.com/pricing/ "GitLab products page"
[serial comma]: https://en.wikipedia.org/wiki/Serial_comma "“Serial comma†in Wikipedia"
[android project]: http://source.android.com/
[creative commons]: http://creativecommons.org/licenses/by/2.5/
diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md
index 47396666879..b668c9de6a0 100644
--- a/doc/development/what_requires_downtime.md
+++ b/doc/development/what_requires_downtime.md
@@ -198,14 +198,14 @@ And that's it, we're done!
## Changing The Schema For Large Tables
While `change_column_type_concurrently` and `rename_column_concurrently` can be
-used for changing the schema of a table without downtime it doesn't work very
+used for changing the schema of a table without downtime, it doesn't work very
well for large tables. Because all of the work happens in sequence the migration
can take a very long time to complete, preventing a deployment from proceeding.
They can also produce a lot of pressure on the database due to it rapidly
updating many rows in sequence.
To reduce database pressure you should instead use
-`change_column_type_using_background_migration` or `rename_column_concurrently`
+`change_column_type_using_background_migration` or `rename_column_using_background_migration`
when migrating a column in a large table (e.g. `issues`). These methods work
similarly to the concurrent counterparts but uses background migration to spread
the work / load over a longer time period, without slowing down deployments.
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index 10e8059756d..2517908e5b1 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -30,6 +30,9 @@
idea to fill this in.
- Changing the **Visibility Level** modifies the project's
[viewing and access rights](../public_access/public_access.md) for users.
+ - Selecting the **Initialize repository with a README** option creates a
+ README so that the Git repository is initialized, has a default branch and
+ can be cloned.
1. Click **Create project**.
@@ -40,7 +43,7 @@
When you create a new repo locally, instead of going to GitLab to manually
create a new project and then push the repo, you can directly push it to
GitLab to create the new project, all without leaving your terminal. If you have access to that
-namespace, we will automatically create a new project under that GitLab namespace with its
+namespace, we will automatically create a new project under that GitLab namespace with its
visibility set to Private by default (you can later change it in the [project's settings](../public_access/public_access.md#how-to-change-project-visibility)).
This can be done by using either SSH or HTTP:
diff --git a/doc/gitlab-basics/img/create_new_project_info.png b/doc/gitlab-basics/img/create_new_project_info.png
index ce4f7d1204b..b4119dc046a 100644
--- a/doc/gitlab-basics/img/create_new_project_info.png
+++ b/doc/gitlab-basics/img/create_new_project_info.png
Binary files differ
diff --git a/doc/install/installation.md b/doc/install/installation.md
index e4011b1a4ab..ea01d88d85f 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -12,7 +12,7 @@ Since installations from source don't have Runit, Sidekiq can't be terminated an
## Select Version to Install
-Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install (e.g., `11-0-stable`).
+Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install (e.g., `11-2-stable`).
You can select the branch in the version dropdown in the top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the [GitLab Blog](https://about.gitlab.com/blog/) for installation guide links by version.
@@ -92,9 +92,9 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source
cd /tmp
- curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.16.3.tar.gz
- echo 'dda229e9c73f4fbb7d4324e0d993e11311673df03f73b194c554c2e9451e17cd git-2.16.3.tar.gz' | shasum -a256 -c - && tar -xzf git-2.16.3.tar.gz
- cd git-2.16.3/
+ curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.18.0.tar.gz
+ echo '94faf2c0b02a7920b0b46f4961d8e9cad08e81418614102898a55f980fa3e7e4 git-2.18.0.tar.gz' | shasum -a256 -c - && tar -xzf git-2.18.0.tar.gz
+ cd git-2.18.0/
./configure
make prefix=/usr/local all
@@ -300,9 +300,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-0-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-2-stable gitlab
-**Note:** You can change `11-0-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `11-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
@@ -447,6 +447,15 @@ You can specify a different Git repository by providing it as an extra parameter
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse,https://example.com/gitlab-workhorse.git]" RAILS_ENV=production
+### Install gitlab-pages
+
+GitLab-Pages uses [GNU Make](https://www.gnu.org/software/make/). This step is optional and only needed if you wish to host static sites from within GitLab. The following commands will install GitLab-Pages in `/home/git/gitlab-pages`. For additional setup steps, please consult the [administration guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/administration/pages/source.md) for your version of GitLab as the GitLab Pages daemon can be ran several different ways.
+
+ cd /home/git
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git
+ cd gitlab-pages
+ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+ sudo -u git -H make
### Initialize Database and Activate Advanced Features
diff --git a/doc/install/kubernetes/gitlab_chart.md b/doc/install/kubernetes/gitlab_chart.md
index 48f3df1925a..692f81dd7cd 100644
--- a/doc/install/kubernetes/gitlab_chart.md
+++ b/doc/install/kubernetes/gitlab_chart.md
@@ -16,16 +16,8 @@ The default deployment includes:
### Limitations
-Some features and functions are not currently available in the beta release:
-* [GitLab Pages](../../user/project/pages/)
-* [Reply by email](../../administration/reply_by_email.html)
-* [Project templates](../../gitlab-basics/create-project.html)
-* [Project import/export](../../user/project/settings/import_export.html)
-* [Geo](https://docs.gitlab.com/ee/administration/geo/replication/)
-
-Currently out of scope:
-* [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/)
-* [MySQL support](https://docs.gitlab.com/omnibus/settings/database.html#using-a-mysql-database-management-server-enterprise-edition-only)
+Some features and functions are not currently available in the beta release.
+For details, see [known issues and limitations](https://gitlab.com/charts/gitlab/blob/master/doc/architecture/beta.md#known-issues-and-limitations) in the charts repository.
## Prerequisites
diff --git a/doc/install/kubernetes/gitlab_omnibus.md b/doc/install/kubernetes/gitlab_omnibus.md
index 852a58a9afc..9aee6b9dc74 100644
--- a/doc/install/kubernetes/gitlab_omnibus.md
+++ b/doc/install/kubernetes/gitlab_omnibus.md
@@ -71,7 +71,7 @@ For most installations, only two parameters are required:
Other common configuration options:
- `baseIP`: the desired [external IP address](#external-ip-recommended)
-- `gitlab`: Choose the [desired edition](https://about.gitlab.com/products), either `ee` or `ce`. `ce` is the default.
+- `gitlab`: Choose the [desired edition](https://about.gitlab.com/pricing), either `ee` or `ce`. `ce` is the default.
- `gitlabEELicense`: For Enterprise Edition, the [license](https://docs.gitlab.com/ee/user/admin_area/license.html) can be installed directly via the Chart
- `provider`: Optimizes the deployment for a cloud provider. The default is `gke` for [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/), with `acs` also supported for the [Azure Container Service](https://azure.microsoft.com/en-us/services/container-service/).
diff --git a/doc/install/kubernetes/preparation/tiller.md b/doc/install/kubernetes/preparation/tiller.md
index c92f8258e41..016aac2abeb 100644
--- a/doc/install/kubernetes/preparation/tiller.md
+++ b/doc/install/kubernetes/preparation/tiller.md
@@ -4,7 +4,7 @@ To make use of Helm, you must have a [Kubernetes][k8s-io] cluster. Ensure you ca
Helm consists of two parts, the `helm` client and a `tiller` server inside Kubernetes.
-> **Note**: If you are not able to run tiller in your cluster, for example on OpenShift, it is possible to use [tiller locally](#local-tiller) and avoid deploying it into the cluster. This should only be used when Tiller cannot be normally deployed.
+> **Note**: If you are not able to run Tiller in your cluster, for example on OpenShift, it is possible to use [Tiller locally](https://gitlab.com/charts/gitlab/tree/master/doc/helm#local-tiller) and avoid deploying it into the cluster. This should only be used when Tiller cannot be normally deployed.
## Initialize Helm and Tiller
@@ -65,28 +65,32 @@ kubectl --username=admin --password=xxxxxxxxxxxxxx create -f rbac-config.yaml
For other clusters like Amazon EKS, you can directly upload the RBAC configuration.
+```
kubectl create -f rbac-config.yaml
+```
## Initialize Helm
-Deploy Helm Tiller with a service account
+Deploy Helm Tiller with a service account:
```
helm init --service-account tiller
```
-If your cluster
-previously had Helm/Tiller installed, run the following to ensure that the deployed version of Tiller matches the local Helm version:
+If your cluster previously had Helm/Tiller installed,
+run the following to ensure that the deployed version of Tiller matches the local Helm version:
```
helm init --upgrade --service-account tiller
```
-### Patching Helm Tiller for EKS
+### Patching Helm Tiller for Amazon EKS
-Helm Tiller requires a flag to be enabled to work properly on EKS:
+Helm Tiller requires a flag to be enabled to work properly on Amazon EKS:
-`kubectl -n kube-system patch deployment tiller-deploy -p '{"spec": {"template": {"spec": {"automountServiceAccountToken": true}}}}'`
+```
+kubectl -n kube-system patch deployment tiller-deploy -p '{"spec": {"template": {"spec": {"automountServiceAccountToken": true}}}}'
+```
[helm]: https://helm.sh
[helm-using]: https://docs.helm.sh/using_helm
diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md
index 2a14c0397ca..bf587b5b296 100644
--- a/doc/integration/bitbucket.md
+++ b/doc/integration/bitbucket.md
@@ -1,4 +1,7 @@
-# Integrate your GitLab server with Bitbucket
+# Integrate your GitLab server with Bitbucket Cloud
+
+NOTE: **Note:**
+You need to [enable OmniAuth](omniauth.md) in order to use this.
Import projects from Bitbucket.org and login to your GitLab instance with your
Bitbucket.org account.
@@ -19,8 +22,8 @@ Bitbucket.org.
> **Note:**
GitLab 8.15 significantly simplified the way to integrate Bitbucket.org with
-GitLab. You are encouraged to upgrade your GitLab instance if you haven't done
-already. If you're using GitLab 8.14 and below, [use the previous integration
+GitLab. You are encouraged to upgrade your GitLab instance if you haven't done so
+already. If you're using GitLab 8.14 or below, [use the previous integration
docs][bb-old].
To enable the Bitbucket OmniAuth provider you must register your application
@@ -61,7 +64,7 @@ you to use.
1. Select **Save**.
1. Select your newly created OAuth consumer and you should now see a Key and
- Secret in the list of OAuth customers. Keep this page open as you continue
+ Secret in the list of OAuth consumers. Keep this page open as you continue
the configuration.
![Bitbucket OAuth key](img/bitbucket_oauth_keys.png)
@@ -76,13 +79,13 @@ you to use.
sudo -u git -H editor /home/git/gitlab/config/gitlab.yml
```
-1. Follow the [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration)
- for initial settings.
1. Add the Bitbucket provider configuration:
For Omnibus packages:
```ruby
+ gitlab_rails['omniauth_enabled'] = true
+
gitlab_rails['omniauth_providers'] = [
{
"name" => "bitbucket",
@@ -96,10 +99,13 @@ you to use.
For installations from source:
```yaml
- - { name: 'bitbucket',
- app_id: 'BITBUCKET_APP_KEY',
- app_secret: 'BITBUCKET_APP_SECRET',
- url: 'https://bitbucket.org/' }
+ omniauth:
+ enabled: true
+ providers:
+ - { name: 'bitbucket',
+ app_id: 'BITBUCKET_APP_KEY',
+ app_secret: 'BITBUCKET_APP_SECRET',
+ url: 'https://bitbucket.org/' }
```
---
@@ -108,8 +114,8 @@ you to use.
from the Bitbucket application page.
1. Save the configuration file.
-1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you
- installed GitLab via Omnibus or from source respectively.
+1. For the changes to take effect, [reconfigure GitLab][] if you installed via
+ Omnibus, or [restart][] if installed from source.
On the sign in page there should now be a Bitbucket icon below the regular sign
in form. Click the icon to begin the authentication process. Bitbucket will ask
@@ -121,9 +127,12 @@ well, the user will be returned to GitLab and will be signed in.
Once the above configuration is set up, you can use Bitbucket to sign into
GitLab and [start importing your projects][bb-import].
+If you want to import projects from Bitbucket, but don't want to enable signing in,
+you can [disable Sign-Ins in the admin panel](omniauth.md#enable-or-disable-sign-in-with-an-omniauth-provider-without-disabling-import-sources).
+
[init-oauth]: omniauth.md#initial-omniauth-configuration
[bb-import]: ../workflow/importing/import_projects_from_bitbucket.md
[bb-old]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-14-stable/doc/integration/bitbucket.md
[bitbucket-docs]: https://confluence.atlassian.com/bitbucket/use-the-ssh-protocol-with-bitbucket-cloud-221449711.html#UsetheSSHprotocolwithBitbucketCloud-KnownhostorBitbucket%27spublickeyfingerprints
-[reconfigure]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
-[restart GitLab]: ../administration/restart_gitlab.md#installations-from-source
+[reconfigure GitLab]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
+[restart]: ../administration/restart_gitlab.md#installations-from-source
diff --git a/doc/integration/google.md b/doc/integration/google.md
index 8906f91b6b4..73e2f5826ff 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -30,7 +30,7 @@ In Google's side:
```
https://gitlab.example.com/users/auth/google_oauth2/callback
- https://gitlab.exampl.com/-/google_api/auth/callback
+ https://gitlab.example.com/-/google_api/auth/callback
```
1. You should now be able to see a Client ID and Client secret. Note them down
@@ -77,7 +77,7 @@ On your GitLab server:
For installations from source:
- ```
+ ```yaml
- { name: 'google_oauth2', app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET',
args: { access_type: 'offline', approval_prompt: '' } }
diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md
index 8ba2e8731c8..acc9db15826 100644
--- a/doc/integration/oauth_provider.md
+++ b/doc/integration/oauth_provider.md
@@ -78,8 +78,8 @@ in the **Authorized applications** section under **Profile Settings > Applicatio
---
GitLab's OAuth applications support scopes, which allow various actions that any given
-application can perform. Although there are only two scopes available at the
-moment – `read_user` and `api` – the groundwork has been laid to add more scopes easily.
+application can perform such as `read_user` and `api`. There are many more scopes
+available.
At any time you can revoke any access by just clicking **Revoke**.
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index db06efdae53..25f396bc9c4 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -1,5 +1,8 @@
# SAML OmniAuth Provider
+NOTE: **Note:**
+You need to [enable OmniAuth](omniauth.md) in order to use this.
+
GitLab can be configured to act as a SAML 2.0 Service Provider (SP). This allows
GitLab to consume assertions from a SAML 2.0 Identity Provider (IdP) such as
Microsoft ADFS to authenticate users.
@@ -15,33 +18,33 @@ in your SAML IdP:
For omnibus package:
```sh
- sudo editor /etc/gitlab/gitlab.rb
+ sudo editor /etc/gitlab/gitlab.rb
```
For installations from source:
```sh
- cd /home/git/gitlab
+ cd /home/git/gitlab
- sudo -u git -H editor config/gitlab.yml
+ sudo -u git -H editor config/gitlab.yml
```
-1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration)
- for initial settings.
-
1. To allow your users to use SAML to sign up without having to manually create
an account first, don't forget to add the following values to your configuration:
For omnibus package:
```ruby
- gitlab_rails['omniauth_allow_single_sign_on'] = ['saml']
- gitlab_rails['omniauth_block_auto_created_users'] = false
+ gitlab_rails['omniauth_enabled'] = true
+ gitlab_rails['omniauth_allow_single_sign_on'] = ['saml']
+ gitlab_rails['omniauth_block_auto_created_users'] = false
```
For installations from source:
```yaml
+ omniauth:
+ enabled: true
allow_single_sign_on: ["saml"]
block_auto_created_users: false
```
@@ -52,13 +55,13 @@ in your SAML IdP:
For omnibus package:
```ruby
- gitlab_rails['omniauth_auto_link_saml_user'] = true
+ gitlab_rails['omniauth_auto_link_saml_user'] = true
```
For installations from source:
```yaml
- auto_link_saml_user: true
+ auto_link_saml_user: true
```
1. Add the provider configuration:
@@ -66,35 +69,37 @@ in your SAML IdP:
For omnibus package:
```ruby
- gitlab_rails['omniauth_providers'] = [
- {
- name: 'saml',
- args: {
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
- idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
- idp_sso_target_url: 'https://login.example.com/idp',
- issuer: 'https://gitlab.example.com',
- name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'
- },
- label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
- }
- ]
- ```
-
- For installations from source:
-
- ```yaml
- - {
- name: 'saml',
- args: {
+ gitlab_rails['omniauth_providers'] = [
+ {
+ name: 'saml',
+ args: {
assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
idp_sso_target_url: 'https://login.example.com/idp',
issuer: 'https://gitlab.example.com',
name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'
},
- label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
- }
+ label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
+ }
+ ]
+ ```
+
+ For installations from source:
+
+ ```yaml
+ omniauth:
+ providers:
+ - {
+ name: 'saml',
+ args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'
+ },
+ label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
+ }
```
1. Change the value for `assertion_consumer_service_url` to match the HTTPS endpoint
@@ -140,8 +145,8 @@ This setting is only available on GitLab 8.7 and above.
SAML login includes support for automatically identifying whether a user should
be considered an [external](../user/permissions.md) user based on the user's group
membership in the SAML identity provider. This feature **does not** allow you to
-automatically add users to GitLab [Groups](../user/group/index.md), it simply
-allows you to mark users as External if they are members of certain groups in the
+automatically add users to GitLab [Groups](../user/group/index.md), it simply
+allows you to mark users as External if they are members of certain groups in the
Identity Provider.
### Requirements
@@ -189,28 +194,28 @@ If you want some SAML authentication methods to count as 2FA on a per session ba
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
- gitlab_rails['omniauth_providers'] = [
- {
- name: 'saml',
- args: {
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
- idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
- idp_sso_target_url: 'https://login.example.com/idp',
- issuer: 'https://gitlab.example.com',
- name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
- upstream_two_factor_authn_contexts:
- %w(
- urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport
- urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS
- urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN
- )
-
- },
- label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
- }
- ]
+ gitlab_rails['omniauth_providers'] = [
+ {
+ name: 'saml',
+ args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
+ upstream_two_factor_authn_contexts:
+ %w(
+ urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport
+ urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS
+ urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN
+ )
+
+ },
+ label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
+ }
+ ]
```
-
+
1. Save the file and [reconfigure][] GitLab for the changes to take effect.
---
@@ -218,40 +223,41 @@ If you want some SAML authentication methods to count as 2FA on a per session ba
**For installations from source:**
1. Edit `config/gitlab.yml`:
-
- ```yaml
- - {
- name: 'saml',
- args: {
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
- idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
- idp_sso_target_url: 'https://login.example.com/idp',
- issuer: 'https://gitlab.example.com',
- name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
- upstream_two_factor_authn_contexts:
- [
- 'urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport',
- 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS',
- 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN'
- ]
-
- },
- label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
- }
+
+ ```yaml
+ omniauth:
+ providers:
+ - {
+ name: 'saml',
+ args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
+ upstream_two_factor_authn_contexts:
+ [
+ 'urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport',
+ 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS',
+ 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN'
+ ]
+ },
+ label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
+ }
```
-
+
1. Save the file and [restart GitLab][] for the changes ot take effect
-
+
In addition to the changes in GitLab, make sure that your Idp is returning the
`AuthnContext`. For example:
```xml
- <saml:AuthnStatement>
- <saml:AuthnContext>
- <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:MediumStrongCertificateProtectedTransport</saml:AuthnContextClassRef>
- </saml:AuthnContext>
- </saml:AuthnStatement>
+<saml:AuthnStatement>
+ <saml:AuthnContext>
+ <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:MediumStrongCertificateProtectedTransport</saml:AuthnContextClassRef>
+ </saml:AuthnContext>
+</saml:AuthnStatement>
```
## Customization
diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md
index 0e43b4a39a4..41fa63ae6f2 100644
--- a/doc/integration/shibboleth.md
+++ b/doc/integration/shibboleth.md
@@ -43,7 +43,19 @@ exclude shibboleth URLs from rewriting, add "RewriteCond %{REQUEST_URI} !/Shibbo
RequestHeader set X_FORWARDED_PROTO 'https'
```
-1. Edit /etc/gitlab/gitlab.rb configuration file, your shibboleth attributes should be in form of "HTTP_ATTRIBUTE" and you should adjust them to your need and environment. Add any other configuration you need.
+1. Edit /etc/gitlab/gitlab.rb configuration file to enable OmniAuth and add
+Shibboleth as an OmniAuth provider. User attributes will be 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
+to the [OmniAuth::Strategies::Shibboleth class](https://github.com/toyokazu/omniauth-shibboleth/blob/master/lib/omniauth/strategies/shibboleth.rb)
+and are documented by the [omniauth-shibboleth gem](https://github.com/toyokazu/omniauth-shibboleth)
+(take care to note the version of the gem packaged with GitLab). If some of
+your users appear to be authenticated by Shibboleth and Apache, but GitLab
+rejects their account with a URI that contains "e-mail is invalid" then your
+Shibboleth Identity Provider or Attribute Authority may be asserting multiple
+e-mail addresses. In this instance, you might consider setting the
+`multi_values` argument to `first`.
File should look like this:
```
@@ -58,14 +70,15 @@ gitlab_rails['omniauth_block_auto_created_users'] = false
gitlab_rails['omniauth_enabled'] = true
gitlab_rails['omniauth_providers'] = [
{
- "name" => 'shibboleth',
- "args" => {
- "shib_session_id_field" => "HTTP_SHIB_SESSION_ID",
+ "name" => "'shibboleth"',
+ "label" => "Text for Login Button",
+ "args" => {
+ "shib_session_id_field" => "HTTP_SHIB_SESSION_ID",
"shib_application_id_field" => "HTTP_SHIB_APPLICATION_ID",
- "uid_field" => 'HTTP_EPPN',
- "name_field" => 'HTTP_CN',
+ "uid_field" => 'HTTP_EPPN',
+ "name_field" => 'HTTP_CN',
"info_fields" => { "email" => 'HTTP_MAIL'}
- }
+ }
}
]
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index e8f4c73120c..81ee7338e4e 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -30,6 +30,12 @@ in users.
Any logged in user will have [Guest](../user/permissions.md) permissions
on the repository.
+### Private projects
+
+Private projects can only be cloned and viewed by project members, and
+they will only appear to project members on the public access directory
+(`https://gitlab.example.com/public`).
+
### How to change project visibility
1. Go to your project's **Settings**
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 95221d8b6b1..f1881e0f767 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -195,6 +195,12 @@ This example can be used for a bucket in Amsterdam (AMS3).
1. [Reconfigure GitLab] for the changes to take effect
+CAUTION: **Warning:**
+If you see `400 Bad Request` by using Digital Ocean Spaces, the cause may be the
+usage of backup encryption. Remove or comment the line that
+contains `gitlab_rails['backup_encryption']` since Digital Ocean Spaces
+doesn't support encryption.
+
#### Other S3 Providers
Not all S3 providers are fully-compatible with the Fog library. For example,
@@ -326,6 +332,16 @@ For installations from source:
1. [Restart GitLab] for the changes to take effect
+#### Specifying a custom directory for backups
+
+Note: This option only works for remote storage. If you want to group your backups
+you can pass a `DIRECTORY` environment variable:
+
+```
+sudo gitlab-rake gitlab:backup:create DIRECTORY=daily
+sudo gitlab-rake gitlab:backup:create DIRECTORY=weekly
+```
+
### Uploading to locally mounted shares
You may also send backups to a mounted share (`NFS` / `CIFS` / `SMB` / etc.) by
@@ -369,15 +385,6 @@ For installations from source:
remote_directory: 'gitlab_backups'
```
-### Specifying a custom directory for backups
-
-If you want to group your backups you can pass a `DIRECTORY` environment variable:
-
-```
-sudo gitlab-rake gitlab:backup:create DIRECTORY=daily
-sudo gitlab-rake gitlab:backup:create DIRECTORY=weekly
-```
-
### Backup archive permissions
The backup archives created by GitLab (`1393513186_2014_02_27_gitlab_backup.tar`)
diff --git a/doc/raketasks/cleanup.md b/doc/raketasks/cleanup.md
index cf891cd90ad..e70a009323e 100644
--- a/doc/raketasks/cleanup.md
+++ b/doc/raketasks/cleanup.md
@@ -22,3 +22,63 @@ sudo gitlab-rake gitlab:cleanup:repos
# installation from source
bundle exec rake gitlab:cleanup:repos RAILS_ENV=production
```
+
+Clean up local project upload files if they don't exist in GitLab database. The
+task attempts to fix the file if it can find its project, otherwise it moves the
+file to a lost and found directory.
+
+```
+# omnibus-gitlab
+sudo gitlab-rake gitlab:cleanup:project_uploads
+
+# installation from source
+bundle exec rake gitlab:cleanup:project_uploads RAILS_ENV=production
+```
+
+Example output:
+
+```
+$ sudo gitlab-rake gitlab:cleanup:project_uploads
+I, [2018-07-27T12:08:27.671559 #89817] INFO -- : Looking for orphaned project uploads to clean up. Dry run...
+D, [2018-07-27T12:08:28.293568 #89817] DEBUG -- : Processing batch of 500 project upload file paths, starting with /opt/gitlab/embedded/service/gitlab-rails/public/uploads/test.out
+I, [2018-07-27T12:08:28.689869 #89817] INFO -- : Can move to lost and found /opt/gitlab/embedded/service/gitlab-rails/public/uploads/test.out -> /opt/gitlab/embedded/service/gitlab-rails/public/uploads/-/project-lost-found/test.out
+I, [2018-07-27T12:08:28.755624 #89817] INFO -- : Can fix /opt/gitlab/embedded/service/gitlab-rails/public/uploads/foo/bar/89a0f7b0b97008a4a18cedccfdcd93fb/foo.txt -> /opt/gitlab/embedded/service/gitlab-rails/public/uploads/qux/foo/bar/89a0f7b0b97008a4a18cedccfdcd93fb/foo.txt
+I, [2018-07-27T12:08:28.760257 #89817] INFO -- : Can move to lost and found /opt/gitlab/embedded/service/gitlab-rails/public/uploads/foo/bar/1dd6f0f7eefd2acc4c2233f89a0f7b0b/image.png -> /opt/gitlab/embedded/service/gitlab-rails/public/uploads/-/project-lost-found/foo/bar/1dd6f0f7eefd2acc4c2233f89a0f7b0b/image.png
+I, [2018-07-27T12:08:28.764470 #89817] INFO -- : To cleanup these files run this command with DRY_RUN=false
+
+$ sudo gitlab-rake gitlab:cleanup:project_uploads DRY_RUN=false
+I, [2018-07-27T12:08:32.944414 #89936] INFO -- : Looking for orphaned project uploads to clean up...
+D, [2018-07-27T12:08:33.293568 #89817] DEBUG -- : Processing batch of 500 project upload file paths, starting with /opt/gitlab/embedded/service/gitlab-rails/public/uploads/test.out
+I, [2018-07-27T12:08:33.689869 #89817] INFO -- : Did move to lost and found /opt/gitlab/embedded/service/gitlab-rails/public/uploads/test.out -> /opt/gitlab/embedded/service/gitlab-rails/public/uploads/-/project-lost-found/test.out
+I, [2018-07-27T12:08:33.755624 #89817] INFO -- : Did fix /opt/gitlab/embedded/service/gitlab-rails/public/uploads/foo/bar/89a0f7b0b97008a4a18cedccfdcd93fb/foo.txt -> /opt/gitlab/embedded/service/gitlab-rails/public/uploads/qux/foo/bar/89a0f7b0b97008a4a18cedccfdcd93fb/foo.txt
+I, [2018-07-27T12:08:33.760257 #89817] INFO -- : Did move to lost and found /opt/gitlab/embedded/service/gitlab-rails/public/uploads/foo/bar/1dd6f0f7eefd2acc4c2233f89a0f7b0b/image.png -> /opt/gitlab/embedded/service/gitlab-rails/public/uploads/-/project-lost-found/foo/bar/1dd6f0f7eefd2acc4c2233f89a0f7b0b/image.png
+```
+
+Remove object store upload files if they don't exist in GitLab database.
+
+```
+# omnibus-gitlab
+sudo gitlab-rake gitlab:cleanup:remote_upload_files
+
+# installation from source
+bundle exec rake gitlab:cleanup:remote_upload_files RAILS_ENV=production
+```
+
+Example output:
+
+```
+$ sudo gitlab-rake gitlab:cleanup:remote_upload_files
+
+I, [2018-08-02T10:26:13.995978 #45011] INFO -- : Looking for orphaned remote uploads to remove. Dry run...
+I, [2018-08-02T10:26:14.120400 #45011] INFO -- : Can be moved to lost and found: @hashed/6b/DSC_6152.JPG
+I, [2018-08-02T10:26:14.120482 #45011] INFO -- : Can be moved to lost and found: @hashed/79/02/7902699be42c8a8e46fbbb4501726517e86b22c56a189f7625a6da49081b2451/711491b29d3eb08837798c4909e2aa4d/DSC00314.jpg
+I, [2018-08-02T10:26:14.120634 #45011] INFO -- : To cleanup these files run this command with DRY_RUN=false
+```
+
+```
+$ sudo gitlab-rake gitlab:cleanup:remote_upload_files DRY_RUN=false
+
+I, [2018-08-02T10:26:47.598424 #45087] INFO -- : Looking for orphaned remote uploads to remove...
+I, [2018-08-02T10:26:47.753131 #45087] INFO -- : Moved to lost and found: @hashed/6b/DSC_6152.JPG -> lost_and_found/@hashed/6b/DSC_6152.JPG
+I, [2018-08-02T10:26:47.764356 #45087] INFO -- : Moved to lost and found: @hashed/79/02/7902699be42c8a8e46fbbb4501726517e86b22c56a189f7625a6da49081b2451/711491b29d3eb08837798c4909e2aa4d/DSC00314.jpg -> lost_and_found/@hashed/79/02/7902699be42c8a8e46fbbb4501726517e86b22c56a189f7625a6da49081b2451/711491b29d3eb08837798c4909e2aa4d/DSC00314.jpg
+```
diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md
index 5554a0c8b78..e1b1912ed47 100644
--- a/doc/raketasks/user_management.md
+++ b/doc/raketasks/user_management.md
@@ -14,7 +14,7 @@ bundle exec rake gitlab:import:user_to_projects[username@domain.tld] RAILS_ENV=p
Notes:
-- admin users are added as masters
+- admin users are added as maintainers
```bash
# omnibus-gitlab
diff --git a/doc/security/rack_attack.md b/doc/security/rack_attack.md
index c61729581e8..3efb19c1526 100644
--- a/doc/security/rack_attack.md
+++ b/doc/security/rack_attack.md
@@ -9,13 +9,17 @@ In case you find throttling is not enough to protect you against abusive clients
Rack Attack offers IP whitelisting, blacklisting, Fail2ban style filtering and
tracking.
+**Note:** Starting with 11.2, Rack Attack is disabled by default. To continue
+using this feature, please enable it in your `gitlab.rb` by setting
+`gitlab_rails['rack_attack_git_basic_auth'] = true`.
+
By default, user sign-in, user sign-up (if enabled), and user password reset is
limited to 6 requests per minute. After trying for 6 times, the client will
have to wait for the next minute to be able to try again.
If you installed or upgraded GitLab by following the [official guides](../install/README.md)
-this should be enabled by default. If your instance is not exposed to any incoming
-connections, it is recommended to disable Rack Attack.
+this should be disabled by default. If your instance is not exposed to any incoming
+connections, it is recommended to leave Rack Attack disabled.
For more information on how to use these options check out
[rack-attack README](https://github.com/kickstarter/rack-attack/blob/master/README.md).
@@ -45,7 +49,7 @@ For more information on how to use these options check out
The following settings can be configured:
-- `enabled`: By default this is set to `true`. Set this to `false` to disable Rack Attack.
+- `enabled`: By default this is set to `false`. Set this to `true` to enable Rack Attack.
- `ip_whitelist`: Whitelist any IPs from being blocked. They must be formatted as strings within a ruby array.
For example, `["127.0.0.1", "127.0.0.2", "127.0.0.3"]`.
- `maxretry`: The maximum amount of times a request can be made in the
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index bab196e7609..63f0a654fcf 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -77,6 +77,8 @@ Note that Public SSH key may also be named as follows:
If you want to change the password of your SSH key pair, you can use
`ssh-keygen -p <keyname>`.
+## Adding a SSH key to your GitLab account
+
1. The next step is to copy the public SSH key as we will need it afterwards.
To copy your public SSH key to the clipboard, use the appropriate code below:
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 1d26a743500..f5574506595 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -91,7 +91,7 @@ To make full use of Auto DevOps, you will need:
for the entire GitLab instance, or [specific Runners](../../ci/runners/README.md#registering-a-specific-runner)
that are assigned to specific projects.
1. **Base domain** (needed for Auto Review Apps and Auto Deploy) - You will need
- a domain configured with wildcard DNS which is gonna be used by all of your
+ a domain configured with wildcard DNS which is going to be used by all of your
Auto DevOps applications. [Read the specifics](#auto-devops-base-domain).
1. **Kubernetes** (needed for Auto Review Apps, Auto Deploy, and Auto Monitoring) -
To enable deployments, you will need Kubernetes 1.5+. You need a [Kubernetes cluster][kubernetes-clusters]
@@ -297,7 +297,7 @@ out.
In GitLab Starter, differences between the source and
target branches are also
-[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
+[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html).
### Auto SAST **[ULTIMATE]**
@@ -527,7 +527,7 @@ repo 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 [default
- one](https://gitlab.com/charts/charts.gitlab.io/tree/master/charts/auto-deploy-app).
+ one](https://gitlab.com/charts/auto-deploy-app).
This can be a great way to control exactly how your application is deployed.
- **Project variable** - Create a [project variable](../../ci/variables/README.md#secret-variables)
`AUTO_DEVOPS_CHART` with the URL of a custom chart to use.
@@ -591,7 +591,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
| `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. |
| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
-| `CODEQUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
+| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. |
| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. |
@@ -840,5 +840,5 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/
[postgresql]: https://www.postgresql.org/
[Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml
[GitLab Omnibus Helm Chart]: ../../install/kubernetes/gitlab_omnibus.md
-[ee]: https://about.gitlab.com/products/
+[ee]: https://about.gitlab.com/pricing/
[ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507
diff --git a/doc/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md
index dc045961ed7..8f7bb8636c5 100644
--- a/doc/university/high-availability/aws/README.md
+++ b/doc/university/high-availability/aws/README.md
@@ -2,10 +2,8 @@
comments: false
---
-DANGER: This guide exists for reference of how an AWS deployment could work.
-We are currently seeing very slow EFS access performance which causes GitLab to
-be 5-10x slower than using NFS or Local disk. We _do not_ recommend follow this
-guide at this time.
+> **Note**: We **do not** recommend using the AWS Elastic File System (EFS), as it can result
+in [significantly degraded performance](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/administration/high_availability/nfs.md#aws-elastic-file-system).
# High Availability on AWS
diff --git a/doc/update/10.6-to-10.7.md b/doc/update/10.6-to-10.7.md
index 4efbb8c65cf..b9c14395a3a 100644
--- a/doc/update/10.6-to-10.7.md
+++ b/doc/update/10.6-to-10.7.md
@@ -340,7 +340,7 @@ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations, the upgrade is complete!
-## Things went south? Revert to previous version (10.5)
+## Things went south? Revert to previous version (10.6)
### 1. Revert the code to the previous version
diff --git a/doc/update/11.0-to-11.1.md b/doc/update/11.0-to-11.1.md
new file mode 100644
index 00000000000..3f10a7edb8a
--- /dev/null
+++ b/doc/update/11.0-to-11.1.md
@@ -0,0 +1,378 @@
+---
+comments: false
+---
+
+# From 11.0 to 11.1
+
+Make sure you view this update guide from the branch (version) of GitLab you would
+like to install (e.g., `11-1-stable`. You can select the branch in the version
+dropdown at the top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+NOTE: If you installed GitLab from source, make sure `rsync` is installed.
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+NOTE: GitLab 11.0 and higher only support Ruby 2.4.x and dropped support for Ruby 2.3.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download Ruby and compile it:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.4.tar.gz
+echo 'ec82b0d53bd0adad9b19e6b45e44d54e9ec3f10c ruby-2.4.4.tar.gz' | shasum -c - && tar xzf ruby-2.4.4.tar.gz
+cd ruby-2.4.4
+
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets.
+This requires a minimum version of node v6.0.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v6.0.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+GitLab also requires the use of yarn `>= v1.2.0` to manage JavaScript
+dependencies.
+
+```bash
+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
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 11.0 and higher only supports Go 1.9.x and newer, and dropped support for Go
+1.5.x through 1.8.x. Be sure to upgrade your installation if necessary.
+
+You can check which version you are running with `go version`.
+
+Download and install Go:
+
+```bash
+# Remove former Go installation folder
+sudo rm -rf /usr/local/go
+
+curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
+echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
+sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+rm go1.10.3.linux-amd64.tar.gz
+```
+
+### 6. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all --prune
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+sudo -u git -H git checkout -- locale
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-1-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-1-stable-ee
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 8. 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.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+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
+```
+
+### 9. Update Gitaly
+
+#### New Gitaly configuration options required
+
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
+
+```shell
+echo '
+[gitaly-ruby]
+dir = "/home/git/gitaly/ruby"
+
+[gitlab-shell]
+dir = "/home/git/gitlab-shell"
+' | sudo -u git tee -a /home/git/gitaly/config.toml
+```
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update gitlab-pages
+
+#### Only needed if you use GitLab Pages.
+
+Install and compile gitlab-pages. GitLab-Pages 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.
+
+```bash
+cd /home/git/gitlab-pages
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+sudo -u git -H make
+```
+
+### 11. Update MySQL permissions
+
+If you are using MySQL you need to grant the GitLab user the necessary
+permissions on the database:
+
+```bash
+mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
+```
+
+If you use MySQL with replication, or just have MySQL configured with binary logging,
+you will need to also run the following on all of your MySQL servers:
+
+```bash
+mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
+```
+
+You can make this setting permanent by adding it to your `my.cnf`:
+
+```
+log_bin_trust_function_creators=1
+```
+
+### 12. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-0-stable:config/gitlab.yml.example origin/11-1-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/11-0-stable:lib/support/nginx/gitlab-ssl origin/11-1-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/11-0-stable:lib/support/nginx/gitlab origin/11-1-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+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
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-1-stable/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 add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-1-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-0-stable:lib/support/init.d/gitlab.default.example origin/11-1-stable:lib/support/init.d/gitlab.default.example
+```
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 13. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Compile GetText PO files
+
+sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
+
+# Update node dependencies and recompile assets
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
+
+# Clean up cache
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 14. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 15. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (11.0)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 10.8 to 11.0](10.8-to-11.0.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-1-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-1-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/update/11.1-to-11.2.md b/doc/update/11.1-to-11.2.md
new file mode 100644
index 00000000000..3edc7e6923e
--- /dev/null
+++ b/doc/update/11.1-to-11.2.md
@@ -0,0 +1,378 @@
+---
+comments: false
+---
+
+# From 11.1 to 11.2
+
+Make sure you view this update guide from the branch (version) of GitLab you would
+like to install (e.g., `11-2-stable`. You can select the branch in the version
+dropdown at the top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+NOTE: If you installed GitLab from source, make sure `rsync` is installed.
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+NOTE: GitLab 11.0 and higher only support Ruby 2.4.x and dropped support for Ruby 2.3.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download Ruby and compile it:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.4.tar.gz
+echo 'ec82b0d53bd0adad9b19e6b45e44d54e9ec3f10c ruby-2.4.4.tar.gz' | shasum -c - && tar xzf ruby-2.4.4.tar.gz
+cd ruby-2.4.4
+
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets.
+This requires a minimum version of node v6.0.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v6.0.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+GitLab also requires the use of yarn `>= v1.2.0` to manage JavaScript
+dependencies.
+
+```bash
+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
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 11.0 and higher only supports Go 1.9.x and newer, and dropped support for Go
+1.5.x through 1.8.x. Be sure to upgrade your installation if necessary.
+
+You can check which version you are running with `go version`.
+
+Download and install Go:
+
+```bash
+# Remove former Go installation folder
+sudo rm -rf /usr/local/go
+
+curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
+echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
+sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+rm go1.10.3.linux-amd64.tar.gz
+```
+
+### 6. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all --prune
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+sudo -u git -H git checkout -- locale
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-2-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-2-stable-ee
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 8. 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.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+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
+```
+
+### 9. Update Gitaly
+
+#### New Gitaly configuration options required
+
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
+
+```shell
+echo '
+[gitaly-ruby]
+dir = "/home/git/gitaly/ruby"
+
+[gitlab-shell]
+dir = "/home/git/gitlab-shell"
+' | sudo -u git tee -a /home/git/gitaly/config.toml
+```
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update gitlab-pages
+
+#### Only needed if you use GitLab Pages.
+
+Install and compile gitlab-pages. GitLab-Pages 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.
+
+```bash
+cd /home/git/gitlab-pages
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+sudo -u git -H make
+```
+
+### 11. Update MySQL permissions
+
+If you are using MySQL you need to grant the GitLab user the necessary
+permissions on the database:
+
+```bash
+mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
+```
+
+If you use MySQL with replication, or just have MySQL configured with binary logging,
+you will need to also run the following on all of your MySQL servers:
+
+```bash
+mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
+```
+
+You can make this setting permanent by adding it to your `my.cnf`:
+
+```
+log_bin_trust_function_creators=1
+```
+
+### 12. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-1-stable:config/gitlab.yml.example origin/11-2-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/11-1-stable:lib/support/nginx/gitlab-ssl origin/11-2-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/11-1-stable:lib/support/nginx/gitlab origin/11-2-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+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
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-2-stable/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 add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-2-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/11-1-stable:lib/support/init.d/gitlab.default.example origin/11-2-stable:lib/support/init.d/gitlab.default.example
+```
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 13. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Compile GetText PO files
+
+sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
+
+# Update node dependencies and recompile assets
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
+
+# Clean up cache
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 14. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 15. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (11.1)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 11.0 to 11.1](11.0-to-11.1.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-2-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-2-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 44e9f6c5516..7c6a14cb104 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -194,12 +194,18 @@ sudo apt-get install pgloader
1. Switch database from MySQL to PostgreSQL
- ``` bash
- cd /home/git/gitlab
- sudo -u git mv config/database.yml config/database.yml.bak
- sudo -u git cp config/database.yml.postgresql config/database.yml
- sudo -u git -H chmod o-rwx config/database.yml
- ```
+ ``` bash
+ cd /home/git/gitlab
+ sudo -u git mv config/database.yml config/database.yml.bak
+ sudo -u git cp config/database.yml.postgresql config/database.yml
+ sudo -u git -H chmod o-rwx config/database.yml
+ ```
+1. Install Gems related to Postgresql
+
+ ``` bash
+ sudo -u git -H rm .bundle/config
+ sudo -u git -H bundle install --deployment --without development test mysql aws kerberos
+ ```
1. Run the following commands to prepare the schema:
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index e1857ce99c6..a4f17746b69 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -92,18 +92,28 @@ sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly]" RAILS_
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
-sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`cat /home/git/gitlab/GITLAB_SHELL_VERSION`
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION) -b v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
sudo -u git -H sh -c 'if [ -x bin/compile ]; then bin/compile; fi'
```
-### 7. Start application
+### 7. Update gitlab-pages to the corresponding version (skip if not using pages)
+
+```bash
+cd /home/git/gitlab-pages
+
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+sudo -u git -H make
+```
+
+### 8. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
-### 8. Check application status
+### 9. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md
index 843fb4ce26b..1b676bfb383 100644
--- a/doc/user/admin_area/monitoring/health_check.md
+++ b/doc/user/admin_area/monitoring/health_check.md
@@ -20,14 +20,24 @@ To access monitoring resources, the client IP needs to be included in a whitelis
[Read how to add IPs to a whitelist for the monitoring endpoints][admin].
-## Using the endpoint
+## Using the endpoints
With default whitelist settings, the probes can be accessed from localhost:
+- `http://localhost/-/health`
- `http://localhost/-/readiness`
- `http://localhost/-/liveness`
-which will then provide a report of system health in JSON format.
+
+The first endpoint, `/-/health/`, only checks whether the application server is running. It does
+-not verify the database or other services are running. A successful response will return
+a 200 status code with the following message:
+
+```
+GitLab OK
+```
+
+The readiness and liveness probes will provide a report of system health in JSON format.
Readiness example output:
@@ -42,12 +52,6 @@ Readiness example output:
"shared_state_check" : {
"status" : "ok"
},
- "fs_shards_check" : {
- "labels" : {
- "shard" : "default"
- },
- "status" : "ok"
- },
"db_check" : {
"status" : "ok"
},
@@ -61,9 +65,6 @@ Liveness example output:
```
{
- "fs_shards_check" : {
- "status" : "ok"
- },
"cache_check" : {
"status" : "ok"
},
diff --git a/doc/user/admin_area/settings/third_party_offers.md b/doc/user/admin_area/settings/third_party_offers.md
new file mode 100644
index 00000000000..177251b52b9
--- /dev/null
+++ b/doc/user/admin_area/settings/third_party_offers.md
@@ -0,0 +1,6 @@
+# Third party offers
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20379)
+> in [GitLab Core](https://about.gitlab.com/pricing/) 11.1
+
+The display of third party offers can be controlled in the Admin Area -> Settings page.
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index d054561d5f3..20886faf418 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -64,7 +64,8 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md).
## Repository size limit
-The maximum size your Git repository is allowed to be including LFS.
+The maximum size your Git repository is allowed to be, including LFS. If you are near
+or over the size limit, you can [reduce your repository size with Git](../project/repository/reducing_the_repo_size_using_git.md).
| Setting | GitLab.com | Default |
| ----------- | ----------------- | ------------- |
diff --git a/doc/user/index.md b/doc/user/index.md
index edf50019c2f..90f0e2285c3 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -7,7 +7,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
-your [subscription](https://about.gitlab.com/products/)
+your [subscription](https://about.gitlab.com/pricing/)
includes, except [GitLab administrator](../README.md#administrator-documentation)
settings, unless you have admin privileges to install, configure,
and upgrade your GitLab instance.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 3906bed8682..d7bf6838fb3 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -9,7 +9,7 @@
> For the best result, we encourage you to check this document out as rendered
by GitLab: [markdown.md]
-_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. Previous content and Markdown files `.md` in the repositories are still processed using the [Redcarpet Ruby library][redcarpet]._
+_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. Previous content, wiki pages and Markdown files (`.md`) in the repositories are still processed using the [Redcarpet Ruby library][redcarpet]._
_Where there are significant differences, we will try to call them out in this document._
@@ -22,7 +22,7 @@ You can use GFM in the following areas:
- merge requests
- milestones
- snippets (the snippet must be named with a `.md` extension)
-- wiki pages
+- wiki pages (currently only rendered by Redcarpet)
- markdown documents inside the repository (currently only rendered by Redcarpet)
You can also use other rich text files in GitLab. You might have to install a
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index a35bf48e62d..b6438397db8 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -313,4 +313,4 @@ Read through the documentation on [LDAP users permissions](https://docs.gitlab.c
[ce-18994]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18994
[new-mod]: project/new_ci_build_permissions_model.md
[ee-998]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998
-[eep]: https://about.gitlab.com/products/
+[eep]: https://about.gitlab.com/pricing/
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index d3a2a7dcd14..e25e1e19b13 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -88,7 +88,7 @@ storage in a safe place. **Each code can be used only once** to log in to your
account.
If you lose the recovery codes or just want to generate new ones, you can do so
-from the **Profile settings âž” Account** page where you first enabled 2FA.
+[using SSH](#generate-new-recovery-codes-using-ssh).
## Logging in with 2FA Enabled
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 91cdef8d1dd..b1b822f25bd 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -30,6 +30,7 @@ You can edit your account settings by navigating from the up-right corner menu b
From there, you can:
- Update your personal information
+- Set a [custom status](#current-status) for your profile
- Manage [2FA](account/two_factor_authentication.md)
- Change your username and [delete your account](account/delete_account.md)
- Manage applications that can
@@ -68,6 +69,49 @@ Alternatively, you can follow [this detailed procedure from the GitLab Team Hand
which also covers the case where you have projects hosted with
[GitLab Pages](../project/pages/index.md).
+## Private profile
+
+The following information will be hidden from the user profile page (https://gitlab.example.com/username) if this feature is enabled:
+
+- Atom feed
+- Date when account is created
+- Activity tab
+- Groups tab
+- Contributed projects tab
+- Personal projects tab
+- Snippets tab
+
+To enable private profile:
+
+1. Navigate to your personal [profile settings](#profile-settings).
+1. Check the "Private profile" option.
+1. Hit **Update profile settings**.
+
+
+NOTE: **Note:**
+You and GitLab admins can see your the abovementioned information on your profile even if it is private.
+
+## Current status
+
+> Introduced in GitLab 11.2.
+
+You can provide a custom status message for your user profile along with an emoji that describes it.
+This may be helpful when you are out of office or otherwise not available.
+Other users can then take your status into consideration when responding to your issues or assigning work to you.
+Please be aware that your status is publicly visible even if your [profile is private](#private-profile).
+
+To set your current status:
+
+1. Navigate to your personal [profile settings](#profile-settings).
+1. In the text field below `Your status`, enter your status message.
+1. Select an emoji from the dropdown if you like.
+1. Hit **Update profile settings**.
+
+Status messages are restricted to 100 characters of plain text.
+They may however contain emoji codes such as `I'm on vacation :palm_tree:`.
+
+You can also set your current status [using the API](../../api/users.md#user-status).
+
## Troubleshooting
### Why do I keep getting signed out?
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index 9b4fdd65e2f..25d6c34409c 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -48,6 +48,7 @@ the following table.
| `api` | Grants complete access to the API (read/write) ([introduced][ce-5951] in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. |
| `read_registry` | Allows to read [container registry] images if a project is private and authorization is required ([introduced][ce-11845] in GitLab 9.3). |
| `sudo` | Allows performing API actions as any user in the system (if the authenticated user is an admin) ([introduced][ce-14838] in GitLab 10.2). |
+| `read_repository` | Allows read-access to the repository through git clone. |
[2fa]: ../account/two_factor_authentication.md
[api]: ../../api/README.md
diff --git a/doc/user/project/bulk_editing.md b/doc/user/project/bulk_editing.md
index 324a7fa6603..4261293b06f 100644
--- a/doc/user/project/bulk_editing.md
+++ b/doc/user/project/bulk_editing.md
@@ -1,17 +1,25 @@
-# Bulk Editing
+# Bulk editing issues and merge requests
->**Note:**
+>
+**Notes:**
- A permission level of `Reporter` or higher is required in order to manage
issues.
- A permission level of `Developer` or higher is required in order to manage
merge requests.
-Fields across multiple issues or merge requests can be updated simutaneously by using the bulk edit feature.
+Attributes can be updated simultaneously across multiple issues or merge requests
+by using the bulk editing feature.
->**Note:**
-- Bulk editing of issues and merge requests is only available at the project level.
+![Bulk editing](img/bulk-editing.png)
-To access the feature, navigate to either the issue or merge request list for the project and click 'Edit Issues' or 'Edit Merge Requests'. This will cause a sidebar to be shown on the right-hand side of the screen, where the available, editable fields are displayed. Checkboxes will also appear to the left-hand side of each issue or merge request, ready to be selected.
+NOTE: **Note:**
+Bulk editing of issues and merge requests is only available at the project level.
-Once all items have been selected, choose the appropriate fields and their values from the sidebar and click 'Update All' to apply these changes.
+To update multiple project issues or merge requests at the same time, navigate to
+their respective lists and click **Edit issues** or **Edit merge requests** available
+in the tab bar. This will open a sidebar on the right-hand side of your screen
+where editable fields will be displayed. Checkboxes will also appear to the left-hand
+side of eachissue or merge request for you to select the items you want to update.
+Once you have selected all relevant items, choose the appropriate fields and their
+values from the sidebar and click **Update all** to apply your changes.
diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md
index 2d8fdf0d1da..ec8467da14f 100644
--- a/doc/user/project/clusters/eks_and_gitlab/index.md
+++ b/doc/user/project/clusters/eks_and_gitlab/index.md
@@ -62,7 +62,7 @@ Click on `Add Kubernetes cluster`, the cluster is now connected to GitLab. At th
If you would like to utilize your own CI/CD scripts to deploy to the cluster, you can stop here.
-## Disable Role Based-Access Control (RBAC)
+## Disable Role-Based Access Control (RBAC)
Presently, Auto DevOps and one-click app installs do not support [Kubernetes role-based access control](https://kubernetes.io/docs/reference/access-authn-authz/rbac/). Support is [being worked on](https://gitlab.com/groups/gitlab-org/-/epics/136), but in the interim RBAC must be disabled to utilize for these features.
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 20c46cafbe5..7c552103412 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -96,8 +96,9 @@ To add an existing Kubernetes cluster to your project:
you can follow the
[Kubernetes documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
to create one. You can also view or create service tokens in the
- [Kubernetes dashboard](https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#config)
- (under **Config > Secrets**).
+ [Kubernetes dashboard](https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/)
+ (under **Config > Secrets**). **The account that will issue the service token
+ must have admin privileges on the cluster.**
- **Project namespace** (optional) - You don't have to fill it in; by leaving
it blank, GitLab will create one for you. Also:
- Each project should have a unique namespace.
@@ -153,13 +154,20 @@ GitLab provides a one-click install for various applications which will be
added directly to your configured cluster. Those applications are needed for
[Review Apps](../../../ci/review_apps/index.md) and [deployments](../../../ci/environments.md).
+NOTE: **Note:**
+The applications will be installed in a dedicated namespace called
+`gitlab-managed-apps`. In case you have added an existing Kubernetes cluster
+with Tiller already installed, you should be careful as GitLab cannot
+detect it. By installing it via the applications will result into having it
+twice, which can lead to confusion during deployments.
+
| Application | GitLab version | Description |
| ----------- | :------------: | ----------- |
| [Helm Tiller](https://docs.helm.sh/) | 10.2+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. |
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. |
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. |
| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. |
-| [JupyterHub](http://jupyter.org/) | 11.0+ | [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. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. |
+| [JupyterHub](http://jupyter.org/) | 11.0+ | [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. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. |
## Getting the external IP address
@@ -206,7 +214,7 @@ kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalanc
> **Note**: Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
> ```bash
-> kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}"`.
+> kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}".
> ```
The output is the external IP address of your cluster. This information can then
@@ -406,5 +414,5 @@ the deployment variables above, ensuring any pods you create are labelled with
- [Connecting and deploying to an Amazon EKS cluster](eks_and_gitlab/index.md)
[permissions]: ../../permissions.md
-[ee]: https://about.gitlab.com/products/
+[ee]: https://about.gitlab.com/pricing/
[Auto DevOps]: ../../../topics/autodevops/index.md
diff --git a/doc/user/project/img/bulk-editing.png b/doc/user/project/img/bulk-editing.png
new file mode 100644
index 00000000000..f6b163f55d9
--- /dev/null
+++ b/doc/user/project/img/bulk-editing.png
Binary files differ
diff --git a/doc/user/project/img/labels_project_list_search.png b/doc/user/project/img/labels_project_list_search.png
new file mode 100644
index 00000000000..ff9bf92e1c3
--- /dev/null
+++ b/doc/user/project/img/labels_project_list_search.png
Binary files differ
diff --git a/doc/user/project/import/bitbucket.md b/doc/user/project/import/bitbucket.md
index e3d625cc621..1cc3bd3363d 100644
--- a/doc/user/project/import/bitbucket.md
+++ b/doc/user/project/import/bitbucket.md
@@ -1,17 +1,13 @@
-# Import your project from Bitbucket to GitLab
+# Import your project from Bitbucket Cloud to GitLab
-Import your projects from Bitbucket to GitLab with minimal effort.
+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).
-## Overview
-
->**Note:**
-The [Bitbucket integration][bb-import] must be first enabled in order to be
-able to import your projects from Bitbucket. Ask your GitLab administrator
-to enable this if not already.
+Import your projects from Bitbucket Cloud to GitLab with minimal effort.
->**Note:**
-The BitBucket importer currently only works with BitBucket's cloud offering
-(bitbucket.org) and does not work with BitBucket Server (aka Stash).
+## Overview
- At its current state, the Bitbucket importer can import:
- the repository description (GitLab 7.7+)
@@ -26,6 +22,11 @@ The BitBucket importer currently only works with BitBucket's cloud offering
- Repository public access is retained. If a repository is private in Bitbucket
it will be created as private in GitLab as well.
+## Requirements
+
+The [Bitbucket Cloud integration][bb-import] must be first enabled in order to be
+able to import your projects from Bitbucket Cloud. Ask your GitLab administrator
+to enable this if not already.
## How it works
@@ -46,9 +47,7 @@ namespace that started the import process.
1. Sign in to GitLab and go to your dashboard.
1. Click on **New project**.
- ![New project in GitLab](img/bitbucket_import_new_project.png)
-
-1. Click on the "Bitbucket" button
+1. Click on the "Bitbucket Cloud" button.
![Bitbucket](img/import_projects_from_new_project_page.png)
diff --git a/doc/user/project/import/bitbucket_server.md b/doc/user/project/import/bitbucket_server.md
new file mode 100644
index 00000000000..dc985e87a96
--- /dev/null
+++ b/doc/user/project/import/bitbucket_server.md
@@ -0,0 +1,75 @@
+# Import your project from Bitbucket Server to GitLab
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20164)
+in GitLab 11.2.
+
+NOTE: **Note:**
+The Bitbucket Server importer does not work with Bitbucket Cloud (aka bitbucket.org).
+Use the [Bitbucket Cloud importer](bitbucket.md) for that.
+
+Import your projects from Bitbucket Server to GitLab with minimal effort.
+
+## Overview
+
+- In its current state, the Bitbucket importer can import:
+ - the repository description (GitLab 11.2+)
+ - the Git repository data (GitLab 11.2+)
+ - the pull requests (GitLab 11.2+)
+ - the pull request comments (GitLab 11.2+)
+- Repository public access is retained. If a repository is private in Bitbucket
+ it will be created as private in GitLab as well.
+
+## Limitations
+
+1. Currently GitLab doesn't allow comments on arbitrary lines of code, so any
+Bitbucket comments out of bounds will be inserted as comments in the merge
+request.
+1. Bitbucket Server allows multiple levels of threading. GitLab
+import will collapse this into one discussion and quote part of the original
+comment.
+1. Declined pull requests have unrecahable commits, which prevents the GitLab
+importer from generating a proper diff. These pull requests will show up as
+empty changes.
+1. Attachments in Markdown are currently not imported.
+1. Task lists are not imported.
+1. Emoji reactions are not imported
+
+## How it works
+
+The Bitbucket Server importer works as follows:
+
+1. The user will be prompted to enter the URl, username, and password or personal access token to login to Bitbucket.
+ These credentials are preserved only as long as the importer is running.
+1. The importer will attempt to list all the current repositories on the Bitbucket Server.
+1. Upon selection, the importer will clone the repository and import pull requests and comments.
+
+### User assignment
+
+When issues/pull requests are being imported, the Bitbucket importer tries to
+find the author's e-mail address with a confirmed e-mail address in the GitLab
+user database. If no such user is available, the project creator is set as
+the author. The importer will append a note in the comment to mark the original
+creator.
+
+The importer will create any new namespaces (groups) if they don't exist or in
+the case the namespace is taken, the repository will be imported under the user's
+namespace that started the import process.
+
+## Importing your Bitbucket repositories
+
+1. Sign in to GitLab and go to your dashboard.
+1. Click on **New project**.
+1. Click on the "Bitbucket Server" button. If the button is not present, enable the importer in
+ **Admin > Application Settings > Visibility and access controls > Import sources**.
+
+ ![Bitbucket](img/import_projects_from_new_project_page.png)
+
+1. Enter your Bitbucket Server credentials.
+
+ ![Grant access](img/bitbucket_server_import_credentials.png)
+
+1. Click on the projects that you'd like to import or **Import all projects**.
+ You can also select the namespace under which each project will be
+ imported.
+
+ ![Import projects](img/bitbucket_server_import_select_project.png)
diff --git a/doc/user/project/import/img/bitbucket_import_new_project.png b/doc/user/project/import/img/bitbucket_import_new_project.png
deleted file mode 100644
index 8ed528c2f09..00000000000
--- a/doc/user/project/import/img/bitbucket_import_new_project.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/img/bitbucket_server_import_credentials.png b/doc/user/project/import/img/bitbucket_server_import_credentials.png
new file mode 100644
index 00000000000..70b26e89d49
--- /dev/null
+++ b/doc/user/project/import/img/bitbucket_server_import_credentials.png
Binary files differ
diff --git a/doc/user/project/import/img/bitbucket_server_import_select_project.png b/doc/user/project/import/img/bitbucket_server_import_select_project.png
new file mode 100644
index 00000000000..e5b1b89e6a3
--- /dev/null
+++ b/doc/user/project/import/img/bitbucket_server_import_select_project.png
Binary files differ
diff --git a/doc/user/project/import/img/import_projects_from_new_project_page.png b/doc/user/project/import/img/import_projects_from_new_project_page.png
index 97ca30b2087..40402eae226 100644
--- a/doc/user/project/import/img/import_projects_from_new_project_page.png
+++ b/doc/user/project/import/img/import_projects_from_new_project_page.png
Binary files differ
diff --git a/doc/user/project/import/img/manifest_status.png b/doc/user/project/import/img/manifest_status.png
new file mode 100644
index 00000000000..b706116a2ac
--- /dev/null
+++ b/doc/user/project/import/img/manifest_status.png
Binary files differ
diff --git a/doc/user/project/import/img/manifest_upload.png b/doc/user/project/import/img/manifest_upload.png
new file mode 100644
index 00000000000..d6bf4b157dd
--- /dev/null
+++ b/doc/user/project/import/img/manifest_upload.png
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index 72cc58546b7..b55435e5b4f 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -11,6 +11,7 @@
1. [From SVN](svn.md)
1. [From TFS](tfs.md)
1. [From repo by URL](repo_by_url.md)
+1. [By uploading a manifest file](manifest.md)
In addition to the specific migration documentation above, you can import any
Git repository via HTTP from the New Project page. Be aware that if the
diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md
new file mode 100644
index 00000000000..06171f11e12
--- /dev/null
+++ b/doc/user/project/import/manifest.md
@@ -0,0 +1,49 @@
+# Import multiple repositories by uploading a manifest file
+
+GitLab allows you to import all the required git repositories
+based a manifest file like the one used by the [Android repository](https://android.googlesource.com/platform/manifest/+/2d6f081a3b05d8ef7a2b1b52b0d536b2b74feab4/default.xml).
+This feature can be very handy when you need to import a project with many repositories like Android Open Source Project (AOSP).
+
+
+>**Note:**
+This feature requires [subgroups](../../group/subgroups/index.md) to be supported by your database.
+
+You can do it by following next steps:
+
+1. From your GitLab dashboard click **New project**
+1. Switch to the **Import project** tab
+1. Click on the **Manifest file** button
+1. Provide GitLab with a manifest xml file
+1. Select a group you want to import to (you need to create a group first if you don't have one)
+1. Click **List available repositories**
+1. You will be redirected to the import status page with projects list based on manifest file
+1. Check the list and click 'Import all repositories' to start import.
+
+![Manifest upload](img/manifest_upload.png)
+
+![Manifest status](img/manifest_status.png)
+
+### Manifest format
+
+A manifest must be an XML file. There must be one `remote` tag with `review` attribute
+that contains a URL to a git server. Each `project` tag must have `name` and `path` attribute.
+GitLab will build URL to the repository by combining URL from `remote` tag with a project name.
+A path attribute will be used to represent project path in GitLab system.
+
+Below is a valid example of manifest file.
+
+```xml
+<manifest>
+ <remote review="https://android-review.googlesource.com/" />
+
+ <project path="build/make" name="platform/build" />
+ <project path="build/blueprint" name="platform/build/blueprint" />
+</manifest>
+```
+
+As result next projects will be created:
+
+| GitLab | Import URL |
+|---|---|
+| https://gitlab/YOUR_GROUP/build/make | https://android-review.googlesource.com/platform/build |
+| https://gitlab/YOUR_GROUP/build/blueprint | https://android-review.googlesource.com/platform/build/blueprint |
diff --git a/doc/user/project/integrations/bamboo.md b/doc/user/project/integrations/bamboo.md
index 1e28646bc97..9b18eb15599 100644
--- a/doc/user/project/integrations/bamboo.md
+++ b/doc/user/project/integrations/bamboo.md
@@ -41,8 +41,11 @@ service in GitLab.
1. Click 'Atlassian Bamboo CI'
1. Select the 'Active' checkbox.
1. Enter the base URL of your Bamboo server. 'https://bamboo.example.com'
-1. Enter the build key from your Bamboo build plan. Build keys are a short,
- all capital letter, identifier that is unique. It will be something like PR-BLD
+1. Enter the build key from your Bamboo build plan. Build keys are typically made
+ up from the Project Key and Plan Key that are set on project/plan creation and
+ separated with a dash (`-`), for example **PROJ-PLAN**. This is a short, all
+ uppercase identifier that is unique. When viewing a plan within Bamboo, the
+ build key is also shown in the browser URL, for example `https://bamboo.example.com/browse/PROJ-PLAN`.
1. If necessary, enter username and password for a Bamboo user that has
access to trigger the build plan. Leave these fields blank if you do not require
authentication.
diff --git a/doc/user/project/integrations/emails_on_push.md b/doc/user/project/integrations/emails_on_push.md
index c01da883562..b050c83fb72 100644
--- a/doc/user/project/integrations/emails_on_push.md
+++ b/doc/user/project/integrations/emails_on_push.md
@@ -6,7 +6,7 @@ every change that is pushed to your project.
Navigate to the [Integrations page](project_services.md#accessing-the-project-services)
and select the **Emails on push** service to configure it.
-In the _Recipients_ area, provide a list of emails separated by commas.
+In the _Recipients_ area, provide a list of emails separated by spaces or newlines.
You can configure any of the following settings depending on your preference.
diff --git a/doc/user/project/integrations/hangouts_chat.md b/doc/user/project/integrations/hangouts_chat.md
new file mode 100644
index 00000000000..6ab44420a10
--- /dev/null
+++ b/doc/user/project/integrations/hangouts_chat.md
@@ -0,0 +1,27 @@
+# Hangouts Chat service
+
+The Hangouts Chat service sends notifications from GitLab to the room for which the webhook was created.
+
+## On Hangouts Chat
+
+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 **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)
+
+## On GitLab
+
+When you have the **Webhook URL** for your Hangouts Chat room webhook, you can setup the GitLab service.
+
+1. Navigate to the [Integrations page](project_services.md#accessing-the-project-services) in your project's settings, i.e. **Project > Settings > Integrations**.
+1. Select the **Hangouts Chat** project service to configure it.
+1. Check the **Active** checkbox to turn on the service.
+1. Check the checkboxes corresponding to the GitLab events you want to receive.
+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.
+
+![Hangouts Chat configuration](img/hangouts_chat_configuration.png)
diff --git a/doc/user/project/integrations/img/hangouts_chat_configuration.png b/doc/user/project/integrations/img/hangouts_chat_configuration.png
new file mode 100644
index 00000000000..33fadbe6547
--- /dev/null
+++ b/doc/user/project/integrations/img/hangouts_chat_configuration.png
Binary files differ
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index 4d5b2c97291..67c543e00fb 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -113,7 +113,7 @@ in the table below.
| `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`. |
| `Username` | The user name created in [configuring JIRA step](#configuring-jira). Using the email address will cause `401 unauthorized`. |
| `Password` |The password of the user created in [configuring JIRA step](#configuring-jira). |
-| `Transition ID` | This is the ID of a transition that moves issues to the desired state. **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** |
+| `Transition ID` | This is the ID of a transition that moves issues to the desired state. It is possible to insert transition ids separated by `,` or `;` which means the issue will be moved to each state after another using the given order. **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** |
### Getting a transition ID
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index 8c51eb9915e..05ee1b4e6d7 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -35,6 +35,7 @@ Click on the service links to see further configuration instructions and details
| External Wiki | Replaces the link to the internal wiki with a link to an external wiki |
| Flowdock | Flowdock is a collaboration web app for technical teams |
| Gemnasium _(Has been deprecated in GitLab 11.0)_ | Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities |
+| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat |
| [HipChat](hipchat.md) | Private group chat and IM |
| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway |
| [JIRA](jira.md) | JIRA issue tracker |
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 19df78f4140..77fa517b5b1 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -6,6 +6,17 @@ Starting from GitLab 8.5:
- the `project.ssh_url` key is deprecated in favor of the `project.git_ssh_url` key
- the `project.http_url` key is deprecated in favor of the `project.git_http_url` key
+>**Note:**
+Starting from GitLab 11.1, the logs of web hooks are automatically removed after
+one month.
+
+>**Note**
+Starting from GitLab 11.2:
+- The `description` field for issues, merge requests, comments, and wiki pages
+ is rewritten so that simple Markdown image references (like
+ `![](/uploads/...)`) have their target URL changed to an absolute URL. See
+ [image URL rewriting](#image-url-rewriting) for more details.
+
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
@@ -54,11 +65,11 @@ Below are described the supported events.
Triggered when you push to the repository except when pushing tags.
-> **Note:** When more than 20 commits are pushed at once, the `commits` web hook
- attribute will only contain the first 20 for performance reasons. Loading
- detailed commit data is expensive. Note that despite only 20 commits being
+> **Note:** When more than 20 commits are pushed at once, the `commits` web hook
+ attribute will only contain 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.
+ contain the actual total.
**Request header**:
@@ -1111,7 +1122,6 @@ X-Gitlab-Event: Build Hook
},
"repository": {
"name": "gitlab_test",
- "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
"description": "Atque in sunt eos similique dolores voluptatem.",
"homepage": "http://192.168.64.1:3005/gitlab-org/gitlab-test",
"git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
@@ -1121,6 +1131,27 @@ X-Gitlab-Event: Build Hook
}
```
+## Image URL rewriting
+
+From GitLab 11.2, simple image references are rewritten to use an absolute URL
+in webhooks. So if an image, merge request, comment, or wiki page has this in
+its description:
+
+```markdown
+![image](/uploads/$sha/image.png)
+```
+
+It will appear in the webhook body as the below (assuming that GitLab is
+installed at gitlab.example.com):
+
+```markdown
+![image](https://gitlab.example.com/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
+Markdown features, like link labels.
+
## Testing webhooks
You can trigger the webhook manually. Sample data from the project will be used.Sample data will take from the project.
@@ -1149,11 +1180,11 @@ From this page, you can repeat delivery with the same data by clicking `Resend R
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.
-If you are receiving multiple requests, you can try increasing the default value to wait for the HTTP response after sending the webhook
+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`:
```
-gitlab_rails['webhook_timeout'] = 10
+gitlab_rails['webhook_timeout'] = 10
```
## Example webhook receiver
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 10647e33f4c..49b49271cff 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -27,7 +27,7 @@ You create issues, host code, perform reviews, build, test,
and deploy from one single platform. Issue Boards help you to visualize
and manage the entire process _in_ GitLab.
-With [Multiple Issue Boards](#multiple-issue-boards), available
+With [Multiple Issue Boards](#use-cases-for-multiple-issue-boards), available
only in [GitLab Enterprise Edition](#features-per-tier),
you go even further, as you can not only keep yourself and your project
organized from a broader perspective with one Issue Board per project,
@@ -70,12 +70,12 @@ beginning of the development lifecycle until deployed to production
### Use cases for Multiple Issue Boards
With [Multiple Issue Boards](#multiple-issue-boards), available only in
-[GitLab Enterprise Edition](https://about.gitlab.com/products/),
+[GitLab Enterprise Edition](https://about.gitlab.com/pricing/),
each team can have their own board to organize their workflow individually.
#### Scrum team
-With multiple Issue Boards, each team has one board. Now you can move issues through each
+With Multiple Issue Boards, each team has one board. Now you can move issues through each
part of the process. For instance: **To Do**, **Doing**, and **Done**.
#### Organization of topics
@@ -119,7 +119,7 @@ Issue Board, that is, create or delete lists and drag issues from one list to an
## Issue Board terminology
- **Issue Board** - Each board represents a unique view for your issues. It can have multiple lists with each list consisting of issues represented by cards.
-- **List** - A column on the issue board that displays issues matching certain attributes. In addition to the default lists of 'Backlog' and 'Closed' issue, each additional list will show issues matching your chosen label or assignee.
+- **List** - A column on the issue board that displays issues matching certain attributes. In addition to the default lists of 'Backlog' and 'Closed' issue, each additional list will show issues matching your chosen label or assignee. On the top of that list you can see the number of issues that belong to it.
- **Label list**: a list based on a label. It shows all opened issues with that label.
- **Assignee list**: a list which includes all issues assigned to a user.
- **Backlog** (default): shows all open issues that do not belong to one of the other lists. Always appears as the leftmost list.
@@ -364,12 +364,12 @@ When dragging issues between lists, different behavior occurs depending on the s
Different issue board features are available in different [GitLab tiers](https://about.gitlab.com/pricing/), as shown in the following table:
-| Tier | Number of Project Issue Boards | Number of Group Issue Boards | Configurable Project Issue Boards | Configurable Group Issue Boards | Assignee Lists
+| Tier | Number of Project Issue Boards | Number of Group Issue Boards | Configurable Issue Boards | Assignee Lists
| --- | --- | --- | --- | --- | --- |
-| Core | 1 | 1 | No | No | No |
-| Starter | Multiple | 1 | Yes | No | No |
-| Premium | Multiple | Multiple | Yes | Yes | Yes |
-| Ultimate | Multiple | Multiple | Yes | Yes | Yes |
+| Core | 1 | 1 | No | No |
+| Starter | Multiple | 1 | Yes | No |
+| Premium | Multiple | Multiple | Yes | Yes |
+| Ultimate | Multiple | Multiple | Yes | Yes |
## Tips
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index bf17731c523..d71273ba970 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -8,7 +8,7 @@ It allows you, your team, and your collaborators to share
and discuss proposals before and while implementing them.
GitLab Issues and the GitLab Issue Tracker are available in all
-[GitLab Products](https://about.gitlab.com/products/) as
+[GitLab Products](https://about.gitlab.com/pricing/) as
part of the [GitLab Workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
## Use cases
@@ -35,7 +35,7 @@ your project public, open to collaboration.
### Streamline collaboration
With [Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html),
-available in [GitLab Starter](https://about.gitlab.com/products/)
+available in [GitLab Starter](https://about.gitlab.com/pricing/)
you can streamline collaboration and allow shared responsibilities to be clearly displayed.
All assignees are shown across your workflows and receive notifications (as they
would as single assignees), simplifying communication and ownership.
@@ -139,7 +139,7 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue
Read through the documentation for [Issue Boards](../issue_board.md)
to find out more about this feature.
-With [GitLab Starter](https://about.gitlab.com/products/), you can also
+With [GitLab Starter](https://about.gitlab.com/pricing/), you can also
create various boards per project with [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards).
### External Issue Tracker
diff --git a/doc/user/project/issues/issues_functionalities.md b/doc/user/project/issues/issues_functionalities.md
index e9903b01c82..46f25417fde 100644
--- a/doc/user/project/issues/issues_functionalities.md
+++ b/doc/user/project/issues/issues_functionalities.md
@@ -47,7 +47,7 @@ Often multiple people likely work on the same issue together,
which can especially be difficult to track in large teams
where there is shared ownership of an issue.
-In [GitLab Starter](https://about.gitlab.com/products/), you can also
+In [GitLab Starter](https://about.gitlab.com/pricing/), you can also
select multiple assignees to an issue.
Learn more on the [Multiple Assignees documentation](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html).
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index 914898ea2ea..3ae6dbe585e 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -69,6 +69,16 @@ Every issue and merge request can be assigned any number of labels. The labels a
|:---:|:---:|
| ![Labels sidebar](img/labels_sidebar.png) | ![Labels sidebar assign](img/labels_sidebar_assign.png) |
+## Searching for project labels
+
+You can search for project labels by navigating from the left sidebar to your
+project's **Issues > Labels** and entering your query to the search bar on the
+top-right:
+
+![Labels project list search](img/labels_project_list_search.png)
+
+GitLab will consider the label title and description for the search.
+
## Filtering issues and merge requests by label
### Filtering in list pages
diff --git a/doc/user/project/merge_requests/img/merge_request.png b/doc/user/project/merge_requests/img/merge_request.png
index f9ca6348953..61b61122b11 100644
--- a/doc/user/project/merge_requests/img/merge_request.png
+++ b/doc/user/project/merge_requests/img/merge_request.png
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 5e2e0c3d171..86ecf33ed31 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -35,7 +35,7 @@ With **[GitLab Enterprise Edition][ee]**, you can also:
- View the deployment process across projects with [Multi-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html#multi-project-pipeline-graphs) **[PREMIUM]**
- Request [approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers **[STARTER]**
-- Analyze the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) **[STARTER]**
+- Analyze the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) **[STARTER]**
## Use cases
@@ -43,7 +43,7 @@ A. Consider you are a software developer working in a team:
1. You checkout a new branch, and submit your changes through a merge request
1. You gather feedback from your team
-1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) **[STARTER]**
+1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) **[STARTER]**
1. You build and test your changes with GitLab CI/CD
1. You request the approval from your manager
1. Your manager pushes a commit with his final review, [approves the merge request](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Starter)
@@ -325,4 +325,4 @@ git checkout origin/merge-requests/1
```
[protected branches]: ../protected_branches.md
-[ee]: https://about.gitlab.com/products/ "GitLab Enterprise Edition"
+[ee]: https://about.gitlab.com/pricing/ "GitLab Enterprise Edition"
diff --git a/doc/user/project/milestones/img/milestones_new_group_milestone.png b/doc/user/project/milestones/img/milestones_new_group_milestone.png
index 8780394d72e..b6defab101d 100644
--- a/doc/user/project/milestones/img/milestones_new_group_milestone.png
+++ b/doc/user/project/milestones/img/milestones_new_group_milestone.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 ba058428dfa..9aaff7dfef1 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/img/milestones_promote_milestone.png b/doc/user/project/milestones/img/milestones_promote_milestone.png
index 99bee1240d4..5e7f94c316f 100644
--- a/doc/user/project/milestones/img/milestones_promote_milestone.png
+++ b/doc/user/project/milestones/img/milestones_promote_milestone.png
Binary files differ
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index bda293bd00e..704c1777e62 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -82,7 +82,7 @@ your implementation with your team.
You can live preview changes submitted to a new branch with
[Review Apps](../../../ci/review_apps/index.md).
-With [GitLab Starter](https://about.gitlab.com/products/), you can also request
+With [GitLab Starter](https://about.gitlab.com/pricing/), you can also request
[approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers.
To create, delete, and [branches](branches/index.md) via GitLab's UI:
@@ -165,12 +165,12 @@ Find it under your project's **Repository > Compare**.
## Locked files
-> Available in [GitLab Premium](https://about.gitlab.com/products/).
+> Available in [GitLab Premium](https://about.gitlab.com/pricing/).
Lock your files to prevent any conflicting changes.
[File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) is available only in
-[GitLab Premium](https://about.gitlab.com/products/).
+[GitLab Premium](https://about.gitlab.com/pricing/).
## Repository's API
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 212e271ce6f..084d1161633 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -42,7 +42,7 @@ Set up your project's merge request settings:
### Service Desk
-Enable [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html) for your project to offer customer support. Service Desk is available in [GitLab Premium](https://about.gitlab.com/products/).
+Enable [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html) for your project to offer customer support. Service Desk is available in [GitLab Premium](https://about.gitlab.com/pricing/).
### Export project
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index b0143e45ab6..511ac2d7e79 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -59,9 +59,18 @@ left.
> [Introduced in](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19318) [GitLab Core][ce] 11.0.
Switching between your authored and assigned merge requests can be done without
-leaving the Web IDE. Click the project name in the top left to open a list of
-merge requests. You will need to commit or discard all your changes before
+leaving the Web IDE. Click the dropdown in the top of the sidebar to open a list
+of merge requests. You will need to commit or discard all your changes before
switching to a different merge request.
+## Switching branches
+
+> [Introduced in](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20850) [GitLab Core][ce] 11.2.
+
+Switching between branches of the current project repository can be done without
+leaving the Web IDE. Click the dropdown in the top of the sidebar to open a list
+of branches. You will need to commit or discard all your changes before
+switching to a different branch.
+
[ce]: https://about.gitlab.com/pricing/
[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md
index d084ee41d8a..ad0ef60373c 100644
--- a/doc/user/project/wiki/index.md
+++ b/doc/user/project/wiki/index.md
@@ -107,3 +107,10 @@ On the right sidebar, click on **Clone repository** and follow the on-screen
instructions.
[permissions]: ../../permissions.md
+
+## Customizing sidebar
+
+By default, the wiki would render a sidebar which lists all the pages for the
+wiki. You could as well provide a `_sidebar` page to replace this default
+sidebar. When this customized sidebar page is provided, the default sidebar
+would not be rendered, but the customized one.
diff --git a/doc/user/search/img/issues_mrs_shortcut.png b/doc/user/search/img/issues_mrs_shortcut.png
index 6380b337b54..cf43df98aa0 100644
--- a/doc/user/search/img/issues_mrs_shortcut.png
+++ b/doc/user/search/img/issues_mrs_shortcut.png
Binary files differ
diff --git a/doc/user/search/img/project_search.png b/doc/user/search/img/project_search.png
index 3150b40de29..0b76d7d6038 100644
--- a/doc/user/search/img/project_search.png
+++ b/doc/user/search/img/project_search.png
Binary files differ
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index 8a2f230f505..6ac3bb8c0b4 100644
--- a/doc/workflow/lfs/lfs_administration.md
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -90,6 +90,7 @@ Here is a configuration example with S3.
| `provider` | The provider name | AWS |
| `aws_access_key_id` | AWS credentials, or compatible | `ABC123DEF456` |
| `aws_secret_access_key` | AWS credentials, or compatible | `ABC123DEF456ABC123DEF456ABC123DEF456` |
+| `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 |
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
@@ -235,5 +236,5 @@ See more information in [!19581](https://gitlab.com/gitlab-org/gitlab-ce/merge_r
[reconfigure gitlab]: ../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: ../../administration/restart_gitlab.md#installations-from-source "How to restart GitLab"
-[eep]: https://about.gitlab.com/products/ "GitLab Premium"
+[eep]: https://about.gitlab.com/pricing/ "GitLab Premium"
[ee-2760]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2760
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index 760cd87d4cc..dda82352c67 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -109,6 +109,7 @@ There are four kinds of filters you can use on your Todos dashboard.
| Filter | Description |
| ------- | ----------- |
| Project | Filter by project |
+| Group | Filter by group |
| Author | Filter by the author that triggered the Todo |
| Type | Filter by issue or merge request |
| Action | Filter by the action that triggered the Todo |
diff --git a/generator_templates/active_record/migration/create_table_migration.rb b/generator_templates/active_record/migration/create_table_migration.rb
index 59a9d37df0f..92e963911d0 100644
--- a/generator_templates/active_record/migration/create_table_migration.rb
+++ b/generator_templates/active_record/migration/create_table_migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
diff --git a/generator_templates/active_record/migration/migration.rb b/generator_templates/active_record/migration/migration.rb
index 08752b3af50..38edab82550 100644
--- a/generator_templates/active_record/migration/migration.rb
+++ b/generator_templates/active_record/migration/migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
diff --git a/generator_templates/rails/post_deployment_migration/migration.rb b/generator_templates/rails/post_deployment_migration/migration.rb
index f2dff84b618..353709f7c9c 100644
--- a/generator_templates/rails/post_deployment_migration/migration.rb
+++ b/generator_templates/rails/post_deployment_migration/migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
diff --git a/lib/additional_email_headers_interceptor.rb b/lib/additional_email_headers_interceptor.rb
deleted file mode 100644
index 3cb1694b9f1..00000000000
--- a/lib/additional_email_headers_interceptor.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class AdditionalEmailHeadersInterceptor
- def self.delivering_email(message)
- message.header['Auto-Submitted'] ||= 'auto-generated'
- message.header['X-Auto-Response-Suppress'] ||= 'All'
- end
-end
diff --git a/lib/api/boards.rb b/lib/api/boards.rb
index 6c706b2b4e1..0f89414148b 100644
--- a/lib/api/boards.rb
+++ b/lib/api/boards.rb
@@ -33,6 +33,7 @@ module API
success Entities::Board
end
get '/:board_id' do
+ authorize!(:read_board, user_project)
present board, with: Entities::Board
end
end
@@ -70,12 +71,10 @@ module API
success Entities::List
end
params do
- requires :label_id, type: Integer, desc: 'The ID of an existing label'
+ use :list_creation_params
end
post '/lists' do
- unless available_labels_for(user_project).exists?(params[:label_id])
- render_api_error!({ error: 'Label not found!' }, 400)
- end
+ authorize_list_type_resource!
authorize!(:admin_list, user_project)
diff --git a/lib/api/boards_responses.rb b/lib/api/boards_responses.rb
index ead0943a74d..7e873012efe 100644
--- a/lib/api/boards_responses.rb
+++ b/lib/api/boards_responses.rb
@@ -14,7 +14,7 @@ module API
def create_list
create_list_service =
- ::Boards::Lists::CreateService.new(board_parent, current_user, { label_id: params[:label_id] })
+ ::Boards::Lists::CreateService.new(board_parent, current_user, create_list_params)
list = create_list_service.execute(board)
@@ -25,6 +25,10 @@ module API
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 })
@@ -44,6 +48,16 @@ module API
end
end
end
+
+ 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
+
+ params :list_creation_params do
+ requires :label_id, type: Integer, desc: 'The ID of an existing label'
+ end
end
end
end
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 4b223a391ae..3e445e6b1fa 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -19,6 +19,7 @@ module API
params :filter_params do
optional :search, type: String, desc: 'Return list of branches matching the search criteria'
+ optional :sort, type: String, desc: 'Return list of branches sorted by the given field'
end
end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 964780cba6a..92329465b2c 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -6,6 +6,18 @@ module API
before { authorize! :download_code, user_project }
+ helpers do
+ def user_access
+ @user_access ||= Gitlab::UserAccess.new(current_user, project: user_project)
+ end
+
+ def authorize_push_to_branch!(branch)
+ unless user_access.can_push_to_branch?(branch)
+ forbidden!("You are not allowed to push into this branch")
+ end
+ end
+ end
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
@@ -67,7 +79,7 @@ module API
optional :author_name, type: String, desc: 'Author name for commit'
end
post ':id/repository/commits' do
- authorize! :push_code, user_project
+ authorize_push_to_branch!(params[:branch])
attrs = declared_params
attrs[:branch_name] = attrs.delete(:branch)
@@ -142,7 +154,7 @@ module API
requires :branch, type: String, desc: 'The name of the branch'
end
post ':id/repository/commits/:sha/cherry_pick', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
- authorize! :push_code, user_project
+ authorize_push_to_branch!(params[:branch])
commit = user_project.commit(params[:sha])
not_found!('Commit') unless commit
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index b7aadc27e71..6769855b899 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -112,9 +112,9 @@ module API
can_push = params[:can_push].nil? ? deploy_keys_project.can_push : params[:can_push]
title = params[:title] || deploy_keys_project.deploy_key.title
- result = deploy_keys_project.update_attributes(can_push: can_push,
- deploy_key_attributes: { id: params[:key_id],
- title: title })
+ result = deploy_keys_project.update(can_push: can_push,
+ deploy_key_attributes: { id: params[:key_id],
+ title: title })
if result
present deploy_keys_project, with: Entities::DeployKeysProject
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index bb48a86fe9e..27f28e1df93 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -30,7 +30,7 @@ module API
end
class User < UserBasic
- expose :created_at
+ expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization
end
@@ -55,12 +55,21 @@ module API
expose :can_create_project?, as: :can_create_project
expose :two_factor_enabled?, as: :two_factor_enabled
expose :external
+ expose :private_profile
end
class UserWithAdmin < UserPublic
expose :admin?, as: :is_admin
end
+ class UserStatus < Grape::Entity
+ expose :emoji
+ expose :message
+ expose :message_html do |entity|
+ MarkupHelper.markdown_field(entity, :message)
+ end
+ end
+
class Email < Grape::Entity
expose :id, :email
end
@@ -132,13 +141,17 @@ module API
expose :star_count, :forks_count
expose :last_activity_at
+ expose :namespace, using: 'API::Entities::NamespaceBasic'
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
def self.preload_relation(projects_relation, options = {})
+ # Preloading tags, should be done with using only `:tags`,
+ # as `:tags` are defined as: `has_many :tags, through: :taggings`
+ # N+1 is solved then by using `subject.tags.map(&:name)`
+ # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555
projects_relation.preload(:project_feature, :route)
- .preload(:import_state)
- .preload(namespace: [:route, :owner],
- tags: :taggings)
+ .preload(:import_state, :tags)
+ .preload(namespace: [:route, :owner])
end
end
@@ -191,7 +204,6 @@ module API
expose :shared_runners_enabled
expose :lfs_enabled?, as: :lfs_enabled
expose :creator_id
- expose :namespace, using: 'API::Entities::NamespaceBasic'
expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? }
expose :import_status
expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] }
@@ -212,11 +224,15 @@ module API
expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
def self.preload_relation(projects_relation, options = {})
+ # Preloading tags, should be done with using only `:tags`,
+ # as `:tags` are defined as: `has_many :tags, through: :taggings`
+ # N+1 is solved then by using `subject.tags.map(&:name)`
+ # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555
super(projects_relation).preload(:group)
.preload(project_group_links: :group,
fork_network: :root_project,
forked_project_link: :forked_from_project,
- forked_from_project: [:route, :forks, namespace: :route, tags: :taggings])
+ forked_from_project: [:route, :forks, :tags, namespace: :route])
end
def self.forks_counting_projects(projects_relation)
@@ -522,6 +538,10 @@ module API
class PipelineBasic < Grape::Entity
expose :id, :sha, :ref, :status
+
+ expose :web_url do |pipeline, _options|
+ Gitlab::Routing.url_helpers.project_pipeline_url(pipeline.project, pipeline)
+ end
end
class MergeRequestSimple < ProjectEntity
@@ -532,6 +552,12 @@ module API
end
class MergeRequestBasic < ProjectEntity
+ expose :title_html, if: -> (_, options) { options[:render_html] } do |entity|
+ MarkupHelper.markdown_field(entity, :title)
+ end
+ expose :description_html, if: -> (_, options) { options[:render_html] } do |entity|
+ MarkupHelper.markdown_field(entity, :description)
+ end
expose :target_branch, :source_branch
expose :upvotes do |merge_request, options|
if options[:issuable_metadata]
@@ -688,7 +714,7 @@ module API
expose :system?, as: :system
expose :noteable_id, :noteable_type
- expose :position, if: ->(note, options) { note.diff_note? } do |note|
+ expose :position, if: ->(note, options) { note.is_a?(DiffNote) } do |note|
note.position.to_h
end
@@ -769,28 +795,33 @@ module API
class Todo < Grape::Entity
expose :id
- expose :project, using: Entities::BasicProjectDetails
+ expose :project, using: Entities::ProjectIdentity, if: -> (todo, _) { todo.project_id }
+ expose :group, using: 'API::Entities::NamespaceBasic', if: -> (todo, _) { todo.group_id }
expose :author, using: Entities::UserBasic
expose :action_name
expose :target_type
expose :target do |todo, options|
- Entities.const_get(todo.target_type).represent(todo.target, options)
+ todo_target_class(todo.target_type).represent(todo.target, options)
end
expose :target_url do |todo, options|
target_type = todo.target_type.underscore
- target_url = "namespace_project_#{target_type}_url"
+ target_url = "#{todo.parent.class.to_s.underscore}_#{target_type}_url"
target_anchor = "note_#{todo.note_id}" if todo.note_id?
Gitlab::Routing
.url_helpers
- .public_send(target_url, todo.project.namespace, todo.project, todo.target, anchor: target_anchor) # rubocop:disable GitlabSecurity/PublicSend
+ .public_send(target_url, todo.parent, todo.target, anchor: target_anchor) # rubocop:disable GitlabSecurity/PublicSend
end
expose :body
expose :state
expose :created_at
+
+ def todo_target_class(target_type)
+ ::API::Entities.const_get(target_type)
+ end
end
class NamespaceBasic < Grape::Entity
@@ -1010,7 +1041,7 @@ module API
expose :description
expose :ip_address
expose :active
- expose :is_shared
+ expose :instance_type?, as: :is_shared
expose :name
expose :online?, as: :online
expose :status
@@ -1024,7 +1055,7 @@ module API
expose :access_level
expose :version, :revision, :platform, :architecture
expose :contacted_at
- expose :token, if: lambda { |runner, options| options[:current_user].admin? || !runner.is_shared? }
+ expose :token, if: lambda { |runner, options| options[:current_user].admin? || !runner.instance_type? }
expose :projects, with: Entities::BasicProjectDetails do |runner, options|
if options[:current_user].admin?
runner.projects
@@ -1056,6 +1087,10 @@ module API
expose :user, with: User
expose :commit, with: Commit
expose :pipeline, with: PipelineBasic
+
+ expose :web_url do |job, _options|
+ Gitlab::Routing.url_helpers.project_job_url(job.project, job)
+ end
end
class Job < JobBasic
@@ -1198,6 +1233,7 @@ module API
class RunnerInfo < Grape::Entity
expose :metadata_timeout, as: :timeout
+ expose :runner_session_url
end
class Step < Grape::Entity
@@ -1213,7 +1249,13 @@ module API
end
class Artifacts < Grape::Entity
- expose :name, :untracked, :paths, :when, :expire_in
+ expose :name
+ expose :untracked
+ expose :paths
+ expose :when
+ expose :expire_in
+ expose :artifact_type
+ expose :artifact_format
end
class Cache < Grape::Entity
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 5c63ec028d9..fa828f43001 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -89,9 +89,10 @@ module API
requires :environment_id, type: Integer, desc: 'The environment ID'
end
post ':id/environments/:environment_id/stop' do
- authorize! :create_deployment, user_project
+ authorize! :read_environment, user_project
environment = user_project.environments.find(params[:environment_id])
+ authorize! :stop_environment, environment
environment.stop_with_action!(current_user)
diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb
index aa9fff25fc8..3832cdc10a8 100644
--- a/lib/api/group_boards.rb
+++ b/lib/api/group_boards.rb
@@ -70,12 +70,10 @@ module API
success Entities::List
end
params do
- requires :label_id, type: Integer, desc: 'The ID of an existing label'
+ use :list_creation_params
end
post '/lists' do
- unless available_labels_for(board_parent).exists?(params[:label_id])
- render_api_error!({ error: 'Label not found!' }, 400)
- end
+ authorize_list_type_resource!
authorize!(:admin_list, user_group)
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index c7f41aba854..b4f441f6a4f 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -34,11 +34,12 @@ module API
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user'
use :pagination
end
def find_groups(params, parent_id = nil)
- find_params = params.slice(:all_available, :custom_attributes, :owned)
+ find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level)
find_params[:parent] = find_group!(parent_id) if parent_id
find_params[:all_available] =
find_params.fetch(:all_available, current_user&.full_private_access?)
@@ -56,6 +57,8 @@ module API
def find_group_projects(params)
group = find_group!(params[:id])
projects = GroupProjectsFinder.new(group: group, current_user: current_user, params: project_finder_params).execute
+ projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled]
+ projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
projects = reorder_projects(projects)
paginate(projects)
end
@@ -148,12 +151,13 @@ module API
end
params do
use :with_custom_attributes
+ optional :with_projects, type: Boolean, default: true, desc: 'Omit project details'
end
get ":id" do
group = find_group!(params[:id])
options = {
- with: Entities::GroupDetail,
+ with: params[:with_projects] ? Entities::GroupDetail : Entities::Group,
current_user: current_user
}
@@ -191,6 +195,8 @@ module API
desc: 'Return only the ID, URL, name, and path of each project'
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
optional :starred, type: Boolean, default: false, desc: 'Limit by starred status'
+ optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature'
+ optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
use :pagination
use :with_custom_attributes
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 9c53b7c3fe7..be17653dbb2 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -385,10 +385,11 @@ module API
finder_params[:non_public] = true if params[:membership].present?
finder_params[:starred] = true if params[:starred].present?
finder_params[:visibility_level] = Gitlab::VisibilityLevel.level_value(params[:visibility]) if params[:visibility]
- finder_params[:archived] = params[:archived]
+ finder_params[:archived] = archived_param unless params[:archived].nil?
finder_params[:search] = params[:search] if params[:search]
finder_params[:user] = params.delete(:user) if params[:user]
finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes]
+ finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level]
finder_params
end
@@ -496,5 +497,11 @@ module API
exception.status == 500
end
+
+ def archived_param
+ return 'only' if params[:archived]
+
+ params[:archived]
+ end
end
end
diff --git a/lib/api/helpers/headers_helpers.rb b/lib/api/helpers/headers_helpers.rb
index cde51fccc62..c9c44e3c218 100644
--- a/lib/api/helpers/headers_helpers.rb
+++ b/lib/api/helpers/headers_helpers.rb
@@ -3,7 +3,11 @@ module API
module HeadersHelpers
def set_http_headers(header_data)
header_data.each do |key, value|
- header "X-Gitlab-#{key.to_s.split('_').collect(&:capitalize).join('-')}", value
+ if value.is_a?(Enumerable)
+ raise ArgumentError.new("Header value should be a string")
+ end
+
+ header "X-Gitlab-#{key.to_s.split('_').collect(&:capitalize).join('-')}", value.to_s
end
end
end
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index a50ea0b52aa..fed8846e505 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -10,6 +10,31 @@ module API
def authorize_admin_source!(source_type, source)
authorize! :"admin_#{source_type}", source
end
+
+ def find_all_members(source_type, source)
+ members = source_type == 'project' ? find_all_members_for_project(source) : find_all_members_for_group(source)
+ members.non_invite
+ .non_request
+ end
+
+ def find_all_members_for_project(project)
+ shared_group_ids = project.project_group_links.pluck(:group_id)
+ project_group_ids = project.group&.self_and_ancestors&.pluck(:id)
+ source_ids = [project.id, project_group_ids, shared_group_ids]
+ .flatten
+ .compact
+ Member.includes(:user)
+ .joins(user: :project_authorizations)
+ .where(project_authorizations: { project_id: project.id })
+ .where(source_id: source_ids)
+ end
+
+ def find_all_members_for_group(group)
+ source_ids = group.self_and_ancestors.pluck(:id)
+ Member.includes(:user)
+ .where(source_id: source_ids)
+ .where(source_type: 'Namespace')
+ end
end
end
end
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index b4bfb677d72..e2984b08eca 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -97,6 +97,8 @@ module API
current_user.admin? || parent.owned_by?(current_user)
end
+ opts[:updated_at] = opts[:created_at] if opts[:created_at]
+
project = parent if parent.is_a?(Project)
::Notes::CreateService.new(project, current_user, opts).execute
end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index a9803be9f69..516f25db15b 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -11,7 +11,8 @@ module API
#
# Params:
# key_id - ssh key id for Git over SSH
- # user_id - user id for Git over HTTP
+ # user_id - user id for Git over HTTP or over SSH in keyless SSH CERT mode
+ # username - user name for Git over SSH in keyless SSH cert mode
# protocol - Git access protocol being used, e.g. HTTP or SSH
# project - project full_path (not path on disk)
# action - git action (git-upload-pack or git-receive-pack)
@@ -28,6 +29,8 @@ module API
Key.find_by(id: params[:key_id])
elsif params[:user_id]
User.find_by(id: params[:user_id])
+ elsif params[:username]
+ User.find_by_username(params[:username])
end
protocol = params[:protocol]
@@ -58,6 +61,7 @@ module API
{
status: true,
gl_repository: gl_repository,
+ gl_id: Gitlab::GlId.gl_id(user),
gl_username: user&.username,
# This repository_path is a bogus value but gitlab-shell still requires
@@ -71,10 +75,17 @@ module API
post "/lfs_authenticate" do
status 200
- key = Key.find(params[:key_id])
- key.update_last_used_at
+ if params[:key_id]
+ actor = Key.find(params[:key_id])
+ actor.update_last_used_at
+ elsif params[:user_id]
+ actor = User.find_by(id: params[:user_id])
+ raise ActiveRecord::RecordNotFound.new("No such user id!") unless actor
+ else
+ raise ActiveRecord::RecordNotFound.new("No key_id or user_id passed!")
+ end
- token_handler = Gitlab::LfsToken.new(key)
+ token_handler = Gitlab::LfsToken.new(actor)
{
username: token_handler.actor_name,
@@ -100,7 +111,7 @@ module API
end
#
- # Discover user by ssh key or user id
+ # Discover user by ssh key, user id or username
#
get "/discover" do
if params[:key_id]
@@ -108,6 +119,8 @@ module API
user = key.user
elsif params[:user_id]
user = User.find_by(id: params[:user_id])
+ elsif params[:username]
+ user = User.find_by(username: params[:username])
end
present user, with: Entities::UserSafe
@@ -141,22 +154,30 @@ module API
post '/two_factor_recovery_codes' do
status 200
- key = Key.find_by(id: params[:key_id])
+ if params[:key_id]
+ key = Key.find_by(id: params[:key_id])
- if key
- key.update_last_used_at
- else
- break { 'success' => false, 'message' => 'Could not find the given key' }
- end
+ if key
+ key.update_last_used_at
+ else
+ break { 'success' => false, 'message' => 'Could not find the given key' }
+ end
- if key.is_a?(DeployKey)
- break { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' }
- end
+ if key.is_a?(DeployKey)
+ break { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' }
+ end
+
+ user = key.user
- user = key.user
+ unless user
+ break { success: false, message: 'Could not find a user for the given key' }
+ end
+ elsif params[:user_id]
+ user = User.find_by(id: params[:user_id])
- unless user
- break { success: false, message: 'Could not find a user for the given key' }
+ unless user
+ break { success: false, message: 'Could not find the given user' }
+ end
end
unless user.two_factor_enabled?
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 25185d6edc8..bda05d1795b 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -162,6 +162,9 @@ module API
desc: 'The IID of a merge request for which to resolve discussions'
optional :discussion_to_resolve, type: String,
desc: 'The ID of a discussion to resolve, also pass `merge_request_to_resolve_discussions_of`'
+ optional :iid, type: Integer,
+ desc: 'The internal ID of a project issue. Available only for admins and project owners.'
+
use :issue_params
end
post ':id/issues' do
@@ -169,9 +172,10 @@ module API
authorize! :create_issue, user_project
- # Setting created_at time only allowed for admins and project owners
+ # Setting created_at time or iid only allowed for admins and project owners
unless current_user.admin? || user_project.owner == current_user
params.delete(:created_at)
+ params.delete(:iid)
end
issue_params = declared_params(include_missing: false)
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index e95b0dd5267..10c6e565f09 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -54,7 +54,7 @@ module API
pipeline = user_project.pipelines.find(params[:pipeline_id])
builds = pipeline.builds
builds = filter_builds(builds, params[:scope])
- builds = builds.preload(:job_artifacts_archive)
+ builds = builds.preload(:job_artifacts_archive, project: [:namespace])
present paginate(builds), with: Entities::Job
end
diff --git a/lib/api/keys.rb b/lib/api/keys.rb
index 767f27ef334..fd93f797f72 100644
--- a/lib/api/keys.rb
+++ b/lib/api/keys.rb
@@ -12,7 +12,7 @@ module API
key = Key.find(params[:id])
- present key, with: Entities::SSHKeyWithUser
+ present key, with: Entities::SSHKeyWithUser, current_user: current_user
end
end
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 8b12986d09e..d23dd834c69 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -28,6 +28,23 @@ module API
present members, with: Entities::Member
end
+ desc 'Gets a list of group or project members viewable by the authenticated user, including those who gained membership through ancestor group.' do
+ success Entities::Member
+ end
+ params do
+ optional :query, type: String, desc: 'A query string to search for members'
+ use :pagination
+ end
+ get ":id/members/all" do
+ source = find_source(source_type, params[:id])
+
+ members = find_all_members(source_type, source)
+ members = members.includes(:user).references(:user).merge(User.search(params[:query])) if params[:query].present?
+ members = paginate(members)
+
+ present members, with: Entities::Member
+ end
+
desc 'Gets a member of a group or project.' do
success Entities::Member
end
@@ -58,7 +75,10 @@ module API
member = source.members.find_by(user_id: params[:user_id])
conflict!('Member already exists') if member
- member = source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at])
+ user = User.find_by_id(params[:user_id])
+ not_found!('User') unless user
+
+ member = source.add_user(user, params[:access_level], current_user: current_user, expires_at: params[:expires_at])
if !member
not_allowed! # This currently can only be reached in EE
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index af7d2471b34..abad418771c 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -72,8 +72,8 @@ module API
end
params :merge_requests_params do
- optional :state, type: String, values: %w[opened closed merged all], default: 'all',
- desc: 'Return opened, closed, merged, or all merge requests'
+ optional :state, type: String, values: %w[opened closed locked merged all], default: 'all',
+ desc: 'Return opened, closed, locked, merged, or all merge requests'
optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at',
desc: 'Return merge requests ordered by `created_at` or `updated_at` fields.'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
@@ -232,6 +232,7 @@ module API
params do
requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
+ optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML'
end
desc 'Get a single merge request' do
success Entities::MergeRequest
@@ -239,7 +240,7 @@ module API
get ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
- present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html]
end
desc 'Get the participants of a merge request' do
@@ -379,7 +380,7 @@ module API
end
get ':id/merge_requests/:merge_request_iid/closes_issues' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
- issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
+ issues = ::Kaminari.paginate_array(merge_request.visible_closing_issues_for(current_user))
issues = paginate(issues)
external_issues, internal_issues = issues.partition { |issue| issue.is_a?(ExternalIssue) }
diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb
index 8374a57edfa..5d33a13d035 100644
--- a/lib/api/pipelines.rb
+++ b/lib/api/pipelines.rb
@@ -31,7 +31,7 @@ module API
get ':id/pipelines' do
authorize! :read_pipeline, user_project
- pipelines = PipelinesFinder.new(user_project, params).execute
+ pipelines = PipelinesFinder.new(user_project, current_user, params).execute
present paginate(pipelines), with: Entities::PipelineBasic
end
diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb
index 5ef4e9d530c..15c57a2fc02 100644
--- a/lib/api/project_export.rb
+++ b/lib/api/project_export.rb
@@ -23,9 +23,13 @@ module API
get ':id/export/download' do
path = user_project.export_project_path
- render_api_error!('404 Not found or has expired', 404) unless path
-
- present_disk_file!(path, File.basename(path), 'application/gzip')
+ if path
+ present_disk_file!(path, File.basename(path), 'application/gzip')
+ elsif user_project.export_project_object_exists?
+ present_carrierwave_file!(user_project.import_export_upload.export_file)
+ else
+ render_api_error!('404 Not found or has expired', 404)
+ end
end
desc 'Start export' do
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 68921ae439b..4760a1c08d7 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -80,7 +80,7 @@ module API
update_params = declared_params(include_missing: false)
- if hook.update_attributes(update_params)
+ if hook.update(update_params)
present hook, with: Entities::ProjectHook
else
error!("Invalid url given", 422) if hook.errors[:url].present?
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index b83da00502d..5738bf220c6 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -9,6 +9,56 @@ module API
before { authenticate_non_get! }
helpers do
+ params :optional_filter_params_ee do
+ # EE::API::Projects would override this helper
+ end
+
+ params :optional_update_params_ee do
+ # EE::API::Projects would override this helper
+ end
+
+ # EE::API::Projects would override this method
+ def apply_filters(projects)
+ projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled]
+ projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
+ projects = projects.with_statistics if params[:statistics]
+
+ projects
+ end
+
+ def verify_update_project_attrs!(project, attrs)
+ end
+ end
+
+ def self.update_params_at_least_one_of
+ [
+ :jobs_enabled,
+ :resolve_outdated_diff_discussions,
+ :ci_config_path,
+ :container_registry_enabled,
+ :default_branch,
+ :description,
+ :issues_enabled,
+ :lfs_enabled,
+ :merge_requests_enabled,
+ :merge_method,
+ :name,
+ :only_allow_merge_if_all_discussions_are_resolved,
+ :only_allow_merge_if_pipeline_succeeds,
+ :path,
+ :printing_merge_request_link_enabled,
+ :public_builds,
+ :request_access_enabled,
+ :shared_runners_enabled,
+ :snippets_enabled,
+ :tag_list,
+ :visibility,
+ :wiki_enabled,
+ :avatar
+ ]
+ end
+
+ helpers do
params :statistics_params do
optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
end
@@ -30,7 +80,7 @@ module API
end
params :filter_params do
- optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
+ optional :archived, type: Boolean, desc: 'Limit by archived status'
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values,
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of projects matching the search criteria'
@@ -39,6 +89,9 @@ module API
optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of'
optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature'
optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
+
+ use :optional_filter_params_ee
end
params :create_params do
@@ -52,9 +105,7 @@ module API
def present_projects(projects, options = {})
projects = reorder_projects(projects)
- projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled]
- projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
- projects = projects.with_statistics if params[:statistics]
+ projects = apply_filters(projects)
projects = paginate(projects)
projects, options = with_custom_attributes(projects, options)
@@ -236,38 +287,13 @@ module API
success Entities::Project
end
params do
- # CE
- at_least_one_of_ce =
- [
- :jobs_enabled,
- :resolve_outdated_diff_discussions,
- :ci_config_path,
- :container_registry_enabled,
- :default_branch,
- :description,
- :issues_enabled,
- :lfs_enabled,
- :merge_requests_enabled,
- :merge_method,
- :name,
- :only_allow_merge_if_all_discussions_are_resolved,
- :only_allow_merge_if_pipeline_succeeds,
- :path,
- :printing_merge_request_link_enabled,
- :public_builds,
- :request_access_enabled,
- :shared_runners_enabled,
- :snippets_enabled,
- :tag_list,
- :visibility,
- :wiki_enabled
- ]
optional :name, type: String, desc: 'The name of the project'
optional :default_branch, type: String, desc: 'The default branch of the project'
optional :path, type: String, desc: 'The path of the repository'
use :optional_project_params
- at_least_one_of(*at_least_one_of_ce)
+
+ at_least_one_of(*::API::Projects.update_params_at_least_one_of)
end
put ':id' do
authorize_admin_project
@@ -277,6 +303,8 @@ module API
attrs = translate_params_for_compatibility(attrs)
+ verify_update_project_attrs!(user_project, attrs)
+
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
if result[:status] == :success
@@ -293,7 +321,7 @@ module API
post ':id/archive' do
authorize!(:archive_project, user_project)
- user_project.archive!
+ ::Projects::UpdateService.new(user_project, current_user, archived: true).execute
present user_project, with: Entities::Project
end
@@ -304,7 +332,7 @@ module API
post ':id/unarchive' do
authorize!(:archive_project, user_project)
- user_project.unarchive!
+ ::Projects::UpdateService.new(@project, current_user, archived: false).execute
present user_project, with: Entities::Project
end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index 96a02914faa..c9931c2d603 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -24,13 +24,13 @@ module API
attributes =
if runner_registration_token_valid?
# Create shared runner. Requires admin access
- attributes.merge(is_shared: true, runner_type: :instance_type)
+ attributes.merge(runner_type: :instance_type)
elsif project = Project.find_by(runners_token: params[:token])
# Create a specific runner for the project
- attributes.merge(is_shared: false, runner_type: :project_type, projects: [project])
+ attributes.merge(runner_type: :project_type, projects: [project])
elsif group = Group.find_by(runners_token: params[:token])
# Create a specific runner for the group
- attributes.merge(is_shared: false, runner_type: :group_type, groups: [group])
+ attributes.merge(runner_type: :group_type, groups: [group])
else
forbidden!
end
@@ -80,7 +80,20 @@ module API
params do
requires :token, type: String, desc: %q(Runner's authentication token)
optional :last_update, type: String, desc: %q(Runner's queue last_update token)
- optional :info, type: Hash, desc: %q(Runner's metadata)
+ optional :info, type: Hash, desc: %q(Runner's metadata) do
+ optional :name, type: String, desc: %q(Runner's name)
+ optional :version, type: String, desc: %q(Runner's version)
+ optional :revision, type: String, desc: %q(Runner's revision)
+ optional :platform, type: String, desc: %q(Runner's platform)
+ optional :architecture, type: String, desc: %q(Runner's architecture)
+ optional :executor, type: String, desc: %q(Runner's executor)
+ optional :features, type: Hash, desc: %q(Runner's features)
+ end
+ optional :session, type: Hash, desc: %q(Runner's session data) do
+ optional :url, type: String, desc: %q(Session's url)
+ optional :certificate, type: String, desc: %q(Session's certificate)
+ optional :authorization, type: String, desc: %q(Session's authorization)
+ end
end
post '/request' do
authenticate_runner!
@@ -90,20 +103,21 @@ module API
break no_content!
end
- if current_runner.runner_queue_value_latest?(params[:last_update])
- header 'X-GitLab-Last-Update', params[:last_update]
+ runner_params = declared_params(include_missing: false)
+
+ if current_runner.runner_queue_value_latest?(runner_params[:last_update])
+ header 'X-GitLab-Last-Update', runner_params[:last_update]
Gitlab::Metrics.add_event(:build_not_found_cached)
break no_content!
end
new_update = current_runner.ensure_runner_queue_value
- result = ::Ci::RegisterJobService.new(current_runner).execute
+ result = ::Ci::RegisterJobService.new(current_runner).execute(runner_params)
if result.valid?
if result.build
- Gitlab::Metrics.add_event(:build_found,
- project: result.build.project.full_path)
- present result.build, with: Entities::JobRequest::Response
+ Gitlab::Metrics.add_event(:build_found)
+ present Ci::BuildRunnerPresenter.new(result.build), with: Entities::JobRequest::Response
else
Gitlab::Metrics.add_event(:build_not_found)
header 'X-GitLab-Last-Update', new_update
@@ -133,8 +147,7 @@ module API
job.trace.set(params[:trace]) if params[:trace]
- Gitlab::Metrics.add_event(:update_build,
- project: job.project.full_path)
+ Gitlab::Metrics.add_event(:update_build)
case params[:state].to_s
when 'running'
@@ -226,6 +239,10 @@ module API
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
optional :expire_in, type: String, desc: %q(Specify when artifacts should expire)
+ optional :artifact_type, type: String, desc: %q(The type of artifact),
+ default: 'archive', values: Ci::JobArtifact.file_types.keys
+ optional :artifact_format, type: String, desc: %q(The format of artifact),
+ default: 'zip', values: Ci::JobArtifact.file_formats.keys
optional 'file.path', type: String, desc: %q(path to locally stored body (generated by Workhorse))
optional 'file.name', type: String, desc: %q(real filename as send in Content-Disposition (generated by Workhorse))
optional 'file.type', type: String, desc: %q(real content type as send in Content-Type (generated by Workhorse))
@@ -249,29 +266,29 @@ module API
bad_request!('Missing artifacts file!') unless artifacts
file_to_large! unless artifacts.size < max_artifacts_size
- bad_request!("Already uploaded") if job.job_artifacts_archive
-
expire_in = params['expire_in'] ||
Gitlab::CurrentSettings.current_application_settings.default_artifacts_expire_in
- job.build_job_artifacts_archive(
+ job.job_artifacts.build(
project: job.project,
file: artifacts,
- file_type: :archive,
+ file_type: params['artifact_type'],
+ file_format: params['artifact_format'],
file_sha256: artifacts.sha256,
expire_in: expire_in)
if metadata
- job.build_job_artifacts_metadata(
+ job.job_artifacts.build(
project: job.project,
file: metadata,
file_type: :metadata,
+ file_format: :gzip,
file_sha256: metadata.sha256,
expire_in: expire_in)
end
if job.update(artifacts_expire_in: expire_in)
- present job, with: Entities::JobRequest::Response
+ present Ci::BuildRunnerPresenter.new(job), with: Entities::JobRequest::Response
else
render_validation_error!(job)
end
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index 2b78075ddbf..51242341dba 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -58,7 +58,7 @@ module API
optional :access_level, type: String, values: Ci::Runner.access_levels.keys,
desc: 'The access_level of the runner'
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
- at_least_one_of :description, :active, :tag_list, :run_untagged, :locked, :access_level
+ at_least_one_of :description, :active, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
end
put ':id' do
runner = get_runner(params.delete(:id))
@@ -119,7 +119,7 @@ module API
use :pagination
end
get ':id/runners' do
- runners = filter_runners(Ci::Runner.owned_or_shared(user_project.id), params[:scope])
+ runners = filter_runners(Ci::Runner.owned_or_instance_wide(user_project.id), params[:scope])
present paginate(runners), with: Entities::Runner
end
@@ -170,6 +170,11 @@ module API
render_api_error!('Scope contains invalid value', 400)
end
+ # Support deprecated scopes
+ if runners.respond_to?("deprecated_#{scope}")
+ scope = "deprecated_#{scope}"
+ end
+
runners.public_send(scope) # rubocop:disable GitlabSecurity/PublicSend
end
@@ -180,7 +185,7 @@ module API
end
def authenticate_show_runner!(runner)
- return if runner.is_shared || current_user.admin?
+ return if runner.instance_type? || current_user.admin?
forbidden!("No access granted") unless can?(current_user, :read_runner, runner)
end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 794fdab8f2b..1f2bf546cd7 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -368,6 +368,14 @@ module API
desc: "The project's slug on gemnasium.com"
}
],
+ 'hangouts-chat' => [
+ {
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces…'
+ }
+ ],
'hipchat' => [
{
required: true,
@@ -688,6 +696,7 @@ module API
ExternalWikiService,
FlowdockService,
GemnasiumService,
+ HangoutsChatService,
HipchatService,
IrkerService,
JiraService,
@@ -787,7 +796,7 @@ module API
service = user_project.find_or_initialize_service(service_slug.underscore)
service_params = declared_params(include_missing: false).merge(active: true)
- if service.update_attributes(service_params)
+ if service.update(service_params)
present service, with: Entities::ProjectService
else
render_api_error!('400 Bad Request', 400)
@@ -807,7 +816,7 @@ module API
hash.merge!(key => nil)
end
- unless service.update_attributes(attrs.merge(active: false))
+ unless service.update(attrs.merge(active: false))
render_api_error!('400 Bad Request', 400)
end
end
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 02ef89f997f..897010217dc 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -20,116 +20,114 @@ module API
success Entities::ApplicationSetting
end
params do
+ optional :admin_notification_email, type: String, desc: 'Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.'
+ optional :after_sign_up_text, type: String, desc: 'Text shown after sign up'
+ optional :after_sign_out_path, type: String, desc: 'We will redirect users to this page after they sign out'
+ optional :akismet_enabled, type: Boolean, desc: 'Helps prevent bots from creating issues'
+ given akismet_enabled: ->(val) { val } do
+ requires :akismet_api_key, type: String, desc: 'Generate API key at http://www.akismet.com'
+ end
+ optional :clientside_sentry_enabled, type: Boolean, desc: 'Sentry can also be used for reporting and logging clientside exceptions. https://sentry.io/for/javascript/'
+ given clientside_sentry_enabled: ->(val) { val } do
+ requires :clientside_sentry_dsn, type: String, desc: 'Clientside Sentry Data Source Name'
+ end
+ optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
+ optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts"
optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master'
+ optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility'
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 :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility'
- optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
- optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
- desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
optional :disabled_oauth_sign_in_sources, type: Array[String], desc: 'Disable certain OAuth sign-in sources'
- optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.'
- optional :project_export_enabled, type: Boolean, desc: 'Enable project export'
- optional :gravatar_enabled, type: Boolean, desc: 'Flag indicating if the Gravatar service is enabled'
- optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects'
- optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB'
- optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.'
- optional :user_oauth_applications, type: Boolean, desc: 'Allow users to register any application to use GitLab as an OAuth provider'
- optional :user_default_external, type: Boolean, desc: 'Newly registered users will by default be external'
- optional :signup_enabled, type: Boolean, desc: 'Flag indicating if sign up is enabled'
- optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up'
- optional :domain_whitelist, type: String, desc: 'ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
optional :domain_blacklist_enabled, type: Boolean, desc: 'Enable domain blacklist for sign ups'
given domain_blacklist_enabled: ->(val) { val } do
requires :domain_blacklist, type: String, 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'
end
- optional :after_sign_up_text, type: String, desc: 'Text shown after sign up'
- optional :password_authentication_enabled_for_web, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface'
- optional :password_authentication_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5
- optional :signin_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5
- mutually_exclusive :password_authentication_enabled_for_web, :password_authentication_enabled, :signin_enabled
- optional :password_authentication_enabled_for_git, type: Boolean, desc: 'Flag indicating if password authentication is enabled for Git over HTTP(S)'
- optional :performance_bar_allowed_group_path, type: String, desc: 'Path of the group that is allowed to toggle the performance bar.'
- optional :performance_bar_allowed_group_id, type: String, desc: 'Depreated: 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_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 :require_two_factor_authentication, type: Boolean, desc: 'Require all users to setup Two-factor authentication'
- given require_two_factor_authentication: ->(val) { val } do
- requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication'
- end
- optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page'
- optional :after_sign_out_path, type: String, desc: 'We will redirect users to this page after they sign out'
- optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application'
+ optional :domain_whitelist, type: String, desc: 'ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
+ optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.'
+ optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.'
+ optional :gitaly_timeout_default, type: Integer, desc: 'Default Gitaly timeout, in seconds. Set to 0 to disable timeouts.'
+ optional :gitaly_timeout_fast, type: Integer, desc: 'Gitaly fast operation timeout, in seconds. Set to 0 to disable timeouts.'
+ optional :gitaly_timeout_medium, type: Integer, desc: 'Medium Gitaly timeout, in seconds. Set to 0 to disable timeouts.'
+ optional :gravatar_enabled, type: Boolean, desc: 'Flag indicating if the Gravatar service is enabled'
optional :help_page_hide_commercial_content, type: Boolean, desc: 'Hide marketing-related entries from help'
- optional :help_page_text, type: String, desc: 'Custom text displayed on the help page'
optional :help_page_support_url, type: String, desc: 'Alternate support URL for help page'
- optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects'
- given shared_runners_enabled: ->(val) { val } do
- requires :shared_runners_text, type: String, desc: 'Shared runners text '
+ optional :help_page_text, type: String, desc: 'Custom text displayed on the help page'
+ optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page'
+ optional :housekeeping_enabled, type: Boolean, desc: 'Enable automatic repository housekeeping (git repack, git gc)'
+ given housekeeping_enabled: ->(val) { val } do
+ requires :housekeeping_bitmaps_enabled, type: Boolean, desc: "Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance."
+ requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run."
+ requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
+ requires :housekeeping_incremental_repack_period, type: Integer, desc: "Number of Git pushes after which an incremental 'git repack' is run."
+ end
+ optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
+ optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project manifest],
+ desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
+ optional :koding_enabled, type: Boolean, desc: 'Enable Koding'
+ given koding_enabled: ->(val) { val } do
+ requires :koding_url, type: String, desc: 'The Koding team URL'
end
optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size for each job's artifacts"
- optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts"
+ optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB'
optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB'
- optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
- optional :prometheus_metrics_enabled, type: Boolean, desc: 'Enable Prometheus metrics'
optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics'
given metrics_enabled: ->(val) { val } do
requires :metrics_host, type: String, desc: 'The InfluxDB host'
- requires :metrics_port, type: Integer, desc: 'The UDP port to use for connecting to InfluxDB'
- requires :metrics_pool_size, type: Integer, desc: 'The amount of InfluxDB connections to open'
- requires :metrics_timeout, type: Integer, desc: 'The amount of seconds after which an InfluxDB connection will time out'
requires :metrics_method_call_threshold, type: Integer, desc: 'A method call is only tracked when it takes longer to complete than the given amount of milliseconds.'
- requires :metrics_sample_interval, type: Integer, desc: 'The sampling interval in seconds'
requires :metrics_packet_size, type: Integer, desc: 'The amount of points to store in a single UDP packet'
+ requires :metrics_pool_size, type: Integer, desc: 'The amount of InfluxDB connections to open'
+ requires :metrics_port, type: Integer, desc: 'The UDP port to use for connecting to InfluxDB'
+ requires :metrics_sample_interval, type: Integer, desc: 'The sampling interval in seconds'
+ requires :metrics_timeout, type: Integer, desc: 'The amount of seconds after which an InfluxDB connection will time out'
end
- optional :sidekiq_throttling_enabled, type: Boolean, desc: 'Enable Sidekiq Job Throttling'
- given sidekiq_throttling_enabled: ->(val) { val } do
- requires :sidekiq_throttling_queus, type: Array[String], desc: 'Choose which queues you wish to throttle'
- requires :sidekiq_throttling_factor, type: Float, desc: 'The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.'
+ optional :password_authentication_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5
+ optional :password_authentication_enabled_for_web, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface'
+ mutually_exclusive :password_authentication_enabled_for_web, :password_authentication_enabled, :signin_enabled
+ optional :password_authentication_enabled_for_git, type: Boolean, desc: 'Flag indicating if password authentication is enabled for Git over HTTP(S)'
+ 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 :plantuml_enabled, type: Boolean, desc: 'Enable PlantUML'
+ given plantuml_enabled: ->(val) { val } do
+ requires :plantuml_url, type: String, desc: 'The PlantUML server URL'
end
+ optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
+ optional :project_export_enabled, type: Boolean, desc: 'Enable project export'
+ optional :prometheus_metrics_enabled, type: Boolean, desc: 'Enable Prometheus metrics'
optional :recaptcha_enabled, type: Boolean, desc: 'Helps prevent bots from creating accounts'
given recaptcha_enabled: ->(val) { val } do
requires :recaptcha_site_key, type: String, desc: 'Generate site key at http://www.google.com/recaptcha'
requires :recaptcha_private_key, type: String, desc: 'Generate private key at http://www.google.com/recaptcha'
end
- optional :akismet_enabled, type: Boolean, desc: 'Helps prevent bots from creating issues'
- given akismet_enabled: ->(val) { val } do
- requires :akismet_api_key, type: String, desc: 'Generate API key at http://www.akismet.com'
+ optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues."
+ optional :repository_storages, type: Array[String], desc: 'Storage paths for new projects'
+ optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to setup Two-factor authentication'
+ given require_two_factor_authentication: ->(val) { val } do
+ requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication'
end
- optional :admin_notification_email, type: String, desc: 'Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.'
+ optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
+ optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up'
optional :sentry_enabled, type: Boolean, desc: 'Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here: https://getsentry.com'
given sentry_enabled: ->(val) { val } do
requires :sentry_dsn, type: String, desc: 'Sentry Data Source Name'
end
- optional :clientside_sentry_enabled, type: Boolean, desc: 'Sentry can also be used for reporting and logging clientside exceptions. https://sentry.io/for/javascript/'
- given clientside_sentry_enabled: ->(val) { val } do
- requires :clientside_sentry_dsn, type: String, desc: 'Clientside Sentry Data Source Name'
- end
- optional :repository_storages, type: Array[String], desc: 'Storage paths for new projects'
- optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues."
- optional :koding_enabled, type: Boolean, desc: 'Enable Koding'
- given koding_enabled: ->(val) { val } do
- requires :koding_url, type: String, desc: 'The Koding team 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'
+ optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.'
+ optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects'
+ given shared_runners_enabled: ->(val) { val } do
+ requires :shared_runners_text, type: String, desc: 'Shared runners text '
end
- optional :version_check_enabled, type: Boolean, desc: 'Let GitLab inform you when an update is available.'
- optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.'
- optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
- optional :housekeeping_enabled, type: Boolean, desc: 'Enable automatic repository housekeeping (git repack, git gc)'
- given housekeeping_enabled: ->(val) { val } do
- requires :housekeeping_bitmaps_enabled, type: Boolean, desc: "Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance."
- requires :housekeeping_incremental_repack_period, type: Integer, desc: "Number of Git pushes after which an incremental 'git repack' is run."
- requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run."
- requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
+ optional :sidekiq_throttling_enabled, type: Boolean, desc: 'Enable Sidekiq Job Throttling'
+ given sidekiq_throttling_enabled: ->(val) { val } do
+ requires :sidekiq_throttling_factor, type: Float, desc: 'The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.'
+ requires :sidekiq_throttling_queues, type: Array[String], desc: 'Choose which queues you wish to throttle'
end
+ optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application'
+ optional :signin_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5
+ optional :signup_enabled, type: Boolean, desc: 'Flag indicating if sign up is enabled'
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
- optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
- optional :gitaly_timeout_default, type: Integer, desc: 'Default Gitaly timeout, in seconds. Set to 0 to disable timeouts.'
- optional :gitaly_timeout_medium, type: Integer, desc: 'Medium Gitaly timeout, in seconds. Set to 0 to disable timeouts.'
- optional :gitaly_timeout_fast, type: Integer, desc: 'Gitaly fast operation timeout, in seconds. Set to 0 to disable timeouts.'
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
+ optional :instance_statistics_visibility_private, type: Boolean, desc: 'When set to `true` Instance statistics will only be available to admins'
ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
optional :"#{type}_key_restriction",
diff --git a/lib/api/users.rb b/lib/api/users.rb
index e8df2c5a74a..b0811bb4aad 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -42,6 +42,8 @@ module API
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
optional :avatar, type: File, desc: 'Avatar image for user'
+ optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
all_or_none_of :extern_uid, :provider
end
@@ -96,7 +98,7 @@ module API
entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin
- users, options = with_custom_attributes(users, with: entity)
+ users, options = with_custom_attributes(users, { with: entity, current_user: current_user })
present paginate(users), options
end
@@ -113,12 +115,23 @@ module API
user = User.find_by(id: params[:id])
not_found!('User') unless user && can?(current_user, :read_user, user)
- opts = current_user&.admin? ? { with: Entities::UserWithAdmin } : { with: Entities::User }
+ opts = { with: current_user&.admin? ? Entities::UserWithAdmin : Entities::User, current_user: current_user }
user, opts = with_custom_attributes(user, opts)
present user, opts
end
+ desc "Get the status of a user"
+ params do
+ requires :id_or_username, type: String, desc: 'The ID or username of the user'
+ end
+ get ":id_or_username/status" do
+ user = find_user(params[:id_or_username])
+ not_found!('User') unless user && can?(current_user, :read_user, user)
+
+ present user.status || {}, with: Entities::UserStatus
+ end
+
desc 'Create a user. Available only for admins.' do
success Entities::UserPublic
end
@@ -139,7 +152,7 @@ module API
user = ::Users::CreateService.new(current_user, params).execute(skip_authorization: true)
if user.persisted?
- present user, with: Entities::UserPublic
+ present user, with: Entities::UserPublic, current_user: current_user
else
conflict!('Email has already been taken') if User
.where(email: user.email)
@@ -186,7 +199,7 @@ module API
identity = user.identities.find_by(provider: identity_attrs[:provider])
if identity
- identity.update_attributes(identity_attrs)
+ identity.update(identity_attrs)
else
identity = user.identities.build(identity_attrs)
identity.save
@@ -198,7 +211,7 @@ module API
result = ::Users::UpdateService.new(current_user, user_params.except(:extern_uid, :provider).merge(user: user)).execute
if result[:status] == :success
- present user, with: Entities::UserPublic
+ present user, with: Entities::UserPublic, current_user: current_user
else
render_validation_error!(user)
end
@@ -545,7 +558,7 @@ module API
Entities::UserPublic
end
- present current_user, with: entity
+ present current_user, with: entity, current_user: current_user
end
end
@@ -738,6 +751,30 @@ module API
present paginate(activities), with: Entities::UserActivity
end
+
+ desc 'Set the status of the current user' do
+ success Entities::UserStatus
+ end
+ params do
+ optional :emoji, type: String, desc: "The emoji to set on the status"
+ optional :message, type: String, desc: "The status message to set"
+ end
+ put "status" do
+ forbidden! unless can?(current_user, :update_user_status, current_user)
+
+ if ::Users::SetStatusService.new(current_user, declared_params).execute
+ present current_user.status, with: Entities::UserStatus
+ else
+ render_validation_error!(current_user.status)
+ end
+ end
+
+ desc 'get the status of the current user' do
+ success Entities::UserStatus
+ end
+ get 'status' do
+ present current_user.status || {}, with: Entities::UserStatus
+ end
end
end
end
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index af762db517c..906ed498026 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -1,10 +1,7 @@
require 'yaml'
-require_relative 'helper'
module Backup
class Repository
- include Backup::Helper
-
attr_reader :progress
def initialize(progress)
@@ -42,131 +39,36 @@ module Backup
end
def prepare_directories
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- delete_all_repositories(name, repository_storage)
+ Gitlab.config.repositories.storages.each do |name, _repository_storage|
+ Gitlab::GitalyClient::StorageService.new(name).delete_all_repositories
end
end
def backup_project(project)
- gitaly_migrate(:repository_backup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- backup_project_gitaly(project)
- else
- backup_project_local(project)
- end
- end
-
- backup_custom_hooks(project)
- rescue => e
- progress_warn(project, e, 'Failed to backup repo')
- end
-
- def backup_project_gitaly(project)
path_to_project_bundle = path_to_bundle(project)
Gitlab::GitalyClient::RepositoryService.new(project.repository)
.create_bundle(path_to_project_bundle)
- end
-
- def backup_project_local(project)
- path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- path_to_repo(project)
- end
-
- path_to_project_bundle = path_to_bundle(project)
-
- cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path_to_project_repo} bundle create #{path_to_project_bundle} --all)
- output, status = Gitlab::Popen.popen(cmd)
- progress_warn(project, cmd.join(' '), output) unless status.zero?
- end
-
- def delete_all_repositories(name, repository_storage)
- gitaly_migrate(:delete_all_repositories, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- Gitlab::GitalyClient::StorageService.new(name).delete_all_repositories
- else
- local_delete_all_repositories(name, repository_storage)
- end
- end
- end
-
- def local_delete_all_repositories(name, repository_storage)
- path = repository_storage.legacy_disk_path
- return unless File.exist?(path)
-
- bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s)
- FileUtils.mkdir_p(bk_repos_path, mode: 0700)
- files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")]
-
- begin
- FileUtils.mv(files, bk_repos_path)
- rescue Errno::EACCES
- access_denied_error(path)
- rescue Errno::EBUSY
- resource_busy_error(path)
- end
- end
- def local_restore_custom_hooks(project, dir)
- path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- path_to_repo(project)
- end
- cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir})
- output, status = Gitlab::Popen.popen(cmd)
- unless status.zero?
- progress_warn(project, cmd.join(' '), output)
- end
- end
-
- def gitaly_restore_custom_hooks(project, dir)
- custom_hooks_path = path_to_tars(project, dir)
- Gitlab::GitalyClient::RepositoryService.new(project.repository)
- .restore_custom_hooks(custom_hooks_path)
+ backup_custom_hooks(project)
+ rescue => e
+ progress_warn(project, e, 'Failed to backup repo')
end
- def local_backup_custom_hooks(project)
- in_path(path_to_tars(project)) do |dir|
- path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- path_to_repo(project)
- end
- break unless File.exist?(File.join(path_to_project_repo, dir))
-
- FileUtils.mkdir_p(path_to_tars(project))
- cmd = %W(tar -cf #{path_to_tars(project, dir)} -c #{path_to_project_repo} #{dir})
- output, status = Gitlab::Popen.popen(cmd)
-
- unless status.zero?
- progress_warn(project, cmd.join(' '), output)
- end
- end
- end
+ def backup_custom_hooks(project)
+ FileUtils.mkdir_p(project_backup_path(project))
- def gitaly_backup_custom_hooks(project)
- FileUtils.mkdir_p(path_to_tars(project))
- custom_hooks_path = path_to_tars(project, 'custom_hooks')
+ custom_hooks_path = custom_hooks_tar(project)
Gitlab::GitalyClient::RepositoryService.new(project.repository)
.backup_custom_hooks(custom_hooks_path)
end
- def backup_custom_hooks(project)
- gitaly_migrate(:backup_custom_hooks, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_backup_custom_hooks(project)
- else
- local_backup_custom_hooks(project)
- end
- end
- end
-
def restore_custom_hooks(project)
- in_path(path_to_tars(project)) do |dir|
- gitaly_migrate(:restore_custom_hooks, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_restore_custom_hooks(project, dir)
- else
- local_restore_custom_hooks(project, dir)
- end
- end
- end
+ return unless Dir.exist?(project_backup_path(project))
+ return if Dir.glob("#{project_backup_path(project)}/custom_hooks*").none?
+
+ custom_hooks_path = custom_hooks_tar(project)
+ Gitlab::GitalyClient::RepositoryService.new(project.repository)
+ .restore_custom_hooks(custom_hooks_path)
end
def restore
@@ -181,7 +83,8 @@ module Backup
restore_repo_success = nil
if File.exist?(path_to_project_bundle)
begin
- project.repository.create_from_bundle path_to_project_bundle
+ project.repository.create_from_bundle(path_to_project_bundle)
+ restore_custom_hooks(project)
restore_repo_success = true
rescue => e
restore_repo_success = false
@@ -197,8 +100,6 @@ module Backup
progress.puts "[Failed] restoring #{project.full_path} repository".color(:red)
end
- restore_custom_hooks(project)
-
wiki = ProjectWiki.new(project)
path_to_wiki_bundle = path_to_bundle(wiki)
@@ -219,48 +120,28 @@ module Backup
protected
- def path_to_repo(project)
- project.repository.path_to_repo
- end
-
def path_to_bundle(project)
File.join(backup_repos_path, project.disk_path + '.bundle')
end
- def path_to_tars(project, dir = nil)
- path = File.join(backup_repos_path, project.disk_path)
+ def project_backup_path(project)
+ File.join(backup_repos_path, project.disk_path)
+ end
- if dir
- File.join(path, "#{dir}.tar")
- else
- path
- end
+ def custom_hooks_tar(project)
+ File.join(project_backup_path(project), "custom_hooks.tar")
end
def backup_repos_path
File.join(Gitlab.config.backup.path, 'repositories')
end
- def in_path(path)
- return unless Dir.exist?(path)
-
- dir_entries = Dir.entries(path)
-
- if dir_entries.include?('custom_hooks') || dir_entries.include?('custom_hooks.tar')
- yield('custom_hooks')
- end
- end
-
def prepare
FileUtils.rm_rf(backup_repos_path)
FileUtils.mkdir_p(Gitlab.config.backup.path)
FileUtils.mkdir(backup_repos_path, mode: 0700)
end
- def silent
- { err: '/dev/null', out: '/dev/null' }
- end
-
private
def progress_warn(project, cmd, output)
@@ -273,18 +154,8 @@ module Backup
project_or_wiki.repository.empty?
end
- def repository_storage_paths_args
- Gitlab.config.repositories.storages.values.map { |rs| rs.legacy_disk_path }
- end
-
def display_repo_path(project)
project.hashed_storage?(:repository) ? "#{project.full_path} (#{project.disk_path})" : project.full_path
end
-
- def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
- Gitlab::GitalyClient.migrate(method, status: status, &block)
- rescue GRPC::NotFound, GRPC::BadStatus => e
- raise Error, e
- end
end
end
diff --git a/lib/banzai/filter/absolute_link_filter.rb b/lib/banzai/filter/absolute_link_filter.rb
index 1ec6201523f..04ec568eee3 100644
--- a/lib/banzai/filter/absolute_link_filter.rb
+++ b/lib/banzai/filter/absolute_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 60a12dca9d3..ad0806df8e6 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Issues, Merge Requests, Snippets, Commits and Commit Ranges share
@@ -100,6 +102,11 @@ module Banzai
ref_pattern = object_class.reference_pattern
link_pattern = object_class.link_reference_pattern
+ # Compile often used regexps only once outside of the loop
+ ref_pattern_anchor = /\A#{ref_pattern}\z/
+ link_pattern_start = /\A#{link_pattern}/
+ link_pattern_anchor = /\A#{link_pattern}\z/
+
nodes.each do |node|
if text_node?(node) && ref_pattern
replace_text_when_pattern_matches(node, ref_pattern) do |content|
@@ -108,7 +115,7 @@ module Banzai
elsif element_node?(node)
yield_valid_link(node) do |link, inner_html|
- if ref_pattern && link =~ /\A#{ref_pattern}\z/
+ if ref_pattern && link =~ ref_pattern_anchor
replace_link_node_with_href(node, link) do
object_link_filter(link, ref_pattern, link_content: inner_html)
end
@@ -118,7 +125,7 @@ module Banzai
next unless link_pattern
- if link == inner_html && inner_html =~ /\A#{link_pattern}/
+ if link == inner_html && inner_html =~ link_pattern_start
replace_link_node_with_text(node, link) do
object_link_filter(inner_html, link_pattern, link_reference: true)
end
@@ -126,7 +133,7 @@ module Banzai
next
end
- if link =~ /\A#{link_pattern}\z/
+ if link =~ link_pattern_anchor
replace_link_node_with_href(node, link) do
object_link_filter(link, link_pattern, link_content: inner_html, link_reference: true)
end
diff --git a/lib/banzai/filter/ascii_doc_post_processing_filter.rb b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
index c9fcf057c5f..88439f06b5f 100644
--- a/lib/banzai/filter/ascii_doc_post_processing_filter.rb
+++ b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class AsciiDocPostProcessingFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index 4a143baeef6..deda4b1872e 100644
--- a/lib/banzai/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index fbfcd72c916..ad367cc5efe 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -1,28 +1,10 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class BlockquoteFenceFilter < HTML::Pipeline::TextFilter
REGEX = %r{
- (?<code>
- # Code blocks:
- # ```
- # Anything, including `>>>` blocks which are ignored by this filter
- # ```
-
- ^```
- .+?
- \n```\ *$
- )
- |
- (?<html>
- # HTML block:
- # <tag>
- # Anything, including `>>>` blocks which are ignored by this filter
- # </tag>
-
- ^<[^>]+?>\ *\n
- .+?
- \n<\/[^>]+?>\ *$
- )
+ #{::Gitlab::Regex.markdown_code_or_html_blocks}
|
(?:
# Blockquote:
diff --git a/lib/banzai/filter/color_filter.rb b/lib/banzai/filter/color_filter.rb
index 6ab29ac281f..6d9bdb9cbd3 100644
--- a/lib/banzai/filter/color_filter.rb
+++ b/lib/banzai/filter/color_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that renders `color` followed by a color "chip".
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index 01b3b0dafb9..d6b46236a49 100644
--- a/lib/banzai/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces commit range references with links.
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index 8cd92a1adba..c3e5ac41cb8 100644
--- a/lib/banzai/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces commit references with links.
diff --git a/lib/banzai/filter/commit_trailers_filter.rb b/lib/banzai/filter/commit_trailers_filter.rb
index 7b55e8b36f6..f49c4b403db 100644
--- a/lib/banzai/filter/commit_trailers_filter.rb
+++ b/lib/banzai/filter/commit_trailers_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces users' names and emails in commit trailers
diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index e1261e7bbbe..c87948a30bf 100644
--- a/lib/banzai/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -1,12 +1,10 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces :emoji: and unicode with images.
#
# Based on HTML::Pipeline::EmojiFilter
- #
- # Context options:
- # :asset_root
- # :asset_host
class EmojiFilter < HTML::Pipeline::Filter
IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set
diff --git a/lib/banzai/filter/epic_reference_filter.rb b/lib/banzai/filter/epic_reference_filter.rb
index 265924abe24..e06e2fb3870 100644
--- a/lib/banzai/filter/epic_reference_filter.rb
+++ b/lib/banzai/filter/epic_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# The actual filter is implemented in the EE mixin
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index ed01a72ff9f..b4a7a44e109 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces external issue tracker references with links.
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index d6327ef31cb..2e6d742de27 100644
--- a/lib/banzai/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML Filter to modify the attributes of external links
diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb
index bb9f488cd87..0c1bbd2d250 100644
--- a/lib/banzai/filter/gollum_tags_filter.rb
+++ b/lib/banzai/filter/gollum_tags_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML Filter for parsing Gollum's tags in HTML. It's only parses the
diff --git a/lib/banzai/filter/html_entity_filter.rb b/lib/banzai/filter/html_entity_filter.rb
index e008fd428b0..406c2d3c96b 100644
--- a/lib/banzai/filter/html_entity_filter.rb
+++ b/lib/banzai/filter/html_entity_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'erb'
module Banzai
diff --git a/lib/banzai/filter/image_lazy_load_filter.rb b/lib/banzai/filter/image_lazy_load_filter.rb
index 4cd9b02b76c..afaee70f351 100644
--- a/lib/banzai/filter/image_lazy_load_filter.rb
+++ b/lib/banzai/filter/image_lazy_load_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that moves the value of image `src` attributes to `data-src`
@@ -5,7 +7,7 @@ module Banzai
class ImageLazyLoadFilter < HTML::Pipeline::Filter
def call
doc.xpath('descendant-or-self::img').each do |img|
- img['class'] ||= '' << 'lazy'
+ img.add_class('lazy')
img['data-src'] = img['src']
img['src'] = LazyImageTagHelper.placeholder_image
end
diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb
index f318c425962..884a94fb761 100644
--- a/lib/banzai/filter/image_link_filter.rb
+++ b/lib/banzai/filter/image_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that wraps links around inline images.
diff --git a/lib/banzai/filter/inline_diff_filter.rb b/lib/banzai/filter/inline_diff_filter.rb
index 73e82a4d7e3..e9ddc6e0e3d 100644
--- a/lib/banzai/filter/inline_diff_filter.rb
+++ b/lib/banzai/filter/inline_diff_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class InlineDiffFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/issuable_reference_filter.rb b/lib/banzai/filter/issuable_reference_filter.rb
index 7addf09be73..2963cba91e8 100644
--- a/lib/banzai/filter/issuable_reference_filter.rb
+++ b/lib/banzai/filter/issuable_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class IssuableReferenceFilter < AbstractReferenceFilter
diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb
index 1a415232545..d7fe012883d 100644
--- a/lib/banzai/filter/issuable_state_filter.rb
+++ b/lib/banzai/filter/issuable_state_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that appends state information to issuable links.
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 6877cae8c55..f85be042999 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces issue references with links. References to
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index a5f38046a43..b92e9e55bb9 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces label references with links.
diff --git a/lib/banzai/filter/markdown_engines/common_mark.rb b/lib/banzai/filter/markdown_engines/common_mark.rb
index bc9597df894..dbb25280849 100644
--- a/lib/banzai/filter/markdown_engines/common_mark.rb
+++ b/lib/banzai/filter/markdown_engines/common_mark.rb
@@ -18,7 +18,7 @@ module Banzai
PARSE_OPTIONS = [
:FOOTNOTES, # parse footnotes.
:STRIKETHROUGH_DOUBLE_TILDE, # parse strikethroughs by double tildes (as redcarpet does).
- :VALIDATE_UTF8 # replace illegal sequences with the replacement character U+FFFD.
+ :VALIDATE_UTF8 # replace illegal sequences with the replacement character U+FFFD.
].freeze
# The `:GITHUB_PRE_LANG` option is not used intentionally because
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index 944363f17d3..cdf758472c1 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class MarkdownFilter < HTML::Pipeline::TextFilter
diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb
index b6e784c886b..9d1bc3cf60c 100644
--- a/lib/banzai/filter/math_filter.rb
+++ b/lib/banzai/filter/math_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 5cbdb01c130..7098767b583 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces merge request references with links. References
@@ -25,7 +27,10 @@ module Banzai
extras = super
if commit_ref = object_link_commit_ref(object, matches)
- return extras.unshift(commit_ref)
+ klass = reference_class(:commit, tooltip: false)
+ commit_ref_tag = %(<span class="#{klass}">#{commit_ref}</span>)
+
+ return extras.unshift(commit_ref_tag)
end
path = matches[:path] if matches.names.include?("path")
diff --git a/lib/banzai/filter/mermaid_filter.rb b/lib/banzai/filter/mermaid_filter.rb
index 65c131e08d9..7c8b165a330 100644
--- a/lib/banzai/filter/mermaid_filter.rb
+++ b/lib/banzai/filter/mermaid_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class MermaidFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index af8448937b3..328c8c1803b 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces milestone references with links.
diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb
index 28933c78966..caba8955bac 100644
--- a/lib/banzai/filter/plantuml_filter.rb
+++ b/lib/banzai/filter/plantuml_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "nokogiri"
require "asciidoctor-plantuml/plantuml"
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index caf11fe94c4..1f091f594f8 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that removes references to records that the current user does
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 2f023f4f242..e5164e7f72a 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Base class for GitLab Flavored Markdown reference filters.
@@ -65,8 +67,12 @@ module Banzai
context[:skip_project_check]
end
- def reference_class(type)
- "gfm gfm-#{type} has-tooltip"
+ def reference_class(type, tooltip: true)
+ gfm_klass = "gfm gfm-#{type}"
+
+ return gfm_klass unless tooltip
+
+ "#{gfm_klass} has-tooltip"
end
# Ensure that a :project key exists in context
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 262458a872a..8e838d04bad 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index afc2ca4e362..8ba09290e6d 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -1,30 +1,25 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Sanitize HTML
#
# Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
class SanitizationFilter < HTML::Pipeline::SanitizationFilter
+ include Gitlab::Utils::StrongMemoize
+
UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze
TABLE_ALIGNMENT_PATTERN = /text-align: (?<alignment>center|left|right)/
def whitelist
- whitelist = super
-
- customize_whitelist(whitelist)
-
- whitelist
+ strong_memoize(:whitelist) do
+ customize_whitelist(super.deep_dup)
+ end
end
private
- def customized?(transformers)
- transformers.last.source_location[0] == __FILE__
- end
-
def customize_whitelist(whitelist)
- # Only push these customizations once
- return if customized?(whitelist[:transformers])
-
# Allow table alignment; we whitelist specific text-align values in a
# transformer below
whitelist[:attributes]['th'] = %w(style)
diff --git a/lib/banzai/filter/set_direction_filter.rb b/lib/banzai/filter/set_direction_filter.rb
index c2976aeb7c6..45b259a2faf 100644
--- a/lib/banzai/filter/set_direction_filter.rb
+++ b/lib/banzai/filter/set_direction_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that sets dir="auto" for RTL languages support
diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index 881e10afb9f..f4b6edb6174 100644
--- a/lib/banzai/filter/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces snippet references with links. References to
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 6dbf0d68fe8..8a7f9045c24 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rouge/plugins/common_mark'
require 'rouge/plugins/redcarpet'
@@ -15,7 +17,7 @@ module Banzai
end
def highlight_node(node)
- css_classes = 'code highlight js-syntax-highlight'
+ css_classes = +'code highlight js-syntax-highlight'
lang = node.attr('lang')
retried = false
diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index b32660a8341..c6d1e028eaa 100644
--- a/lib/banzai/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that adds an anchor child element to all Headers in a
@@ -19,7 +21,7 @@ module Banzai
def call
return doc if context[:no_header_anchors]
- result[:toc] = ""
+ result[:toc] = +""
headers = Hash.new(0)
header_root = current_header = HeaderNode.new
diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb
index 9fa5f589f3e..ef35a49edcb 100644
--- a/lib/banzai/filter/task_list_filter.rb
+++ b/lib/banzai/filter/task_list_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'task_list/filter'
module Banzai
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index c7fa8a8119f..11960047e5b 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces user or group references with links.
diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb
index 35cb10eae5d..0fb59c914c3 100644
--- a/lib/banzai/filter/video_link_filter.rb
+++ b/lib/banzai/filter/video_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Find every image that isn't already wrapped in an `a` tag, and that has
diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb
index 269d5bf74fa..870721f895d 100644
--- a/lib/banzai/filter/wiki_link_filter.rb
+++ b/lib/banzai/filter/wiki_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/yaml_front_matter_filter.rb b/lib/banzai/filter/yaml_front_matter_filter.rb
index 58e3e81209e..295964dd75d 100644
--- a/lib/banzai/filter/yaml_front_matter_filter.rb
+++ b/lib/banzai/filter/yaml_front_matter_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class YamlFrontMatterFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/pipeline/emoji_pipeline.rb b/lib/banzai/pipeline/emoji_pipeline.rb
new file mode 100644
index 00000000000..a1b522f4303
--- /dev/null
+++ b/lib/banzai/pipeline/emoji_pipeline.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Pipeline
+ class EmojiPipeline < BasePipeline
+ # These filters will only perform sanitization of the content, preventing
+ # XSS, and replace emoji.
+ def self.filters
+ @filters ||= FilterArray[
+ Filter::HtmlEntityFilter,
+ Filter::SanitizationFilter,
+ Filter::EmojiFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 9295ca9efcc..e9be05e174e 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -24,6 +24,17 @@ module Banzai
Filter::AutolinkFilter,
Filter::ExternalLinkFilter,
+ *reference_filters,
+
+ Filter::TaskListFilter,
+ Filter::InlineDiffFilter,
+
+ Filter::SetDirectionFilter
+ ]
+ end
+
+ def self.reference_filters
+ [
Filter::UserReferenceFilter,
Filter::ProjectReferenceFilter,
Filter::IssueReferenceFilter,
@@ -33,12 +44,7 @@ module Banzai
Filter::CommitRangeReferenceFilter,
Filter::CommitReferenceFilter,
Filter::LabelReferenceFilter,
- Filter::MilestoneReferenceFilter,
-
- Filter::TaskListFilter,
- Filter::InlineDiffFilter,
-
- Filter::SetDirectionFilter
+ Filter::MilestoneReferenceFilter
]
end
diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb
index dcd52bc03c7..0b2e584ef16 100644
--- a/lib/banzai/pipeline/post_process_pipeline.rb
+++ b/lib/banzai/pipeline/post_process_pipeline.rb
@@ -2,11 +2,17 @@ module Banzai
module Pipeline
class PostProcessPipeline < BasePipeline
def self.filters
- FilterArray[
+ @filters ||= FilterArray[
+ *internal_link_filters,
+ Filter::AbsoluteLinkFilter
+ ]
+ end
+
+ def self.internal_link_filters
+ [
Filter::RedactorFilter,
Filter::RelativeLinkFilter,
- Filter::IssuableStateFilter,
- Filter::AbsoluteLinkFilter
+ Filter::IssuableStateFilter
]
end
diff --git a/lib/banzai/pipeline/single_line_pipeline.rb b/lib/banzai/pipeline/single_line_pipeline.rb
index 1929099931b..cd5a6c8875c 100644
--- a/lib/banzai/pipeline/single_line_pipeline.rb
+++ b/lib/banzai/pipeline/single_line_pipeline.rb
@@ -10,13 +10,19 @@ module Banzai
Filter::AutolinkFilter,
Filter::ExternalLinkFilter,
+ *reference_filters
+ ]
+ end
+
+ def self.reference_filters
+ [
Filter::UserReferenceFilter,
Filter::IssueReferenceFilter,
Filter::ExternalIssueReferenceFilter,
Filter::MergeRequestReferenceFilter,
Filter::SnippetReferenceFilter,
Filter::CommitRangeReferenceFilter,
- Filter::CommitReferenceFilter,
+ Filter::CommitReferenceFilter
]
end
end
diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb
new file mode 100644
index 00000000000..15e59f93141
--- /dev/null
+++ b/lib/bitbucket_server/client.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ class Client
+ attr_reader :connection
+
+ ServerError = Class.new(StandardError)
+
+ SERVER_ERRORS = [SocketError,
+ OpenSSL::SSL::SSLError,
+ Errno::ECONNRESET,
+ Errno::ECONNREFUSED,
+ Errno::EHOSTUNREACH,
+ Net::OpenTimeout,
+ Net::ReadTimeout,
+ Gitlab::HTTP::BlockedUrlError,
+ BitbucketServer::Connection::ConnectionError].freeze
+
+ def initialize(options = {})
+ @connection = Connection.new(options)
+ end
+
+ def pull_requests(project_key, repo)
+ path = "/projects/#{project_key}/repos/#{repo}/pull-requests?state=ALL"
+ get_collection(path, :pull_request)
+ end
+
+ def activities(project_key, repo, pull_request_id)
+ path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request_id}/activities"
+ get_collection(path, :activity)
+ end
+
+ def repo(project, repo_name)
+ parsed_response = connection.get("/projects/#{project}/repos/#{repo_name}")
+ BitbucketServer::Representation::Repo.new(parsed_response)
+ end
+
+ def repos
+ path = "/repos"
+ get_collection(path, :repo)
+ end
+
+ def create_branch(project_key, repo, branch_name, sha)
+ payload = {
+ name: branch_name,
+ startPoint: sha,
+ message: 'GitLab temporary branch for import'
+ }
+
+ connection.post("/projects/#{project_key}/repos/#{repo}/branches", payload.to_json)
+ end
+
+ def delete_branch(project_key, repo, branch_name, sha)
+ payload = {
+ name: Gitlab::Git::BRANCH_REF_PREFIX + branch_name,
+ dryRun: false
+ }
+
+ connection.delete(:branches, "/projects/#{project_key}/repos/#{repo}/branches", payload.to_json)
+ end
+
+ private
+
+ def get_collection(path, type)
+ paginator = BitbucketServer::Paginator.new(connection, Addressable::URI.escape(path), type)
+ BitbucketServer::Collection.new(paginator)
+ rescue *SERVER_ERRORS => e
+ raise ServerError, e
+ end
+ end
+end
diff --git a/lib/bitbucket_server/collection.rb b/lib/bitbucket_server/collection.rb
new file mode 100644
index 00000000000..b50c5dde352
--- /dev/null
+++ b/lib/bitbucket_server/collection.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ class Collection < Enumerator
+ def initialize(paginator)
+ super() do |yielder|
+ loop do
+ paginator.items.each { |item| yielder << item }
+ end
+ end
+
+ lazy
+ end
+
+ def method_missing(method, *args)
+ return super unless self.respond_to?(method)
+
+ self.__send__(method, *args) do |item| # rubocop:disable GitlabSecurity/PublicSend
+ block_given? ? yield(item) : item
+ end
+ end
+ end
+end
diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb
new file mode 100644
index 00000000000..45a437844bd
--- /dev/null
+++ b/lib/bitbucket_server/connection.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ class Connection
+ include ActionView::Helpers::SanitizeHelper
+
+ DEFAULT_API_VERSION = '1.0'
+ SEPARATOR = '/'
+
+ attr_reader :api_version, :base_uri, :username, :token
+
+ ConnectionError = Class.new(StandardError)
+
+ def initialize(options = {})
+ @api_version = options.fetch(:api_version, DEFAULT_API_VERSION)
+ @base_uri = options[:base_uri]
+ @username = options[:user]
+ @token = options[:password]
+ end
+
+ def get(path, extra_query = {})
+ response = Gitlab::HTTP.get(build_url(path),
+ basic_auth: auth,
+ headers: accept_headers,
+ query: extra_query)
+
+ check_errors!(response)
+
+ response.parsed_response
+ end
+
+ def post(path, body)
+ response = Gitlab::HTTP.post(build_url(path),
+ basic_auth: auth,
+ headers: post_headers,
+ body: body)
+
+ check_errors!(response)
+
+ response.parsed_response
+ end
+
+ # We need to support two different APIs for deletion:
+ #
+ # /rest/api/1.0/projects/{projectKey}/repos/{repositorySlug}/branches/default
+ # /rest/branch-utils/1.0/projects/{projectKey}/repos/{repositorySlug}/branches
+ def delete(resource, path, body)
+ url = delete_url(resource, path)
+
+ response = Gitlab::HTTP.delete(url,
+ basic_auth: auth,
+ headers: post_headers,
+ body: body)
+
+ check_errors!(response)
+
+ response.parsed_response
+ end
+
+ private
+
+ def check_errors!(response)
+ raise ConnectionError, "Response is not valid JSON" unless response.parsed_response.is_a?(Hash)
+
+ return if response.code >= 200 && response.code < 300
+
+ details = sanitize(response.parsed_response.dig('errors', 0, 'message'))
+ message = "Error #{response.code}"
+ message += ": #{details}" if details
+
+ raise ConnectionError, message
+ rescue JSON::ParserError
+ raise ConnectionError, "Unable to parse the server response as JSON"
+ end
+
+ def auth
+ @auth ||= { username: username, password: token }
+ end
+
+ def accept_headers
+ @accept_headers ||= { 'Accept' => 'application/json' }
+ end
+
+ def post_headers
+ @post_headers ||= accept_headers.merge({ 'Content-Type' => 'application/json' })
+ end
+
+ def build_url(path)
+ return path if path.starts_with?(root_url)
+
+ url_join_paths(root_url, path)
+ end
+
+ def root_url
+ url_join_paths(base_uri, "/rest/api/#{api_version}")
+ end
+
+ def delete_url(resource, path)
+ if resource == :branches
+ url_join_paths(base_uri, "/rest/branch-utils/#{api_version}#{path}")
+ else
+ build_url(path)
+ end
+ end
+
+ # URI.join is stupid in that slashes are important:
+ #
+ # # URI.join('http://example.com/subpath', 'hello')
+ # => http://example.com/hello
+ #
+ # We really want http://example.com/subpath/hello
+ #
+ def url_join_paths(*paths)
+ paths.map { |path| strip_slashes(path) }.join(SEPARATOR)
+ end
+
+ def strip_slashes(path)
+ path = path[1..-1] if path.starts_with?(SEPARATOR)
+ path.chomp(SEPARATOR)
+ end
+ end
+end
diff --git a/lib/bitbucket_server/page.rb b/lib/bitbucket_server/page.rb
new file mode 100644
index 00000000000..5d9a3168876
--- /dev/null
+++ b/lib/bitbucket_server/page.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ class Page
+ attr_reader :attrs, :items
+
+ def initialize(raw, type)
+ @attrs = parse_attrs(raw)
+ @items = parse_values(raw, representation_class(type))
+ end
+
+ def next?
+ !attrs.fetch(:isLastPage, true)
+ end
+
+ def next
+ attrs.fetch(:nextPageStart)
+ end
+
+ private
+
+ def parse_attrs(raw)
+ raw.slice('size', 'nextPageStart', 'isLastPage').symbolize_keys
+ end
+
+ def parse_values(raw, bitbucket_rep_class)
+ return [] unless raw['values'] && raw['values'].is_a?(Array)
+
+ bitbucket_rep_class.decorate(raw['values'])
+ end
+
+ def representation_class(type)
+ BitbucketServer::Representation.const_get(type.to_s.camelize)
+ end
+ end
+end
diff --git a/lib/bitbucket_server/paginator.rb b/lib/bitbucket_server/paginator.rb
new file mode 100644
index 00000000000..c351fb2f11f
--- /dev/null
+++ b/lib/bitbucket_server/paginator.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ class Paginator
+ PAGE_LENGTH = 25
+
+ def initialize(connection, url, type)
+ @connection = connection
+ @type = type
+ @url = url
+ @page = nil
+ end
+
+ def items
+ raise StopIteration unless has_next_page?
+
+ @page = fetch_next_page
+ @page.items
+ end
+
+ private
+
+ attr_reader :connection, :page, :url, :type
+
+ def has_next_page?
+ page.nil? || page.next?
+ end
+
+ def next_offset
+ page.nil? ? 0 : page.next
+ end
+
+ def fetch_next_page
+ parsed_response = connection.get(@url, start: next_offset, limit: PAGE_LENGTH)
+ Page.new(parsed_response, type)
+ end
+ end
+end
diff --git a/lib/bitbucket_server/representation/activity.rb b/lib/bitbucket_server/representation/activity.rb
new file mode 100644
index 00000000000..08bf30a5d1e
--- /dev/null
+++ b/lib/bitbucket_server/representation/activity.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ module Representation
+ class Activity < Representation::Base
+ def comment?
+ action == 'COMMENTED'
+ end
+
+ def inline_comment?
+ !!(comment? && comment_anchor)
+ end
+
+ def comment
+ return unless comment?
+
+ @comment ||=
+ if inline_comment?
+ PullRequestComment.new(raw)
+ else
+ Comment.new(raw)
+ end
+ end
+
+ # TODO Move this into MergeEvent
+ def merge_event?
+ action == 'MERGED'
+ end
+
+ def committer_user
+ commit.dig('committer', 'displayName')
+ end
+
+ def committer_email
+ commit.dig('committer', 'emailAddress')
+ end
+
+ def merge_timestamp
+ timestamp = commit['committerTimestamp']
+
+ self.class.convert_timestamp(timestamp)
+ end
+
+ def merge_commit
+ commit['id']
+ end
+
+ def created_at
+ self.class.convert_timestamp(created_date)
+ end
+
+ private
+
+ def commit
+ raw.fetch('commit', {})
+ end
+
+ def action
+ raw['action']
+ end
+
+ def comment_anchor
+ raw['commentAnchor']
+ end
+
+ def created_date
+ raw['createdDate']
+ end
+ end
+ end
+end
diff --git a/lib/bitbucket_server/representation/base.rb b/lib/bitbucket_server/representation/base.rb
new file mode 100644
index 00000000000..a1961bae6ef
--- /dev/null
+++ b/lib/bitbucket_server/representation/base.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ module Representation
+ class Base
+ attr_reader :raw
+
+ def initialize(raw)
+ @raw = raw
+ end
+
+ def self.decorate(entries)
+ entries.map { |entry| new(entry)}
+ end
+
+ def self.convert_timestamp(time_usec)
+ Time.at(time_usec / 1000) if time_usec.is_a?(Integer)
+ end
+ end
+ end
+end
diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb
new file mode 100644
index 00000000000..99b97a3b181
--- /dev/null
+++ b/lib/bitbucket_server/representation/comment.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ module Representation
+ # A general comment with the structure:
+ # "comment": {
+ # "author": {
+ # "active": true,
+ # "displayName": "root",
+ # "emailAddress": "stanhu+bitbucket@gitlab.com",
+ # "id": 1,
+ # "links": {
+ # "self": [
+ # {
+ # "href": "http://localhost:7990/users/root"
+ # }
+ # ]
+ # },
+ # "name": "root",
+ # "slug": "root",
+ # "type": "NORMAL"
+ # }
+ # }
+ # }
+ class Comment < Representation::Base
+ attr_reader :parent_comment
+
+ CommentNode = Struct.new(:raw_comments, :parent)
+
+ def initialize(raw, parent_comment: nil)
+ super(raw)
+
+ @parent_comment = parent_comment
+ end
+
+ def id
+ raw_comment['id']
+ end
+
+ def author_username
+ author['displayName']
+ end
+
+ def author_email
+ author['emailAddress']
+ end
+
+ def note
+ raw_comment['text']
+ end
+
+ def created_at
+ self.class.convert_timestamp(created_date)
+ end
+
+ def updated_at
+ self.class.convert_timestamp(created_date)
+ end
+
+ # Bitbucket Server supports the ability to reply to any comment
+ # and created multiple threads. It represents these as a linked list
+ # of comments within comments. For example:
+ #
+ # "comments": [
+ # {
+ # "author" : ...
+ # "comments": [
+ # {
+ # "author": ...
+ #
+ # Since GitLab only supports a single thread, we flatten all these
+ # comments into a single discussion.
+ def comments
+ @comments ||= flatten_comments
+ end
+
+ private
+
+ # In order to provide context for each reply, we need to track
+ # the parent of each comment. This method works as follows:
+ #
+ # 1. Insert the root comment into the workset. The root element is the current note.
+ # 2. For each node in the workset:
+ # a. Examine if it has replies to that comment. If it does,
+ # insert that node into the workset.
+ # b. Parse that note into a Comment structure and add it to a flat list.
+ def flatten_comments
+ comments = raw_comment['comments']
+ workset =
+ if comments
+ [CommentNode.new(comments, self)]
+ else
+ []
+ end
+
+ all_comments = []
+
+ until workset.empty?
+ node = workset.pop
+ parent = node.parent
+
+ node.raw_comments.each do |comment|
+ new_comments = comment.delete('comments')
+ current_comment = Comment.new({ 'comment' => comment }, parent_comment: parent)
+ all_comments << current_comment
+ workset << CommentNode.new(new_comments, current_comment) if new_comments
+ end
+ end
+
+ all_comments
+ end
+
+ def raw_comment
+ raw.fetch('comment', {})
+ end
+
+ def author
+ raw_comment['author']
+ end
+
+ def created_date
+ raw_comment['createdDate']
+ end
+
+ def updated_date
+ raw_comment['updatedDate']
+ end
+ end
+ end
+end
diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb
new file mode 100644
index 00000000000..c3e927d8de7
--- /dev/null
+++ b/lib/bitbucket_server/representation/pull_request.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ module Representation
+ class PullRequest < Representation::Base
+ def author
+ raw.dig('author', 'user', 'name')
+ end
+
+ def author_email
+ raw.dig('author', 'user', 'emailAddress')
+ end
+
+ def description
+ raw['description']
+ end
+
+ def iid
+ raw['id']
+ end
+
+ def state
+ case raw['state']
+ when 'MERGED'
+ 'merged'
+ when 'DECLINED'
+ 'closed'
+ else
+ 'opened'
+ end
+ end
+
+ def merged?
+ state == 'merged'
+ end
+
+ def created_at
+ self.class.convert_timestamp(created_date)
+ end
+
+ def updated_at
+ self.class.convert_timestamp(updated_date)
+ end
+
+ def title
+ raw['title']
+ end
+
+ def source_branch_name
+ raw.dig('fromRef', 'id')
+ end
+
+ def source_branch_sha
+ raw.dig('fromRef', 'latestCommit')
+ end
+
+ def target_branch_name
+ raw.dig('toRef', 'id')
+ end
+
+ def target_branch_sha
+ raw.dig('toRef', 'latestCommit')
+ end
+
+ private
+
+ def created_date
+ raw['createdDate']
+ end
+
+ def updated_date
+ raw['updatedDate']
+ end
+ end
+ end
+end
diff --git a/lib/bitbucket_server/representation/pull_request_comment.rb b/lib/bitbucket_server/representation/pull_request_comment.rb
new file mode 100644
index 00000000000..a2b3873a397
--- /dev/null
+++ b/lib/bitbucket_server/representation/pull_request_comment.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ module Representation
+ # An inline comment with the following structure that identifies
+ # the part of the diff:
+ #
+ # "commentAnchor": {
+ # "diffType": "EFFECTIVE",
+ # "fileType": "TO",
+ # "fromHash": "c5f4288162e2e6218180779c7f6ac1735bb56eab",
+ # "line": 1,
+ # "lineType": "ADDED",
+ # "orphaned": false,
+ # "path": "CHANGELOG.md",
+ # "toHash": "a4c2164330f2549f67c13f36a93884cf66e976be"
+ # }
+ #
+ # More details in https://docs.atlassian.com/bitbucket-server/rest/5.12.0/bitbucket-rest.html.
+ class PullRequestComment < Comment
+ def from_sha
+ comment_anchor['fromHash']
+ end
+
+ def to_sha
+ comment_anchor['toHash']
+ end
+
+ def to?
+ file_type == 'TO'
+ end
+
+ def from?
+ file_type == 'FROM'
+ end
+
+ def added?
+ line_type == 'ADDED'
+ end
+
+ def removed?
+ line_type == 'REMOVED'
+ end
+
+ # There are three line comment types: added, removed, or context.
+ #
+ # 1. An added type means a new line was inserted, so there is no old position.
+ # 2. A removed type means a line was removed, so there is no new position.
+ # 3. A context type means the line was unmodified, so there is both a
+ # old and new position.
+ def new_pos
+ return if removed?
+ return unless line_position
+
+ line_position[1]
+ end
+
+ def old_pos
+ return if added?
+ return unless line_position
+
+ line_position[0]
+ end
+
+ def file_path
+ comment_anchor.fetch('path')
+ end
+
+ private
+
+ def file_type
+ comment_anchor['fileType']
+ end
+
+ def line_type
+ comment_anchor['lineType']
+ end
+
+ # Each comment contains the following information about the diff:
+ #
+ # hunks: [
+ # {
+ # segments: [
+ # {
+ # "lines": [
+ # {
+ # "commentIds": [ N ],
+ # "source": X,
+ # "destination": Y
+ # }, ...
+ # ] ....
+ #
+ # To determine the line position of a comment, we search all the lines
+ # entries until we find this comment ID.
+ def line_position
+ @line_position ||= diff_hunks.each do |hunk|
+ segments = hunk.fetch('segments', [])
+ segments.each do |segment|
+ lines = segment.fetch('lines', [])
+ lines.each do |line|
+ if line['commentIds']&.include?(id)
+ return [line['source'], line['destination']]
+ end
+ end
+ end
+ end
+ end
+
+ def comment_anchor
+ raw.fetch('commentAnchor', {})
+ end
+
+ def diff
+ raw.fetch('diff', {})
+ end
+
+ def diff_hunks
+ diff.fetch('hunks', [])
+ end
+ end
+ end
+end
diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb
new file mode 100644
index 00000000000..6c494b79166
--- /dev/null
+++ b/lib/bitbucket_server/representation/repo.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+module BitbucketServer
+ module Representation
+ class Repo < Representation::Base
+ def initialize(raw)
+ super(raw)
+ end
+
+ def project_key
+ raw.dig('project', 'key')
+ end
+
+ def project_name
+ raw.dig('project', 'name')
+ end
+
+ def slug
+ raw['slug']
+ end
+
+ def browse_url
+ # The JSON reponse contains an array of 1 element. Not sure if there
+ # are cases where multiple links would be provided.
+ raw.dig('links', 'self').first.fetch('href')
+ end
+
+ def clone_url
+ raw['links']['clone'].find { |link| link['name'].starts_with?('http') }.fetch('href')
+ end
+
+ def description
+ project['description']
+ end
+
+ def full_name
+ "#{project_name}/#{name}"
+ end
+
+ def issues_enabled?
+ true
+ end
+
+ def name
+ raw['name']
+ end
+
+ def valid?
+ raw['scmId'] == 'git'
+ end
+
+ def visibility_level
+ if project['public']
+ Gitlab::VisibilityLevel::PUBLIC
+ else
+ Gitlab::VisibilityLevel::PRIVATE
+ end
+ end
+
+ def project
+ raw['project']
+ end
+
+ def to_s
+ full_name
+ end
+ end
+ end
+end
diff --git a/lib/declarative_policy.rb b/lib/declarative_policy.rb
index 1dd2855063d..dda6cd38dcd 100644
--- a/lib/declarative_policy.rb
+++ b/lib/declarative_policy.rb
@@ -21,7 +21,17 @@ module DeclarativePolicy
cache = opts[:cache] || {}
key = Cache.policy_key(user, subject)
- cache[key] ||= class_for(subject).new(user, subject, opts)
+ cache[key] ||=
+ if Gitlab.rails5?
+ # to avoid deadlocks in multi-threaded environment when
+ # autoloading is enabled, we allow concurrent loads,
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/48263
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ class_for(subject).new(user, subject, opts)
+ end
+ else
+ class_for(subject).new(user, subject, opts)
+ end
end
def class_for(subject)
diff --git a/lib/declarative_policy/base.rb b/lib/declarative_policy/base.rb
index 47542194497..da3fabba39b 100644
--- a/lib/declarative_policy/base.rb
+++ b/lib/declarative_policy/base.rb
@@ -119,8 +119,8 @@ module DeclarativePolicy
# a PolicyDsl which is used for registering the rule with
# this class. PolicyDsl will call back into Base.enable_when,
# Base.prevent_when, and Base.prevent_all_when.
- def rule(&b)
- rule = RuleDsl.new(self).instance_eval(&b)
+ def rule(&block)
+ rule = RuleDsl.new(self).instance_eval(&block)
PolicyDsl.new(self, rule)
end
@@ -222,8 +222,8 @@ module DeclarativePolicy
# computes the given ability and prints a helpful debugging output
# showing which
- def debug(ability, *a)
- runner(ability).debug(*a)
+ def debug(ability, *args)
+ runner(ability).debug(*args)
end
desc "Unknown user"
@@ -274,7 +274,7 @@ module DeclarativePolicy
#
# NOTE we can't use ||= here because the value might be the
# boolean `false`
- def cache(key, &b)
+ def cache(key)
return @cache[key] if cached?(key)
@cache[key] = yield
diff --git a/lib/declarative_policy/delegate_dsl.rb b/lib/declarative_policy/delegate_dsl.rb
index f544dffe888..ca2eb98e3e8 100644
--- a/lib/declarative_policy/delegate_dsl.rb
+++ b/lib/declarative_policy/delegate_dsl.rb
@@ -7,10 +7,10 @@ module DeclarativePolicy
@delegate_name = delegate_name
end
- def method_missing(m, *a, &b)
- return super unless a.empty? && !block_given?
+ def method_missing(msg, *args)
+ return super unless args.empty? && !block_given?
- @rule_dsl.delegate(@delegate_name, m)
+ @rule_dsl.delegate(@delegate_name, msg)
end
end
end
diff --git a/lib/declarative_policy/policy_dsl.rb b/lib/declarative_policy/policy_dsl.rb
index f11b6e9f730..c96049768a1 100644
--- a/lib/declarative_policy/policy_dsl.rb
+++ b/lib/declarative_policy/policy_dsl.rb
@@ -15,8 +15,8 @@ module DeclarativePolicy
@rule = rule
end
- def policy(&b)
- instance_eval(&b)
+ def policy(&block)
+ instance_eval(&block)
end
def enable(*abilities)
@@ -31,14 +31,14 @@ module DeclarativePolicy
@context_class.prevent_all_when(@rule)
end
- def method_missing(m, *a, &b)
- return super unless @context_class.respond_to?(m)
+ def method_missing(msg, *args, &block)
+ return super unless @context_class.respond_to?(msg)
- @context_class.__send__(m, *a, &b) # rubocop:disable GitlabSecurity/PublicSend
+ @context_class.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
- def respond_to_missing?(m)
- @context_class.respond_to?(m) || super
+ def respond_to_missing?(msg)
+ @context_class.respond_to?(msg) || super
end
end
end
diff --git a/lib/declarative_policy/preferred_scope.rb b/lib/declarative_policy/preferred_scope.rb
index 5c214408dd0..c77784cb49d 100644
--- a/lib/declarative_policy/preferred_scope.rb
+++ b/lib/declarative_policy/preferred_scope.rb
@@ -2,7 +2,7 @@ module DeclarativePolicy # rubocop:disable Naming/FileName
PREFERRED_SCOPE_KEY = :"DeclarativePolicy.preferred_scope"
class << self
- def with_preferred_scope(scope, &b)
+ def with_preferred_scope(scope)
Thread.current[PREFERRED_SCOPE_KEY], old_scope = scope, Thread.current[PREFERRED_SCOPE_KEY]
yield
ensure
@@ -13,12 +13,12 @@ module DeclarativePolicy # rubocop:disable Naming/FileName
Thread.current[PREFERRED_SCOPE_KEY]
end
- def user_scope(&b)
- with_preferred_scope(:user, &b)
+ def user_scope(&block)
+ with_preferred_scope(:user, &block)
end
- def subject_scope(&b)
- with_preferred_scope(:subject, &b)
+ def subject_scope(&block)
+ with_preferred_scope(:subject, &block)
end
def preferred_scope=(scope)
diff --git a/lib/declarative_policy/rule.rb b/lib/declarative_policy/rule.rb
index e309244a3b3..407398cc770 100644
--- a/lib/declarative_policy/rule.rb
+++ b/lib/declarative_policy/rule.rb
@@ -8,8 +8,8 @@ module DeclarativePolicy
# how that affects the actual ability decision - for that, a
# `Step` is used.
class Base
- def self.make(*a)
- new(*a).simplify
+ def self.make(*args)
+ new(*args).simplify
end
# true or false whether this rule passes.
diff --git a/lib/declarative_policy/rule_dsl.rb b/lib/declarative_policy/rule_dsl.rb
index e948b7f2de1..7254b08eda5 100644
--- a/lib/declarative_policy/rule_dsl.rb
+++ b/lib/declarative_policy/rule_dsl.rb
@@ -32,13 +32,13 @@ module DeclarativePolicy
Rule::DelegatedCondition.new(delegate_name, condition)
end
- def method_missing(m, *a, &b)
- return super unless a.empty? && !block_given?
+ def method_missing(msg, *args)
+ return super unless args.empty? && !block_given?
- if @context_class.delegations.key?(m)
- DelegateDsl.new(self, m)
+ if @context_class.delegations.key?(msg)
+ DelegateDsl.new(self, msg)
else
- cond(m.to_sym)
+ cond(msg.to_sym)
end
end
end
diff --git a/lib/declarative_policy/runner.rb b/lib/declarative_policy/runner.rb
index 87f14b3b0d2..fec672f4b8c 100644
--- a/lib/declarative_policy/runner.rb
+++ b/lib/declarative_policy/runner.rb
@@ -127,7 +127,7 @@ module DeclarativePolicy
#
# For each step, we yield the step object along with the computed score
# for debugging purposes.
- def steps_by_score(&b)
+ def steps_by_score
flatten_steps!
if @steps.size > 50
diff --git a/lib/disable_email_interceptor.rb b/lib/disable_email_interceptor.rb
deleted file mode 100644
index cee664b8951..00000000000
--- a/lib/disable_email_interceptor.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
-class DisableEmailInterceptor
- def self.delivering_email(message)
- message.perform_deliveries = false
- Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}"
- end
-end
diff --git a/lib/email_template_interceptor.rb b/lib/email_template_interceptor.rb
deleted file mode 100644
index 3978a6d9fe4..00000000000
--- a/lib/email_template_interceptor.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
-class EmailTemplateInterceptor
- def self.delivering_email(message)
- # Remove HTML part if HTML emails are disabled.
- unless Gitlab::CurrentSettings.html_emails_enabled
- message.parts.delete_if do |part|
- part.content_type.start_with?('text/html')
- end
- end
- end
-end
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index a9b04c183ad..e8dbde176ef 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -139,6 +139,11 @@ module ExtractsPath
def lfs_blob_ids
blob_ids = tree.blobs.map(&:id)
+
+ # When current endpoint is a Blob then `tree.blobs` will be empty, it means we need to analyze
+ # the current Blob in order to determine if it's a LFS object
+ blob_ids = Array.wrap(@repo.blob_at(@commit.id, @path)&.id) if blob_ids.empty? # rubocop:disable Gitlab/ModuleWithInstanceVariables
+
@lfs_blob_ids = Gitlab::Git::Blob.batch_lfs_pointers(@project.repository, blob_ids).map(&:id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
diff --git a/lib/feature.rb b/lib/feature.rb
index 314ae224d90..09c5ef3ad94 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -39,13 +39,17 @@ class Feature
# Flipper creates on-memory features when asked for a not-yet-created one.
# If we want to check if a feature has been actually set, we look for it
# on the persisted features list.
- persisted_names.include?(feature.name)
+ persisted_names.include?(feature.name.to_s)
end
def enabled?(key, thing = nil)
get(key).enabled?(thing)
end
+ def disabled?(key, thing = nil)
+ !enabled?(key, thing)
+ end
+
def enable(key, thing = true)
get(key).enable(thing)
end
diff --git a/lib/gitaly/server.rb b/lib/gitaly/server.rb
index 2760211fee8..f95e423ef22 100644
--- a/lib/gitaly/server.rb
+++ b/lib/gitaly/server.rb
@@ -50,7 +50,7 @@ module Gitaly
@info ||=
begin
Gitlab::GitalyClient::ServerService.new(@storage).info
- rescue GRPC::Unavailable, GRPC::GRPC::DeadlineExceeded
+ rescue GRPC::Unavailable, GRPC::DeadlineExceeded
# This will show the server as being out of date
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
end
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index b9a148f35bf..ab6b609d099 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -9,10 +9,6 @@ module Gitlab
Settings
end
- def self.migrations_hash
- @_migrations_hash ||= Digest::MD5.hexdigest(ActiveRecord::Migrator.get_all_versions.to_s)
- end
-
def self.revision
@_revision ||= begin
if File.exist?(root.join("REVISION"))
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index 87e377de4d3..b170145f013 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -7,12 +7,14 @@ module Gitlab
module Access
AccessDeniedError = Class.new(StandardError)
- NO_ACCESS = 0
- GUEST = 10
- REPORTER = 20
- DEVELOPER = 30
- MASTER = 40
- OWNER = 50
+ NO_ACCESS = 0
+ GUEST = 10
+ REPORTER = 20
+ DEVELOPER = 30
+ MAINTAINER = 40
+ # @deprecated
+ MASTER = MAINTAINER
+ OWNER = 50
# Branch protection settings
PROTECTION_NONE = 0
@@ -32,7 +34,7 @@ module Gitlab
"Guest" => GUEST,
"Reporter" => REPORTER,
"Developer" => DEVELOPER,
- "Maintainer" => MASTER
+ "Maintainer" => MAINTAINER
}
end
@@ -44,10 +46,10 @@ module Gitlab
def sym_options
{
- guest: GUEST,
- reporter: REPORTER,
- developer: DEVELOPER,
- master: MASTER
+ guest: GUEST,
+ reporter: REPORTER,
+ developer: DEVELOPER,
+ maintainer: MAINTAINER
}
end
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 7de66539848..111e18b2076 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -14,23 +14,8 @@ module Gitlab
DEFAULT_SCOPES = [:api].freeze
class << self
- def omniauth_customized_providers
- @omniauth_customized_providers ||= %w[bitbucket jwt]
- end
-
- def omniauth_setup_providers(provider_names)
- provider_names.each do |provider|
- omniauth_setup_a_provider(provider)
- end
- end
-
- def omniauth_setup_a_provider(provider)
- case provider
- when 'kerberos'
- require 'omniauth-kerberos'
- when *omniauth_customized_providers
- require_dependency "omni_auth/strategies/#{provider}"
- end
+ def omniauth_enabled?
+ Gitlab.config.omniauth.enabled
end
def find_for_git_client(login, password, project:, ip:)
diff --git a/lib/gitlab/auth/activity.rb b/lib/gitlab/auth/activity.rb
new file mode 100644
index 00000000000..761f0819c60
--- /dev/null
+++ b/lib/gitlab/auth/activity.rb
@@ -0,0 +1,78 @@
+module Gitlab
+ module Auth
+ ##
+ # Metrics and logging for user authentication activity.
+ #
+ class Activity
+ extend Gitlab::Utils::StrongMemoize
+
+ COUNTERS = {
+ user_authenticated: 'Counter of successful authentication events',
+ user_unauthenticated: 'Counter of authentication failures',
+ user_not_found: 'Counter of failed log-ins when user is unknown',
+ user_password_invalid: 'Counter of failed log-ins with invalid password',
+ user_session_override: 'Counter of manual log-ins and sessions overrides',
+ user_session_destroyed: 'Counter of user sessions being destroyed',
+ user_two_factor_authenticated: 'Counter of two factor authentications',
+ user_sessionless_authentication: 'Counter of sessionless authentications',
+ user_blocked: 'Counter of sign in attempts when user is blocked'
+ }.freeze
+
+ def initialize(opts)
+ @opts = opts
+ end
+
+ def user_authentication_failed!
+ self.class.user_unauthenticated_counter_increment!
+
+ case @opts[:message]
+ when :not_found_in_database
+ self.class.user_not_found_counter_increment!
+ when :invalid
+ self.class.user_password_invalid_counter_increment!
+ end
+ end
+
+ def user_authenticated!
+ self.class.user_authenticated_counter_increment!
+ end
+
+ def user_session_override!
+ self.class.user_session_override_counter_increment!
+
+ case @opts[:message]
+ when :two_factor_authenticated
+ self.class.user_two_factor_authenticated_counter_increment!
+ when :sessionless_sign_in
+ self.class.user_sessionless_authentication_counter_increment!
+ end
+ end
+
+ def user_blocked!
+ self.class.user_blocked_counter_increment!
+ end
+
+ def user_session_destroyed!
+ self.class.user_session_destroyed_counter_increment!
+ end
+
+ def self.each_counter
+ COUNTERS.each_pair do |metric, description|
+ yield "#{metric}_counter", metric, description
+ end
+ end
+
+ each_counter do |counter, metric, description|
+ define_singleton_method(counter) do
+ strong_memoize(counter) do
+ Gitlab::Metrics.counter("gitlab_auth_#{metric}_total".to_sym, description)
+ end
+ end
+
+ define_singleton_method("#{counter}_increment!") do
+ public_send(counter).increment # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/auth/blocked_user_tracker.rb b/lib/gitlab/auth/blocked_user_tracker.rb
index 7609a7b04f6..50712d7eac2 100644
--- a/lib/gitlab/auth/blocked_user_tracker.rb
+++ b/lib/gitlab/auth/blocked_user_tracker.rb
@@ -2,35 +2,19 @@
module Gitlab
module Auth
class BlockedUserTracker
- ACTIVE_RECORD_REQUEST_PARAMS = 'action_dispatch.request.request_parameters'
-
- def self.log_if_user_blocked(env)
- message = env.dig('warden.options', :message)
-
- # Devise calls User#active_for_authentication? on the User model and then
- # throws an exception to Warden with User#inactive_message:
- # https://github.com/plataformatec/devise/blob/v4.2.1/lib/devise/hooks/activatable.rb#L8
- #
- # Since Warden doesn't pass the user record to the failure handler, we
- # need to do a database lookup with the username. We can limit the
- # lookups to happen when the user was blocked by checking the inactive
- # message passed along by Warden.
- return unless message == User::BLOCKED_MESSAGE
-
- # Check for either LDAP or regular GitLab account logins
- login = env.dig(ACTIVE_RECORD_REQUEST_PARAMS, 'username') ||
- env.dig(ACTIVE_RECORD_REQUEST_PARAMS, 'user', 'login')
-
- return unless login.present?
-
- user = User.by_login(login)
+ def initialize(user, auth)
+ @user = user
+ @auth = auth
+ end
- return unless user&.blocked?
+ def log_activity!
+ return unless @user.blocked?
- Gitlab::AppLogger.info("Failed login for blocked user: user=#{user.username} ip=#{env['REMOTE_ADDR']}")
- SystemHooksService.new.execute_hooks_for(user, :failed_login)
+ Gitlab::AppLogger.info <<~INFO
+ "Failed login for blocked user: user=#{@user.username} ip=#{@auth.request.ip}")
+ INFO
- true
+ SystemHooksService.new.execute_hooks_for(@user, :failed_login)
rescue TypeError
end
end
diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb
index 5fb61ffe00d..e73743944a9 100644
--- a/lib/gitlab/auth/o_auth/provider.rb
+++ b/lib/gitlab/auth/o_auth/provider.rb
@@ -30,7 +30,7 @@ module Gitlab
def self.enabled?(name)
return true if name == 'database'
- providers.include?(name.to_sym)
+ Gitlab::Auth.omniauth_enabled? && providers.include?(name.to_sym)
end
def self.ldap_provider?(name)
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index e7283b2f9e8..589e8062226 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -48,7 +48,7 @@ module Gitlab
gl_user
rescue ActiveRecord::RecordInvalid => e
log.info "(#{provider}) Error saving user #{auth_hash.uid} (#{auth_hash.email}): #{gl_user.errors.full_messages}"
- return self, e.record.errors
+ [self, e.record.errors]
end
def gl_user
diff --git a/lib/gitlab/background_migration.rb b/lib/gitlab/background_migration.rb
index d3f66877672..36c85dec544 100644
--- a/lib/gitlab/background_migration.rb
+++ b/lib/gitlab/background_migration.rb
@@ -46,7 +46,11 @@ module Gitlab
# arguments - The arguments to pass to the background migration's "perform"
# method.
def self.perform(class_name, arguments)
- const_get(class_name).new.perform(*arguments)
+ migration_class_for(class_name).new.perform(*arguments)
+ end
+
+ def self.migration_class_for(class_name)
+ const_get(class_name)
end
end
end
diff --git a/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb b/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb
index d5cf9e0d53a..cb2bdea755c 100644
--- a/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb
+++ b/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
# rubocop:disable Style/Documentation
-# rubocop:disable Metrics/LineLength
module Gitlab
module BackgroundMigration
diff --git a/lib/gitlab/background_migration/archive_legacy_traces.rb b/lib/gitlab/background_migration/archive_legacy_traces.rb
index 5a4e5b2c471..92096e29ef1 100644
--- a/lib/gitlab/background_migration/archive_legacy_traces.rb
+++ b/lib/gitlab/background_migration/archive_legacy_traces.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/create_fork_network_memberships_range.rb b/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
index 1b4a9e8a194..ccd1f9b4dba 100644
--- a/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
+++ b/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb b/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb
index c2bf42f846d..da8265a3a5f 100644
--- a/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb
+++ b/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
class Gitlab::BackgroundMigration::CreateGpgKeySubkeysFromGpgKeys
diff --git a/lib/gitlab/background_migration/delete_diff_files.rb b/lib/gitlab/background_migration/delete_diff_files.rb
index 0b785e1b056..664ead1af44 100644
--- a/lib/gitlab/background_migration/delete_diff_files.rb
+++ b/lib/gitlab/background_migration/delete_diff_files.rb
@@ -1,45 +1,80 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
class DeleteDiffFiles
- def perform(merge_request_diff_id)
- merge_request_diff = MergeRequestDiff.find_by(id: merge_request_diff_id)
+ class MergeRequestDiff < ActiveRecord::Base
+ self.table_name = 'merge_request_diffs'
- return unless merge_request_diff
- return unless should_delete_diff_files?(merge_request_diff)
+ belongs_to :merge_request
+ has_many :merge_request_diff_files
+ end
- MergeRequestDiff.transaction do
- merge_request_diff.update_column(:state, 'without_files')
-
- # explain (analyze, buffers) when deleting 453 diff files:
- #
- # Delete on merge_request_diff_files (cost=0.57..8487.35 rows=4846 width=6) (actual time=43.265..43.265 rows=0 loops=1)
- # Buffers: shared hit=2043 read=259 dirtied=254
- # -> Index Scan using index_merge_request_diff_files_on_mr_diff_id_and_order on merge_request_diff_files (cost=0.57..8487.35 rows=4846 width=6) (actu
- # al time=0.466..26.317 rows=453 loops=1)
- # Index Cond: (merge_request_diff_id = 463448)
- # Buffers: shared hit=17 read=84
- # Planning time: 0.107 ms
- # Execution time: 43.287 ms
- #
- MergeRequestDiffFile.where(merge_request_diff_id: merge_request_diff.id).delete_all
+ class MergeRequestDiffFile < ActiveRecord::Base
+ self.table_name = 'merge_request_diff_files'
+ end
+
+ DEAD_TUPLES_THRESHOLD = 50_000
+ VACUUM_WAIT_TIME = 5.minutes
+
+ def perform(ids)
+ @ids = ids
+
+ # We should reschedule until deadtuples get in a desirable
+ # state (e.g. < 50_000). That may take more than one reschedule.
+ #
+ if should_wait_deadtuple_vacuum?
+ reschedule
+ return
end
+
+ prune_diff_files
+ end
+
+ def should_wait_deadtuple_vacuum?
+ return false unless Gitlab::Database.postgresql?
+
+ diff_files_dead_tuples_count >= DEAD_TUPLES_THRESHOLD
end
private
- def should_delete_diff_files?(merge_request_diff)
- return false if merge_request_diff.state == 'without_files'
+ def reschedule
+ BackgroundMigrationWorker.perform_in(VACUUM_WAIT_TIME, self.class.name.demodulize, [@ids])
+ end
+
+ def diffs_collection
+ MergeRequestDiff.where(id: @ids)
+ end
+
+ def diff_files_dead_tuples_count
+ dead_tuple =
+ execute_statement("SELECT n_dead_tup FROM pg_stat_all_tables "\
+ "WHERE relname = 'merge_request_diff_files'")[0]
- merge_request = merge_request_diff.merge_request
+ dead_tuple&.fetch('n_dead_tup', 0).to_i
+ end
+
+ def prune_diff_files
+ removed = 0
+ updated = 0
- return false unless merge_request.state == 'merged'
- return false if merge_request_diff.id == merge_request.latest_merge_request_diff_id
+ MergeRequestDiff.transaction do
+ updated = diffs_collection.update_all(state: 'without_files')
+ removed = MergeRequestDiffFile.where(merge_request_diff_id: @ids).delete_all
+ end
+
+ log_info("Removed #{removed} merge_request_diff_files rows, "\
+ "updated #{updated} merge_request_diffs rows")
+ end
+
+ def execute_statement(sql)
+ ActiveRecord::Base.connection.execute(sql)
+ end
- true
+ def log_info(message)
+ Rails.logger.info("BackgroundMigration::DeleteDiffFiles - #{message}")
end
end
end
diff --git a/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb b/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb
index a357538a885..58df74cfa9b 100644
--- a/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb
+++ b/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
# rubocop:disable Metrics/MethodLength
-# rubocop:disable Metrics/LineLength
# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
diff --git a/lib/gitlab/background_migration/fill_file_store_job_artifact.rb b/lib/gitlab/background_migration/fill_file_store_job_artifact.rb
index 22b0ac71920..103bd98af14 100644
--- a/lib/gitlab/background_migration/fill_file_store_job_artifact.rb
+++ b/lib/gitlab/background_migration/fill_file_store_job_artifact.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/fill_file_store_lfs_object.rb b/lib/gitlab/background_migration/fill_file_store_lfs_object.rb
index d0816ae3ed5..77c1f1ffaf0 100644
--- a/lib/gitlab/background_migration/fill_file_store_lfs_object.rb
+++ b/lib/gitlab/background_migration/fill_file_store_lfs_object.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/fill_store_upload.rb b/lib/gitlab/background_migration/fill_store_upload.rb
index 94c65459a67..cba3e21cea6 100644
--- a/lib/gitlab/background_migration/fill_store_upload.rb
+++ b/lib/gitlab/background_migration/fill_store_upload.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/fix_cross_project_label_links.rb b/lib/gitlab/background_migration/fix_cross_project_label_links.rb
new file mode 100644
index 00000000000..0a12401c35f
--- /dev/null
+++ b/lib/gitlab/background_migration/fix_cross_project_label_links.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class FixCrossProjectLabelLinks
+ GROUP_NESTED_LEVEL = 10.freeze
+
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+ end
+
+ class Label < ActiveRecord::Base
+ self.inheritance_column = :_type_disabled
+ self.table_name = 'labels'
+ end
+
+ class LabelLink < ActiveRecord::Base
+ self.table_name = 'label_links'
+ end
+
+ class Issue < ActiveRecord::Base
+ self.table_name = 'issues'
+ end
+
+ class MergeRequest < ActiveRecord::Base
+ self.table_name = 'merge_requests'
+ end
+
+ class Namespace < ActiveRecord::Base
+ self.inheritance_column = :_type_disabled
+ self.table_name = 'namespaces'
+
+ def self.groups_with_descendants_ids(start_id, stop_id)
+ # To isolate migration code, we avoid usage of
+ # Gitlab::GroupHierarchy#base_and_descendants which already
+ # does this job better
+ ids = Namespace.where(type: 'Group', id: Label.where(type: 'GroupLabel').select('distinct group_id')).where(id: start_id..stop_id).pluck(:id)
+ group_ids = ids
+
+ GROUP_NESTED_LEVEL.times do
+ ids = Namespace.where(type: 'Group', parent_id: ids).pluck(:id)
+ break if ids.empty?
+
+ group_ids += ids
+ end
+
+ group_ids.uniq
+ end
+ end
+
+ def perform(start_id, stop_id)
+ group_ids = Namespace.groups_with_descendants_ids(start_id, stop_id)
+ project_ids = Project.where(namespace_id: group_ids).select(:id)
+
+ fix_issues(project_ids)
+ fix_merge_requests(project_ids)
+ end
+
+ private
+
+ # select IDs of issues which reference a label which is:
+ # a) a project label of a different project, or
+ # b) a group label of a different group than issue's project group
+ def fix_issues(project_ids)
+ issue_ids = Label
+ .joins('INNER JOIN label_links ON label_links.label_id = labels.id AND label_links.target_type = \'Issue\'
+ INNER JOIN issues ON issues.id = label_links.target_id
+ INNER JOIN projects ON projects.id = issues.project_id')
+ .where('issues.project_id in (?)', project_ids)
+ .where('(labels.project_id is not null and labels.project_id != issues.project_id) '\
+ 'or (labels.group_id is not null and labels.group_id != projects.namespace_id)')
+ .select('distinct issues.id')
+
+ Issue.where(id: issue_ids).find_each { |issue| check_resource_labels(issue, issue.project_id) }
+ end
+
+ # select IDs of MRs which reference a label which is:
+ # a) a project label of a different project, or
+ # b) a group label of a different group than MR's project group
+ def fix_merge_requests(project_ids)
+ mr_ids = Label
+ .joins('INNER JOIN label_links ON label_links.label_id = labels.id AND label_links.target_type = \'MergeRequest\'
+ INNER JOIN merge_requests ON merge_requests.id = label_links.target_id
+ INNER JOIN projects ON projects.id = merge_requests.target_project_id')
+ .where('merge_requests.target_project_id in (?)', project_ids)
+ .where('(labels.project_id is not null and labels.project_id != merge_requests.target_project_id) '\
+ 'or (labels.group_id is not null and labels.group_id != projects.namespace_id)')
+ .select('distinct merge_requests.id')
+
+ MergeRequest.where(id: mr_ids).find_each { |merge_request| check_resource_labels(merge_request, merge_request.target_project_id) }
+ end
+
+ def check_resource_labels(resource, project_id)
+ local_labels = available_labels(project_id)
+
+ # get all label links for the given resource (issue/MR)
+ # which reference a label not included in avaiable_labels
+ # (other than its project labels and labels of ancestor groups)
+ cross_labels = LabelLink
+ .select('label_id, labels.title as title, labels.color as color, label_links.id as label_link_id')
+ .joins('INNER JOIN labels ON labels.id = label_links.label_id')
+ .where(target_type: resource.class.name.demodulize, target_id: resource.id)
+ .where('labels.id not in (?)', local_labels.select(:id))
+
+ cross_labels.each do |label|
+ matching_label = local_labels.find {|l| l.title == label.title && l.color == label.color}
+
+ next unless matching_label
+
+ Rails.logger.info "#{resource.class.name.demodulize} #{resource.id}: replacing #{label.label_id} with #{matching_label.id}"
+ LabelLink.update(label.label_link_id, label_id: matching_label.id)
+ end
+ end
+
+ # get all labels available for the project (including
+ # group labels of ancestor groups)
+ def available_labels(project_id)
+ @labels ||= {}
+ @labels[project_id] ||= Label
+ .where("(type = 'GroupLabel' and group_id in (?)) or (type = 'ProjectLabel' and id = ?)",
+ project_group_ids(project_id),
+ project_id)
+ end
+
+ def project_group_ids(project_id)
+ ids = [Project.find(project_id).namespace_id]
+
+ GROUP_NESTED_LEVEL.times do
+ group = Namespace.find(ids.last)
+ break unless group.parent_id
+
+ ids << group.parent_id
+ end
+
+ ids
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/migrate_build_stage.rb b/lib/gitlab/background_migration/migrate_build_stage.rb
index 242e3143e71..268c6083d3c 100644
--- a/lib/gitlab/background_migration/migrate_build_stage.rb
+++ b/lib/gitlab/background_migration/migrate_build_stage.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb b/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb
index 7088aa0860a..38fecac1bfe 100644
--- a/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb
+++ b/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb b/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb
index 7f243073fd0..ef50fe4adb1 100644
--- a/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb
+++ b/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/move_personal_snippet_files.rb b/lib/gitlab/background_migration/move_personal_snippet_files.rb
index a4ef51fd0e8..5b2b2af718a 100644
--- a/lib/gitlab/background_migration/move_personal_snippet_files.rb
+++ b/lib/gitlab/background_migration/move_personal_snippet_files.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb b/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
index d9d3d2e667b..698f5e46c0c 100644
--- a/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
+++ b/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
# rubocop:disable Metrics/MethodLength
-# rubocop:disable Metrics/LineLength
# rubocop:disable Metrics/ClassLength
# rubocop:disable Metrics/BlockLength
# rubocop:disable Style/Documentation
diff --git a/lib/gitlab/background_migration/populate_fork_networks_range.rb b/lib/gitlab/background_migration/populate_fork_networks_range.rb
index a976cb4c243..aa4f130538c 100644
--- a/lib/gitlab/background_migration/populate_fork_networks_range.rb
+++ b/lib/gitlab/background_migration/populate_fork_networks_range.rb
@@ -19,7 +19,7 @@ module Gitlab
create_fork_networks_for_missing_projects(start_id, end_id)
create_fork_networks_memberships_for_root_projects(start_id, end_id)
- delay = BackgroundMigration::CreateForkNetworkMembershipsRange::RESCHEDULE_DELAY # rubocop:disable Metrics/LineLength
+ delay = BackgroundMigration::CreateForkNetworkMembershipsRange::RESCHEDULE_DELAY
BackgroundMigrationWorker.perform_in(
delay, "CreateForkNetworkMembershipsRange", [start_id, end_id]
)
diff --git a/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb b/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb
index 8a901a9bf39..d89ce358bb9 100644
--- a/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb
+++ b/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb
@@ -1,7 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
-# rubocop:disable Metrics/MethodLength
-# rubocop:disable Metrics/ClassLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/populate_untracked_uploads.rb b/lib/gitlab/background_migration/populate_untracked_uploads.rb
index 9232f20a063..a19dc9747fb 100644
--- a/lib/gitlab/background_migration/populate_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/populate_untracked_uploads.rb
@@ -4,7 +4,7 @@ module Gitlab
module BackgroundMigration
# This class processes a batch of rows in `untracked_files_for_uploads` by
# adding each file to the `uploads` table if it does not exist.
- class PopulateUntrackedUploads # rubocop:disable Metrics/ClassLength
+ class PopulateUntrackedUploads
def perform(start_id, end_id)
return unless migrate?
diff --git a/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
index a2c5acbde71..4a9a62aaeb5 100644
--- a/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
+++ b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
@@ -4,7 +4,7 @@ module Gitlab
module PopulateUntrackedUploadsDependencies
# This class is responsible for producing the attributes necessary to
# track an uploaded file in the `uploads` table.
- class UntrackedFile < ActiveRecord::Base # rubocop:disable Metrics/ClassLength, Metrics/LineLength
+ class UntrackedFile < ActiveRecord::Base # rubocop:disable Metrics/ClassLength
self.table_name = 'untracked_files_for_uploads'
# Ends with /:random_hex/:filename
@@ -134,7 +134,7 @@ module Gitlab
# Not including a leading slash
def path_relative_to_upload_dir
- upload_dir = Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR # rubocop:disable Metrics/LineLength
+ upload_dir = Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR
base = %r{\A#{Regexp.escape(upload_dir)}/}
@path_relative_to_upload_dir ||= path.sub(base, '')
end
diff --git a/lib/gitlab/background_migration/prepare_untracked_uploads.rb b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
index 522c69a0bb1..81ca2b0a9b7 100644
--- a/lib/gitlab/background_migration/prepare_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
@@ -144,7 +144,7 @@ module Gitlab
def table_columns_and_values_for_insert(file_paths)
values = file_paths.map do |file_path|
- ActiveRecord::Base.send(:sanitize_sql_array, ['(?)', file_path]) # rubocop:disable GitlabSecurity/PublicSend, Metrics/LineLength
+ ActiveRecord::Base.send(:sanitize_sql_array, ['(?)', file_path]) # rubocop:disable GitlabSecurity/PublicSend
end.join(', ')
"#{UntrackedFile.table_name} (path) VALUES #{values}"
diff --git a/lib/gitlab/background_migration/remove_restricted_todos.rb b/lib/gitlab/background_migration/remove_restricted_todos.rb
new file mode 100644
index 00000000000..68f3fa62170
--- /dev/null
+++ b/lib/gitlab/background_migration/remove_restricted_todos.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class RemoveRestrictedTodos
+ PRIVATE_FEATURE = 10
+ PRIVATE_PROJECT = 0
+
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+ end
+
+ class ProjectAuthorization < ActiveRecord::Base
+ self.table_name = 'project_authorizations'
+ end
+
+ class ProjectFeature < ActiveRecord::Base
+ self.table_name = 'project_features'
+ end
+
+ class Todo < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'todos'
+ end
+
+ class Issue < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'issues'
+ end
+
+ def perform(start_id, stop_id)
+ projects = Project.where('EXISTS (SELECT 1 FROM todos WHERE todos.project_id = projects.id)')
+ .where(id: start_id..stop_id)
+
+ projects.each do |project|
+ remove_confidential_issue_todos(project.id)
+
+ if project.visibility_level == PRIVATE_PROJECT
+ remove_non_members_todos(project.id)
+ else
+ remove_restricted_features_todos(project.id)
+ end
+ end
+ end
+
+ private
+
+ def remove_non_members_todos(project_id)
+ Todo.where(project_id: project_id)
+ .where('user_id NOT IN (?)', authorized_users(project_id))
+ .each_batch(of: 5000) do |batch|
+ batch.delete_all
+ end
+ end
+
+ def remove_confidential_issue_todos(project_id)
+ # min access level to access a confidential issue is reporter
+ min_reporters = authorized_users(project_id)
+ .select(:user_id)
+ .where('access_level >= ?', 20)
+
+ confidential_issues = Issue.select(:id, :author_id).where(confidential: true, project_id: project_id)
+ confidential_issues.each_batch(of: 100) do |batch|
+ batch.each do |issue|
+ assigned_users = IssueAssignee.select(:user_id).where(issue_id: issue.id)
+
+ todos = Todo.where(target_type: 'Issue', target_id: issue.id)
+ .where('user_id NOT IN (?)', min_reporters)
+ .where('user_id NOT IN (?)', assigned_users)
+ todos = todos.where('user_id != ?', issue.author_id) if issue.author_id
+
+ todos.delete_all
+ end
+ end
+ end
+
+ def remove_restricted_features_todos(project_id)
+ ProjectFeature.where(project_id: project_id).each do |project_features|
+ target_types = []
+ target_types << 'Issue' if private?(project_features.issues_access_level)
+ target_types << 'MergeRequest' if private?(project_features.merge_requests_access_level)
+ target_types << 'Commit' if private?(project_features.repository_access_level)
+
+ next if target_types.empty?
+
+ Todo.where(project_id: project_id)
+ .where('user_id NOT IN (?)', authorized_users(project_id))
+ .where(target_type: target_types)
+ .delete_all
+ end
+ end
+
+ def private?(feature_level)
+ feature_level == PRIVATE_FEATURE
+ end
+
+ def authorized_users(project_id)
+ ProjectAuthorization.select(:user_id).where(project_id: project_id)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/schedule_diff_files_deletion.rb b/lib/gitlab/background_migration/schedule_diff_files_deletion.rb
new file mode 100644
index 00000000000..609cf19187c
--- /dev/null
+++ b/lib/gitlab/background_migration/schedule_diff_files_deletion.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class ScheduleDiffFilesDeletion
+ class MergeRequestDiff < ActiveRecord::Base
+ self.table_name = 'merge_request_diffs'
+
+ belongs_to :merge_request
+
+ include EachBatch
+ end
+
+ DIFF_BATCH_SIZE = 5_000
+ INTERVAL = 5.minutes
+ MIGRATION = 'DeleteDiffFiles'
+
+ def perform
+ diffs = MergeRequestDiff
+ .from("(#{diffs_collection.to_sql}) merge_request_diffs")
+ .where('merge_request_diffs.id != merge_request_diffs.latest_merge_request_diff_id')
+ .select(:id)
+
+ diffs.each_batch(of: DIFF_BATCH_SIZE) do |relation, index|
+ ids = relation.pluck(:id)
+
+ BackgroundMigrationWorker.perform_in(index * INTERVAL, MIGRATION, [ids])
+ end
+ end
+
+ private
+
+ def diffs_collection
+ MergeRequestDiff
+ .joins(:merge_request)
+ .where("merge_requests.state = 'merged'")
+ .where('merge_requests.latest_merge_request_diff_id IS NOT NULL')
+ .where("merge_request_diffs.state NOT IN ('without_files', 'empty')")
+ .select('merge_requests.latest_merge_request_diff_id, merge_request_diffs.id')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/bare_repository_import/importer.rb b/lib/gitlab/bare_repository_import/importer.rb
index 4ca5a78e068..04aa6aab771 100644
--- a/lib/gitlab/bare_repository_import/importer.rb
+++ b/lib/gitlab/bare_repository_import/importer.rb
@@ -26,6 +26,12 @@ module Gitlab
end
end
+ # This is called from within a rake task only used by Admins, so allow writing
+ # to STDOUT
+ def self.log(message)
+ puts message # rubocop:disable Rails/Output
+ end
+
attr_reader :user, :project_name, :bare_repo
delegate :log, to: :class
@@ -59,11 +65,10 @@ module Gitlab
import_type: 'bare_repository',
namespace_id: group&.id).execute
- if project.persisted? && mv_repo(project)
+ if project.persisted? && mv_repositories(project)
log " * Created #{project.name} (#{project_full_path})".color(:green)
project.write_repository_config
- Gitlab::Git::Repository.create_hooks(project.repository.path_to_repo, Gitlab.config.gitlab_shell.hooks_path)
ProjectCacheWorker.perform_async(project.id)
else
@@ -74,12 +79,11 @@ module Gitlab
project
end
- def mv_repo(project)
- storage_path = storage_path_for_shard(project.repository_storage)
- FileUtils.mv(repo_path, project.repository.path_to_repo)
+ def mv_repositories(project)
+ mv_repo(bare_repo.repo_path, project.repository)
if bare_repo.wiki_exists?
- FileUtils.mv(wiki_path, File.join(storage_path, project.disk_path + '.wiki.git'))
+ mv_repo(bare_repo.wiki_path, project.wiki.repository)
end
true
@@ -89,6 +93,11 @@ module Gitlab
false
end
+ def mv_repo(path, repository)
+ repository.create_from_bundle(bundle(path))
+ FileUtils.rm_rf(path)
+ end
+
def storage_path_for_shard(shard)
Gitlab.config.repositories.storages[shard].legacy_disk_path
end
@@ -101,10 +110,17 @@ module Gitlab
Groups::NestedCreateService.new(user, group_path: group_path).execute
end
- # This is called from within a rake task only used by Admins, so allow writing
- # to STDOUT
- def self.log(message)
- puts message # rubocop:disable Rails/Output
+ def bundle(repo_path)
+ # TODO: we could save some time and disk space by using
+ # `git bundle create - --all` and streaming the bundle directly to
+ # Gitaly, rather than writing it on disk first
+ bundle_path = "#{repo_path}.bundle"
+ cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} bundle create #{bundle_path} --all)
+ output, status = Gitlab::Popen.popen(cmd)
+
+ raise output unless status.zero?
+
+ bundle_path
end
end
end
diff --git a/lib/gitlab/bare_repository_import/repository.rb b/lib/gitlab/bare_repository_import/repository.rb
index fe267248275..c0c666dfb7b 100644
--- a/lib/gitlab/bare_repository_import/repository.rb
+++ b/lib/gitlab/bare_repository_import/repository.rb
@@ -1,5 +1,3 @@
-# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/953
-#
module Gitlab
module BareRepositoryImport
class Repository
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index f3999e690fa..fa0186c854c 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -188,7 +188,8 @@ module Gitlab
end
def import_inline_comments(inline_comments, pull_request, merge_request)
- line_code_map = {}
+ position_map = {}
+ discussion_map = {}
children, parents = inline_comments.partition(&:has_parent?)
@@ -196,22 +197,28 @@ module Gitlab
# relationships. We assume that the child can appear in any order in
# the JSON.
parents.each do |comment|
- line_code_map[comment.iid] = generate_line_code(comment)
+ position_map[comment.iid] = build_position(merge_request, comment)
end
children.each do |comment|
- line_code_map[comment.iid] = line_code_map.fetch(comment.parent_id, nil)
+ position_map[comment.iid] = position_map.fetch(comment.parent_id, nil)
end
inline_comments.each do |comment|
begin
attributes = pull_request_comment_attributes(comment)
+ attributes[:discussion_id] = discussion_map[comment.parent_id] if comment.has_parent?
+
attributes.merge!(
- position: build_position(merge_request, comment),
- line_code: line_code_map.fetch(comment.iid),
+ position: position_map[comment.iid],
type: 'DiffNote')
- merge_request.notes.create!(attributes)
+ note = merge_request.notes.create!(attributes)
+
+ # We can't store a discussion ID until a note is created, so if
+ # replies are created before the parent the discussion ID won't be
+ # linked properly.
+ discussion_map[comment.iid] = note.discussion_id
rescue StandardError => e
errors << { type: :pull_request, iid: comment.iid, errors: e.message }
end
@@ -240,10 +247,6 @@ module Gitlab
end
end
- def generate_line_code(pr_comment)
- Gitlab::Git.diff_line_code(pr_comment.file_path, pr_comment.new_pos, pr_comment.old_pos)
- end
-
def pull_request_comment_attributes(comment)
{
project: project,
diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb
new file mode 100644
index 00000000000..268d21a77d1
--- /dev/null
+++ b/lib/gitlab/bitbucket_server_import/importer.rb
@@ -0,0 +1,327 @@
+module Gitlab
+ module BitbucketServerImport
+ class Importer
+ include Gitlab::ShellAdapter
+ attr_reader :recover_missing_commits
+ attr_reader :project, :project_key, :repository_slug, :client, :errors, :users
+
+ REMOTE_NAME = 'bitbucket_server'.freeze
+ BATCH_SIZE = 100
+
+ TempBranch = Struct.new(:name, :sha)
+
+ def self.imports_repository?
+ true
+ end
+
+ def self.refmap
+ [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head']
+ end
+
+ # Unlike GitHub, you can't grab the commit SHAs for pull requests that
+ # have been closed but not merged even though Bitbucket has these
+ # commits internally. We can recover these pull requests by creating a
+ # branch with the Bitbucket REST API, but by default we turn this
+ # behavior off.
+ def initialize(project, recover_missing_commits: false)
+ @project = project
+ @recover_missing_commits = recover_missing_commits
+ @project_key = project.import_data.data['project_key']
+ @repository_slug = project.import_data.data['repo_slug']
+ @client = BitbucketServer::Client.new(project.import_data.credentials)
+ @formatter = Gitlab::ImportFormatter.new
+ @errors = []
+ @users = {}
+ @temp_branches = []
+ end
+
+ def execute
+ import_repository
+ import_pull_requests
+ delete_temp_branches
+ handle_errors
+
+ true
+ end
+
+ private
+
+ def handle_errors
+ return unless errors.any?
+
+ project.update_column(:import_error, {
+ message: 'The remote data could not be fully imported.',
+ errors: errors
+ }.to_json)
+ end
+
+ def gitlab_user_id(email)
+ find_user_id(email) || project.creator_id
+ end
+
+ def find_user_id(email)
+ return nil unless email
+
+ return users[email] if users.key?(email)
+
+ user = User.find_by_any_email(email, confirmed: true)
+ users[email] = user&.id
+
+ user&.id
+ end
+
+ def repo
+ @repo ||= client.repo(project_key, repository_slug)
+ end
+
+ def sha_exists?(sha)
+ project.repository.commit(sha)
+ end
+
+ def temp_branch_name(pull_request, suffix)
+ "gitlab/import/pull-request/#{pull_request.iid}/#{suffix}"
+ end
+
+ # This method restores required SHAs that GitLab needs to create diffs
+ # into branch names as the following:
+ #
+ # gitlab/import/pull-request/N/{to,from}
+ def restore_branches(pull_requests)
+ shas_to_restore = []
+
+ pull_requests.each do |pull_request|
+ shas_to_restore << TempBranch.new(temp_branch_name(pull_request, :from),
+ pull_request.source_branch_sha)
+ shas_to_restore << TempBranch.new(temp_branch_name(pull_request, :to),
+ pull_request.target_branch_sha)
+ end
+
+ # Create the branches on the Bitbucket Server first
+ created_branches = restore_branch_shas(shas_to_restore)
+
+ @temp_branches += created_branches
+ # Now sync the repository so we get the new branches
+ import_repository unless created_branches.empty?
+ end
+
+ def restore_branch_shas(shas_to_restore)
+ shas_to_restore.each_with_object([]) do |temp_branch, branches_created|
+ branch_name = temp_branch.name
+ sha = temp_branch.sha
+
+ next if sha_exists?(sha)
+
+ begin
+ client.create_branch(project_key, repository_slug, branch_name, sha)
+ branches_created << temp_branch
+ rescue BitbucketServer::Connection::ConnectionError => e
+ Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{e}")
+ end
+ end
+ end
+
+ def import_repository
+ project.ensure_repository
+ project.repository.fetch_as_mirror(project.import_url, refmap: self.class.refmap, remote_name: REMOTE_NAME)
+ rescue Gitlab::Shell::Error, Gitlab::Git::RepositoryMirroring::RemoteError => e
+ # Expire cache to prevent scenarios such as:
+ # 1. First import failed, but the repo was imported successfully, so +exists?+ returns true
+ # 2. Retried import, repo is broken or not imported but +exists?+ still returns true
+ project.repository.expire_content_cache if project.repository_exists?
+
+ raise e.message
+ end
+
+ # Bitbucket Server keeps tracks of references for open pull requests in
+ # refs/heads/pull-requests, but closed and merged requests get moved
+ # into hidden internal refs under stash-refs/pull-requests. Unless the
+ # SHAs involved are at the tip of a branch or tag, there is no way to
+ # retrieve the server for those commits.
+ #
+ # To avoid losing history, we use the Bitbucket API to re-create the branch
+ # on the remote server. Then we have to issue a `git fetch` to download these
+ # branches.
+ def import_pull_requests
+ pull_requests = client.pull_requests(project_key, repository_slug).to_a
+
+ # Creating branches on the server and fetching the newly-created branches
+ # may take a number of network round-trips. Do this in batches so that we can
+ # avoid doing a git fetch for every new branch.
+ pull_requests.each_slice(BATCH_SIZE) do |batch|
+ restore_branches(batch) if recover_missing_commits
+
+ batch.each do |pull_request|
+ begin
+ import_bitbucket_pull_request(pull_request)
+ rescue StandardError => e
+ errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw }
+ end
+ end
+ end
+ end
+
+ def delete_temp_branches
+ @temp_branches.each do |branch|
+ begin
+ client.delete_branch(project_key, repository_slug, branch.name, branch.sha)
+ project.repository.delete_branch(branch.name)
+ rescue BitbucketServer::Connection::ConnectionError => e
+ @errors << { type: :delete_temp_branches, branch_name: branch.name, errors: e.message }
+ end
+ end
+ end
+
+ def import_bitbucket_pull_request(pull_request)
+ description = ''
+ description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email)
+ description += pull_request.description if pull_request.description
+
+ source_branch_sha = pull_request.source_branch_sha
+ target_branch_sha = pull_request.target_branch_sha
+ author_id = gitlab_user_id(pull_request.author_email)
+
+ attributes = {
+ iid: pull_request.iid,
+ title: pull_request.title,
+ description: description,
+ source_project: project,
+ source_branch: Gitlab::Git.ref_name(pull_request.source_branch_name),
+ source_branch_sha: source_branch_sha,
+ target_project: project,
+ target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name),
+ target_branch_sha: target_branch_sha,
+ state: pull_request.state,
+ author_id: author_id,
+ assignee_id: nil,
+ created_at: pull_request.created_at,
+ updated_at: pull_request.updated_at
+ }
+
+ merge_request = project.merge_requests.create!(attributes)
+ import_pull_request_comments(pull_request, merge_request) if merge_request.persisted?
+ end
+
+ def import_pull_request_comments(pull_request, merge_request)
+ comments, other_activities = client.activities(project_key, repository_slug, pull_request.iid).partition(&:comment?)
+
+ merge_event = other_activities.find(&:merge_event?)
+ import_merge_event(merge_request, merge_event) if merge_event
+
+ inline_comments, pr_comments = comments.partition(&:inline_comment?)
+
+ import_inline_comments(inline_comments.map(&:comment), merge_request)
+ import_standalone_pr_comments(pr_comments.map(&:comment), merge_request)
+ end
+
+ def import_merge_event(merge_request, merge_event)
+ committer = merge_event.committer_email
+
+ user_id = gitlab_user_id(committer)
+ timestamp = merge_event.merge_timestamp
+ merge_request.update({ merge_commit_sha: merge_event.merge_commit })
+ metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request)
+ metric.update(merged_by_id: user_id, merged_at: timestamp)
+ end
+
+ def import_inline_comments(inline_comments, merge_request)
+ inline_comments.each do |comment|
+ position = build_position(merge_request, comment)
+ parent = create_diff_note(merge_request, comment, position)
+
+ next unless parent&.persisted?
+
+ discussion_id = parent.discussion_id
+
+ comment.comments.each do |reply|
+ create_diff_note(merge_request, reply, position, discussion_id)
+ end
+ end
+ end
+
+ def create_diff_note(merge_request, comment, position, discussion_id = nil)
+ attributes = pull_request_comment_attributes(comment)
+ attributes.merge!(position: position, type: 'DiffNote')
+ attributes[:discussion_id] = discussion_id if discussion_id
+
+ note = merge_request.notes.build(attributes)
+
+ if note.valid?
+ note.save
+ return note
+ end
+
+ # Bitbucket Server supports the ability to comment on any line, not just the
+ # line in the diff. If we can't add the note as a DiffNote, fallback to creating
+ # a regular note.
+ create_fallback_diff_note(merge_request, comment, position)
+ rescue StandardError => e
+ errors << { type: :pull_request, id: comment.id, errors: e.message }
+ nil
+ end
+
+ def create_fallback_diff_note(merge_request, comment, position)
+ attributes = pull_request_comment_attributes(comment)
+ note = "*Comment on"
+
+ note += " #{position.old_path}:#{position.old_line} -->" if position.old_line
+ note += " #{position.new_path}:#{position.new_line}" if position.new_line
+ note += "*\n\n#{comment.note}"
+
+ attributes[:note] = note
+ merge_request.notes.create!(attributes)
+ end
+
+ def build_position(merge_request, pr_comment)
+ params = {
+ diff_refs: merge_request.diff_refs,
+ old_path: pr_comment.file_path,
+ new_path: pr_comment.file_path,
+ old_line: pr_comment.old_pos,
+ new_line: pr_comment.new_pos
+ }
+
+ Gitlab::Diff::Position.new(params)
+ end
+
+ def import_standalone_pr_comments(pr_comments, merge_request)
+ pr_comments.each do |comment|
+ begin
+ merge_request.notes.create!(pull_request_comment_attributes(comment))
+
+ comment.comments.each do |replies|
+ merge_request.notes.create!(pull_request_comment_attributes(replies))
+ end
+ rescue StandardError => e
+ errors << { type: :pull_request, iid: comment.id, errors: e.message }
+ end
+ end
+ end
+
+ def pull_request_comment_attributes(comment)
+ author = find_user_id(comment.author_email)
+ note = ''
+
+ unless author
+ author = project.creator_id
+ note = "*By #{comment.author_username} (#{comment.author_email})*\n\n"
+ end
+
+ note +=
+ # Provide some context for replying
+ if comment.parent_comment
+ "> #{comment.parent_comment.note.truncate(80)}\n\n#{comment.note}"
+ else
+ comment.note
+ end
+
+ {
+ project: project,
+ note: note,
+ author_id: author,
+ created_at: comment.created_at,
+ updated_at: comment.updated_at
+ }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/bitbucket_server_import/project_creator.rb b/lib/gitlab/bitbucket_server_import/project_creator.rb
new file mode 100644
index 00000000000..35e8cd7e0ab
--- /dev/null
+++ b/lib/gitlab/bitbucket_server_import/project_creator.rb
@@ -0,0 +1,36 @@
+module Gitlab
+ module BitbucketServerImport
+ class ProjectCreator
+ attr_reader :project_key, :repo_slug, :repo, :name, :namespace, :current_user, :session_data
+
+ def initialize(project_key, repo_slug, repo, name, namespace, current_user, session_data)
+ @project_key = project_key
+ @repo_slug = repo_slug
+ @repo = repo
+ @name = name
+ @namespace = namespace
+ @current_user = current_user
+ @session_data = session_data
+ end
+
+ def execute
+ ::Projects::CreateService.new(
+ current_user,
+ name: name,
+ path: name,
+ description: repo.description,
+ namespace_id: namespace.id,
+ visibility_level: repo.visibility_level,
+ import_type: 'bitbucket_server',
+ import_source: repo.browse_url,
+ import_url: repo.clone_url,
+ import_data: {
+ credentials: session_data,
+ data: { project_key: project_key, repo_slug: repo_slug }
+ },
+ skip_wiki: true
+ ).execute
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index f76a6fb5f17..7a4224e5bbe 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -93,7 +93,7 @@ module Gitlab
end
else
unless user_access.can_push_to_branch?(branch_name)
- raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:push_protected_branch]
+ raise GitAccess::UnauthorizedError, push_to_protected_branch_rejected_message
end
end
end
@@ -140,6 +140,29 @@ module Gitlab
private
+ def push_to_protected_branch_rejected_message
+ if project.empty_repo?
+ empty_project_push_message
+ else
+ ERROR_MESSAGES[:push_protected_branch]
+ end
+ end
+
+ def empty_project_push_message
+ <<~MESSAGE
+
+ A default branch (e.g. master) does not yet exist for #{project.full_path}
+ Ask a project Owner or Maintainer to create a default branch:
+
+ #{project_members_url}
+
+ MESSAGE
+ end
+
+ def project_members_url
+ Gitlab::Routing.url_helpers.project_project_members_url(project)
+ end
+
def should_run_commit_validations?
commit_check.validate_lfs_file_locks?
end
diff --git a/lib/gitlab/checks/lfs_integrity.rb b/lib/gitlab/checks/lfs_integrity.rb
index f0e5773ec3c..b816a8f00cd 100644
--- a/lib/gitlab/checks/lfs_integrity.rb
+++ b/lib/gitlab/checks/lfs_integrity.rb
@@ -1,8 +1,6 @@
module Gitlab
module Checks
class LfsIntegrity
- REV_LIST_OBJECT_LIMIT = 2_000
-
def initialize(project, newrev)
@project = project
@newrev = newrev
@@ -11,7 +9,8 @@ module Gitlab
def objects_missing?
return false unless @newrev && @project.lfs_enabled?
- new_lfs_pointers = Gitlab::Git::LfsChanges.new(@project.repository, @newrev).new_pointers(object_limit: REV_LIST_OBJECT_LIMIT)
+ new_lfs_pointers = Gitlab::Git::LfsChanges.new(@project.repository, @newrev)
+ .new_pointers(object_limit: ::Gitlab::Git::Repository::REV_LIST_COMMIT_LIMIT)
return false unless new_lfs_pointers.present?
diff --git a/lib/gitlab/ci/ansi2html.rb b/lib/gitlab/ci/ansi2html.rb
index 35eadf6fa93..e780f8c646b 100644
--- a/lib/gitlab/ci/ansi2html.rb
+++ b/lib/gitlab/ci/ansi2html.rb
@@ -29,105 +29,105 @@ module Gitlab
end
class Converter
- def on_0(s) reset() end
+ def on_0(_) reset() end
- def on_1(s) enable(STYLE_SWITCHES[:bold]) end
+ def on_1(_) enable(STYLE_SWITCHES[:bold]) end
- def on_3(s) enable(STYLE_SWITCHES[:italic]) end
+ def on_3(_) enable(STYLE_SWITCHES[:italic]) end
- def on_4(s) enable(STYLE_SWITCHES[:underline]) end
+ def on_4(_) enable(STYLE_SWITCHES[:underline]) end
- def on_8(s) enable(STYLE_SWITCHES[:conceal]) end
+ def on_8(_) enable(STYLE_SWITCHES[:conceal]) end
- def on_9(s) enable(STYLE_SWITCHES[:cross]) end
+ def on_9(_) enable(STYLE_SWITCHES[:cross]) end
- def on_21(s) disable(STYLE_SWITCHES[:bold]) end
+ def on_21(_) disable(STYLE_SWITCHES[:bold]) end
- def on_22(s) disable(STYLE_SWITCHES[:bold]) end
+ def on_22(_) disable(STYLE_SWITCHES[:bold]) end
- def on_23(s) disable(STYLE_SWITCHES[:italic]) end
+ def on_23(_) disable(STYLE_SWITCHES[:italic]) end
- def on_24(s) disable(STYLE_SWITCHES[:underline]) end
+ def on_24(_) disable(STYLE_SWITCHES[:underline]) end
- def on_28(s) disable(STYLE_SWITCHES[:conceal]) end
+ def on_28(_) disable(STYLE_SWITCHES[:conceal]) end
- def on_29(s) disable(STYLE_SWITCHES[:cross]) end
+ def on_29(_) disable(STYLE_SWITCHES[:cross]) end
- def on_30(s) set_fg_color(0) end
+ def on_30(_) set_fg_color(0) end
- def on_31(s) set_fg_color(1) end
+ def on_31(_) set_fg_color(1) end
- def on_32(s) set_fg_color(2) end
+ def on_32(_) set_fg_color(2) end
- def on_33(s) set_fg_color(3) end
+ def on_33(_) set_fg_color(3) end
- def on_34(s) set_fg_color(4) end
+ def on_34(_) set_fg_color(4) end
- def on_35(s) set_fg_color(5) end
+ def on_35(_) set_fg_color(5) end
- def on_36(s) set_fg_color(6) end
+ def on_36(_) set_fg_color(6) end
- def on_37(s) set_fg_color(7) end
+ def on_37(_) set_fg_color(7) end
- def on_38(s) set_fg_color_256(s) end
+ def on_38(stack) set_fg_color_256(stack) end
- def on_39(s) set_fg_color(9) end
+ def on_39(_) set_fg_color(9) end
- def on_40(s) set_bg_color(0) end
+ def on_40(_) set_bg_color(0) end
- def on_41(s) set_bg_color(1) end
+ def on_41(_) set_bg_color(1) end
- def on_42(s) set_bg_color(2) end
+ def on_42(_) set_bg_color(2) end
- def on_43(s) set_bg_color(3) end
+ def on_43(_) set_bg_color(3) end
- def on_44(s) set_bg_color(4) end
+ def on_44(_) set_bg_color(4) end
- def on_45(s) set_bg_color(5) end
+ def on_45(_) set_bg_color(5) end
- def on_46(s) set_bg_color(6) end
+ def on_46(_) set_bg_color(6) end
- def on_47(s) set_bg_color(7) end
+ def on_47(_) set_bg_color(7) end
- def on_48(s) set_bg_color_256(s) end
+ def on_48(stack) set_bg_color_256(stack) end
- def on_49(s) set_bg_color(9) end
+ def on_49(_) set_bg_color(9) end
- def on_90(s) set_fg_color(0, 'l') end
+ def on_90(_) set_fg_color(0, 'l') end
- def on_91(s) set_fg_color(1, 'l') end
+ def on_91(_) set_fg_color(1, 'l') end
- def on_92(s) set_fg_color(2, 'l') end
+ def on_92(_) set_fg_color(2, 'l') end
- def on_93(s) set_fg_color(3, 'l') end
+ def on_93(_) set_fg_color(3, 'l') end
- def on_94(s) set_fg_color(4, 'l') end
+ def on_94(_) set_fg_color(4, 'l') end
- def on_95(s) set_fg_color(5, 'l') end
+ def on_95(_) set_fg_color(5, 'l') end
- def on_96(s) set_fg_color(6, 'l') end
+ def on_96(_) set_fg_color(6, 'l') end
- def on_97(s) set_fg_color(7, 'l') end
+ def on_97(_) set_fg_color(7, 'l') end
- def on_99(s) set_fg_color(9, 'l') end
+ def on_99(_) set_fg_color(9, 'l') end
- def on_100(s) set_bg_color(0, 'l') end
+ def on_100(_) set_bg_color(0, 'l') end
- def on_101(s) set_bg_color(1, 'l') end
+ def on_101(_) set_bg_color(1, 'l') end
- def on_102(s) set_bg_color(2, 'l') end
+ def on_102(_) set_bg_color(2, 'l') end
- def on_103(s) set_bg_color(3, 'l') end
+ def on_103(_) set_bg_color(3, 'l') end
- def on_104(s) set_bg_color(4, 'l') end
+ def on_104(_) set_bg_color(4, 'l') end
- def on_105(s) set_bg_color(5, 'l') end
+ def on_105(_) set_bg_color(5, 'l') end
- def on_106(s) set_bg_color(6, 'l') end
+ def on_106(_) set_bg_color(6, 'l') end
- def on_107(s) set_bg_color(7, 'l') end
+ def on_107(_) set_bg_color(7, 'l') end
- def on_109(s) set_bg_color(9, 'l') end
+ def on_109(_) set_bg_color(9, 'l') end
attr_accessor :offset, :n_open_tags, :fg_color, :bg_color, :style_mask
@@ -188,19 +188,19 @@ module Gitlab
)
end
- def handle_section(s)
- action = s[1]
- timestamp = s[2]
- section = s[3]
- line = s.matched()[0...-5] # strips \r\033[0K
+ def handle_section(scanner)
+ action = scanner[1]
+ timestamp = scanner[2]
+ section = scanner[3]
+ line = scanner.matched()[0...-5] # strips \r\033[0K
@out << %{<div class="hidden" data-action="#{action}" data-timestamp="#{timestamp}" data-section="#{section}">#{line}</div>}
end
- def handle_sequence(s)
- indicator = s[1]
- commands = s[2].split ';'
- terminator = s[3]
+ def handle_sequence(scanner)
+ indicator = scanner[1]
+ commands = scanner[2].split ';'
+ terminator = scanner[3]
# We are only interested in color and text style changes - triggered by
# sequences starting with '\e[' and ending with 'm'. Any other control
diff --git a/lib/gitlab/ci/build/artifacts/gzip_file_adapter.rb b/lib/gitlab/ci/build/artifacts/gzip_file_adapter.rb
new file mode 100644
index 00000000000..65f65cdce08
--- /dev/null
+++ b/lib/gitlab/ci/build/artifacts/gzip_file_adapter.rb
@@ -0,0 +1,46 @@
+module Gitlab
+ module Ci
+ module Build
+ module Artifacts
+ class GzipFileAdapter
+ attr_reader :stream
+
+ InvalidStreamError = Class.new(StandardError)
+
+ def initialize(stream)
+ raise InvalidStreamError, "Stream is required" unless stream
+
+ @stream = stream
+ end
+
+ def each_blob
+ stream.seek(0)
+
+ until stream.eof?
+ gzip(stream) do |gz|
+ yield gz.read, gz.orig_name
+ unused = gz.unused&.length.to_i
+ # pos has already reached to EOF at the moment
+ # We rewind the pos to the top of unused files
+ # to read next gzip stream, to support multistream archives
+ # https://golang.org/src/compress/gzip/gunzip.go#L117
+ stream.seek(-unused, IO::SEEK_CUR)
+ end
+ end
+ end
+
+ private
+
+ def gzip(stream, &block)
+ gz = Zlib::GzipReader.new(stream)
+ yield(gz)
+ rescue Zlib::Error => e
+ raise InvalidStreamError, e.message
+ ensure
+ gz&.finish
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
index 0bbd60d8ffe..375d8bc1ff5 100644
--- a/lib/gitlab/ci/build/artifacts/metadata.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -7,14 +7,15 @@ module Gitlab
module Artifacts
class Metadata
ParserError = Class.new(StandardError)
+ InvalidStreamError = Class.new(StandardError)
VERSION_PATTERN = /^[\w\s]+(\d+\.\d+\.\d+)/
INVALID_PATH_PATTERN = %r{(^\.?\.?/)|(/\.?\.?/)}
- attr_reader :file, :path, :full_version
+ attr_reader :stream, :path, :full_version
- def initialize(file, path, **opts)
- @file, @path, @opts = file, path, opts
+ def initialize(stream, path, **opts)
+ @stream, @path, @opts = stream, path, opts
@full_version = read_version
end
@@ -103,7 +104,17 @@ module Gitlab
end
def gzip(&block)
- Zlib::GzipReader.open(@file, &block)
+ raise InvalidStreamError, "Invalid stream" unless @stream
+
+ # restart gzip reading
+ @stream.seek(0)
+
+ gz = Zlib::GzipReader.new(@stream)
+ yield(gz)
+ rescue Zlib::Error => e
+ raise InvalidStreamError, e.message
+ ensure
+ gz&.finish
end
end
end
diff --git a/lib/gitlab/ci/config/entry/artifacts.rb b/lib/gitlab/ci/config/entry/artifacts.rb
index 8275aacee9b..e80f9d2e452 100644
--- a/lib/gitlab/ci/config/entry/artifacts.rb
+++ b/lib/gitlab/ci/config/entry/artifacts.rb
@@ -6,13 +6,16 @@ module Gitlab
# Entry that represents a configuration of job artifacts.
#
class Artifacts < Node
+ include Configurable
include Validatable
include Attributable
- ALLOWED_KEYS = %i[name untracked paths when expire_in].freeze
+ ALLOWED_KEYS = %i[name untracked paths reports when expire_in].freeze
attributes ALLOWED_KEYS
+ entry :reports, Entry::Reports, description: 'Report-type artifacts.'
+
validations do
validates :config, type: Hash
validates :config, allowed_keys: ALLOWED_KEYS
@@ -21,6 +24,7 @@ module Gitlab
validates :name, type: String
validates :untracked, boolean: true
validates :paths, array_of_strings: true
+ validates :reports, type: Hash
validates :when,
inclusion: { in: %w[on_success on_failure always],
message: 'should be on_success, on_failure ' \
@@ -28,6 +32,13 @@ module Gitlab
validates :expire_in, duration: true
end
end
+
+ helpers :reports
+
+ def value
+ @config[:reports] = reports_value if @config.key?(:reports)
+ @config
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/commands.rb b/lib/gitlab/ci/config/entry/commands.rb
index 65d19db249c..9f66f11be9b 100644
--- a/lib/gitlab/ci/config/entry/commands.rb
+++ b/lib/gitlab/ci/config/entry/commands.rb
@@ -9,18 +9,7 @@ module Gitlab
include Validatable
validations do
- include LegacyValidationHelpers
-
- validate do
- unless string_or_array_of_strings?(config)
- errors.add(:config,
- 'should be a string or an array of strings')
- end
- end
-
- def string_or_array_of_strings?(field)
- validate_string(field) || validate_array_of_strings(field)
- end
+ validates :config, array_of_strings_or_string: true
end
def value
diff --git a/lib/gitlab/ci/config/entry/configurable.rb b/lib/gitlab/ci/config/entry/configurable.rb
index db47c2f6185..7cddd2c7b7e 100644
--- a/lib/gitlab/ci/config/entry/configurable.rb
+++ b/lib/gitlab/ci/config/entry/configurable.rb
@@ -47,7 +47,7 @@ module Gitlab
Hash[(@nodes || {}).map { |key, factory| [key, factory.dup] }]
end
- private # rubocop:disable Lint/UselessAccessModifier
+ private
def entry(key, entry, metadata)
factory = Entry::Factory.new(entry)
diff --git a/lib/gitlab/ci/config/entry/reports.rb b/lib/gitlab/ci/config/entry/reports.rb
new file mode 100644
index 00000000000..5963f3eb90c
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/reports.rb
@@ -0,0 +1,32 @@
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a configuration of job artifacts.
+ #
+ class Reports < Node
+ include Validatable
+ include Attributable
+
+ ALLOWED_KEYS = %i[junit].freeze
+
+ attributes ALLOWED_KEYS
+
+ validations do
+ validates :config, type: Hash
+ validates :config, allowed_keys: ALLOWED_KEYS
+
+ with_options allow_nil: true do
+ validates :junit, array_of_strings_or_string: true
+ end
+ end
+
+ def value
+ @config.transform_values { |v| Array(v) }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/validators.rb b/lib/gitlab/ci/config/entry/validators.rb
index 55658900628..b3c889ee92f 100644
--- a/lib/gitlab/ci/config/entry/validators.rb
+++ b/lib/gitlab/ci/config/entry/validators.rb
@@ -130,6 +130,20 @@ module Gitlab
end
end
+ class ArrayOfStringsOrStringValidator < RegexpValidator
+ def validate_each(record, attribute, value)
+ unless validate_array_of_strings_or_string(value)
+ record.errors.add(attribute, 'should be an array of strings or a string')
+ end
+ end
+
+ private
+
+ def validate_array_of_strings_or_string(values)
+ validate_array_of_strings(values) || validate_string(values)
+ end
+ end
+
class TypeValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
type = options[:with]
diff --git a/lib/gitlab/ci/parsers.rb b/lib/gitlab/ci/parsers.rb
new file mode 100644
index 00000000000..a4eccc08dfc
--- /dev/null
+++ b/lib/gitlab/ci/parsers.rb
@@ -0,0 +1,9 @@
+module Gitlab
+ module Ci
+ module Parsers
+ def self.fabricate!(file_type)
+ "Gitlab::Ci::Parsers::#{file_type.classify}".constantize.new
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/parsers/junit.rb b/lib/gitlab/ci/parsers/junit.rb
new file mode 100644
index 00000000000..3c4668ec13b
--- /dev/null
+++ b/lib/gitlab/ci/parsers/junit.rb
@@ -0,0 +1,69 @@
+module Gitlab
+ module Ci
+ module Parsers
+ class Junit
+ attr_reader :data
+
+ JunitParserError = Class.new(StandardError)
+
+ def parse!(xml_data, test_suite)
+ @data = Hash.from_xml(xml_data)
+
+ each_suite do |testcases|
+ testcases.each do |testcase|
+ test_case = create_test_case(testcase)
+ test_suite.add_test_case(test_case)
+ end
+ end
+ rescue REXML::ParseException => e
+ raise JunitParserError, "XML parsing failed: #{e.message}"
+ rescue => e
+ raise JunitParserError, "JUnit parsing failed: #{e.message}"
+ end
+
+ private
+
+ def each_suite
+ testsuites.each do |testsuite|
+ yield testcases(testsuite)
+ end
+ end
+
+ def testsuites
+ if data['testsuites']
+ data['testsuites']['testsuite']
+ else
+ [data['testsuite']]
+ end
+ end
+
+ def testcases(testsuite)
+ if testsuite['testcase'].is_a?(Array)
+ testsuite['testcase']
+ else
+ [testsuite['testcase']]
+ end
+ end
+
+ def create_test_case(data)
+ if data['failure']
+ status = ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED
+ system_output = data['failure']
+ else
+ status = ::Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS
+ system_output = nil
+ end
+
+ ::Gitlab::Ci::Reports::TestCase.new(
+ classname: data['classname'],
+ name: data['name'],
+ file: data['file'],
+ execution_time: data['time'],
+ status: status,
+ system_output: system_output
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/reports/test_case.rb b/lib/gitlab/ci/reports/test_case.rb
new file mode 100644
index 00000000000..b4d08ed257f
--- /dev/null
+++ b/lib/gitlab/ci/reports/test_case.rb
@@ -0,0 +1,32 @@
+module Gitlab
+ module Ci
+ module Reports
+ class TestCase
+ STATUS_SUCCESS = 'success'.freeze
+ STATUS_FAILED = 'failed'.freeze
+ STATUS_SKIPPED = 'skipped'.freeze
+ STATUS_ERROR = 'error'.freeze
+ STATUS_TYPES = [STATUS_SUCCESS, STATUS_FAILED, STATUS_SKIPPED, STATUS_ERROR].freeze
+
+ attr_reader :name, :classname, :execution_time, :status, :file, :system_output, :stack_trace, :key
+
+ def initialize(name:, classname:, execution_time:, status:, file: nil, system_output: nil, stack_trace: nil)
+ @name = name
+ @classname = classname
+ @file = file
+ @execution_time = execution_time.to_f
+ @status = status
+ @system_output = system_output
+ @stack_trace = stack_trace
+ @key = sanitize_key_name("#{classname}_#{name}")
+ end
+
+ private
+
+ def sanitize_key_name(key)
+ key.gsub(/[^0-9A-Za-z]/, '-')
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/reports/test_reports.rb b/lib/gitlab/ci/reports/test_reports.rb
new file mode 100644
index 00000000000..c6e732e68eb
--- /dev/null
+++ b/lib/gitlab/ci/reports/test_reports.rb
@@ -0,0 +1,39 @@
+module Gitlab
+ module Ci
+ module Reports
+ class TestReports
+ attr_reader :test_suites
+
+ def initialize
+ @test_suites = {}
+ end
+
+ def get_suite(suite_name)
+ test_suites[suite_name] ||= TestSuite.new(suite_name)
+ end
+
+ def total_time
+ test_suites.values.sum(&:total_time)
+ end
+
+ def total_count
+ test_suites.values.sum(&:total_count)
+ end
+
+ def total_status
+ if failed_count > 0 || error_count > 0
+ TestCase::STATUS_FAILED
+ else
+ TestCase::STATUS_SUCCESS
+ end
+ end
+
+ TestCase::STATUS_TYPES.each do |status_type|
+ define_method("#{status_type}_count") do
+ test_suites.values.sum { |suite| suite.public_send("#{status_type}_count") } # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/reports/test_reports_comparer.rb b/lib/gitlab/ci/reports/test_reports_comparer.rb
new file mode 100644
index 00000000000..c0943f5a51a
--- /dev/null
+++ b/lib/gitlab/ci/reports/test_reports_comparer.rb
@@ -0,0 +1,38 @@
+module Gitlab
+ module Ci
+ module Reports
+ class TestReportsComparer
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :base_reports, :head_reports
+
+ def initialize(base_reports, head_reports)
+ @base_reports = base_reports || TestReports.new
+ @head_reports = head_reports
+ end
+
+ def suite_comparers
+ strong_memoize(:suite_comparers) do
+ head_reports.test_suites.map do |name, test_suite|
+ TestSuiteComparer.new(name, base_reports.get_suite(name), test_suite)
+ end
+ end
+ end
+
+ def total_status
+ if suite_comparers.any? { |suite| suite.total_status == TestCase::STATUS_FAILED }
+ TestCase::STATUS_FAILED
+ else
+ TestCase::STATUS_SUCCESS
+ end
+ end
+
+ %w(total_count resolved_count failed_count).each do |method|
+ define_method(method) do
+ suite_comparers.sum { |suite| suite.public_send(method) } # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/reports/test_suite.rb b/lib/gitlab/ci/reports/test_suite.rb
new file mode 100644
index 00000000000..b722d0ba735
--- /dev/null
+++ b/lib/gitlab/ci/reports/test_suite.rb
@@ -0,0 +1,54 @@
+module Gitlab
+ module Ci
+ module Reports
+ class TestSuite
+ attr_reader :name
+ attr_reader :test_cases
+ attr_reader :total_time
+
+ def initialize(name = nil)
+ @name = name
+ @test_cases = {}
+ @total_time = 0.0
+ @duplicate_cases = []
+ end
+
+ def add_test_case(test_case)
+ @duplicate_cases << test_case if existing_key?(test_case)
+
+ @test_cases[test_case.status] ||= {}
+ @test_cases[test_case.status][test_case.key] = test_case
+ @total_time += test_case.execution_time
+ end
+
+ def total_count
+ test_cases.values.sum(&:count)
+ end
+
+ def total_status
+ if failed_count > 0 || error_count > 0
+ TestCase::STATUS_FAILED
+ else
+ TestCase::STATUS_SUCCESS
+ end
+ end
+
+ TestCase::STATUS_TYPES.each do |status_type|
+ define_method("#{status_type}") do
+ test_cases[status_type] || {}
+ end
+
+ define_method("#{status_type}_count") do
+ test_cases[status_type]&.length.to_i
+ end
+ end
+
+ private
+
+ def existing_key?(test_case)
+ @test_cases[test_case.status]&.key?(test_case.key)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/reports/test_suite_comparer.rb b/lib/gitlab/ci/reports/test_suite_comparer.rb
new file mode 100644
index 00000000000..642aa593092
--- /dev/null
+++ b/lib/gitlab/ci/reports/test_suite_comparer.rb
@@ -0,0 +1,57 @@
+module Gitlab
+ module Ci
+ module Reports
+ class TestSuiteComparer
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :name, :base_suite, :head_suite
+
+ def initialize(name, base_suite, head_suite)
+ @name = name
+ @base_suite = base_suite || TestSuite.new
+ @head_suite = head_suite
+ end
+
+ def new_failures
+ strong_memoize(:new_failures) do
+ head_suite.failed.reject do |key, _|
+ base_suite.failed.include?(key)
+ end.values
+ end
+ end
+
+ def existing_failures
+ strong_memoize(:existing_failures) do
+ head_suite.failed.select do |key, _|
+ base_suite.failed.include?(key)
+ end.values
+ end
+ end
+
+ def resolved_failures
+ strong_memoize(:resolved_failures) do
+ head_suite.success.select do |key, _|
+ base_suite.failed.include?(key)
+ end.values
+ end
+ end
+
+ def total_count
+ head_suite.total_count
+ end
+
+ def total_status
+ head_suite.total_status
+ end
+
+ def resolved_count
+ resolved_failures.count
+ end
+
+ def failed_count
+ new_failures.count + existing_failures.count
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index 155f4fc1343..703f0b9217b 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -4,12 +4,13 @@ module Gitlab
module Build
class Failed < Status::Extended
REASONS = {
- 'unknown_failure' => 'unknown failure',
- 'script_failure' => 'script failure',
- 'api_failure' => 'API failure',
- 'stuck_or_timeout_failure' => 'stuck or timeout failure',
- 'runner_system_failure' => 'runner system failure',
- 'missing_dependency_failure' => 'missing dependency failure'
+ unknown_failure: 'unknown failure',
+ script_failure: 'script failure',
+ api_failure: 'API failure',
+ stuck_or_timeout_failure: 'stuck or timeout failure',
+ runner_system_failure: 'runner system failure',
+ missing_dependency_failure: 'missing dependency failure',
+ runner_unsupported: 'unsupported runner'
}.freeze
def status_tooltip
@@ -31,7 +32,11 @@ module Gitlab
end
def description
- "<br> (#{REASONS[subject.failure_reason]})"
+ "<br> (#{failure_reason_message})"
+ end
+
+ def failure_reason_message
+ REASONS.fetch(subject.failure_reason.to_sym)
end
end
end
diff --git a/lib/gitlab/ci/trace.rb b/lib/gitlab/ci/trace.rb
index a52d71225bb..93e219a21f9 100644
--- a/lib/gitlab/ci/trace.rb
+++ b/lib/gitlab/ci/trace.rb
@@ -6,6 +6,7 @@ module Gitlab
LEASE_TIMEOUT = 1.hour
ArchiveError = Class.new(StandardError)
+ AlreadyArchivedError = Class.new(StandardError)
attr_reader :job
@@ -81,7 +82,9 @@ module Gitlab
def write(mode)
stream = Gitlab::Ci::Trace::Stream.new do
- if current_path
+ if trace_artifact
+ raise AlreadyArchivedError, 'Could not write to the archived trace'
+ elsif current_path
File.open(current_path, mode)
elsif Feature.enabled?('ci_enable_live_trace')
Gitlab::Ci::Trace::ChunkedIO.new(job)
@@ -98,14 +101,17 @@ module Gitlab
end
def erase!
- trace_artifact&.destroy
-
- paths.each do |trace_path|
- FileUtils.rm(trace_path, force: true)
- end
-
- job.trace_chunks.fast_destroy_all
- job.erase_old_trace!
+ ##
+ # Erase the archived trace
+ trace_artifact&.destroy!
+
+ ##
+ # Erase the live trace
+ job.trace_chunks.fast_destroy_all # Destroy chunks of a live trace
+ FileUtils.rm_f(current_path) if current_path # Remove a trace file of a live trace
+ job.erase_old_trace! if job.has_old_trace? # Remove a trace in database of a live trace
+ ensure
+ @current_path = nil
end
def archive!
@@ -117,7 +123,7 @@ module Gitlab
private
def unsafe_archive!
- raise ArchiveError, 'Already archived' if trace_artifact
+ raise AlreadyArchivedError, 'Could not archive again' if trace_artifact
raise ArchiveError, 'Job is not finished yet' unless job.complete?
if job.trace_chunks.any?
@@ -158,6 +164,8 @@ module Gitlab
def create_build_trace!(job, path)
File.open(path) do |stream|
+ # TODO: Set `file_format: :raw` after we've cleaned up legacy traces migration
+ # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20307
job.create_job_artifacts_trace!(
project: job.project,
file_type: :trace,
diff --git a/lib/gitlab/ci/trace/http_io.rb b/lib/gitlab/ci/trace/http_io.rb
deleted file mode 100644
index 8788af57a67..00000000000
--- a/lib/gitlab/ci/trace/http_io.rb
+++ /dev/null
@@ -1,197 +0,0 @@
-##
-# This class is compatible with IO class (https://ruby-doc.org/core-2.3.1/IO.html)
-# source: https://gitlab.com/snippets/1685610
-module Gitlab
- module Ci
- class Trace
- class HttpIO
- BUFFER_SIZE = 128.kilobytes
-
- InvalidURLError = Class.new(StandardError)
- FailedToGetChunkError = Class.new(StandardError)
-
- attr_reader :uri, :size
- attr_reader :tell
- attr_reader :chunk, :chunk_range
-
- alias_method :pos, :tell
-
- def initialize(url, size)
- raise InvalidURLError unless ::Gitlab::UrlSanitizer.valid?(url)
-
- @uri = URI(url)
- @size = size
- @tell = 0
- end
-
- def close
- # no-op
- end
-
- def binmode
- # no-op
- end
-
- def binmode?
- true
- end
-
- def path
- nil
- end
-
- def url
- @uri.to_s
- end
-
- def seek(pos, where = IO::SEEK_SET)
- new_pos =
- case where
- when IO::SEEK_END
- size + pos
- when IO::SEEK_SET
- pos
- when IO::SEEK_CUR
- tell + pos
- else
- -1
- end
-
- raise 'new position is outside of file' if new_pos < 0 || new_pos > size
-
- @tell = new_pos
- end
-
- def eof?
- tell == size
- end
-
- def each_line
- until eof?
- line = readline
- break if line.nil?
-
- yield(line)
- end
- end
-
- def read(length = nil, outbuf = "")
- out = ""
-
- length ||= size - tell
-
- until length <= 0 || eof?
- data = get_chunk
- break if data.empty?
-
- chunk_bytes = [BUFFER_SIZE - chunk_offset, length].min
- chunk_data = data.byteslice(0, chunk_bytes)
-
- out << chunk_data
- @tell += chunk_data.bytesize
- length -= chunk_data.bytesize
- end
-
- # If outbuf is passed, we put the output into the buffer. This supports IO.copy_stream functionality
- if outbuf
- outbuf.slice!(0, outbuf.bytesize)
- outbuf << out
- end
-
- out
- end
-
- def readline
- out = ""
-
- until eof?
- data = get_chunk
- new_line = data.index("\n")
-
- if !new_line.nil?
- out << data[0..new_line]
- @tell += new_line + 1
- break
- else
- out << data
- @tell += data.bytesize
- end
- end
-
- out
- end
-
- def write(data)
- raise NotImplementedError
- end
-
- def truncate(offset)
- raise NotImplementedError
- end
-
- def flush
- raise NotImplementedError
- end
-
- def present?
- true
- end
-
- private
-
- ##
- # The below methods are not implemented in IO class
- #
- def in_range?
- @chunk_range&.include?(tell)
- end
-
- def get_chunk
- unless in_range?
- response = Net::HTTP.start(uri.hostname, uri.port, proxy_from_env: true, use_ssl: uri.scheme == 'https') do |http|
- http.request(request)
- end
-
- raise FailedToGetChunkError unless response.code == '200' || response.code == '206'
-
- @chunk = response.body.force_encoding(Encoding::BINARY)
- @chunk_range = response.content_range
-
- ##
- # Note: If provider does not return content_range, then we set it as we requested
- # Provider: minio
- # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # Provider: AWS
- # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # Provider: GCS
- # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPOK 200
- @chunk_range ||= (chunk_start...(chunk_start + @chunk.bytesize))
- end
-
- @chunk[chunk_offset..BUFFER_SIZE]
- end
-
- def request
- Net::HTTP::Get.new(uri).tap do |request|
- request.set_range(chunk_start, BUFFER_SIZE)
- end
- end
-
- def chunk_offset
- tell % BUFFER_SIZE
- end
-
- def chunk_start
- (tell / BUFFER_SIZE) * BUFFER_SIZE
- end
-
- def chunk_end
- [chunk_start + BUFFER_SIZE, size].min
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/ci/trace/section_parser.rb b/lib/gitlab/ci/trace/section_parser.rb
index 9bb0166c9e3..c09089d6475 100644
--- a/lib/gitlab/ci/trace/section_parser.rb
+++ b/lib/gitlab/ci/trace/section_parser.rb
@@ -75,19 +75,19 @@ module Gitlab
@beginning_of_section_regex ||= /section_/.freeze
end
- def find_next_marker(s)
+ def find_next_marker(scanner)
beginning_of_section_len = 8
- maybe_marker = s.exist?(beginning_of_section_regex)
+ maybe_marker = scanner.exist?(beginning_of_section_regex)
if maybe_marker.nil?
- s.terminate
+ scanner.terminate
else
# repositioning at the beginning of the match
- s.pos += maybe_marker - beginning_of_section_len
+ scanner.pos += maybe_marker - beginning_of_section_len
if block_given?
- good_marker = yield(s)
+ good_marker = yield(scanner)
# if not a good marker: Consuming the matched beginning_of_section_regex
- s.pos += beginning_of_section_len unless good_marker
+ scanner.pos += beginning_of_section_len unless good_marker
end
end
end
diff --git a/lib/gitlab/ci/variables/collection/item.rb b/lib/gitlab/ci/variables/collection/item.rb
index 222aa06b800..7da6d09d440 100644
--- a/lib/gitlab/ci/variables/collection/item.rb
+++ b/lib/gitlab/ci/variables/collection/item.rb
@@ -34,7 +34,7 @@ module Gitlab
def self.fabricate(resource)
case resource
when Hash
- self.new(resource)
+ self.new(resource.symbolize_keys)
when ::HasVariable
self.new(resource.to_runner_variable)
when self
diff --git a/lib/gitlab/cleanup/project_upload_file_finder.rb b/lib/gitlab/cleanup/project_upload_file_finder.rb
new file mode 100644
index 00000000000..2ee8b60e76a
--- /dev/null
+++ b/lib/gitlab/cleanup/project_upload_file_finder.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Cleanup
+ class ProjectUploadFileFinder
+ FIND_BATCH_SIZE = 500
+ ABSOLUTE_UPLOAD_DIR = FileUploader.root.freeze
+ EXCLUDED_SYSTEM_UPLOADS_PATH = "#{ABSOLUTE_UPLOAD_DIR}/-/*".freeze
+ EXCLUDED_HASHED_UPLOADS_PATH = "#{ABSOLUTE_UPLOAD_DIR}/@hashed/*".freeze
+ EXCLUDED_TMP_UPLOADS_PATH = "#{ABSOLUTE_UPLOAD_DIR}/tmp/*".freeze
+
+ # Paths are relative to the upload directory
+ def each_file_batch(batch_size: FIND_BATCH_SIZE, &block)
+ cmd = build_find_command(ABSOLUTE_UPLOAD_DIR)
+
+ Open3.popen2(*cmd) do |stdin, stdout, status_thread|
+ yield_paths_in_batches(stdout, batch_size, &block)
+
+ raise "Find command failed" unless status_thread.value.success?
+ end
+ end
+
+ private
+
+ def yield_paths_in_batches(stdout, batch_size, &block)
+ paths = []
+
+ stdout.each_line("\0") do |line|
+ paths << line.chomp("\0")
+
+ if paths.size >= batch_size
+ yield(paths)
+ paths = []
+ end
+ end
+
+ yield(paths) if paths.any?
+ end
+
+ def build_find_command(search_dir)
+ cmd = %W[find -L #{search_dir}
+ -type f
+ ! ( -path #{EXCLUDED_SYSTEM_UPLOADS_PATH} -prune )
+ ! ( -path #{EXCLUDED_HASHED_UPLOADS_PATH} -prune )
+ ! ( -path #{EXCLUDED_TMP_UPLOADS_PATH} -prune )
+ -print0]
+
+ ionice = which_ionice
+ cmd = %W[#{ionice} -c Idle] + cmd if ionice
+
+ log_msg = "find command: \"#{cmd.join(' ')}\""
+ Rails.logger.info log_msg
+
+ cmd
+ end
+
+ def which_ionice
+ Gitlab::Utils.which('ionice')
+ rescue StandardError
+ # In this case, returning false is relatively safe,
+ # even though it isn't very nice
+ false
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cleanup/project_uploads.rb b/lib/gitlab/cleanup/project_uploads.rb
new file mode 100644
index 00000000000..f55ab535efe
--- /dev/null
+++ b/lib/gitlab/cleanup/project_uploads.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Cleanup
+ class ProjectUploads
+ LOST_AND_FOUND = File.join(ProjectUploadFileFinder::ABSOLUTE_UPLOAD_DIR, '-', 'project-lost-found')
+
+ attr_reader :logger
+
+ def initialize(logger: nil)
+ @logger = logger || Rails.logger
+ end
+
+ def run!(dry_run: true)
+ logger.info "Looking for orphaned project uploads to clean up#{'. Dry run' if dry_run}..."
+
+ each_orphan_file do |path, upload_path|
+ result = cleanup(path, upload_path, dry_run)
+
+ logger.info result
+ end
+ end
+
+ private
+
+ def cleanup(path, upload_path, dry_run)
+ # This happened in staging:
+ # `find` returned a path on which `File.delete` raised `Errno::ENOENT`
+ return "Cannot find file: #{path}" unless File.exist?(path)
+
+ correct_path = upload_path && find_correct_path(upload_path)
+
+ if correct_path
+ move(path, correct_path, 'fix', dry_run)
+ else
+ move_to_lost_and_found(path, dry_run)
+ end
+ end
+
+ # Accepts a path in the form of "#{hex_secret}/#{filename}"
+ def find_correct_path(upload_path)
+ upload = Upload.find_by(uploader: 'FileUploader', path: upload_path)
+ return unless upload && upload.local? && upload.model
+
+ upload.absolute_path
+ rescue => e
+ logger.error e.message
+
+ # absolute_path depends on a lot of code. If it doesn't work, then it
+ # it doesn't matter if the upload file is in the right place. Treat it
+ # as uncorrectable.
+ # I.e. the project record might be missing, which raises an exception.
+ nil
+ end
+
+ def move_to_lost_and_found(path, dry_run)
+ new_path = path.sub(/\A#{ProjectUploadFileFinder::ABSOLUTE_UPLOAD_DIR}/, LOST_AND_FOUND)
+
+ move(path, new_path, 'move to lost and found', dry_run)
+ end
+
+ def move(path, new_path, prefix, dry_run)
+ action = "#{prefix} #{path} -> #{new_path}"
+
+ if dry_run
+ "Can #{action}"
+ else
+ begin
+ FileUtils.mkdir_p(File.dirname(new_path))
+ FileUtils.mv(path, new_path)
+
+ "Did #{action}"
+ rescue => e
+ "Error during #{action}: #{e.inspect}"
+ end
+ end
+ end
+
+ # Yields absolute paths of project upload files that are not in the
+ # uploads table
+ def each_orphan_file
+ ProjectUploadFileFinder.new.each_file_batch do |file_paths|
+ logger.debug "Processing batch of #{file_paths.size} project upload file paths, starting with #{file_paths.first}"
+
+ file_paths.each do |path|
+ pup = ProjectUploadPath.from_path(path)
+
+ yield(path, pup.upload_path) if pup.orphan?
+ end
+ end
+ end
+
+ class ProjectUploadPath
+ PROJECT_FULL_PATH_REGEX = %r{\A#{FileUploader.root}/(.+)/(\h+/[^/]+)\z}.freeze
+
+ attr_reader :full_path, :upload_path
+
+ def initialize(full_path, upload_path)
+ @full_path = full_path
+ @upload_path = upload_path
+ end
+
+ def self.from_path(path)
+ path_matched = path.match(PROJECT_FULL_PATH_REGEX)
+ return new(nil, nil) unless path_matched
+
+ new(path_matched[1], path_matched[2])
+ end
+
+ def orphan?
+ return true if full_path.nil? || upload_path.nil?
+
+ # It's possible to reduce to one query, but `where_full_path_in` is complex
+ !Upload.exists?(path: upload_path, model_id: project_id, model_type: 'Project', uploader: 'FileUploader')
+ end
+
+ private
+
+ def project_id
+ @project_id ||= Project.where_full_path_in([full_path]).pluck(:id)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cleanup/remote_uploads.rb b/lib/gitlab/cleanup/remote_uploads.rb
new file mode 100644
index 00000000000..45a5aea4fcd
--- /dev/null
+++ b/lib/gitlab/cleanup/remote_uploads.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+module Gitlab
+ module Cleanup
+ class RemoteUploads
+ attr_reader :logger
+
+ BATCH_SIZE = 100
+
+ def initialize(logger: nil)
+ @logger = logger || Rails.logger
+ end
+
+ def run!(dry_run: false)
+ unless configuration.enabled
+ logger.warn "Object storage not enabled. Exit".color(:yellow)
+
+ return
+ end
+
+ logger.info "Looking for orphaned remote uploads to remove#{'. Dry run' if dry_run}..."
+
+ each_orphan_file do |file|
+ info = if dry_run
+ "Can be moved to lost and found: #{file.key}"
+ else
+ new_path = move_to_lost_and_found(file)
+ "Moved to lost and found: #{file.key} -> #{new_path}"
+ end
+
+ logger.info(info)
+ end
+ end
+
+ private
+
+ def each_orphan_file
+ # we want to skip files already moved to lost_and_found directory
+ lost_dir_match = "^#{lost_and_found_dir}\/"
+
+ remote_directory.files.each_slice(BATCH_SIZE) do |remote_files|
+ remote_files.reject! { |file| file.key.match(/#{lost_dir_match}/) }
+ file_paths = remote_files.map(&:key)
+ tracked_paths = Upload
+ .where(store: ObjectStorage::Store::REMOTE, path: file_paths)
+ .pluck(:path)
+
+ remote_files.reject! { |file| tracked_paths.include?(file.key) }
+ remote_files.each do |file|
+ yield file
+ end
+ end
+ end
+
+ def move_to_lost_and_found(file)
+ new_path = "#{lost_and_found_dir}/#{file.key}"
+
+ file.copy(configuration['remote_directory'], new_path)
+ file.destroy
+
+ new_path
+ end
+
+ def lost_and_found_dir
+ 'lost_and_found'
+ end
+
+ def remote_directory
+ connection.directories.get(configuration['remote_directory'])
+ end
+
+ def connection
+ ::Fog::Storage.new(configuration['connection'].symbolize_keys)
+ end
+
+ def configuration
+ Gitlab.config.uploads.object_store
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 3cf35f499cd..9147ef401da 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -60,7 +60,7 @@ module Gitlab
def in_memory_application_settings
with_fallback_to_fake_application_settings do
- @in_memory_application_settings ||= ::ApplicationSetting.build_from_defaults # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ @in_memory_application_settings ||= ::ApplicationSetting.build_from_defaults
end
end
diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb
index 1e283cc092b..eb246d393a1 100644
--- a/lib/gitlab/data_builder/pipeline.rb
+++ b/lib/gitlab/data_builder/pipeline.rb
@@ -55,7 +55,7 @@ module Gitlab
id: runner.id,
description: runner.description,
active: runner.active?,
- is_shared: runner.is_shared?
+ is_shared: runner.instance_type?
}
end
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 4ad106e7b0a..8eacad078c8 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -42,10 +42,29 @@ module Gitlab
!self.read_only?
end
+ # check whether the underlying database is in read-only mode
+ def self.db_read_only?
+ if postgresql?
+ ActiveRecord::Base.connection.execute('SELECT pg_is_in_recovery()')
+ .first
+ .fetch('pg_is_in_recovery') == 't'
+ else
+ false
+ end
+ end
+
+ def self.db_read_write?
+ !self.db_read_only?
+ end
+
def self.version
@version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
end
+ def self.postgresql_9_or_less?
+ postgresql? && version.to_f < 10
+ end
+
def self.join_lateral_supported?
postgresql? && version.to_f >= 9.3
end
@@ -54,6 +73,28 @@ module Gitlab
postgresql? && version.to_f >= 9.4
end
+ def self.pg_stat_wal_receiver_supported?
+ postgresql? && version.to_f >= 9.6
+ end
+
+ # map some of the function names that changed between PostgreSQL 9 and 10
+ # https://wiki.postgresql.org/wiki/New_in_postgres_10
+ def self.pg_wal_lsn_diff
+ Gitlab::Database.postgresql_9_or_less? ? 'pg_xlog_location_diff' : 'pg_wal_lsn_diff'
+ end
+
+ def self.pg_current_wal_insert_lsn
+ Gitlab::Database.postgresql_9_or_less? ? 'pg_current_xlog_insert_location' : 'pg_current_wal_insert_lsn'
+ end
+
+ def self.pg_last_wal_receive_lsn
+ Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_receive_location' : 'pg_last_wal_receive_lsn'
+ end
+
+ def self.pg_last_wal_replay_lsn
+ Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_replay_location' : 'pg_last_wal_replay_lsn'
+ end
+
def self.nulls_last_order(field, direction = 'ASC')
order = "#{field} #{direction}"
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 4fe5b4cc835..f39b3b6eb5b 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -979,8 +979,8 @@ into similar problems in the future (e.g. when new tables are created).
# To not overload the worker too much we enforce a minimum interval both
# when scheduling and performing jobs.
- if delay_interval < BackgroundMigrationWorker::MIN_INTERVAL
- delay_interval = BackgroundMigrationWorker::MIN_INTERVAL
+ if delay_interval < BackgroundMigrationWorker.minimum_interval
+ delay_interval = BackgroundMigrationWorker.minimum_interval
end
model_class.each_batch(of: batch_size) do |relation, index|
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index 40bcfa20e7d..d16a55720b7 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -247,10 +247,11 @@ module Gitlab
lines = highlighted_diff_lines
return if lines.empty?
+ return if blob.nil?
last_line = lines.last
- if last_line.new_pos < total_blob_lines(blob)
+ if last_line.new_pos < total_blob_lines(blob) && !deleted_file?
match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos)
lines.push(match_line)
end
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb
index c358ae428cf..be25e1bab21 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb
@@ -34,7 +34,7 @@ module Gitlab
end
def cache_key
- [@merge_request_diff, 'highlighted-diff-files', diff_options]
+ [@merge_request_diff, 'highlighted-diff-files', Gitlab::Diff::Line::SERIALIZE_KEYS, diff_options]
end
private
diff --git a/lib/gitlab/diff/image_point.rb b/lib/gitlab/diff/image_point.rb
index 65332dfd239..1f157354ea4 100644
--- a/lib/gitlab/diff/image_point.rb
+++ b/lib/gitlab/diff/image_point.rb
@@ -3,11 +3,11 @@ module Gitlab
class ImagePoint
attr_reader :width, :height, :x, :y
- def initialize(width, height, x, y)
+ def initialize(width, height, new_x, new_y)
@width = width
@height = height
- @x = x
- @y = y
+ @x = new_x
+ @y = new_y
end
def to_h
diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb
index 54783a07919..99970779c67 100644
--- a/lib/gitlab/diff/inline_diff.rb
+++ b/lib/gitlab/diff/inline_diff.rb
@@ -93,7 +93,7 @@ module Gitlab
private
- def longest_common_prefix(a, b)
+ def longest_common_prefix(a, b) # rubocop:disable Naming/UncommunicativeMethodParamName
max_length = [a.length, b.length].max
length = 0
@@ -109,7 +109,7 @@ module Gitlab
length
end
- def longest_common_suffix(a, b)
+ def longest_common_suffix(a, b) # rubocop:disable Naming/UncommunicativeMethodParamName
longest_common_prefix(a.reverse, b.reverse)
end
end
diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb
index 2b3ebfbb9ff..1faf7770634 100644
--- a/lib/gitlab/diff/line.rb
+++ b/lib/gitlab/diff/line.rb
@@ -1,6 +1,8 @@
module Gitlab
module Diff
class Line
+ SERIALIZE_KEYS = %i(line_code text type index old_pos new_pos).freeze
+
attr_reader :line_code, :type, :index, :old_pos, :new_pos
attr_writer :rich_text
attr_accessor :text
@@ -19,13 +21,9 @@ module Gitlab
new(hash[:text], hash[:type], hash[:index], hash[:old_pos], hash[:new_pos], line_code: hash[:line_code])
end
- def serialize_keys
- @serialize_keys ||= %i(line_code text type index old_pos new_pos)
- end
-
def to_hash
hash = {}
- serialize_keys.each { |key| hash[key] = send(key) } # rubocop:disable GitlabSecurity/PublicSend
+ SERIALIZE_KEYS.each { |key| hash[key] = send(key) } # rubocop:disable GitlabSecurity/PublicSend
hash
end
diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb
index 8c72d00c1f3..ee604e66154 100644
--- a/lib/gitlab/ee_compat_check.rb
+++ b/lib/gitlab/ee_compat_check.rb
@@ -138,15 +138,23 @@ module Gitlab
def ee_branch_presence_check!
ee_remotes.keys.each do |remote|
- [ce_branch, ee_branch_prefix, ee_branch_suffix].each do |branch|
- _, status = step("Fetching #{remote}/#{branch}", %W[git fetch #{remote} #{branch}])
+ output, _ = step(
+ "Searching #{remote}",
+ %W[git ls-remote #{remote} *#{minimal_ee_branch_name}*])
- if status.zero?
- @ee_remote_with_branch = remote
- @ee_branch_found = branch
- return true
- end
- end
+ branches =
+ output.scan(%r{(?<=refs/heads/|refs/tags/).+}).sort_by(&:size)
+
+ next if branches.empty?
+
+ branch = branches.first
+
+ step("Fetching #{remote}/#{branch}", %W[git fetch #{remote} #{branch}])
+
+ @ee_remote_with_branch = remote
+ @ee_branch_found = branch
+
+ return true
end
puts
@@ -271,6 +279,10 @@ module Gitlab
@ee_patch_full_path ||= patches_dir.join(ee_patch_name)
end
+ def minimal_ee_branch_name
+ @minimal_ee_branch_name ||= ce_branch.sub(/(\Ace\-|\-ce\z)/, '')
+ end
+
def patch_name_from_branch(branch_name)
branch_name.parameterize << '.patch'
end
diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb
index 764f93f6d3d..fc8615afcae 100644
--- a/lib/gitlab/email/handler/create_issue_handler.rb
+++ b/lib/gitlab/email/handler/create_issue_handler.rb
@@ -36,10 +36,6 @@ module Gitlab
@project ||= Project.find_by_full_path(project_path)
end
- def metrics_params
- super.merge(project: project&.full_path)
- end
-
private
def create_issue
diff --git a/lib/gitlab/email/handler/create_merge_request_handler.rb b/lib/gitlab/email/handler/create_merge_request_handler.rb
index 2f864f2082b..2316e58c3fc 100644
--- a/lib/gitlab/email/handler/create_merge_request_handler.rb
+++ b/lib/gitlab/email/handler/create_merge_request_handler.rb
@@ -40,10 +40,6 @@ module Gitlab
@project ||= Project.find_by_full_path(project_path)
end
- def metrics_params
- super.merge(project: project&.full_path)
- end
-
private
def create_merge_request
diff --git a/lib/gitlab/email/handler/create_note_handler.rb b/lib/gitlab/email/handler/create_note_handler.rb
index 5791dbd0484..379b114e957 100644
--- a/lib/gitlab/email/handler/create_note_handler.rb
+++ b/lib/gitlab/email/handler/create_note_handler.rb
@@ -28,10 +28,6 @@ module Gitlab
record_name: 'comment')
end
- def metrics_params
- super.merge(project: project&.full_path)
- end
-
private
def author
diff --git a/lib/gitlab/email/handler/unsubscribe_handler.rb b/lib/gitlab/email/handler/unsubscribe_handler.rb
index ea80e21532e..56751e4e41e 100644
--- a/lib/gitlab/email/handler/unsubscribe_handler.rb
+++ b/lib/gitlab/email/handler/unsubscribe_handler.rb
@@ -20,10 +20,6 @@ module Gitlab
noteable.unsubscribe(sent_notification.recipient)
end
- def metrics_params
- super.merge(project: project&.full_path)
- end
-
private
def sent_notification
diff --git a/lib/gitlab/email/hook/additional_headers_interceptor.rb b/lib/gitlab/email/hook/additional_headers_interceptor.rb
new file mode 100644
index 00000000000..064cb5e659a
--- /dev/null
+++ b/lib/gitlab/email/hook/additional_headers_interceptor.rb
@@ -0,0 +1,12 @@
+module Gitlab
+ module Email
+ module Hook
+ class AdditionalHeadersInterceptor
+ def self.delivering_email(message)
+ message.header['Auto-Submitted'] ||= 'auto-generated'
+ message.header['X-Auto-Response-Suppress'] ||= 'All'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/hook/delivery_metrics_observer.rb b/lib/gitlab/email/hook/delivery_metrics_observer.rb
new file mode 100644
index 00000000000..1c2985f6045
--- /dev/null
+++ b/lib/gitlab/email/hook/delivery_metrics_observer.rb
@@ -0,0 +1,31 @@
+module Gitlab
+ module Email
+ module Hook
+ class DeliveryMetricsObserver
+ extend Gitlab::Utils::StrongMemoize
+
+ def self.delivering_email(_message)
+ delivery_attempts_counter.increment
+ end
+
+ def self.delivered_email(_message)
+ delivered_emails_counter.increment
+ end
+
+ def self.delivery_attempts_counter
+ strong_memoize(:delivery_attempts_counter) do
+ Gitlab::Metrics.counter(:gitlab_emails_delivery_attempts_total,
+ 'Counter of total emails delivery attempts')
+ end
+ end
+
+ def self.delivered_emails_counter
+ strong_memoize(:delivered_emails_counter) do
+ Gitlab::Metrics.counter(:gitlab_emails_delivered_total,
+ 'Counter of total emails delievered')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/hook/disable_email_interceptor.rb b/lib/gitlab/email/hook/disable_email_interceptor.rb
new file mode 100644
index 00000000000..7bb8b53f0c8
--- /dev/null
+++ b/lib/gitlab/email/hook/disable_email_interceptor.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module Email
+ module Hook
+ class DisableEmailInterceptor
+ def self.delivering_email(message)
+ message.perform_deliveries = false
+
+ Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/hook/email_template_interceptor.rb b/lib/gitlab/email/hook/email_template_interceptor.rb
new file mode 100644
index 00000000000..be0c4dd862e
--- /dev/null
+++ b/lib/gitlab/email/hook/email_template_interceptor.rb
@@ -0,0 +1,18 @@
+module Gitlab
+ module Email
+ module Hook
+ class EmailTemplateInterceptor
+ ##
+ # Remove HTML part if HTML emails are disabled.
+ #
+ def self.delivering_email(message)
+ unless Gitlab::CurrentSettings.html_emails_enabled
+ message.parts.delete_if do |part|
+ part.content_type.start_with?('text/html')
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb
index 0b8f6cfe3cb..d1fd5dfe0cb 100644
--- a/lib/gitlab/encoding_helper.rb
+++ b/lib/gitlab/encoding_helper.rb
@@ -65,17 +65,17 @@ module Gitlab
clean(message)
end
rescue ArgumentError
- return nil
+ nil
end
- def encode_binary(s)
- return "" if s.nil?
+ def encode_binary(str)
+ return "" if str.nil?
- s.dup.force_encoding(Encoding::ASCII_8BIT)
+ str.dup.force_encoding(Encoding::ASCII_8BIT)
end
- def binary_stringio(s)
- StringIO.new(s || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
+ def binary_stringio(str)
+ StringIO.new(str || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
end
private
diff --git a/lib/gitlab/exclusive_lease_helpers.rb b/lib/gitlab/exclusive_lease_helpers.rb
new file mode 100644
index 00000000000..e998548cff9
--- /dev/null
+++ b/lib/gitlab/exclusive_lease_helpers.rb
@@ -0,0 +1,29 @@
+module Gitlab
+ # This module provides helper methods which are intregrated with GitLab::ExclusiveLease
+ module ExclusiveLeaseHelpers
+ FailedToObtainLockError = Class.new(StandardError)
+
+ ##
+ # This helper method blocks a process/thread until the other process cancel the obrainted lease key.
+ #
+ # Note: It's basically discouraged to use this method in the unicorn's thread,
+ # because it holds the connection until all `retries` is consumed.
+ # This could potentially eat up all connection pools.
+ def in_lock(key, ttl: 1.minute, retries: 10, sleep_sec: 0.01.seconds)
+ lease = Gitlab::ExclusiveLease.new(key, timeout: ttl)
+
+ until uuid = lease.try_obtain
+ # Keep trying until we obtain the lease. To prevent hammering Redis too
+ # much we'll wait for a bit.
+ sleep(sleep_sec)
+ break if (retries -= 1) < 0
+ end
+
+ raise FailedToObtainLockError, 'Failed to obtain a lock' unless uuid
+
+ yield
+ ensure
+ Gitlab::ExclusiveLease.cancel(key, uuid)
+ end
+ end
+end
diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb
index 8953bc8c148..a91de278cf3 100644
--- a/lib/gitlab/fogbugz_import/importer.rb
+++ b/lib/gitlab/fogbugz_import/importer.rb
@@ -191,19 +191,19 @@ module Gitlab
end
end
- def linkify_issues(s)
- s = s.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
- s = s.gsub(/([Cc]ase) ([0-9]+)/, '\1 #\2')
- s
+ def linkify_issues(str)
+ str = str.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
+ str = str.gsub(/([Cc]ase) ([0-9]+)/, '\1 #\2')
+ str
end
- def escape_for_markdown(s)
- s = s.gsub(/^#/, "\\#")
- s = s.gsub(/^-/, "\\-")
- s = s.gsub("`", "\\~")
- s = s.delete("\r")
- s = s.gsub("\n", " \n")
- s
+ def escape_for_markdown(str)
+ str = str.gsub(/^#/, "\\#")
+ str = str.gsub(/^-/, "\\-")
+ str = str.gsub("`", "\\~")
+ str = str.delete("\r")
+ str = str.gsub("\n", " \n")
+ str
end
def format_content(raw_content)
diff --git a/lib/gitlab/gfm/uploads_rewriter.rb b/lib/gitlab/gfm/uploads_rewriter.rb
index b6eeb5d9a2b..f7e66697da3 100644
--- a/lib/gitlab/gfm/uploads_rewriter.rb
+++ b/lib/gitlab/gfm/uploads_rewriter.rb
@@ -23,11 +23,8 @@ module Gitlab
file = find_file(@source_project, $~[:secret], $~[:file])
break markdown unless file.try(:exists?)
- new_uploader = FileUploader.new(target_project)
- with_link_in_tmp_dir(file.file) do |open_tmp_file|
- new_uploader.store!(open_tmp_file)
- end
- new_uploader.markdown_link
+ moved = FileUploader.copy_to(file, target_project)
+ moved.markdown_link
end
end
@@ -48,20 +45,7 @@ module Gitlab
def find_file(project, secret, file)
uploader = FileUploader.new(project, secret: secret)
uploader.retrieve_from_store!(file)
- uploader.file
- end
-
- # Because the uploaders use 'move_to_store' we must have a temporary
- # file that is allowed to be (re)moved.
- def with_link_in_tmp_dir(file)
- dir = Dir.mktmpdir('UploadsRewriter', File.dirname(file))
- # The filename matters to Carrierwave so we make sure to preserve it
- tmp_file = File.join(dir, File.basename(file))
- File.link(file, tmp_file)
- # Open the file to placate Carrierwave
- File.open(tmp_file) { |open_file| yield open_file }
- ensure
- FileUtils.rm_rf(dir)
+ uploader
end
end
end
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 604bb11e712..71857bd2d87 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -50,13 +50,7 @@ module Gitlab
end
def raw(repository, sha)
- Gitlab::GitalyClient.migrate(:git_blob_raw) do |is_enabled|
- if is_enabled
- repository.gitaly_blob_client.get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
- else
- rugged_raw(repository, sha, limit: MAX_DATA_DISPLAY_SIZE)
- end
- end
+ repository.gitaly_blob_client.get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
end
# Returns an array of Blob instances, specified in blob_references as
@@ -67,17 +61,8 @@ module Gitlab
# Keep in mind that this method may allocate a lot of memory. It is up
# to the caller to limit the number of blobs and blob_size_limit.
#
- # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/798
def batch(repository, blob_references, blob_size_limit: MAX_DATA_DISPLAY_SIZE)
- Gitlab::GitalyClient.migrate(:list_blobs_by_sha_path) do |is_enabled|
- if is_enabled
- repository.gitaly_blob_client.get_blobs(blob_references, blob_size_limit).to_a
- else
- blob_references.map do |sha, path|
- find(repository, sha, path, limit: blob_size_limit)
- end
- end
- end
+ repository.gitaly_blob_client.get_blobs(blob_references, blob_size_limit).to_a
end
# Returns an array of Blob instances just with the metadata, that means
@@ -90,16 +75,8 @@ module Gitlab
# Returns array of Gitlab::Git::Blob
# Does not guarantee blob data will be set
def batch_lfs_pointers(repository, blob_ids)
- repository.gitaly_migrate(:batch_lfs_pointers) do |is_enabled|
- if is_enabled
- repository.gitaly_blob_client.batch_lfs_pointers(blob_ids.to_a)
- else
- blob_ids.lazy
- .select { |sha| possible_lfs_blob?(repository, sha) }
- .map { |sha| rugged_raw(repository, sha, limit: LFS_POINTER_MAX_SIZE) }
- .select(&:lfs_pointer?)
- .force
- end
+ repository.wrapped_gitaly_errors do
+ repository.gitaly_blob_client.batch_lfs_pointers(blob_ids.to_a)
end
end
@@ -110,72 +87,6 @@ module Gitlab
def size_could_be_lfs?(size)
size.between?(LFS_POINTER_MIN_SIZE, LFS_POINTER_MAX_SIZE)
end
-
- private
-
- # Recursive search of blob id by path
- #
- # Ex.
- # blog/ # oid: 1a
- # app/ # oid: 2a
- # models/ # oid: 3a
- # file.rb # oid: 4a
- #
- #
- # Blob.find_entry_by_path(repo, '1a', 'blog', 'app', 'file.rb') # => '4a'
- #
- def find_entry_by_path(repository, root_id, *path_parts)
- root_tree = repository.lookup(root_id)
-
- entry = root_tree.find do |entry|
- entry[:name] == path_parts[0]
- end
-
- return nil unless entry
-
- if path_parts.size > 1
- return nil unless entry[:type] == :tree
-
- path_parts.shift
- find_entry_by_path(repository, entry[:oid], *path_parts)
- else
- [:blob, :commit].include?(entry[:type]) ? entry : nil
- end
- end
-
- def submodule_blob(blob_entry, path, sha)
- new(
- id: blob_entry[:oid],
- name: blob_entry[:name],
- size: 0,
- data: '',
- path: path,
- commit_id: sha
- )
- end
-
- def rugged_raw(repository, sha, limit:)
- blob = repository.lookup(sha)
-
- return unless blob.is_a?(Rugged::Blob)
-
- new(
- id: blob.oid,
- size: blob.size,
- data: blob.content(limit),
- binary: blob.binary?
- )
- end
-
- # Efficient lookup to determine if object size
- # and type make it a possible LFS blob without loading
- # blob content into memory with repository.lookup(sha)
- def possible_lfs_blob?(repository, sha)
- object_header = repository.rugged.read_header(sha)
-
- object_header[:type] == :blob &&
- size_could_be_lfs?(object_header[:len])
- end
end
def initialize(options)
@@ -207,16 +118,7 @@ module Gitlab
return if @loaded_all_data
- @data = Gitlab::GitalyClient.migrate(:git_blob_load_all_data) do |is_enabled|
- begin
- if is_enabled
- repository.gitaly_blob_client.get_blob(oid: id, limit: -1).data
- else
- repository.lookup(id).content
- end
- end
- end
-
+ @data = repository.gitaly_blob_client.get_blob(oid: id, limit: -1).data
@loaded_all_data = true
@loaded_size = @data.bytesize
end
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index c67826da1d2..5b264868af0 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -1,4 +1,4 @@
-# Gitlab::Git::Commit is a wrapper around native Rugged::Commit object
+# Gitlab::Git::Commit is a wrapper around Gitaly::GitCommit
module Gitlab
module Git
class Commit
@@ -55,7 +55,6 @@ module Gitlab
# A rugged reference?
commit_id = Gitlab::Git::Ref.dereference_object(commit_id)
- return decorate(repo, commit_id) if commit_id.is_a?(Rugged::Commit)
# Some weird thing?
return nil unless commit_id.is_a?(String)
@@ -63,27 +62,15 @@ module Gitlab
# This saves us an RPC round trip.
return nil if commit_id.include?(':')
- commit = repo.gitaly_migrate(:find_commit) do |is_enabled|
- if is_enabled
- repo.gitaly_commit_client.find_commit(commit_id)
- else
- rugged_find(repo, commit_id)
- end
+ commit = repo.wrapped_gitaly_errors do
+ repo.gitaly_commit_client.find_commit(commit_id)
end
decorate(repo, commit) if commit
- rescue Rugged::ReferenceError, Rugged::InvalidError, Rugged::ObjectError,
- Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository,
- Rugged::OdbError, Rugged::TreeError, ArgumentError
+ rescue Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository, ArgumentError
nil
end
- def rugged_find(repo, commit_id)
- obj = repo.rev_parse_target(commit_id)
-
- obj.is_a?(Rugged::Commit) ? obj : nil
- end
-
# Get last commit for HEAD
#
# Ex.
@@ -152,20 +139,6 @@ module Gitlab
Gitlab::Git::Commit.new(repository, commit, ref)
end
- # Returns the `Rugged` sorting type constant for one or more given
- # sort types. Valid keys are `:none`, `:topo`, and `:date`, or an array
- # containing more than one of them. `:date` uses a combination of date and
- # topological sorting to closer mimic git's native ordering.
- def rugged_sort_type(sort_type)
- @rugged_sort_types ||= {
- none: Rugged::SORT_NONE,
- topo: Rugged::SORT_TOPO,
- date: Rugged::SORT_DATE | Rugged::SORT_TOPO
- }
-
- @rugged_sort_types.fetch(sort_type, Rugged::SORT_NONE)
- end
-
def shas_with_signatures(repository, shas)
Gitlab::GitalyClient::CommitService.new(repository).filter_shas_with_signatures(shas)
end
@@ -174,13 +147,8 @@ module Gitlab
# relation to each other. The last 10 commits for a branch for example,
# should go through .where
def batch_by_oid(repo, oids)
- repo.gitaly_migrate(:list_commits_by_oid,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- repo.gitaly_commit_client.list_commits_by_oid(oids)
- else
- oids.map { |oid| find(repo, oid) }.compact
- end
+ repo.wrapped_gitaly_errors do
+ repo.gitaly_commit_client.list_commits_by_oid(oids)
end
end
@@ -238,8 +206,6 @@ module Gitlab
case raw_commit
when Hash
init_from_hash(raw_commit)
- when Rugged::Commit
- init_from_rugged(raw_commit)
when Gitaly::GitCommit
init_from_gitaly(raw_commit)
else
@@ -280,33 +246,9 @@ module Gitlab
@repository.gitaly_commit_client.diff_from_parent(self, options)
end
- # Not to be called directly, but right now its used for tests and in old
- # migrations
- def rugged_diff_from_parent(options = {})
- options ||= {}
- break_rewrites = options[:break_rewrites]
- actual_options = Gitlab::Git::Diff.filter_diff_options(options)
-
- diff = if rugged_commit.parents.empty?
- rugged_commit.diff(actual_options.merge(reverse: true))
- else
- rugged_commit.parents[0].diff(rugged_commit, actual_options)
- end
-
- diff.find_similar!(break_rewrites: break_rewrites)
- diff
- end
-
def deltas
@deltas ||= begin
- deltas = Gitlab::GitalyClient.migrate(:commit_deltas) do |is_enabled|
- if is_enabled
- @repository.gitaly_commit_client.commit_deltas(self)
- else
- rugged_diff_from_parent.each_delta
- end
- end
-
+ deltas = @repository.gitaly_commit_client.commit_deltas(self)
deltas.map { |delta| Gitlab::Git::Diff.new(delta) }
end
end
@@ -374,14 +316,6 @@ module Gitlab
encode! @committer_email
end
- def rugged_commit
- @rugged_commit ||= if raw_commit.is_a?(Rugged::Commit)
- raw_commit
- else
- @repository.rev_parse_target(id)
- end
- end
-
def merge_commit?
parent_ids.size > 1
end
@@ -427,22 +361,6 @@ module Gitlab
end
end
- def init_from_rugged(commit)
- author = commit.author
- committer = commit.committer
-
- @raw_commit = commit
- @id = commit.oid
- @message = commit.message
- @authored_date = author[:time]
- @committed_date = committer[:time]
- @author_name = author[:name]
- @author_email = author[:email]
- @committer_name = committer[:name]
- @committer_email = committer[:email]
- @parent_ids = commit.parents.map(&:oid)
- end
-
def init_from_gitaly(commit)
@raw_commit = commit
@id = commit.id
diff --git a/lib/gitlab/git/commit_stats.rb b/lib/gitlab/git/commit_stats.rb
index 8463b1eb794..ae6f554bc06 100644
--- a/lib/gitlab/git/commit_stats.rb
+++ b/lib/gitlab/git/commit_stats.rb
@@ -1,5 +1,3 @@
-# Gitaly note: JV: 1 RPC, migration in progress.
-
# Gitlab::Git::CommitStats counts the additions, deletions, and total changes
# in a commit.
module Gitlab
@@ -16,12 +14,8 @@ module Gitlab
@deletions = 0
@total = 0
- repo.gitaly_migrate(:commit_stats) do |is_enabled|
- if is_enabled
- gitaly_stats(repo, commit)
- else
- rugged_stats(commit)
- end
+ repo.wrapped_gitaly_errors do
+ gitaly_stats(repo, commit)
end
end
@@ -31,12 +25,6 @@ module Gitlab
@deletions = stats.deletions
@total = @additions + @deletions
end
-
- def rugged_stats(commit)
- diff = commit.rugged_diff_from_parent
- _files_changed, @additions, @deletions = diff.stat
- @total = @additions + @deletions
- end
end
end
end
diff --git a/lib/gitlab/git/conflict/resolver.rb b/lib/gitlab/git/conflict/resolver.rb
index c3cb0264112..6dc792c16b8 100644
--- a/lib/gitlab/git/conflict/resolver.rb
+++ b/lib/gitlab/git/conflict/resolver.rb
@@ -12,28 +12,18 @@ module Gitlab
end
def conflicts
- @conflicts ||= begin
- @target_repository.gitaly_migrate(:conflicts_list_conflict_files) do |is_enabled|
- if is_enabled
- gitaly_conflicts_client(@target_repository).list_conflict_files.to_a
- else
- rugged_list_conflict_files
- end
- end
+ @conflicts ||= @target_repository.wrapped_gitaly_errors do
+ gitaly_conflicts_client(@target_repository).list_conflict_files.to_a
end
rescue GRPC::FailedPrecondition => e
raise Gitlab::Git::Conflict::Resolver::ConflictSideMissing.new(e.message)
- rescue Rugged::ReferenceError, Rugged::OdbError, GRPC::BadStatus => e
+ rescue GRPC::BadStatus => e
raise Gitlab::Git::CommandError.new(e)
end
def resolve_conflicts(source_repository, resolution, source_branch:, target_branch:)
- source_repository.gitaly_migrate(:conflicts_resolve_conflicts) do |is_enabled|
- if is_enabled
- gitaly_conflicts_client(source_repository).resolve_conflicts(@target_repository, resolution, source_branch, target_branch)
- else
- rugged_resolve_conflicts(source_repository, resolution, source_branch, target_branch)
- end
+ source_repository.wrapped_gitaly_errors do
+ gitaly_conflicts_client(source_repository).resolve_conflicts(@target_repository, resolution, source_branch, target_branch)
end
end
@@ -61,57 +51,6 @@ module Gitlab
def gitaly_conflicts_client(repository)
repository.gitaly_conflicts_client(@our_commit_oid, @their_commit_oid)
end
-
- def write_resolved_file_to_index(repository, index, file, params)
- if params[:sections]
- resolved_lines = file.resolve_lines(params[:sections])
- new_file = resolved_lines.map { |line| line[:full_line] }.join("\n")
-
- new_file << "\n" if file.our_blob.data.end_with?("\n")
- elsif params[:content]
- new_file = file.resolve_content(params[:content])
- end
-
- our_path = file.our_path
-
- oid = repository.rugged.write(new_file, :blob)
- index.add(path: our_path, oid: oid, mode: file.our_mode)
- index.conflict_remove(our_path)
- end
-
- def rugged_list_conflict_files
- target_index = @target_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid)
-
- # We don't need to do `with_repo_branch_commit` here, because the target
- # project always fetches source refs when creating merge request diffs.
- conflict_files(@target_repository, target_index)
- end
-
- def rugged_resolve_conflicts(source_repository, resolution, source_branch, target_branch)
- source_repository.with_repo_branch_commit(@target_repository, target_branch) do
- index = source_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid)
- conflicts = conflict_files(source_repository, index)
-
- resolution.files.each do |file_params|
- conflict_file = conflict_for_path(conflicts, file_params[:old_path], file_params[:new_path])
-
- write_resolved_file_to_index(source_repository, index, conflict_file, file_params)
- end
-
- unless index.conflicts.empty?
- missing_files = index.conflicts.map { |file| file[:ours][:path] }
-
- raise ResolutionError, "Missing resolutions for the following files: #{missing_files.join(', ')}"
- end
-
- commit_params = {
- message: resolution.commit_message,
- parents: [@our_commit_oid, @their_commit_oid]
- }
-
- source_repository.commit_index(resolution.user, source_branch, index, commit_params)
- end
- end
end
end
end
diff --git a/lib/gitlab/git/diff_collection.rb b/lib/gitlab/git/diff_collection.rb
index 6a601561c2a..219c69893ad 100644
--- a/lib/gitlab/git/diff_collection.rb
+++ b/lib/gitlab/git/diff_collection.rb
@@ -42,12 +42,10 @@ module Gitlab
return if @overflow
return if @iterator.nil?
- Gitlab::GitalyClient.migrate(:commit_raw_diffs) do |is_enabled|
- if is_enabled && @iterator.is_a?(Gitlab::GitalyClient::DiffStitcher)
- each_gitaly_patch(&block)
- else
- each_rugged_patch(&block)
- end
+ if @iterator.is_a?(Gitlab::GitalyClient::DiffStitcher)
+ each_gitaly_patch(&block)
+ else
+ each_serialized_patch(&block)
end
@populated = true
@@ -118,7 +116,7 @@ module Gitlab
end
end
- def each_rugged_patch
+ def each_serialized_patch
i = @array.length
@iterator.each do |raw|
diff --git a/lib/gitlab/git/operation_service.rb b/lib/gitlab/git/operation_service.rb
index 280def182d5..57d748343be 100644
--- a/lib/gitlab/git/operation_service.rb
+++ b/lib/gitlab/git/operation_service.rb
@@ -8,6 +8,8 @@ module Gitlab
alias_method :branch_created?, :branch_created
def self.from_gitaly(branch_update)
+ return if branch_update.nil?
+
new(
branch_update.commit_id,
branch_update.repo_created,
diff --git a/lib/gitlab/git/popen.rb b/lib/gitlab/git/popen.rb
index f9f24ecc48d..7426688fc55 100644
--- a/lib/gitlab/git/popen.rb
+++ b/lib/gitlab/git/popen.rb
@@ -21,6 +21,10 @@ module Gitlab
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
stdout.set_encoding(Encoding::ASCII_8BIT)
+ # stderr and stdout pipes can block if stderr/stdout aren't drained: https://bugs.ruby-lang.org/issues/9082
+ # Mimic what Ruby does with capture3: https://github.com/ruby/ruby/blob/1ec544695fa02d714180ef9c34e755027b6a2103/lib/open3.rb#L257-L273
+ err_reader = Thread.new { stderr.read }
+
yield(stdin) if block_given?
stdin.close
@@ -32,7 +36,7 @@ module Gitlab
cmd_output << stdout.read
end
- cmd_output << stderr.read
+ cmd_output << err_reader.value
cmd_status = wait_thr.value.exitstatus
end
@@ -55,16 +59,20 @@ module Gitlab
rerr, werr = IO.pipe
pid = Process.spawn(vars, *cmd, out: wout, err: werr, chdir: path, pgroup: true)
+ # stderr and stdout pipes can block if stderr/stdout aren't drained: https://bugs.ruby-lang.org/issues/9082
+ # Mimic what Ruby does with capture3: https://github.com/ruby/ruby/blob/1ec544695fa02d714180ef9c34e755027b6a2103/lib/open3.rb#L257-L273
+ out_reader = Thread.new { rout.read }
+ err_reader = Thread.new { rerr.read }
begin
- status = process_wait_with_timeout(pid, timeout)
-
# close write ends so we could read them
wout.close
werr.close
- cmd_output = rout.readlines.join
- cmd_output << rerr.readlines.join # Copying the behaviour of `popen` which merges stderr into output
+ status = process_wait_with_timeout(pid, timeout)
+
+ cmd_output = out_reader.value
+ cmd_output << err_reader.value # Copying the behaviour of `popen` which merges stderr into output
[cmd_output, status.exitstatus]
rescue Timeout::Error => e
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 7c3b91f6efb..3e11355435b 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -1,4 +1,3 @@
-# Gitlab::Git::Repository is a wrapper around native Rugged::Repository object
require 'tempfile'
require 'forwardable'
require "rubygems/package"
@@ -20,6 +19,7 @@ module Gitlab
GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE
].freeze
SEARCH_CONTEXT_LINES = 3
+ REV_LIST_COMMIT_LIMIT = 2_000
# In https://gitlab.com/gitlab-org/gitaly/merge_requests/698
# We copied these two prefixes into gitaly-go, so don't change these
# or things will break! (REBASE_WORKTREE_PREFIX and SQUASH_WORKTREE_PREFIX)
@@ -40,19 +40,6 @@ module Gitlab
ChecksumError = Class.new(StandardError)
class << self
- # Unlike `new`, `create` takes the repository path
- def create(repo_path, bare: true, symlink_hooks_to: nil)
- FileUtils.mkdir_p(repo_path, mode: 0770)
-
- # Equivalent to `git --git-path=#{repo_path} init [--bare]`
- repo = Rugged::Repository.init_at(repo_path, bare)
- repo.close
-
- create_hooks(repo_path, symlink_hooks_to) if symlink_hooks_to.present?
-
- true
- end
-
def create_hooks(repo_path, global_hooks_path)
local_hooks_path = File.join(repo_path, 'hooks')
real_local_hooks_path = :not_found
@@ -86,9 +73,6 @@ module Gitlab
# Relative path of repo
attr_reader :relative_path
- # Rugged repo object
- attr_reader :rugged
-
attr_reader :gitlab_projects, :storage, :gl_repository, :relative_path
# This initializer method is only used on the client side (gitlab-ce).
@@ -112,8 +96,9 @@ module Gitlab
[storage, relative_path] == [other.storage, other.relative_path]
end
+ # This method will be removed when Gitaly reaches v1.1.
def path
- @path ||= File.join(
+ File.join(
Gitlab.config.repositories.storages[@storage].legacy_disk_path, @relative_path
)
end
@@ -127,8 +112,9 @@ module Gitlab
raise Gitlab::Git::CommandError.new(e.message)
end
+ # This method will be removed when Gitaly reaches v1.1.
def rugged
- @rugged ||= circuit_breaker.perform do
+ circuit_breaker.perform do
Rugged::Repository.new(path, alternates: alternate_object_directories)
end
rescue Rugged::RepositoryError, Rugged::OSError
@@ -168,24 +154,9 @@ module Gitlab
# Directly find a branch with a simple name (e.g. master)
#
- # force_reload causes a new Rugged repository to be instantiated
- #
- # This is to work around a bug in libgit2 that causes in-memory refs to
- # be stale/invalid when packed-refs is changed.
- # See https://gitlab.com/gitlab-org/gitlab-ce/issues/15392#note_14538333
- def find_branch(name, force_reload = false)
- gitaly_migrate(:find_branch) do |is_enabled|
- if is_enabled
- gitaly_ref_client.find_branch(name)
- else
- reload_rugged if force_reload
-
- rugged_ref = rugged.branches[name]
- if rugged_ref
- target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
- Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
- end
- end
+ def find_branch(name)
+ wrapped_gitaly_errors do
+ gitaly_ref_client.find_branch(name)
end
end
@@ -197,20 +168,8 @@ module Gitlab
# Returns the number of valid branches
def branch_count
- gitaly_migrate(:branch_names) do |is_enabled|
- if is_enabled
- gitaly_ref_client.count_branch_names
- else
- rugged.branches.each(:local).count do |ref|
- begin
- ref.name && ref.target # ensures the branch is valid
-
- true
- rescue Rugged::ReferenceError
- false
- end
- end
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.count_branch_names
end
end
@@ -233,12 +192,8 @@ module Gitlab
# Returns the number of valid tags
def tag_count
- gitaly_migrate(:tag_names) do |is_enabled|
- if is_enabled
- gitaly_ref_client.count_tag_names
- else
- rugged.tags.count
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.count_tag_names
end
end
@@ -251,7 +206,6 @@ module Gitlab
# Returns an Array of Tags
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/390
def tags
wrapped_gitaly_errors do
gitaly_ref_client.tags
@@ -262,13 +216,8 @@ module Gitlab
#
# Ref names must start with `refs/`.
def ref_exists?(ref_name)
- gitaly_migrate(:ref_exists,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_exists?(ref_name)
- else
- rugged_ref_exists?(ref_name)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_exists?(ref_name)
end
end
@@ -276,12 +225,8 @@ module Gitlab
#
# name - The name of the tag as a String.
def tag_exists?(name)
- gitaly_migrate(:ref_exists_tags, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_exists?("refs/tags/#{name}")
- else
- rugged_tag_exists?(name)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_exists?("refs/tags/#{name}")
end
end
@@ -289,20 +234,8 @@ module Gitlab
#
# name - The name of the branch as a String.
def branch_exists?(name)
- gitaly_migrate(:ref_exists_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_exists?("refs/heads/#{name}")
- else
- rugged_branch_exists?(name)
- end
- end
- end
-
- def batch_existence(object_ids, existing: true)
- filter_method = existing ? :select : :reject
-
- object_ids.public_send(filter_method) do |oid| # rubocop:disable GitlabSecurity/PublicSend
- rugged.exists?(oid)
+ wrapped_gitaly_errors do
+ gitaly_ref_exists?("refs/heads/#{name}")
end
end
@@ -312,12 +245,8 @@ module Gitlab
end
def delete_all_refs_except(prefixes)
- gitaly_migrate(:ref_delete_refs) do |is_enabled|
- if is_enabled
- gitaly_ref_client.delete_refs(except_with_prefixes: prefixes)
- else
- delete_refs(*all_ref_names_except(prefixes))
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.delete_refs(except_with_prefixes: prefixes)
end
end
@@ -330,12 +259,6 @@ module Gitlab
end.map(&:name)
end
- def rugged_head
- rugged.head
- rescue Rugged::ReferenceError
- nil
- end
-
def archive_metadata(ref, storage_path, project_path, format = "tar.gz", append_sha:)
ref ||= root_ref
commit = Gitlab::Git::Commit.find(self, ref)
@@ -418,8 +341,6 @@ module Gitlab
# offset: 5,
# after: Time.new(2016, 4, 21, 14, 32, 10)
# )
- #
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/446
def log(options)
default_options = {
limit: 10,
@@ -440,29 +361,34 @@ module Gitlab
raise ArgumentError.new("invalid Repository#log limit: #{limit.inspect}")
end
- gitaly_migrate(:find_commits) do |is_enabled|
+ wrapped_gitaly_errors do
+ gitaly_commit_client.find_commits(options)
+ end
+ end
+
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1233
+ def new_commits(newrev)
+ gitaly_migrate(:new_commits) do |is_enabled|
if is_enabled
- gitaly_commit_client.find_commits(options)
+ gitaly_ref_client.list_new_commits(newrev)
else
- raw_log(options).map { |c| Commit.decorate(self, c) }
+ refs = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ rev_list(including: newrev, excluding: :all).split("\n").map(&:strip)
+ end
+
+ Gitlab::Git::Commit.batch_by_oid(self, refs)
end
end
end
- # Used in gitaly-ruby
- def raw_log(options)
- sha =
- unless options[:all]
- actual_ref = options[:ref] || root_ref
- begin
- sha_from_ref(actual_ref)
- rescue Rugged::OdbError, Rugged::InvalidError, Rugged::ReferenceError
- # Return an empty array if the ref wasn't found
- return []
- end
- end
+ def new_blobs(newrev)
+ return [] if newrev.blank? || newrev == ::Gitlab::Git::BLANK_SHA
- log_by_shell(sha, options)
+ strong_memoize("new_blobs_#{newrev}") do
+ wrapped_gitaly_errors do
+ gitaly_ref_client.list_new_blobs(newrev, REV_LIST_COMMIT_LIMIT)
+ end
+ end
end
def count_commits(options)
@@ -485,13 +411,6 @@ module Gitlab
end
end
- # Return the object that +revspec+ points to. If +revspec+ is an
- # annotated tag, then return the tag's target instead.
- def rev_parse_target(revspec)
- obj = rugged.rev_parse(revspec)
- Ref.dereference_object(obj)
- end
-
# Counts the amount of commits between `from` and `to`.
def count_commits_between(from, to, options = {})
count_commits(from: from, to: to, **options)
@@ -519,12 +438,8 @@ module Gitlab
# Returns the SHA of the most recent common ancestor of +from+ and +to+
def merge_base(from, to)
- gitaly_migrate(:merge_base) do |is_enabled|
- if is_enabled
- gitaly_repository_client.find_merge_base(from, to)
- else
- rugged_merge_base(from, to)
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.find_merge_base(from, to)
end
end
@@ -540,12 +455,8 @@ module Gitlab
return [] unless root_sha
- branches = gitaly_migrate(:merged_branch_names) do |is_enabled|
- if is_enabled
- gitaly_merged_branch_names(branch_names, root_sha)
- else
- git_merged_branch_names(branch_names, root_sha)
- end
+ branches = wrapped_gitaly_errors do
+ gitaly_merged_branch_names(branch_names, root_sha)
end
Set.new(branches)
@@ -556,13 +467,7 @@ module Gitlab
# diff options. The +options+ hash can also include :break_rewrites to
# split larger rewrites into delete/add pairs.
def diff(from, to, options = {}, *paths)
- iterator = gitaly_migrate(:diff_between) do |is_enabled|
- if is_enabled
- gitaly_commit_client.diff(from, to, options.merge(paths: paths))
- else
- diff_patches(from, to, options, *paths)
- end
- end
+ iterator = gitaly_commit_client.diff(from, to, options.merge(paths: paths))
Gitlab::Git::DiffCollection.new(iterator, options)
end
@@ -591,28 +496,15 @@ module Gitlab
@refs_hash
end
- # Lookup for rugged object by oid or ref name
- def lookup(oid_or_ref_name)
- rugged.rev_parse(oid_or_ref_name)
- end
-
# Returns url for submodule
#
# Ex.
# @repository.submodule_url_for('master', 'rack')
# # => git@localhost:rack.git
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/329
def submodule_url_for(ref, path)
- Gitlab::GitalyClient.migrate(:submodule_url_for) do |is_enabled|
- if is_enabled
- gitaly_submodule_url_for(ref, path)
- else
- if submodules(ref).any?
- submodule = submodules(ref)[path]
- submodule['url'] if submodule
- end
- end
+ wrapped_gitaly_errors do
+ gitaly_submodule_url_for(ref, path)
end
end
@@ -648,42 +540,38 @@ module Gitlab
end
def add_branch(branch_name, user:, target:)
- gitaly_operation_client.user_create_branch(branch_name, user, target)
- rescue GRPC::FailedPrecondition => ex
- raise InvalidRef, ex
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_create_branch(branch_name, user, target)
+ end
end
def add_tag(tag_name, user:, target:, message: nil)
- gitaly_migrate(:operation_user_add_tag, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_add_tag(tag_name, user: user, target: target, message: message)
- else
- rugged_add_tag(tag_name, user: user, target: target, message: message)
- end
+ wrapped_gitaly_errors do
+ gitaly_operation_client.add_tag(tag_name, user, target, message)
end
end
def update_branch(branch_name, user:, newrev:, oldrev:)
- OperationService.new(user, self).update_branch(branch_name, newrev, oldrev)
- end
-
- def rm_branch(branch_name, user:)
- gitaly_migrate(:operation_user_delete_branch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
+ gitaly_migrate(:operation_user_update_branch) do |is_enabled|
if is_enabled
- gitaly_operations_client.user_delete_branch(branch_name, user)
+ gitaly_operation_client.user_update_branch(branch_name, user, newrev, oldrev)
else
- OperationService.new(user, self).rm_branch(find_branch(branch_name))
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ OperationService.new(user, self).update_branch(branch_name, newrev, oldrev)
+ end
end
end
end
+ def rm_branch(branch_name, user:)
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_delete_branch(branch_name, user)
+ end
+ end
+
def rm_tag(tag_name, user:)
- gitaly_migrate(:operation_user_delete_tag) do |is_enabled|
- if is_enabled
- gitaly_operations_client.rm_tag(tag_name, user)
- else
- Gitlab::Git::OperationService.new(user, self).rm_tag(find_tag(tag_name))
- end
+ wrapped_gitaly_errors do
+ gitaly_operation_client.rm_tag(tag_name, user)
end
end
@@ -692,72 +580,29 @@ module Gitlab
end
def merge(user, source_sha, target_branch, message, &block)
- gitaly_migrate(:operation_user_merge_branch) do |is_enabled|
- if is_enabled
- gitaly_operation_client.user_merge_branch(user, source_sha, target_branch, message, &block)
- else
- rugged_merge(user, source_sha, target_branch, message, &block)
- end
- end
- end
-
- def rugged_merge(user, source_sha, target_branch, message)
- committer = Gitlab::Git.committer_hash(email: user.email, name: user.name)
-
- OperationService.new(user, self).with_branch(target_branch) do |start_commit|
- our_commit = start_commit.sha
- their_commit = source_sha
-
- raise 'Invalid merge target' unless our_commit
- raise 'Invalid merge source' unless their_commit
-
- merge_index = rugged.merge_commits(our_commit, their_commit)
- break if merge_index.conflicts?
-
- options = {
- parents: [our_commit, their_commit],
- tree: merge_index.write_tree(rugged),
- message: message,
- author: committer,
- committer: committer
- }
-
- commit_id = create_commit(options)
-
- yield commit_id
-
- commit_id
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_merge_branch(user, source_sha, target_branch, message, &block)
end
- rescue Gitlab::Git::CommitError # when merge_index.conflicts?
- nil
end
def ff_merge(user, source_sha, target_branch)
- gitaly_migrate(:operation_user_ff_branch) do |is_enabled|
- if is_enabled
- gitaly_ff_merge(user, source_sha, target_branch)
- else
- rugged_ff_merge(user, source_sha, target_branch)
- end
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_ff_branch(user, source_sha, target_branch)
end
end
def revert(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
- gitaly_migrate(:revert, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- args = {
- user: user,
- commit: commit,
- branch_name: branch_name,
- message: message,
- start_branch_name: start_branch_name,
- start_repository: start_repository
- }
+ args = {
+ user: user,
+ commit: commit,
+ branch_name: branch_name,
+ message: message,
+ start_branch_name: start_branch_name,
+ start_repository: start_repository
+ }
- if is_enabled
- gitaly_operations_client.user_revert(args)
- else
- rugged_revert(args)
- end
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_revert(args)
end
end
@@ -775,21 +620,17 @@ module Gitlab
end
def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
- gitaly_migrate(:cherry_pick) do |is_enabled|
- args = {
- user: user,
- commit: commit,
- branch_name: branch_name,
- message: message,
- start_branch_name: start_branch_name,
- start_repository: start_repository
- }
+ args = {
+ user: user,
+ commit: commit,
+ branch_name: branch_name,
+ message: message,
+ start_branch_name: start_branch_name,
+ start_repository: start_repository
+ }
- if is_enabled
- gitaly_operations_client.user_cherry_pick(args)
- else
- rugged_cherry_pick(args)
- end
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_cherry_pick(args)
end
end
@@ -801,33 +642,18 @@ module Gitlab
Gitlab::Git.committer_hash(email: user.email, name: user.name)
end
- def create_commit(params = {})
- params[:message].delete!("\r")
-
- Rugged::Commit.create(rugged, params)
- end
-
# Delete the specified branch from the repository
def delete_branch(branch_name)
- gitaly_migrate(:delete_branch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_client.delete_branch(branch_name)
- else
- rugged.branches.delete(branch_name)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.delete_branch(branch_name)
end
- rescue Rugged::ReferenceError, CommandError => e
+ rescue CommandError => e
raise DeleteBranchError, e
end
def delete_refs(*ref_names)
- gitaly_migrate(:delete_refs,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_delete_refs(*ref_names)
- else
- git_delete_refs(*ref_names)
- end
+ wrapped_gitaly_errors do
+ gitaly_delete_refs(*ref_names)
end
end
@@ -837,46 +663,24 @@ module Gitlab
# create_branch("feature")
# create_branch("other-feature", "master")
def create_branch(ref, start_point = "HEAD")
- gitaly_migrate(:create_branch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_client.create_branch(ref, start_point)
- else
- rugged_create_branch(ref, start_point)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.create_branch(ref, start_point)
end
end
# If `mirror_refmap` is present the remote is set as mirror with that mapping
def add_remote(remote_name, url, mirror_refmap: nil)
- gitaly_migrate(:remote_add_remote) do |is_enabled|
- if is_enabled
- gitaly_remote_client.add_remote(remote_name, url, mirror_refmap)
- else
- rugged_add_remote(remote_name, url, mirror_refmap)
- end
+ wrapped_gitaly_errors do
+ gitaly_remote_client.add_remote(remote_name, url, mirror_refmap)
end
end
def remove_remote(remote_name)
- gitaly_migrate(:remote_remove_remote) do |is_enabled|
- if is_enabled
- gitaly_remote_client.remove_remote(remote_name)
- else
- rugged_remove_remote(remote_name)
- end
+ wrapped_gitaly_errors do
+ gitaly_remote_client.remove_remote(remote_name)
end
end
- # Update the specified remote using the values in the +options+ hash
- #
- # Example
- # repo.update_remote("origin", url: "path/to/repo")
- def remote_update(remote_name, url:)
- # TODO: Implement other remote options
- rugged.remotes.set_url(remote_name, url)
- nil
- end
-
AUTOCRLF_VALUES = {
"true" => true,
"false" => false,
@@ -896,22 +700,14 @@ module Gitlab
# Ex.
# repo.ls_files('master')
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/327
def ls_files(ref)
gitaly_commit_client.ls_files(ref)
end
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/328
def copy_gitattributes(ref)
- Gitlab::GitalyClient.migrate(:apply_gitattributes) do |is_enabled|
- if is_enabled
- gitaly_copy_gitattributes(ref)
- else
- rugged_copy_gitattributes(ref)
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.apply_gitattributes(ref)
end
- rescue GRPC::InvalidArgument
- raise InvalidRef
end
def info_attributes
@@ -1000,12 +796,8 @@ module Gitlab
end
def fetch_source_branch!(source_repository, source_branch, local_ref)
- Gitlab::GitalyClient.migrate(:fetch_source_branch) do |is_enabled|
- if is_enabled
- gitaly_repository_client.fetch_source_branch(source_repository, source_branch, local_ref)
- else
- rugged_fetch_source_branch(source_repository, source_branch, local_ref)
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.fetch_source_branch(source_repository, source_branch, local_ref)
end
end
@@ -1027,31 +819,22 @@ module Gitlab
def write_ref(ref_path, ref, old_ref: nil, shell: true)
ref_path = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{ref_path}" unless ref_path.start_with?("refs/") || ref_path == "HEAD"
- gitaly_migrate(:write_ref) do |is_enabled|
- if is_enabled
- gitaly_repository_client.write_ref(ref_path, ref, old_ref, shell)
- else
- local_write_ref(ref_path, ref, old_ref: old_ref, shell: shell)
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.write_ref(ref_path, ref, old_ref, shell)
end
end
+ # This method, fetch_ref, is used from within
+ # Gitlab::Git::OperationService. OperationService will eventually only
+ # exist in gitaly-ruby. When we delete OperationService from gitlab-ce
+ # we can also remove fetch_ref.
def fetch_ref(source_repository, source_ref:, target_ref:)
Gitlab::Git.check_namespace!(source_repository)
source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository)
- message, status = GitalyClient.migrate(:fetch_ref) do |is_enabled|
- if is_enabled
- gitaly_fetch_ref(source_repository, source_ref: source_ref, target_ref: target_ref)
- else
- # When removing this code, also remove source_repository#path
- # to remove deprecated method calls
- local_fetch_ref(source_repository.path, source_ref: source_ref, target_ref: target_ref)
- end
- end
-
- # Make sure ref was created, and raise Rugged::ReferenceError when not
- raise Rugged::ReferenceError, message if status != 0
+ args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref})
+ message, status = run_git(args, env: source_repository.fetch_env)
+ raise Gitlab::Git::CommandError, message if status != 0
target_ref
end
@@ -1066,12 +849,8 @@ module Gitlab
end
def fetch_repository_as_mirror(repository)
- gitaly_migrate(:remote_fetch_internal_remote) do |is_enabled|
- if is_enabled
- gitaly_remote_client.fetch_internal_remote(repository)
- else
- rugged_fetch_repository_as_mirror(repository)
- end
+ wrapped_gitaly_errors do
+ gitaly_remote_client.fetch_internal_remote(repository)
end
end
@@ -1084,20 +863,6 @@ module Gitlab
Gitlab::Git::Blob.batch(self, items, blob_size_limit: blob_size_limit)
end
- def commit_index(user, branch_name, index, options)
- committer = user_to_committer(user)
-
- OperationService.new(user, self).with_branch(branch_name) do
- commit_params = options.merge(
- tree: index.write_tree(rugged),
- author: committer,
- committer: committer
- )
-
- create_commit(commit_params)
- end
- end
-
def fsck
msg, status = gitaly_repository_client.fsck
@@ -1113,20 +878,12 @@ module Gitlab
end
def rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:)
- gitaly_migrate(:rebase) do |is_enabled|
- if is_enabled
- gitaly_rebase(user, rebase_id,
- branch: branch,
- branch_sha: branch_sha,
- remote_repository: remote_repository,
- remote_branch: remote_branch)
- else
- git_rebase(user, rebase_id,
- branch: branch,
- branch_sha: branch_sha,
- remote_repository: remote_repository,
- remote_branch: remote_branch)
- end
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_rebase(user, rebase_id,
+ branch: branch,
+ branch_sha: branch_sha,
+ remote_repository: remote_repository,
+ remote_branch: remote_branch)
end
end
@@ -1137,13 +894,9 @@ module Gitlab
end
def squash(user, squash_id, branch:, start_sha:, end_sha:, author:, message:)
- gitaly_migrate(:squash) do |is_enabled|
- if is_enabled
- gitaly_operation_client.user_squash(user, squash_id, branch,
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_squash(user, squash_id, branch,
start_sha, end_sha, author, message)
- else
- git_squash(user, squash_id, branch, start_sha, end_sha, author, message)
- end
end
end
@@ -1172,35 +925,24 @@ module Gitlab
end
def bundle_to_disk(save_path)
- gitaly_migrate(:bundle_to_disk) do |is_enabled|
- if is_enabled
- gitaly_repository_client.create_bundle(save_path)
- else
- run_git!(%W(bundle create #{save_path} --all))
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.create_bundle(save_path)
end
true
end
- # rubocop:disable Metrics/ParameterLists
def multi_action(
user, branch_name:, message:, actions:,
author_email: nil, author_name: nil,
start_branch_name: nil, start_repository: self)
- gitaly_migrate(:operation_user_commit_files) do |is_enabled|
- if is_enabled
- gitaly_operation_client.user_commit_files(user, branch_name,
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_commit_files(user, branch_name,
message, actions, author_email, author_name,
start_branch_name, start_repository)
- else
- rugged_multi_action(user, branch_name, message, actions,
- author_email, author_name, start_branch_name, start_repository)
- end
end
end
- # rubocop:enable Metrics/ParameterLists
def write_config(full_path:)
return unless full_path.present?
@@ -1208,17 +950,23 @@ module Gitlab
# This guard avoids Gitaly log/error spam
raise NoRepository, 'repository does not exist' unless exists?
+ set_config('gitlab.fullpath' => full_path)
+ end
+
+ def set_config(entries)
wrapped_gitaly_errors do
- gitaly_repository_client.write_config(full_path: full_path)
+ gitaly_repository_client.set_config(entries)
end
end
- def gitaly_repository
- Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository)
+ def delete_config(*keys)
+ wrapped_gitaly_errors do
+ gitaly_repository_client.delete_config(keys)
+ end
end
- def gitaly_operations_client
- @gitaly_operations_client ||= Gitlab::GitalyClient::OperationService.new(self)
+ def gitaly_repository
+ Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository)
end
def gitaly_ref_client
@@ -1270,8 +1018,8 @@ module Gitlab
end
def clean_stale_repository_files
- gitaly_migrate(:repository_cleanup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- gitaly_repository_client.cleanup if is_enabled && exists?
+ wrapped_gitaly_errors do
+ gitaly_repository_client.cleanup if exists?
end
rescue Gitlab::Git::CommandError => e # Don't fail if we can't cleanup
Rails.logger.error("Unable to clean repository on storage #{storage} with relative path #{relative_path}: #{e.message}")
@@ -1299,7 +1047,7 @@ module Gitlab
end
def can_be_merged?(source_sha, target_branch)
- if target_sha = find_branch(target_branch, true)&.target
+ if target_sha = find_branch(target_branch)&.target
!gitaly_conflicts_client(source_sha, target_sha).conflicts?
else
false
@@ -1316,12 +1064,10 @@ module Gitlab
end
def find_commits_by_message(query, ref, path, limit, offset)
- gitaly_migrate(:commits_by_message) do |is_enabled|
- if is_enabled
- find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
- else
- find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
- end
+ wrapped_gitaly_errors do
+ gitaly_commit_client
+ .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
+ .map { |c| commit(c) }
end
end
@@ -1331,12 +1077,8 @@ module Gitlab
end
def last_commit_for_path(sha, path)
- gitaly_migrate(:last_commit_for_path) do |is_enabled|
- if is_enabled
- last_commit_for_path_by_gitaly(sha, path)
- else
- last_commit_for_path_by_rugged(sha, path)
- end
+ wrapped_gitaly_errors do
+ gitaly_commit_client.last_commit_for_path(sha, path)
end
end
@@ -1360,33 +1102,6 @@ module Gitlab
run_git!(args, lazy_block: block)
end
- def with_worktree(worktree_path, branch, sparse_checkout_files: nil, env:)
- base_args = %w(worktree add --detach)
-
- # Note that we _don't_ want to test for `.present?` here: If the caller
- # passes an non nil empty value it means it still wants sparse checkout
- # but just isn't interested in any file, perhaps because it wants to
- # checkout files in by a changeset but that changeset only adds files.
- if sparse_checkout_files
- # Create worktree without checking out
- run_git!(base_args + ['--no-checkout', worktree_path], env: env)
- worktree_git_path = run_git!(%w(rev-parse --git-dir), chdir: worktree_path).chomp
-
- configure_sparse_checkout(worktree_git_path, sparse_checkout_files)
-
- # After sparse checkout configuration, checkout `branch` in worktree
- run_git!(%W(checkout --detach #{branch}), chdir: worktree_path, env: env)
- else
- # Create worktree and checkout `branch` in it
- run_git!(base_args + [worktree_path, branch], env: env)
- end
-
- yield
- ensure
- FileUtils.rm_rf(worktree_path) if File.exist?(worktree_path)
- FileUtils.rm_rf(worktree_git_path) if worktree_git_path && File.exist?(worktree_git_path)
- end
-
def checksum
# The exists? RPC is much cheaper, so we perform this request first
raise NoRepository, "Repository does not exists" unless exists?
@@ -1404,37 +1119,6 @@ module Gitlab
end
end
- def local_write_ref(ref_path, ref, old_ref: nil, shell: true)
- if shell
- shell_write_ref(ref_path, ref, old_ref)
- else
- rugged_write_ref(ref_path, ref)
- end
- end
-
- def rugged_write_config(full_path:)
- rugged.config['gitlab.fullpath'] = full_path
- end
-
- def shell_write_ref(ref_path, ref, old_ref)
- raise ArgumentError, "invalid ref_path #{ref_path.inspect}" if ref_path.include?(' ')
- raise ArgumentError, "invalid ref #{ref.inspect}" if ref.include?("\x00")
- raise ArgumentError, "invalid old_ref #{old_ref.inspect}" if !old_ref.nil? && old_ref.include?("\x00")
-
- input = "update #{ref_path}\x00#{ref}\x00#{old_ref}\x00"
- run_git!(%w[update-ref --stdin -z]) { |stdin| stdin.write(input) }
- end
-
- def rugged_write_ref(ref_path, ref)
- rugged.references.create(ref_path, ref, force: true)
- rescue Rugged::ReferenceError => ex
- Rails.logger.error "Unable to create #{ref_path} reference for repository #{path}: #{ex}"
- rescue Rugged::OSError => ex
- raise unless ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/
-
- Rails.logger.error "Unable to create #{ref_path} reference for repository #{path}: #{ex}"
- end
-
def run_git(args, chdir: path, env: {}, nice: false, lazy_block: nil, &block)
cmd = [Gitlab.config.git.bin_path, *args]
cmd.unshift("nice") if nice
@@ -1463,38 +1147,6 @@ module Gitlab
end
end
- # Adding a worktree means checking out the repository. For large repos,
- # this can be very expensive, so set up sparse checkout for the worktree
- # to only check out the files we're interested in.
- def configure_sparse_checkout(worktree_git_path, files)
- run_git!(%w(config core.sparseCheckout true))
-
- return if files.empty?
-
- worktree_info_path = File.join(worktree_git_path, 'info')
- FileUtils.mkdir_p(worktree_info_path)
- File.write(File.join(worktree_info_path, 'sparse-checkout'), files)
- end
-
- def rugged_fetch_source_branch(source_repository, source_branch, local_ref)
- with_repo_branch_commit(source_repository, source_branch) do |commit|
- if commit
- write_ref(local_ref, commit.sha)
- true
- else
- false
- end
- end
- end
-
- def worktree_path(prefix, id)
- id = id.to_s
- raise ArgumentError, "worktree id can't be empty" unless id.present?
- raise ArgumentError, "worktree id can't contain slashes " if id.include?("/")
-
- File.join(path, 'gitlab-worktree', "#{prefix}-#{id}")
- end
-
def git_env_for_user(user)
{
'GIT_COMMITTER_NAME' => user.name,
@@ -1505,20 +1157,6 @@ module Gitlab
}
end
- def git_merged_branch_names(branch_names, root_sha)
- git_arguments =
- %W[branch --merged #{root_sha}
- --format=%(refname:short)\ %(objectname)] + branch_names
-
- lines = run_git(git_arguments).first.lines
-
- lines.each_with_object([]) do |line, branches|
- name, sha = line.strip.split(' ', 2)
-
- branches << name if sha != root_sha
- end
- end
-
def gitaly_merged_branch_names(branch_names, root_sha)
qualified_branch_names = branch_names.map { |b| "refs/heads/#{b}" }
@@ -1547,63 +1185,6 @@ module Gitlab
end
end
- # Gitaly note: JV: although #log_by_shell shells out to Git I think the
- # complexity is such that we should migrate it as Ruby before trying to
- # do it in Go.
- def log_by_shell(sha, options)
- limit = options[:limit].to_i
- offset = options[:offset].to_i
- use_follow_flag = options[:follow] && options[:path].present?
-
- # We will perform the offset in Ruby because --follow doesn't play well with --skip.
- # See: https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
- offset_in_ruby = use_follow_flag && options[:offset].present?
- limit += offset if offset_in_ruby
-
- cmd = %w[log]
- cmd << "--max-count=#{limit}"
- cmd << '--format=%H'
- cmd << "--skip=#{offset}" unless offset_in_ruby
- cmd << '--follow' if use_follow_flag
- cmd << '--no-merges' if options[:skip_merges]
- cmd << "--after=#{options[:after].iso8601}" if options[:after]
- cmd << "--before=#{options[:before].iso8601}" if options[:before]
-
- if options[:all]
- cmd += %w[--all --reverse]
- else
- cmd << sha
- end
-
- # :path can be a string or an array of strings
- if options[:path].present?
- cmd << '--'
- cmd += Array(options[:path])
- end
-
- raw_output, _status = run_git(cmd)
- lines = offset_in_ruby ? raw_output.lines.drop(offset) : raw_output.lines
-
- lines.map! { |c| Rugged::Commit.new(rugged, c.strip) }
- end
-
- # We are trying to deprecate this method because it does a lot of work
- # but it seems to be used only to look up submodule URL's.
- # https://gitlab.com/gitlab-org/gitaly/issues/329
- def submodules(ref)
- commit = rev_parse_target(ref)
- return {} unless commit
-
- begin
- content = blob_content(commit, ".gitmodules")
- rescue InvalidBlobName
- return {}
- end
-
- parser = GitmodulesParser.new(content)
- fill_submodule_ids(commit, parser.parse)
- end
-
def gitaly_submodule_url_for(ref, path)
# We don't care about the contents so 1 byte is enough. Can't request 0 bytes, 0 means unlimited.
commit_object = gitaly_commit_client.tree_entry(ref, path, 1)
@@ -1626,79 +1207,6 @@ module Gitlab
Gitlab::Git::HookEnv.all(gl_repository).values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
end
- # Get the content of a blob for a given commit. If the blob is a commit
- # (for submodules) then return the blob's OID.
- def blob_content(commit, blob_name)
- blob_entry = tree_entry(commit, blob_name)
-
- unless blob_entry
- raise InvalidBlobName.new("Invalid blob name: #{blob_name}")
- end
-
- case blob_entry[:type]
- when :commit
- blob_entry[:oid]
- when :tree
- raise InvalidBlobName.new("#{blob_name} is a tree, not a blob")
- when :blob
- rugged.lookup(blob_entry[:oid]).content
- end
- end
-
- # Fill in the 'id' field of a submodule hash from its values
- # as-of +commit+. Return a Hash consisting only of entries
- # from the submodule hash for which the 'id' field is filled.
- def fill_submodule_ids(commit, submodule_data)
- submodule_data.each do |path, data|
- id = begin
- blob_content(commit, path)
- rescue InvalidBlobName
- nil
- end
- data['id'] = id
- end
- submodule_data.select { |path, data| data['id'] }
- end
-
- # Find the entry for +path+ in the tree for +commit+
- def tree_entry(commit, path)
- pathname = Pathname.new(path)
- first = true
- tmp_entry = nil
-
- pathname.each_filename do |dir|
- if first
- tmp_entry = commit.tree[dir]
- first = false
- elsif tmp_entry.nil?
- return nil
- else
- begin
- tmp_entry = rugged.lookup(tmp_entry[:oid])
- rescue Rugged::OdbError, Rugged::InvalidError, Rugged::ReferenceError
- return nil
- end
-
- return nil unless tmp_entry.type == :tree
-
- tmp_entry = tmp_entry[dir]
- end
- end
-
- tmp_entry
- end
-
- # Return the Rugged patches for the diff between +from+ and +to+.
- def diff_patches(from, to, options = {}, *paths)
- options ||= {}
- break_rewrites = options[:break_rewrites]
- actual_options = Gitlab::Git::Diff.filter_diff_options(options.merge(paths: paths))
-
- diff = rugged.diff(from, to, actual_options)
- diff.find_similar!(break_rewrites: break_rewrites)
- diff.each_patch
- end
-
def sort_branches(branches, sort_by)
case sort_by
when 'name'
@@ -1716,22 +1224,6 @@ module Gitlab
end
end
- def last_commit_for_path_by_rugged(sha, path)
- sha = last_commit_id_for_path_by_shelling_out(sha, path)
- commit(sha)
- end
-
- # Returns true if the given ref name exists
- #
- # Ref names must start with `refs/`.
- def rugged_ref_exists?(ref_name)
- raise ArgumentError, 'invalid refname' unless ref_name.start_with?('refs/')
-
- rugged.references.exist?(ref_name)
- rescue Rugged::ReferenceError
- false
- end
-
# Returns true if the given ref name exists
#
# Ref names must start with `refs/`.
@@ -1739,414 +1231,21 @@ module Gitlab
gitaly_ref_client.ref_exists?(ref_name)
end
- # Returns true if the given tag exists
- #
- # name - The name of the tag as a String.
- def rugged_tag_exists?(name)
- !!rugged.tags[name]
- end
-
- # Returns true if the given branch exists
- #
- # name - The name of the branch as a String.
- def rugged_branch_exists?(name)
- rugged.branches.exists?(name)
-
- # If the branch name is invalid (e.g. ".foo") Rugged will raise an error.
- # Whatever code calls this method shouldn't have to deal with that so
- # instead we just return `false` (which is true since a branch doesn't
- # exist when it has an invalid name).
- rescue Rugged::ReferenceError
- false
- end
-
- def gitaly_add_tag(tag_name, user:, target:, message: nil)
- gitaly_operations_client.add_tag(tag_name, user, target, message)
- end
-
- def rugged_add_tag(tag_name, user:, target:, message: nil)
- target_object = Ref.dereference_object(lookup(target))
- raise InvalidRef.new("target not found: #{target}") unless target_object
-
- user = Gitlab::Git::User.from_gitlab(user) unless user.respond_to?(:gl_id)
-
- options = nil # Use nil, not the empty hash. Rugged cares about this.
- if message
- options = {
- message: message,
- tagger: Gitlab::Git.committer_hash(email: user.email, name: user.name)
- }
- end
-
- Gitlab::Git::OperationService.new(user, self).add_tag(tag_name, target_object.oid, options)
-
- find_tag(tag_name)
- rescue Rugged::ReferenceError => ex
- raise InvalidRef, ex
- rescue Rugged::TagError
- raise TagExistsError
- end
-
- def rugged_create_branch(ref, start_point)
- rugged_ref = rugged.branches.create(ref, start_point)
- target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
- Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
- rescue Rugged::ReferenceError => e
- raise InvalidRef.new("Branch #{ref} already exists") if e.to_s =~ %r{'refs/heads/#{ref}'}
-
- raise InvalidRef.new("Invalid reference #{start_point}")
- end
-
def gitaly_copy_gitattributes(revision)
gitaly_repository_client.apply_gitattributes(revision)
end
- def rugged_copy_gitattributes(ref)
- begin
- commit = lookup(ref)
- rescue Rugged::ReferenceError
- raise InvalidRef.new("Ref #{ref} is invalid")
- end
-
- # Create the paths
- info_dir_path = File.join(path, 'info')
- info_attributes_path = File.join(info_dir_path, 'attributes')
-
- begin
- # Retrieve the contents of the blob
- gitattributes_content = blob_content(commit, '.gitattributes')
- rescue InvalidBlobName
- # No .gitattributes found. Should now remove any info/attributes and return
- File.delete(info_attributes_path) if File.exist?(info_attributes_path)
- return
- end
-
- # Create the info directory if needed
- Dir.mkdir(info_dir_path) unless File.directory?(info_dir_path)
-
- # Write the contents of the .gitattributes file to info/attributes
- # Use binary mode to prevent Rails from converting ASCII-8BIT to UTF-8
- File.open(info_attributes_path, "wb") do |file|
- file.write(gitattributes_content)
- end
- end
-
- def rugged_revert(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
- OperationService.new(user, self).with_branch(
- branch_name,
- start_branch_name: start_branch_name,
- start_repository: start_repository
- ) do |start_commit|
-
- Gitlab::Git.check_namespace!(commit, start_repository)
-
- revert_tree_id = check_revert_content(commit, start_commit.sha)
- raise CreateTreeError unless revert_tree_id
-
- committer = user_to_committer(user)
-
- create_commit(message: message,
- author: committer,
- committer: committer,
- tree: revert_tree_id,
- parents: [start_commit.sha])
- end
- end
-
- def rugged_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
- OperationService.new(user, self).with_branch(
- branch_name,
- start_branch_name: start_branch_name,
- start_repository: start_repository
- ) do |start_commit|
-
- Gitlab::Git.check_namespace!(commit, start_repository)
-
- cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha)
- raise CreateTreeError unless cherry_pick_tree_id
-
- committer = user_to_committer(user)
-
- create_commit(message: message,
- author: {
- email: commit.author_email,
- name: commit.author_name,
- time: commit.authored_date
- },
- committer: committer,
- tree: cherry_pick_tree_id,
- parents: [start_commit.sha])
- end
- end
-
- def check_cherry_pick_content(target_commit, source_sha)
- args = [target_commit.sha, source_sha]
- args << 1 if target_commit.merge_commit?
-
- cherry_pick_index = rugged.cherrypick_commit(*args)
- return false if cherry_pick_index.conflicts?
-
- tree_id = cherry_pick_index.write_tree(rugged)
- return false unless diff_exists?(source_sha, tree_id)
-
- tree_id
- end
-
- def gitaly_rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:)
- gitaly_operation_client.user_rebase(user, rebase_id,
- branch: branch,
- branch_sha: branch_sha,
- remote_repository: remote_repository,
- remote_branch: remote_branch)
- end
-
- def git_rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:)
- rebase_path = worktree_path(REBASE_WORKTREE_PREFIX, rebase_id)
- env = git_env_for_user(user)
-
- if remote_repository.is_a?(RemoteRepository)
- env.merge!(remote_repository.fetch_env)
- remote_repo_path = GITALY_INTERNAL_URL
- else
- remote_repo_path = remote_repository.path
- end
-
- with_worktree(rebase_path, branch, env: env) do
- run_git!(
- %W(pull --rebase #{remote_repo_path} #{remote_branch}),
- chdir: rebase_path, env: env
- )
-
- rebase_sha = run_git!(%w(rev-parse HEAD), chdir: rebase_path, env: env).strip
-
- update_branch(branch, user: user, newrev: rebase_sha, oldrev: branch_sha)
-
- rebase_sha
- end
- end
-
- def git_squash(user, squash_id, branch, start_sha, end_sha, author, message)
- squash_path = worktree_path(SQUASH_WORKTREE_PREFIX, squash_id)
- env = git_env_for_user(user).merge(
- 'GIT_AUTHOR_NAME' => author.name,
- 'GIT_AUTHOR_EMAIL' => author.email
- )
- diff_range = "#{start_sha}...#{end_sha}"
- diff_files = run_git!(
- %W(diff --name-only --diff-filter=ar --binary #{diff_range})
- ).chomp
-
- with_worktree(squash_path, branch, sparse_checkout_files: diff_files, env: env) do
- # Apply diff of the `diff_range` to the worktree
- diff = run_git!(%W(diff --binary #{diff_range}))
- run_git!(%w(apply --index --whitespace=nowarn), chdir: squash_path, env: env) do |stdin|
- stdin.binmode
- stdin.write(diff)
- end
-
- # Commit the `diff_range` diff
- run_git!(%W(commit --no-verify --message #{message}), chdir: squash_path, env: env)
-
- # Return the squash sha. May print a warning for ambiguous refs, but
- # we can ignore that with `--quiet` and just take the SHA, if present.
- # HEAD here always refers to the current HEAD commit, even if there is
- # another ref called HEAD.
- run_git!(
- %w(rev-parse --quiet --verify HEAD), chdir: squash_path, env: env
- ).chomp
- end
- end
-
- def local_fetch_ref(source_path, source_ref:, target_ref:)
- args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
- run_git(args)
- end
-
- def gitaly_fetch_ref(source_repository, source_ref:, target_ref:)
- args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref})
-
- run_git(args, env: source_repository.fetch_env)
- end
-
- def gitaly_ff_merge(user, source_sha, target_branch)
- gitaly_operations_client.user_ff_branch(user, source_sha, target_branch)
- rescue GRPC::FailedPrecondition => e
- raise CommitError, e
- end
-
- def rugged_ff_merge(user, source_sha, target_branch)
- OperationService.new(user, self).with_branch(target_branch) do |our_commit|
- raise ArgumentError, 'Invalid merge target' unless our_commit
-
- source_sha
- end
- rescue Rugged::ReferenceError, InvalidRef
- raise ArgumentError, 'Invalid merge source'
- end
-
- def rugged_add_remote(remote_name, url, mirror_refmap)
- rugged.remotes.create(remote_name, url)
-
- set_remote_as_mirror(remote_name, refmap: mirror_refmap) if mirror_refmap
- rescue Rugged::ConfigError
- remote_update(remote_name, url: url)
- end
-
- def git_delete_refs(*ref_names)
- instructions = ref_names.map do |ref|
- "delete #{ref}\x00\x00"
- end
-
- message, status = run_git(%w[update-ref --stdin -z]) do |stdin|
- stdin.write(instructions.join)
- end
-
- unless status.zero?
- raise GitError.new("Could not delete refs #{ref_names}: #{message}")
- end
- end
-
def gitaly_delete_refs(*ref_names)
gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any?
end
- def rugged_remove_remote(remote_name)
- # When a remote is deleted all its remote refs are deleted too, but in
- # the case of mirrors we map its refs (that would usualy go under
- # [remote_name]/) to the top level namespace. We clean the mapping so
- # those don't get deleted.
- if rugged.config["remote.#{remote_name}.mirror"]
- rugged.config.delete("remote.#{remote_name}.fetch")
- end
-
- rugged.remotes.delete(remote_name)
- true
- rescue Rugged::ConfigError
- false
- end
-
- def rugged_fetch_repository_as_mirror(repository)
- remote_name = "tmp-#{SecureRandom.hex}"
- repository = RemoteRepository.new(repository) unless repository.is_a?(RemoteRepository)
-
- add_remote(remote_name, GITALY_INTERNAL_URL, mirror_refmap: :all_refs)
- fetch_remote(remote_name, env: repository.fetch_env)
- ensure
- remove_remote(remote_name)
- end
-
- def rugged_multi_action(
- user, branch_name, message, actions, author_email, author_name,
- start_branch_name, start_repository)
-
- OperationService.new(user, self).with_branch(
- branch_name,
- start_branch_name: start_branch_name,
- start_repository: start_repository
- ) do |start_commit|
- index = Gitlab::Git::Index.new(self)
- parents = []
-
- if start_commit
- index.read_tree(start_commit.rugged_commit.tree)
- parents = [start_commit.sha]
- end
-
- actions.each { |opts| index.apply(opts.delete(:action), opts) }
-
- committer = user_to_committer(user)
- author = Gitlab::Git.committer_hash(email: author_email, name: author_name) || committer
- options = {
- tree: index.write_tree,
- message: message,
- parents: parents,
- author: author,
- committer: committer
- }
-
- create_commit(options)
- end
- end
-
- def fetch_remote(remote_name = 'origin', env: nil)
- run_git(['fetch', remote_name], env: env).last.zero?
- end
-
def gitlab_projects_error
raise CommandError, @gitlab_projects.output
end
- def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
- ref ||= root_ref
-
- args = %W(
- log #{ref} --pretty=%H --skip #{offset}
- --max-count #{limit} --grep=#{query} --regexp-ignore-case
- )
- args = args.concat(%W(-- #{path})) if path.present?
-
- git_log_results = run_git(args).first.lines
-
- git_log_results.map { |c| commit(c.chomp) }.compact
- end
-
- def find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
- gitaly_commit_client
- .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
- .map { |c| commit(c) }
- end
-
- def last_commit_for_path_by_gitaly(sha, path)
- gitaly_commit_client.last_commit_for_path(sha, path)
- end
-
- def last_commit_id_for_path_by_shelling_out(sha, path)
- args = %W(rev-list --max-count=1 #{sha} -- #{path})
- run_git_with_timeout(args, Gitlab::Git::Popen::FAST_GIT_PROCESS_TIMEOUT).first.strip
- end
-
- def rugged_merge_base(from, to)
- rugged.merge_base(from, to)
- rescue Rugged::ReferenceError
- nil
- end
-
def rev_list_param(spec)
spec == :all ? ['--all'] : spec
end
-
- def sha_from_ref(ref)
- rev_parse_target(ref).oid
- end
-
- def build_git_cmd(*args)
- object_directories = alternate_object_directories.join(File::PATH_SEPARATOR)
-
- env = { 'PWD' => self.path }
- env['GIT_ALTERNATE_OBJECT_DIRECTORIES'] = object_directories if object_directories.present?
-
- [
- env,
- ::Gitlab.config.git.bin_path,
- *args,
- { chdir: self.path }
- ]
- end
-
- def git_diff_cmd(old_rev, new_rev)
- old_rev = old_rev == ::Gitlab::Git::BLANK_SHA ? ::Gitlab::Git::EMPTY_TREE_ID : old_rev
-
- build_git_cmd('diff', old_rev, new_rev, '--raw')
- end
-
- def git_cat_file_cmd
- format = '%(objectname) %(objectsize) %(rest)'
- build_git_cmd('cat-file', "--batch-check=#{format}")
- end
-
- def format_git_cat_file_script
- File.expand_path('../support/format-git-cat-file-input', __FILE__)
- end
end
end
end
diff --git a/lib/gitlab/git/repository_mirroring.rb b/lib/gitlab/git/repository_mirroring.rb
index e35ea5762eb..65eb5cc18cf 100644
--- a/lib/gitlab/git/repository_mirroring.rb
+++ b/lib/gitlab/git/repository_mirroring.rb
@@ -1,56 +1,28 @@
module Gitlab
module Git
module RepositoryMirroring
- REFMAPS = {
- # With `:all_refs`, the repository is equivalent to the result of `git clone --mirror`
- all_refs: '+refs/*:refs/*',
- heads: '+refs/heads/*:refs/heads/*',
- tags: '+refs/tags/*:refs/tags/*'
- }.freeze
-
- RemoteError = Class.new(StandardError)
-
- def set_remote_as_mirror(remote_name, refmap: :all_refs)
- set_remote_refmap(remote_name, refmap)
-
- rugged.config["remote.#{remote_name}.mirror"] = true
- rugged.config["remote.#{remote_name}.prune"] = true
- end
-
- def remote_tags(remote)
- # Each line has this format: "dc872e9fa6963f8f03da6c8f6f264d0845d6b092\trefs/tags/v1.10.0\n"
- # We want to convert it to: [{ 'v1.10.0' => 'dc872e9fa6963f8f03da6c8f6f264d0845d6b092' }, ...]
- list_remote_tags(remote).map do |line|
- target, path = line.strip.split("\t")
-
- # When the remote repo does not have tags.
- if target.nil? || path.nil?
- Rails.logger.info "Empty or invalid list of tags for remote: #{remote}. Output: #{output}"
- break []
+ def remote_branches(remote_name)
+ gitaly_migrate(:ref_find_all_remote_branches) do |is_enabled|
+ if is_enabled
+ gitaly_ref_client.remote_branches(remote_name)
+ else
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ rugged_remote_branches(remote_name)
+ end
end
-
- name = path.split('/', 3).last
- # We're only interested in tag references
- # See: http://stackoverflow.com/questions/15472107/when-listing-git-ls-remote-why-theres-after-the-tag-name
- next if name =~ /\^\{\}\Z/
-
- target_commit = Gitlab::Git::Commit.find(self, target)
- Gitlab::Git::Tag.new(self, {
- name: name,
- target: target,
- target_commit: target_commit
- })
- end.compact
+ end
end
- def remote_branches(remote_name)
+ private
+
+ def rugged_remote_branches(remote_name)
branches = []
rugged.references.each("refs/remotes/#{remote_name}/*").map do |ref|
name = ref.name.sub(%r{\Arefs/remotes/#{remote_name}/}, '')
begin
- target_commit = Gitlab::Git::Commit.find(self, ref.target)
+ target_commit = Gitlab::Git::Commit.find(self, ref.target.oid)
branches << Gitlab::Git::Branch.new(self, name, ref.target, target_commit)
rescue Rugged::ReferenceError
# Omit invalid branch
@@ -59,37 +31,6 @@ module Gitlab
branches
end
-
- private
-
- def set_remote_refmap(remote_name, refmap)
- Array(refmap).each_with_index do |refspec, i|
- refspec = REFMAPS[refspec] || refspec
-
- # We need multiple `fetch` entries, but Rugged only allows replacing a config, not adding to it.
- # To make sure we start from scratch, we set the first using rugged, and use `git` for any others
- if i == 0
- rugged.config["remote.#{remote_name}.fetch"] = refspec
- else
- run_git(%W[config --add remote.#{remote_name}.fetch #{refspec}])
- end
- end
- end
-
- def list_remote_tags(remote)
- tag_list, exit_code, error = nil
- cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path} ls-remote --tags #{remote})
-
- Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thr|
- tag_list = stdout.read
- error = stderr.read
- exit_code = wait_thr.value.exitstatus
- end
-
- raise RemoteError, error unless exit_code.zero?
-
- tag_list.split("\n")
- end
end
end
end
diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb
deleted file mode 100644
index 5fdad077eea..00000000000
--- a/lib/gitlab/git/rev_list.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-module Gitlab
- module Git
- class RevList
- include Gitlab::Git::Popen
-
- attr_reader :oldrev, :newrev, :repository
-
- def initialize(repository, newrev:, oldrev: nil)
- @oldrev = oldrev
- @newrev = newrev
- @repository = repository
- end
-
- # This method returns an array of new commit references
- def new_refs
- repository.rev_list(including: newrev, excluding: :all).split("\n")
- end
-
- # Finds newly added objects
- # Returns an array of shas
- #
- # Can skip objects which do not have a path using required_path: true
- # This skips commit objects and root trees, which might not be needed when
- # looking for blobs
- #
- # When given a block it will yield objects as a lazy enumerator so
- # the caller can limit work done instead of processing megabytes of data
- def new_objects(options: [], require_path: nil, not_in: nil, &lazy_block)
- opts = {
- including: newrev,
- options: options,
- excluding: not_in.nil? ? :all : not_in,
- require_path: require_path
- }
-
- get_objects(opts, &lazy_block)
- end
-
- def all_objects(options: [], require_path: nil, &lazy_block)
- get_objects(including: :all,
- options: options,
- require_path: require_path,
- &lazy_block)
- end
-
- private
-
- def execute(args)
- repository.rev_list(args).split("\n")
- end
-
- def get_objects(including: [], excluding: [], options: [], require_path: nil)
- opts = { including: including, excluding: excluding, options: options, objects: true }
-
- repository.rev_list(opts) do |lazy_output|
- objects = objects_from_output(lazy_output, require_path: require_path)
-
- yield(objects)
- end
- end
-
- def objects_from_output(object_output, require_path: nil)
- object_output.map do |output_line|
- sha, path = output_line.split(' ', 2)
-
- next if require_path && path.to_s.empty?
-
- sha
- end.reject(&:nil?)
- end
- end
- end
-end
diff --git a/lib/gitlab/git/support/format-git-cat-file-input b/lib/gitlab/git/support/format-git-cat-file-input
deleted file mode 100755
index 2e93c646d0f..00000000000
--- a/lib/gitlab/git/support/format-git-cat-file-input
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env ruby
-
-# This script formats the output of the `git diff <old_rev> <new_rev> --raw`
-# command so it can be processed by `git cat-file`
-
-# We need to convert this:
-# ":100644 100644 5f53439... 85bc2f9... R060\tfiles/js/commit.js.coffee\tfiles/js/commit.coffee"
-# To:
-# "85bc2f9 R\tfiles/js/commit.js.coffee\tfiles/js/commit.coffee"
-
-ARGF.each do |line|
- _, _, old_blob_id, new_blob_id, rest = line.split(/\s/, 5)
-
- old_blob_id.gsub!(/[^\h]/, '')
- new_blob_id.gsub!(/[^\h]/, '')
-
- # We can't pass '0000000...' to `git cat-file` given it will not return info about the deleted file
- blob_id = new_blob_id =~ /\A0+\z/ ? old_blob_id : new_blob_id
-
- $stdout.puts "#{blob_id} #{rest}"
-end
diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb
index b6ceb542dd1..cb851b76a23 100644
--- a/lib/gitlab/git/tree.rb
+++ b/lib/gitlab/git/tree.rb
@@ -1,5 +1,3 @@
-# Gitaly note: JV: needs 1 RPC, migration is in progress.
-
module Gitlab
module Git
class Tree
@@ -17,12 +15,8 @@ module Gitlab
def where(repository, sha, path = nil, recursive = false)
path = nil if path == '' || path == '/'
- Gitlab::GitalyClient.migrate(:tree_entries) do |is_enabled|
- if is_enabled
- repository.gitaly_commit_client.tree_entries(repository, sha, path, recursive)
- else
- tree_entries_from_rugged(repository, sha, path, recursive)
- end
+ repository.wrapped_gitaly_errors do
+ repository.gitaly_commit_client.tree_entries(repository, sha, path, recursive)
end
end
diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb
index 8ee46b59830..9d992be66eb 100644
--- a/lib/gitlab/git/wiki.rb
+++ b/lib/gitlab/git/wiki.rb
@@ -44,9 +44,9 @@ module Gitlab
end
end
- def pages(limit: nil)
+ def pages(limit: 0)
@repository.wrapped_gitaly_errors do
- gitaly_get_all_pages
+ gitaly_get_all_pages(limit: limit)
end
end
@@ -158,8 +158,8 @@ module Gitlab
Gitlab::Git::WikiFile.new(wiki_file)
end
- def gitaly_get_all_pages
- gitaly_wiki_client.get_all_pages.map do |wiki_page, version|
+ def gitaly_get_all_pages(limit: 0)
+ gitaly_wiki_client.get_all_pages(limit: limit).map do |wiki_page, version|
Gitlab::Git::WikiPage.new(wiki_page, version)
end
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index db7c29be94b..35808149b90 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -2,6 +2,8 @@
# class return an instance of `GitlabAccessStatus`
module Gitlab
class GitAccess
+ include Gitlab::Utils::StrongMemoize
+
UnauthorizedError = Class.new(StandardError)
NotFoundError = Class.new(StandardError)
ProjectCreationError = Class.new(StandardError)
@@ -26,7 +28,7 @@ module Gitlab
PUSH_COMMANDS = %w{ git-receive-pack }.freeze
ALL_COMMANDS = DOWNLOAD_COMMANDS + PUSH_COMMANDS
- attr_reader :actor, :project, :protocol, :authentication_abilities, :namespace_path, :project_path, :redirected_path, :auth_result_type
+ attr_reader :actor, :project, :protocol, :authentication_abilities, :namespace_path, :project_path, :redirected_path, :auth_result_type, :changes
def initialize(actor, project, protocol, authentication_abilities:, namespace_path: nil, project_path: nil, redirected_path: nil, auth_result_type: nil)
@actor = actor
@@ -40,6 +42,8 @@ module Gitlab
end
def check(cmd, changes)
+ @changes = changes
+
check_protocol!
check_valid_actor!
check_active_user!
@@ -58,7 +62,7 @@ module Gitlab
when *DOWNLOAD_COMMANDS
check_download_access!
when *PUSH_COMMANDS
- check_push_access!(changes)
+ check_push_access!
end
true
@@ -218,7 +222,7 @@ module Gitlab
end
end
- def check_push_access!(changes)
+ def check_push_access!
if project.repository_read_only?
raise UnauthorizedError, ERROR_MESSAGES[:read_only]
end
@@ -235,17 +239,15 @@ module Gitlab
return if changes.blank? # Allow access this is needed for EE.
- check_change_access!(changes)
+ check_change_access!
end
- def check_change_access!(changes)
+ def check_change_access!
# If there are worktrees with a HEAD pointing to a non-existent object,
# calls to `git rev-list --all` will fail in git 2.15+. This should also
# clear stale lock files.
project.repository.clean_stale_repository_files
- changes_list = Gitlab::ChangesList.new(changes)
-
# Iterate over all changes to find if user allowed all of them to be applied
changes_list.each.with_index do |change, index|
first_change = index == 0
@@ -321,6 +323,10 @@ module Gitlab
protected
+ def changes_list
+ @changes_list ||= Gitlab::ChangesList.new(changes)
+ end
+
def user
return @user if defined?(@user)
diff --git a/lib/gitlab/git_post_receive.rb b/lib/gitlab/git_post_receive.rb
index 742118b76a8..e731e654f3c 100644
--- a/lib/gitlab/git_post_receive.rb
+++ b/lib/gitlab/git_post_receive.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
class GitPostReceive
include Gitlab::Identifier
@@ -14,10 +16,11 @@ module Gitlab
end
def changes_refs
- return enum_for(:changes_refs) unless block_given?
+ return changes unless block_given?
changes.each do |change|
- oldrev, newrev, ref = change.strip.split(' ')
+ change.strip!
+ oldrev, newrev, ref = change.split(' ')
yield oldrev, newrev, ref
end
@@ -26,13 +29,10 @@ module Gitlab
private
def deserialize_changes(changes)
- changes = utf8_encode_changes(changes)
- changes.lines
+ utf8_encode_changes(changes).each_line
end
def utf8_encode_changes(changes)
- changes = changes.dup
-
changes.force_encoding('UTF-8')
return changes if changes.valid_encoding?
diff --git a/lib/gitlab/git_ref_validator.rb b/lib/gitlab/git_ref_validator.rb
index 2e3e4fc3f1f..40636fb204e 100644
--- a/lib/gitlab/git_ref_validator.rb
+++ b/lib/gitlab/git_ref_validator.rb
@@ -7,11 +7,11 @@ module Gitlab
#
# Returns true for a valid reference name, false otherwise
def validate(ref_name)
- return false if ref_name.start_with?('refs/heads/')
- return false if ref_name.start_with?('refs/remotes/')
+ not_allowed_prefixes = %w(refs/heads/ refs/remotes/ -)
+ return false if ref_name.start_with?(*not_allowed_prefixes)
+ return false if ref_name == 'HEAD'
- Gitlab::Utils.system_silent(
- %W(#{Gitlab.config.git.bin_path} check-ref-format --branch #{ref_name}))
+ Rugged::Reference.valid_name? "refs/heads/#{ref_name}"
end
end
end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 620362b52a9..c27972a84a4 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -401,13 +401,13 @@ module Gitlab
path.read.chomp
end
- def self.timestamp(t)
- Google::Protobuf::Timestamp.new(seconds: t.to_i)
+ def self.timestamp(time)
+ Google::Protobuf::Timestamp.new(seconds: time.to_i)
end
# The default timeout on all Gitaly calls
def self.default_timeout
- return 0 if Sidekiq.server?
+ return no_timeout if Sidekiq.server?
timeout(:gitaly_timeout_default)
end
@@ -420,6 +420,10 @@ module Gitlab
timeout(:gitaly_timeout_medium)
end
+ def self.no_timeout
+ 0
+ end
+
def self.timeout(timeout_name)
Gitlab::CurrentSettings.current_application_settings[timeout_name]
end
@@ -429,7 +433,7 @@ module Gitlab
def self.count_stack
return unless RequestStore.active?
- stack_string = caller.drop(1).join("\n")
+ stack_string = Gitlab::Profiler.clean_backtrace(caller).drop(1).join("\n")
RequestStore.store[:stack_counter] ||= Hash.new
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index 28554208984..1840bf45154 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -13,7 +13,7 @@ module Gitlab
oid: oid,
limit: limit
)
- response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_blob, request)
+ response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_blob, request, timeout: GitalyClient.fast_timeout)
data = ''
blob = nil
@@ -43,7 +43,7 @@ module Gitlab
blob_ids: blob_ids
)
- response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_lfs_pointers, request)
+ response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
map_lfs_pointers(response)
end
@@ -66,7 +66,7 @@ module Gitlab
:blob_service,
:get_blobs,
request,
- timeout: GitalyClient.default_timeout
+ timeout: GitalyClient.fast_timeout
)
GitalyClient::BlobsStitcher.new(response)
@@ -85,7 +85,7 @@ module Gitlab
request.not_in_refs += not_in
end
- response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_new_lfs_pointers, request)
+ response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_new_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
map_lfs_pointers(response)
end
@@ -96,7 +96,7 @@ module Gitlab
revision: encode_binary(revision)
)
- response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_all_lfs_pointers, request)
+ response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_all_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
map_lfs_pointers(response)
end
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index c9c414e5d33..6a97cd8ed17 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -70,7 +70,7 @@ module Gitlab
def commit_deltas(commit)
request = Gitaly::CommitDeltaRequest.new(diff_from_parent_request_params(commit))
- response = GitalyClient.call(@repository.storage, :diff_service, :commit_delta, request)
+ response = GitalyClient.call(@repository.storage, :diff_service, :commit_delta, request, timeout: GitalyClient.fast_timeout)
response.flat_map { |msg| msg.deltas }
end
@@ -302,7 +302,7 @@ module Gitlab
end
end
- response = GitalyClient.call(@repository.storage, :commit_service, :filter_shas_with_signatures, enum)
+ response = GitalyClient.call(@repository.storage, :commit_service, :filter_shas_with_signatures, enum, timeout: GitalyClient.fast_timeout)
response.flat_map do |msg|
msg.shas.map { |sha| EncodingHelper.encode!(sha) }
@@ -330,7 +330,7 @@ module Gitlab
def get_commit_signatures(commit_ids)
request = Gitaly::GetCommitSignaturesRequest.new(repository: @gitaly_repo, commit_ids: commit_ids)
- response = GitalyClient.call(@repository.storage, :commit_service, :get_commit_signatures, request)
+ response = GitalyClient.call(@repository.storage, :commit_service, :get_commit_signatures, request, timeout: GitalyClient.fast_timeout)
signatures = Hash.new { |h, k| h[k] = [''.b, ''.b] }
current_commit_id = nil
@@ -349,7 +349,7 @@ module Gitlab
def get_commit_messages(commit_ids)
request = Gitaly::GetCommitMessagesRequest.new(repository: @gitaly_repo, commit_ids: commit_ids)
- response = GitalyClient.call(@repository.storage, :commit_service, :get_commit_messages, request)
+ response = GitalyClient.call(@repository.storage, :commit_service, :get_commit_messages, request, timeout: GitalyClient.fast_timeout)
messages = Hash.new { |h, k| h[k] = ''.b }
current_commit_id = nil
@@ -368,7 +368,7 @@ module Gitlab
def call_commit_diff(request_params, options = {})
request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false)
request_params[:enforce_limits] = options.fetch(:limits, true)
- request_params[:collapse_diffs] = request_params[:enforce_limits] || !options.fetch(:expanded, true)
+ request_params[:collapse_diffs] = !options.fetch(:expanded, true)
request_params.merge!(Gitlab::Git::DiffCollection.collection_limits(options).to_h)
request = Gitaly::CommitDiffRequest.new(request_params)
@@ -399,8 +399,8 @@ module Gitlab
end
end
- def encode_repeated(a)
- Google::Protobuf::RepeatedField.new(:bytes, a.map { |s| encode_binary(s) } )
+ def encode_repeated(array)
+ Google::Protobuf::RepeatedField.new(:bytes, array.map { |s| encode_binary(s) } )
end
def call_find_commit(revision)
diff --git a/lib/gitlab/gitaly_client/conflicts_service.rb b/lib/gitlab/gitaly_client/conflicts_service.rb
index e14734495a8..aa7e03301f5 100644
--- a/lib/gitlab/gitaly_client/conflicts_service.rb
+++ b/lib/gitlab/gitaly_client/conflicts_service.rb
@@ -25,10 +25,12 @@ module Gitlab
def conflicts?
list_conflict_files.any?
- rescue GRPC::FailedPrecondition
- # The server raises this exception when it encounters ConflictSideMissing, which
- # means a conflict exists but its `theirs` or `ours` data is nil due to a non-existent
- # file in one of the trees.
+ rescue GRPC::FailedPrecondition, GRPC::Unknown
+ # The server raises FailedPrecondition when it encounters
+ # ConflictSideMissing, which means a conflict exists but its `theirs` or
+ # `ours` data is nil due to a non-existent file in one of the trees.
+ #
+ # GRPC::Unknown comes from Rugged::ReferenceError and Rugged::OdbError.
true
end
@@ -46,7 +48,7 @@ module Gitlab
end
end
- response = GitalyClient.call(@repository.storage, :conflicts_service, :resolve_conflicts, req_enum, remote_storage: target_repository.storage)
+ response = GitalyClient.call(@repository.storage, :conflicts_service, :resolve_conflicts, req_enum, remote_storage: target_repository.storage, timeout: GitalyClient.medium_timeout)
if response.resolution_error.present?
raise Gitlab::Git::Conflict::Resolver::ResolutionError, response.resolution_error
diff --git a/lib/gitlab/gitaly_client/namespace_service.rb b/lib/gitlab/gitaly_client/namespace_service.rb
index bd7c345ac01..d4e982b649a 100644
--- a/lib/gitlab/gitaly_client/namespace_service.rb
+++ b/lib/gitlab/gitaly_client/namespace_service.rb
@@ -8,31 +8,31 @@ module Gitlab
def exists?(name)
request = Gitaly::NamespaceExistsRequest.new(storage_name: @storage, name: name)
- gitaly_client_call(:namespace_exists, request).exists
+ gitaly_client_call(:namespace_exists, request, timeout: GitalyClient.fast_timeout).exists
end
def add(name)
request = Gitaly::AddNamespaceRequest.new(storage_name: @storage, name: name)
- gitaly_client_call(:add_namespace, request)
+ gitaly_client_call(:add_namespace, request, timeout: GitalyClient.fast_timeout)
end
def remove(name)
request = Gitaly::RemoveNamespaceRequest.new(storage_name: @storage, name: name)
- gitaly_client_call(:remove_namespace, request)
+ gitaly_client_call(:remove_namespace, request, timeout: nil)
end
def rename(from, to)
request = Gitaly::RenameNamespaceRequest.new(storage_name: @storage, from: from, to: to)
- gitaly_client_call(:rename_namespace, request)
+ gitaly_client_call(:rename_namespace, request, timeout: GitalyClient.fast_timeout)
end
private
- def gitaly_client_call(type, request)
- GitalyClient.call(@storage, :namespace_service, type, request)
+ def gitaly_client_call(type, request, timeout: nil)
+ GitalyClient.call(@storage, :namespace_service, type, request, timeout: timeout)
end
end
end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index e9d4bb4c4b6..54c78fdb680 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -17,7 +17,7 @@ module Gitlab
user: Gitlab::Git::User.from_gitlab(user).to_gitaly
)
- response = GitalyClient.call(@repository.storage, :operation_service, :user_delete_tag, request)
+ response = GitalyClient.call(@repository.storage, :operation_service, :user_delete_tag, request, timeout: GitalyClient.medium_timeout)
if pre_receive_error = response.pre_receive_error.presence
raise Gitlab::Git::PreReceiveError, pre_receive_error
@@ -33,7 +33,7 @@ module Gitlab
message: encode_binary(message.to_s)
)
- response = GitalyClient.call(@repository.storage, :operation_service, :user_create_tag, request)
+ response = GitalyClient.call(@repository.storage, :operation_service, :user_create_tag, request, timeout: GitalyClient.medium_timeout)
if pre_receive_error = response.pre_receive_error.presence
raise Gitlab::Git::PreReceiveError, pre_receive_error
elsif response.exists
@@ -64,6 +64,24 @@ module Gitlab
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit)
+ rescue GRPC::FailedPrecondition => ex
+ raise Gitlab::Git::Repository::InvalidRef, ex
+ end
+
+ def user_update_branch(branch_name, user, newrev, oldrev)
+ request = Gitaly::UserUpdateBranchRequest.new(
+ repository: @gitaly_repo,
+ branch_name: encode_binary(branch_name),
+ user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
+ newrev: encode_binary(newrev),
+ oldrev: encode_binary(oldrev)
+ )
+
+ response = GitalyClient.call(@repository.storage, :operation_service, :user_update_branch, request)
+
+ if pre_receive_error = response.pre_receive_error.presence
+ raise Gitlab::Git::PreReceiveError, pre_receive_error
+ end
end
def user_delete_branch(branch_name, user)
@@ -126,13 +144,16 @@ module Gitlab
branch: encode_binary(target_branch)
)
- branch_update = GitalyClient.call(
+ response = GitalyClient.call(
@repository.storage,
:operation_service,
:user_ff_branch,
request
- ).branch_update
- Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update)
+ )
+
+ Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
+ rescue GRPC::FailedPrecondition => e
+ raise Gitlab::Git::CommitError, e
end
def user_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
@@ -272,7 +293,8 @@ module Gitlab
:operation_service,
:"user_#{rpc}",
request,
- remote_storage: start_repository.storage
+ remote_storage: start_repository.storage,
+ timeout: GitalyClient.medium_timeout
)
handle_cherry_pick_or_revert_response(response)
@@ -285,9 +307,9 @@ module Gitlab
raise Gitlab::Git::CommitError, response.commit_error
elsif response.create_tree_error.presence
raise Gitlab::Git::Repository::CreateTreeError, response.create_tree_error
- else
- Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
end
+
+ Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
end
def user_commit_files_request_header(
diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb
index 3ac46be6208..8acc22e809e 100644
--- a/lib/gitlab/gitaly_client/ref_service.rb
+++ b/lib/gitlab/gitaly_client/ref_service.rb
@@ -12,37 +12,44 @@ module Gitlab
def branches
request = Gitaly::FindAllBranchesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :ref_service, :find_all_branches, request)
+ response = GitalyClient.call(@storage, :ref_service, :find_all_branches, request, timeout: GitalyClient.fast_timeout)
consume_find_all_branches_response(response)
end
+ def remote_branches(remote_name)
+ request = Gitaly::FindAllRemoteBranchesRequest.new(repository: @gitaly_repo, remote_name: remote_name)
+ response = GitalyClient.call(@repository.storage, :ref_service, :find_all_remote_branches, request)
+
+ consume_find_all_remote_branches_response(remote_name, response)
+ end
+
def merged_branches(branch_names = [])
request = Gitaly::FindAllBranchesRequest.new(
repository: @gitaly_repo,
merged_only: true,
merged_branches: branch_names.map { |s| encode_binary(s) }
)
- response = GitalyClient.call(@storage, :ref_service, :find_all_branches, request)
+ response = GitalyClient.call(@storage, :ref_service, :find_all_branches, request, timeout: GitalyClient.fast_timeout)
consume_find_all_branches_response(response)
end
def default_branch_name
request = Gitaly::FindDefaultBranchNameRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :ref_service, :find_default_branch_name, request)
+ response = GitalyClient.call(@storage, :ref_service, :find_default_branch_name, request, timeout: GitalyClient.fast_timeout)
Gitlab::Git.branch_name(response.name)
end
def branch_names
request = Gitaly::FindAllBranchNamesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :ref_service, :find_all_branch_names, request)
+ response = GitalyClient.call(@storage, :ref_service, :find_all_branch_names, request, timeout: GitalyClient.fast_timeout)
consume_refs_response(response) { |name| Gitlab::Git.branch_name(name) }
end
def tag_names
request = Gitaly::FindAllTagNamesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :ref_service, :find_all_tag_names, request)
+ response = GitalyClient.call(@storage, :ref_service, :find_all_tag_names, request, timeout: GitalyClient.fast_timeout)
consume_refs_response(response) { |name| Gitlab::Git.tag_name(name) }
end
@@ -56,6 +63,42 @@ module Gitlab
encode!(response.name.dup)
end
+ def list_new_commits(newrev)
+ request = Gitaly::ListNewCommitsRequest.new(
+ repository: @gitaly_repo,
+ commit_id: newrev
+ )
+
+ response = GitalyClient
+ .call(@storage, :ref_service, :list_new_commits, request, timeout: GitalyClient.medium_timeout)
+
+ commits = []
+ response.each do |msg|
+ msg.commits.each do |c|
+ commits << Gitlab::Git::Commit.new(@repository, c)
+ end
+ end
+
+ commits
+ end
+
+ def list_new_blobs(newrev, limit = 0)
+ request = Gitaly::ListNewBlobsRequest.new(
+ repository: @gitaly_repo,
+ commit_id: newrev,
+ limit: limit
+ )
+
+ response = GitalyClient
+ .call(@storage, :ref_service, :list_new_blobs, request, timeout: GitalyClient.medium_timeout)
+
+ response.flat_map do |msg|
+ # Returns an Array of Gitaly::NewBlobObject objects
+ # Available methods are: #size, #oid and #path
+ msg.new_blob_objects
+ end
+ end
+
def count_tag_names
tag_names.count
end
@@ -67,19 +110,19 @@ module Gitlab
def local_branches(sort_by: nil)
request = Gitaly::FindLocalBranchesRequest.new(repository: @gitaly_repo)
request.sort_by = sort_by_param(sort_by) if sort_by
- response = GitalyClient.call(@storage, :ref_service, :find_local_branches, request)
+ response = GitalyClient.call(@storage, :ref_service, :find_local_branches, request, timeout: GitalyClient.fast_timeout)
consume_find_local_branches_response(response)
end
def tags
request = Gitaly::FindAllTagsRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :ref_service, :find_all_tags, request)
+ response = GitalyClient.call(@storage, :ref_service, :find_all_tags, request, timeout: GitalyClient.medium_timeout)
consume_tags_response(response)
end
def ref_exists?(ref_name)
request = Gitaly::RefExistsRequest.new(repository: @gitaly_repo, ref: encode_binary(ref_name))
- response = GitalyClient.call(@storage, :ref_service, :ref_exists, request)
+ response = GitalyClient.call(@storage, :ref_service, :ref_exists, request, timeout: GitalyClient.fast_timeout)
response.value
rescue GRPC::InvalidArgument => e
raise ArgumentError, e.message
@@ -91,7 +134,7 @@ module Gitlab
name: encode_binary(branch_name)
)
- response = GitalyClient.call(@repository.storage, :ref_service, :find_branch, request)
+ response = GitalyClient.call(@repository.storage, :ref_service, :find_branch, request, timeout: GitalyClient.medium_timeout)
branch = response.branch
return unless branch
@@ -140,7 +183,7 @@ module Gitlab
except_with_prefix: except_with_prefixes.map { |r| encode_binary(r) }
)
- response = GitalyClient.call(@repository.storage, :ref_service, :delete_refs, request)
+ response = GitalyClient.call(@repository.storage, :ref_service, :delete_refs, request, timeout: GitalyClient.default_timeout)
raise Gitlab::Git::Repository::GitError, response.git_error if response.git_error.present?
end
@@ -153,7 +196,7 @@ module Gitlab
limit: limit
)
- stream = GitalyClient.call(@repository.storage, :ref_service, :list_tag_names_containing_commit, request)
+ stream = GitalyClient.call(@repository.storage, :ref_service, :list_tag_names_containing_commit, request, timeout: GitalyClient.medium_timeout)
consume_ref_contains_sha_response(stream, :tag_names)
end
@@ -166,14 +209,14 @@ module Gitlab
limit: limit
)
- stream = GitalyClient.call(@repository.storage, :ref_service, :list_branch_names_containing_commit, request)
+ stream = GitalyClient.call(@repository.storage, :ref_service, :list_branch_names_containing_commit, request, timeout: GitalyClient.medium_timeout)
consume_ref_contains_sha_response(stream, :branch_names)
end
def get_tag_messages(tag_ids)
request = Gitaly::GetTagMessagesRequest.new(repository: @gitaly_repo, tag_ids: tag_ids)
- response = GitalyClient.call(@repository.storage, :ref_service, :get_tag_messages, request)
+ response = GitalyClient.call(@repository.storage, :ref_service, :get_tag_messages, request, timeout: GitalyClient.fast_timeout)
messages = Hash.new { |h, k| h[k] = ''.b }
current_tag_id = nil
@@ -224,6 +267,18 @@ module Gitlab
end
end
+ def consume_find_all_remote_branches_response(remote_name, response)
+ remote_name += '/' unless remote_name.ends_with?('/')
+
+ response.flat_map do |message|
+ message.branches.map do |branch|
+ target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
+ branch_name = branch.name.sub(remote_name, '')
+ Gitlab::Git::Branch.new(@repository, branch_name, branch.target_commit.id, target_commit)
+ end
+ end
+ end
+
def consume_tags_response(response)
response.flat_map do |message|
message.tags.map { |gitaly_tag| Gitlab::Git::Tag.new(@repository, gitaly_tag) }
diff --git a/lib/gitlab/gitaly_client/remote_service.rb b/lib/gitlab/gitaly_client/remote_service.rb
index f2d699d9dfb..1381e033d4b 100644
--- a/lib/gitlab/gitaly_client/remote_service.rb
+++ b/lib/gitlab/gitaly_client/remote_service.rb
@@ -28,13 +28,13 @@ module Gitlab
mirror_refmaps: Array.wrap(mirror_refmaps).map(&:to_s)
)
- GitalyClient.call(@storage, :remote_service, :add_remote, request)
+ GitalyClient.call(@storage, :remote_service, :add_remote, request, timeout: GitalyClient.fast_timeout)
end
def remove_remote(name)
request = Gitaly::RemoveRemoteRequest.new(repository: @gitaly_repo, name: name)
- response = GitalyClient.call(@storage, :remote_service, :remove_remote, request)
+ response = GitalyClient.call(@storage, :remote_service, :remove_remote, request, timeout: GitalyClient.fast_timeout)
response.result
end
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index ca986434221..2956ed4b911 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -21,7 +21,7 @@ module Gitlab
def cleanup
request = Gitaly::CleanupRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@storage, :repository_service, :cleanup, request)
+ GitalyClient.call(@storage, :repository_service, :cleanup, request, timeout: GitalyClient.fast_timeout)
end
def garbage_collect(create_bitmap)
@@ -41,19 +41,21 @@ module Gitlab
def repository_size
request = Gitaly::RepositorySizeRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :repository_size, request)
+ response = GitalyClient.call(@storage, :repository_service, :repository_size, request, timeout: GitalyClient.medium_timeout)
response.size
end
def apply_gitattributes(revision)
request = Gitaly::ApplyGitattributesRequest.new(repository: @gitaly_repo, revision: encode_binary(revision))
- GitalyClient.call(@storage, :repository_service, :apply_gitattributes, request)
+ GitalyClient.call(@storage, :repository_service, :apply_gitattributes, request, timeout: GitalyClient.fast_timeout)
+ rescue GRPC::InvalidArgument => ex
+ raise Gitlab::Git::Repository::InvalidRef, ex
end
def info_attributes
request = Gitaly::GetInfoAttributesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :get_info_attributes, request)
+ response = GitalyClient.call(@storage, :repository_service, :get_info_attributes, request, timeout: GitalyClient.fast_timeout)
response.each_with_object("") do |message, attributes|
attributes << message.attributes
end
@@ -80,7 +82,7 @@ module Gitlab
def create_repository
request = Gitaly::CreateRepositoryRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@storage, :repository_service, :create_repository, request)
+ GitalyClient.call(@storage, :repository_service, :create_repository, request, timeout: GitalyClient.medium_timeout)
end
def has_local_branches?
@@ -96,7 +98,7 @@ module Gitlab
revisions: revisions.map { |r| encode_binary(r) }
)
- response = GitalyClient.call(@storage, :repository_service, :find_merge_base, request)
+ response = GitalyClient.call(@storage, :repository_service, :find_merge_base, request, timeout: GitalyClient.fast_timeout)
response.base.presence
end
@@ -200,7 +202,7 @@ module Gitlab
save_path,
:create_bundle,
Gitaly::CreateBundleRequest,
- GitalyClient.default_timeout
+ GitalyClient.no_timeout
)
end
@@ -218,7 +220,7 @@ module Gitlab
bundle_path,
:create_repository_from_bundle,
Gitaly::CreateRepositoryFromBundleRequest,
- GitalyClient.default_timeout
+ GitalyClient.no_timeout
)
end
@@ -243,7 +245,7 @@ module Gitlab
:repository_service,
:create_repository_from_snapshot,
request,
- timeout: GitalyClient.default_timeout
+ timeout: GitalyClient.no_timeout
)
end
@@ -256,24 +258,46 @@ module Gitlab
)
request.old_revision = old_ref.b unless old_ref.nil?
- response = GitalyClient.call(@storage, :repository_service, :write_ref, request)
+ response = GitalyClient.call(@storage, :repository_service, :write_ref, request, timeout: GitalyClient.fast_timeout)
raise Gitlab::Git::CommandError, encode!(response.error) if response.error.present?
true
end
- def write_config(full_path:)
- request = Gitaly::WriteConfigRequest.new(repository: @gitaly_repo, full_path: full_path)
- response = GitalyClient.call(
+ def set_config(entries)
+ return if entries.empty?
+
+ request = Gitaly::SetConfigRequest.new(repository: @gitaly_repo)
+ entries.each do |key, value|
+ request.entries << build_set_config_entry(key, value)
+ end
+
+ GitalyClient.call(
@storage,
:repository_service,
- :write_config,
+ :set_config,
request,
timeout: GitalyClient.fast_timeout
)
- raise Gitlab::Git::OSError.new(response.error) unless response.error.empty?
+ nil
+ end
+
+ def delete_config(keys)
+ return if keys.empty?
+
+ request = Gitaly::DeleteConfigRequest.new(repository: @gitaly_repo, keys: keys)
+
+ GitalyClient.call(
+ @storage,
+ :repository_service,
+ :delete_config,
+ request,
+ timeout: GitalyClient.fast_timeout
+ )
+
+ nil
end
def license_short_name
@@ -286,7 +310,7 @@ module Gitlab
def calculate_checksum
request = Gitaly::CalculateChecksumRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@storage, :repository_service, :calculate_checksum, request)
+ response = GitalyClient.call(@storage, :repository_service, :calculate_checksum, request, timeout: GitalyClient.fast_timeout)
response.checksum.presence
rescue GRPC::DataLoss => e
raise Gitlab::Git::Repository::InvalidRepository.new(e)
@@ -295,12 +319,12 @@ module Gitlab
def raw_changes_between(from, to)
request = Gitaly::GetRawChangesRequest.new(repository: @gitaly_repo, from_revision: from, to_revision: to)
- GitalyClient.call(@storage, :repository_service, :get_raw_changes, request)
+ GitalyClient.call(@storage, :repository_service, :get_raw_changes, request, timeout: GitalyClient.fast_timeout)
end
def search_files_by_name(ref, query)
request = Gitaly::SearchFilesByNameRequest.new(repository: @gitaly_repo, ref: ref, query: query)
- GitalyClient.call(@storage, :repository_service, :search_files_by_name, request).flat_map(&:files)
+ GitalyClient.call(@storage, :repository_service, :search_files_by_name, request, timeout: GitalyClient.fast_timeout).flat_map(&:files)
end
def search_files_by_content(ref, query)
@@ -350,6 +374,23 @@ module Gitlab
timeout: timeout
)
end
+
+ def build_set_config_entry(key, value)
+ entry = Gitaly::SetConfigRequest::Entry.new(key: key)
+
+ case value
+ when String
+ entry.value_str = value
+ when Integer
+ entry.value_int32 = value
+ when TrueClass, FalseClass
+ entry.value_bool = value
+ else
+ raise InvalidArgument, "invalid git config value: #{value.inspect}"
+ end
+
+ entry
+ end
end
end
end
diff --git a/lib/gitlab/gitaly_client/server_service.rb b/lib/gitlab/gitaly_client/server_service.rb
index 2e1076d1f66..ad898278353 100644
--- a/lib/gitlab/gitaly_client/server_service.rb
+++ b/lib/gitlab/gitaly_client/server_service.rb
@@ -9,7 +9,7 @@ module Gitlab
end
def info
- GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new)
+ GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new, timeout: GitalyClient.fast_timeout)
end
end
end
diff --git a/lib/gitlab/gitaly_client/storage_settings.rb b/lib/gitlab/gitaly_client/storage_settings.rb
index 02fcb413abd..8e530de174d 100644
--- a/lib/gitlab/gitaly_client/storage_settings.rb
+++ b/lib/gitlab/gitaly_client/storage_settings.rb
@@ -60,8 +60,8 @@ module Gitlab
private
- def method_missing(m, *args, &block)
- @hash.public_send(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
+ def method_missing(msg, *args, &block)
+ @hash.public_send(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/lib/gitlab/gitaly_client/wiki_service.rb b/lib/gitlab/gitaly_client/wiki_service.rb
index 2dfe055a496..75be7d1f5a0 100644
--- a/lib/gitlab/gitaly_client/wiki_service.rb
+++ b/lib/gitlab/gitaly_client/wiki_service.rb
@@ -69,7 +69,7 @@ module Gitlab
commit_details: gitaly_commit_details(commit_details)
)
- GitalyClient.call(@repository.storage, :wiki_service, :wiki_delete_page, request)
+ GitalyClient.call(@repository.storage, :wiki_service, :wiki_delete_page, request, timeout: GitalyClient.medium_timeout)
end
def find_page(title:, version: nil, dir: nil)
@@ -80,14 +80,14 @@ module Gitlab
directory: encode_binary(dir)
)
- response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_find_page, request)
+ response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_find_page, request, timeout: GitalyClient.fast_timeout)
wiki_page_from_iterator(response)
end
- def get_all_pages
- request = Gitaly::WikiGetAllPagesRequest.new(repository: @gitaly_repo)
- response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_all_pages, request)
+ def get_all_pages(limit: 0)
+ request = Gitaly::WikiGetAllPagesRequest.new(repository: @gitaly_repo, limit: limit)
+ response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_all_pages, request, timeout: GitalyClient.medium_timeout)
pages = []
loop do
@@ -113,7 +113,7 @@ module Gitlab
per_page: options[:per_page] || Gollum::Page.per_page
)
- stream = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_page_versions, request)
+ stream = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_page_versions, request, timeout: GitalyClient.medium_timeout)
versions = []
stream.each do |message|
@@ -132,7 +132,7 @@ module Gitlab
revision: encode_binary(revision)
)
- response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_find_file, request)
+ response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_find_file, request, timeout: GitalyClient.fast_timeout)
wiki_file = nil
response.each do |message|
diff --git a/lib/gitlab/github_import/importer/pull_requests_importer.rb b/lib/gitlab/github_import/importer/pull_requests_importer.rb
index e70361c163b..a52866c4b08 100644
--- a/lib/gitlab/github_import/importer/pull_requests_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_requests_importer.rb
@@ -43,7 +43,7 @@ module Gitlab
Rails.logger
.info("GitHub importer finished updating repository for #{pname}")
- repository_updates_counter.increment(project: pname)
+ repository_updates_counter.increment
end
def update_repository?(pr)
diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb
index 22719e9a003..38ef12491df 100644
--- a/lib/gitlab/gitlab_import/client.rb
+++ b/lib/gitlab/gitlab_import/client.rb
@@ -32,15 +32,15 @@ module Gitlab
api.get("/api/v4/user").parsed
end
- def issues(project_identifier)
- lazy_page_iterator(PER_PAGE) do |page|
- api.get("/api/v4/projects/#{project_identifier}/issues?per_page=#{PER_PAGE}&page=#{page}").parsed
+ def issues(project_identifier, **kwargs)
+ lazy_page_iterator(**kwargs) do |page, per_page|
+ api.get("/api/v4/projects/#{project_identifier}/issues?per_page=#{per_page}&page=#{page}").parsed
end
end
- def issue_comments(project_identifier, issue_id)
- lazy_page_iterator(PER_PAGE) do |page|
- api.get("/api/v4/projects/#{project_identifier}/issues/#{issue_id}/notes?per_page=#{PER_PAGE}&page=#{page}").parsed
+ def issue_comments(project_identifier, issue_id, **kwargs)
+ lazy_page_iterator(**kwargs) do |page, per_page|
+ api.get("/api/v4/projects/#{project_identifier}/issues/#{issue_id}/notes?per_page=#{per_page}&page=#{page}").parsed
end
end
@@ -48,23 +48,27 @@ module Gitlab
api.get("/api/v4/projects/#{id}").parsed
end
- def projects
- lazy_page_iterator(PER_PAGE) do |page|
- api.get("/api/v4/projects?per_page=#{PER_PAGE}&page=#{page}").parsed
+ def projects(**kwargs)
+ lazy_page_iterator(**kwargs) do |page, per_page|
+ api.get("/api/v4/projects?per_page=#{per_page}&page=#{page}&simple=true&membership=true").parsed
end
end
private
- def lazy_page_iterator(per_page)
+ def lazy_page_iterator(starting_page: 1, page_limit: nil, per_page: PER_PAGE)
Enumerator.new do |y|
- page = 1
+ page = starting_page
+ page_limit = (starting_page - 1) + page_limit if page_limit
+
loop do
- items = yield(page)
+ items = yield(page, per_page)
+
items.each do |item|
y << item
end
- break if items.empty? || items.size < per_page
+
+ break if items.empty? || items.size < per_page || (page_limit && page >= page_limit)
page += 1
end
diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb
index 46b49128140..5070f4e3cfe 100644
--- a/lib/gitlab/google_code_import/importer.rb
+++ b/lib/gitlab/google_code_import/importer.rb
@@ -200,27 +200,27 @@ module Gitlab
"Status: #{name}"
end
- def linkify_issues(s)
- s = s.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
- s = s.gsub(/([Cc]omment) #([0-9]+)/, '\1 \2')
- s
+ 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(s)
+ def escape_for_markdown(str)
# No headings and lists
- s = s.gsub(/^#/, "\\#")
- s = s.gsub(/^-/, "\\-")
+ str = str.gsub(/^#/, "\\#")
+ str = str.gsub(/^-/, "\\-")
# No inline code
- s = s.gsub("`", "\\`")
+ str = str.gsub("`", "\\`")
# Carriage returns make me sad
- s = s.delete("\r")
+ str = str.delete("\r")
# Markdown ignores single newlines, but we need them as <br />.
- s = s.gsub("\n", " \n")
+ str = str.gsub("\n", " \n")
- s
+ str
end
def create_label(name)
diff --git a/lib/gitlab/gpg.rb b/lib/gitlab/gpg.rb
index a4263369269..8a91e034377 100644
--- a/lib/gitlab/gpg.rb
+++ b/lib/gitlab/gpg.rb
@@ -71,8 +71,16 @@ module Gitlab
if MUTEX.locked? && MUTEX.owned?
optimistic_using_tmp_keychain(&block)
else
- MUTEX.synchronize do
- optimistic_using_tmp_keychain(&block)
+ if Gitlab.rails5?
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ MUTEX.synchronize do
+ optimistic_using_tmp_keychain(&block)
+ end
+ end
+ else
+ MUTEX.synchronize do
+ optimistic_using_tmp_keychain(&block)
+ end
end
end
end
diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb
index 6d2278d0876..2716834f566 100644
--- a/lib/gitlab/gpg/commit.rb
+++ b/lib/gitlab/gpg/commit.rb
@@ -39,7 +39,7 @@ module Gitlab
def update_signature!(cached_signature)
using_keychain do |gpg_key|
- cached_signature.update_attributes!(attributes(gpg_key))
+ cached_signature.update!(attributes(gpg_key))
end
@signature = cached_signature
diff --git a/lib/gitlab/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb
index 04f25c53e49..93a903915b0 100644
--- a/lib/gitlab/graphql/authorize.rb
+++ b/lib/gitlab/graphql/authorize.rb
@@ -10,7 +10,14 @@ module Gitlab
end
def required_permissions
- @required_permissions ||= []
+ # If the `#authorize` call is used on multiple classes, we add the
+ # permissions specified on a subclass, to the ones that were specified
+ # on it's superclass.
+ @required_permissions ||= if self.respond_to?(:superclass) && superclass.respond_to?(:required_permissions)
+ superclass.required_permissions.dup
+ else
+ []
+ end
end
def authorize(*permissions)
diff --git a/lib/gitlab/graphql/authorize/authorize_resource.rb b/lib/gitlab/graphql/authorize/authorize_resource.rb
new file mode 100644
index 00000000000..40895686a8a
--- /dev/null
+++ b/lib/gitlab/graphql/authorize/authorize_resource.rb
@@ -0,0 +1,46 @@
+module Gitlab
+ module Graphql
+ module Authorize
+ module AuthorizeResource
+ extend ActiveSupport::Concern
+
+ included do
+ extend Gitlab::Graphql::Authorize
+ end
+
+ def find_object(*args)
+ raise NotImplementedError, "Implement #find_object in #{self.class.name}"
+ end
+
+ def authorized_find(*args)
+ object = find_object(*args)
+
+ object if authorized?(object)
+ end
+
+ def authorized_find!(*args)
+ object = find_object(*args)
+ authorize!(object)
+
+ object
+ end
+
+ def authorize!(object)
+ unless authorized?(object)
+ raise 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
+
+ def authorized?(object)
+ self.class.required_permissions.all? do |ability|
+ # The actions could be performed across multiple objects. In which
+ # case the current user is common, and we could benefit from the
+ # caching in `DeclarativePolicy`.
+ Ability.allowed?(current_user, ability, object, scope: :user)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/connections.rb b/lib/gitlab/graphql/connections.rb
new file mode 100644
index 00000000000..2582ffeb2a8
--- /dev/null
+++ b/lib/gitlab/graphql/connections.rb
@@ -0,0 +1,12 @@
+module Gitlab
+ module Graphql
+ module Connections
+ def self.use(_schema)
+ GraphQL::Relay::BaseConnection.register_connection_implementation(
+ ActiveRecord::Relation,
+ Gitlab::Graphql::Connections::KeysetConnection
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/connections/keyset_connection.rb b/lib/gitlab/graphql/connections/keyset_connection.rb
new file mode 100644
index 00000000000..abee2afe144
--- /dev/null
+++ b/lib/gitlab/graphql/connections/keyset_connection.rb
@@ -0,0 +1,73 @@
+module Gitlab
+ module Graphql
+ module Connections
+ class KeysetConnection < GraphQL::Relay::BaseConnection
+ def cursor_from_node(node)
+ encode(node[order_field].to_s)
+ end
+
+ def sliced_nodes
+ @sliced_nodes ||=
+ begin
+ sliced = nodes
+
+ sliced = sliced.where(before_slice) if before.present?
+ sliced = sliced.where(after_slice) if after.present?
+
+ sliced
+ end
+ end
+
+ def paged_nodes
+ if first && last
+ raise Gitlab::Graphql::Errors::ArgumentError.new("Can only provide either `first` or `last`, not both")
+ end
+
+ if last
+ sliced_nodes.last(limit_value)
+ else
+ sliced_nodes.limit(limit_value)
+ end
+ end
+
+ private
+
+ def before_slice
+ if sort_direction == :asc
+ table[order_field].lt(decode(before))
+ else
+ table[order_field].gt(decode(before))
+ end
+ end
+
+ def after_slice
+ if sort_direction == :asc
+ table[order_field].gt(decode(after))
+ else
+ table[order_field].lt(decode(after))
+ end
+ end
+
+ def limit_value
+ @limit_value ||= [first, last, max_page_size].compact.min
+ end
+
+ def table
+ nodes.arel_table
+ end
+
+ def order_info
+ @order_info ||= nodes.order_values.first
+ end
+
+ def order_field
+ @order_field ||= order_info&.expr&.name || nodes.primary_key
+ end
+
+ def sort_direction
+ @order_direction ||= order_info&.direction || :desc
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/errors.rb b/lib/gitlab/graphql/errors.rb
new file mode 100644
index 00000000000..f8c7ec24be1
--- /dev/null
+++ b/lib/gitlab/graphql/errors.rb
@@ -0,0 +1,9 @@
+module Gitlab
+ module Graphql
+ module Errors
+ BaseError = Class.new(GraphQL::ExecutionError)
+ ArgumentError = Class.new(BaseError)
+ ResourceNotAvailable = Class.new(BaseError)
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/mount_mutation.rb b/lib/gitlab/graphql/mount_mutation.rb
new file mode 100644
index 00000000000..8cab84d7a5f
--- /dev/null
+++ b/lib/gitlab/graphql/mount_mutation.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module MountMutation
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def mount_mutation(mutation_class)
+ # Using an underscored field name symbol will make `graphql-ruby`
+ # standardize the field name
+ field mutation_class.graphql_name.underscore.to_sym,
+ mutation: mutation_class
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/present/instrumentation.rb b/lib/gitlab/graphql/present/instrumentation.rb
index 6f2b26c9676..f87fd147b15 100644
--- a/lib/gitlab/graphql/present/instrumentation.rb
+++ b/lib/gitlab/graphql/present/instrumentation.rb
@@ -3,6 +3,8 @@ module Gitlab
module Present
class Instrumentation
def instrument(type, field)
+ return field unless field.metadata[:type_class]
+
presented_in = field.metadata[:type_class].owner
return field unless presented_in.respond_to?(:presenter_class)
return field unless presented_in.presenter_class
diff --git a/lib/gitlab/graphs/commits.rb b/lib/gitlab/graphs/commits.rb
index 3caf9036459..c4ffc19df09 100644
--- a/lib/gitlab/graphs/commits.rb
+++ b/lib/gitlab/graphs/commits.rb
@@ -18,7 +18,7 @@ module Gitlab
end
def commit_per_day
- @commit_per_day ||= @commits.size / (@duration + 1)
+ @commit_per_day ||= (@commits.size.to_f / (@duration + 1)).round(1)
end
def collect_data
diff --git a/lib/gitlab/hashed_storage/migrator.rb b/lib/gitlab/hashed_storage/migrator.rb
index 9251ed654cd..d11fcc6a3e3 100644
--- a/lib/gitlab/hashed_storage/migrator.rb
+++ b/lib/gitlab/hashed_storage/migrator.rb
@@ -30,7 +30,7 @@ module Gitlab
end
end
- # Flag a project to me migrated
+ # Flag a project to be migrated
#
# @param [Object] project that will be migrated
def migrate(project)
diff --git a/lib/gitlab/hook_data/base_builder.rb b/lib/gitlab/hook_data/base_builder.rb
new file mode 100644
index 00000000000..4ffca356b29
--- /dev/null
+++ b/lib/gitlab/hook_data/base_builder.rb
@@ -0,0 +1,38 @@
+module Gitlab
+ module HookData
+ class BaseBuilder
+ attr_accessor :object
+
+ MARKDOWN_SIMPLE_IMAGE = %r{
+ #{::Gitlab::Regex.markdown_code_or_html_blocks}
+ |
+ (?<image>
+ !
+ \[(?<title>[^\n]*?)\]
+ \((?<url>(?!(https?://|//))[^\n]+?)\)
+ )
+ }mx.freeze
+
+ def initialize(object)
+ @object = object
+ end
+
+ private
+
+ def absolute_image_urls(markdown_text)
+ return markdown_text unless markdown_text.present?
+
+ markdown_text.gsub(MARKDOWN_SIMPLE_IMAGE) do
+ if $~[:image]
+ url = $~[:url]
+ url = "/#{url}" unless url.start_with?('/')
+
+ "![#{$~[:title]}](#{Gitlab.config.gitlab.url}#{url})"
+ else
+ $~[0]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hook_data/issuable_builder.rb b/lib/gitlab/hook_data/issuable_builder.rb
index 6ab36676127..f2eda398b8f 100644
--- a/lib/gitlab/hook_data/issuable_builder.rb
+++ b/lib/gitlab/hook_data/issuable_builder.rb
@@ -1,13 +1,9 @@
module Gitlab
module HookData
- class IssuableBuilder
+ class IssuableBuilder < BaseBuilder
CHANGES_KEYS = %i[previous current].freeze
- attr_accessor :issuable
-
- def initialize(issuable)
- @issuable = issuable
- end
+ alias_method :issuable, :object
def build(user: nil, changes: {})
hook_data = {
diff --git a/lib/gitlab/hook_data/issue_builder.rb b/lib/gitlab/hook_data/issue_builder.rb
index f9b1a3caf5e..0d71c748dc6 100644
--- a/lib/gitlab/hook_data/issue_builder.rb
+++ b/lib/gitlab/hook_data/issue_builder.rb
@@ -1,6 +1,6 @@
module Gitlab
module HookData
- class IssueBuilder
+ class IssueBuilder < BaseBuilder
SAFE_HOOK_ATTRIBUTES = %i[
assignee_id
author_id
@@ -30,14 +30,11 @@ module Gitlab
total_time_spent
].freeze
- attr_accessor :issue
-
- def initialize(issue)
- @issue = issue
- end
+ alias_method :issue, :object
def build
attrs = {
+ description: absolute_image_urls(issue.description),
url: Gitlab::UrlBuilder.build(issue),
total_time_spent: issue.total_time_spent,
human_total_time_spent: issue.human_total_time_spent,
diff --git a/lib/gitlab/hook_data/merge_request_builder.rb b/lib/gitlab/hook_data/merge_request_builder.rb
index aff786864f2..dfbed0597ed 100644
--- a/lib/gitlab/hook_data/merge_request_builder.rb
+++ b/lib/gitlab/hook_data/merge_request_builder.rb
@@ -1,6 +1,6 @@
module Gitlab
module HookData
- class MergeRequestBuilder
+ class MergeRequestBuilder < BaseBuilder
SAFE_HOOK_ATTRIBUTES = %i[
assignee_id
author_id
@@ -35,14 +35,11 @@ module Gitlab
total_time_spent
].freeze
- attr_accessor :merge_request
-
- def initialize(merge_request)
- @merge_request = merge_request
- end
+ alias_method :merge_request, :object
def build
attrs = {
+ description: absolute_image_urls(merge_request.description),
url: Gitlab::UrlBuilder.build(merge_request),
source: merge_request.source_project.try(:hook_attrs),
target: merge_request.target_project.hook_attrs,
diff --git a/lib/gitlab/hook_data/note_builder.rb b/lib/gitlab/hook_data/note_builder.rb
new file mode 100644
index 00000000000..81873e345d5
--- /dev/null
+++ b/lib/gitlab/hook_data/note_builder.rb
@@ -0,0 +1,43 @@
+module Gitlab
+ module HookData
+ class NoteBuilder < BaseBuilder
+ SAFE_HOOK_ATTRIBUTES = %i[
+ attachment
+ author_id
+ change_position
+ commit_id
+ created_at
+ discussion_id
+ id
+ line_code
+ note
+ noteable_id
+ noteable_type
+ original_position
+ position
+ project_id
+ resolved_at
+ resolved_by_id
+ resolved_by_push
+ st_diff
+ system
+ type
+ updated_at
+ updated_by_id
+ ].freeze
+
+ alias_method :note, :object
+
+ def build
+ note
+ .attributes
+ .with_indifferent_access
+ .slice(*SAFE_HOOK_ATTRIBUTES)
+ .merge(
+ description: absolute_image_urls(note.note),
+ url: Gitlab::UrlBuilder.build(note)
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hook_data/wiki_page_builder.rb b/lib/gitlab/hook_data/wiki_page_builder.rb
new file mode 100644
index 00000000000..59c94a61cf2
--- /dev/null
+++ b/lib/gitlab/hook_data/wiki_page_builder.rb
@@ -0,0 +1,15 @@
+module Gitlab
+ module HookData
+ class WikiPageBuilder < BaseBuilder
+ alias_method :wiki_page, :object
+
+ def build
+ wiki_page
+ .attributes
+ .merge(
+ 'content' => absolute_image_urls(wiki_page.content)
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/http_io.rb b/lib/gitlab/http_io.rb
new file mode 100644
index 00000000000..ce24817db54
--- /dev/null
+++ b/lib/gitlab/http_io.rb
@@ -0,0 +1,193 @@
+##
+# This class is compatible with IO class (https://ruby-doc.org/core-2.3.1/IO.html)
+# source: https://gitlab.com/snippets/1685610
+module Gitlab
+ class HttpIO
+ BUFFER_SIZE = 128.kilobytes
+
+ InvalidURLError = Class.new(StandardError)
+ FailedToGetChunkError = Class.new(StandardError)
+
+ attr_reader :uri, :size
+ attr_reader :tell
+ attr_reader :chunk, :chunk_range
+
+ alias_method :pos, :tell
+
+ def initialize(url, size)
+ raise InvalidURLError unless ::Gitlab::UrlSanitizer.valid?(url)
+
+ @uri = URI(url)
+ @size = size
+ @tell = 0
+ end
+
+ def close
+ # no-op
+ end
+
+ def binmode
+ # no-op
+ end
+
+ def binmode?
+ true
+ end
+
+ def path
+ nil
+ end
+
+ def url
+ @uri.to_s
+ end
+
+ def seek(pos, where = IO::SEEK_SET)
+ new_pos =
+ case where
+ when IO::SEEK_END
+ size + pos
+ when IO::SEEK_SET
+ pos
+ when IO::SEEK_CUR
+ tell + pos
+ else
+ -1
+ end
+
+ raise 'new position is outside of file' if new_pos < 0 || new_pos > size
+
+ @tell = new_pos
+ end
+
+ def eof?
+ tell == size
+ end
+
+ def each_line
+ until eof?
+ line = readline
+ break if line.nil?
+
+ yield(line)
+ end
+ end
+
+ def read(length = nil, outbuf = "")
+ out = ""
+
+ length ||= size - tell
+
+ until length <= 0 || eof?
+ data = get_chunk
+ break if data.empty?
+
+ chunk_bytes = [BUFFER_SIZE - chunk_offset, length].min
+ chunk_data = data.byteslice(0, chunk_bytes)
+
+ out << chunk_data
+ @tell += chunk_data.bytesize
+ length -= chunk_data.bytesize
+ end
+
+ # If outbuf is passed, we put the output into the buffer. This supports IO.copy_stream functionality
+ if outbuf
+ outbuf.slice!(0, outbuf.bytesize)
+ outbuf << out
+ end
+
+ out
+ end
+
+ def readline
+ out = ""
+
+ until eof?
+ data = get_chunk
+ new_line = data.index("\n")
+
+ if !new_line.nil?
+ out << data[0..new_line]
+ @tell += new_line + 1
+ break
+ else
+ out << data
+ @tell += data.bytesize
+ end
+ end
+
+ out
+ end
+
+ def write(data)
+ raise NotImplementedError
+ end
+
+ def truncate(offset)
+ raise NotImplementedError
+ end
+
+ def flush
+ raise NotImplementedError
+ end
+
+ def present?
+ true
+ end
+
+ private
+
+ ##
+ # The below methods are not implemented in IO class
+ #
+ def in_range?
+ @chunk_range&.include?(tell)
+ end
+
+ def get_chunk
+ unless in_range?
+ response = Net::HTTP.start(uri.hostname, uri.port, proxy_from_env: true, use_ssl: uri.scheme == 'https') do |http|
+ http.request(request)
+ end
+
+ raise FailedToGetChunkError unless response.code == '200' || response.code == '206'
+
+ @chunk = response.body.force_encoding(Encoding::BINARY)
+ @chunk_range = response.content_range
+
+ ##
+ # Note: If provider does not return content_range, then we set it as we requested
+ # Provider: minio
+ # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # Provider: AWS
+ # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # Provider: GCS
+ # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPOK 200
+ @chunk_range ||= (chunk_start...(chunk_start + @chunk.bytesize))
+ end
+
+ @chunk[chunk_offset..BUFFER_SIZE]
+ end
+
+ def request
+ Net::HTTP::Get.new(uri).tap do |request|
+ request.set_range(chunk_start, BUFFER_SIZE)
+ end
+ end
+
+ def chunk_offset
+ tell % BUFFER_SIZE
+ end
+
+ def chunk_start
+ (tell / BUFFER_SIZE) * BUFFER_SIZE
+ end
+
+ def chunk_end
+ [chunk_start + BUFFER_SIZE, size].min
+ end
+ end
+end
diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb
index 53fe2f8e436..be3710c5b7f 100644
--- a/lib/gitlab/import_export.rb
+++ b/lib/gitlab/import_export.rb
@@ -40,6 +40,10 @@ module Gitlab
"#{basename[0..FILENAME_LIMIT]}_export.tar.gz"
end
+ def object_storage?
+ Feature.enabled?(:import_export_object_storage)
+ end
+
def version
VERSION
end
diff --git a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
index aef371d81eb..83134bb0769 100644
--- a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
@@ -2,6 +2,7 @@ module Gitlab
module ImportExport
module AfterExportStrategies
class BaseAfterExportStrategy
+ extend Gitlab::ImportExport::CommandLineUtil
include ActiveModel::Validations
extend Forwardable
@@ -24,9 +25,10 @@ module Gitlab
end
def execute(current_user, project)
- return unless project&.export_project_path
-
@project = project
+
+ return unless @project.export_status == :finished
+
@current_user = current_user
if invalid?
@@ -51,9 +53,12 @@ module Gitlab
end
def self.lock_file_path(project)
- return unless project&.export_path
+ return unless project.export_path || object_storage?
- File.join(project.export_path, AFTER_EXPORT_LOCK_FILE_NAME)
+ lock_path = project.import_export_shared.archive_path
+
+ mkdir_p(lock_path)
+ File.join(lock_path, AFTER_EXPORT_LOCK_FILE_NAME)
end
protected
@@ -77,6 +82,10 @@ module Gitlab
def log_validation_errors
errors.full_messages.each { |msg| project.import_export_shared.add_error_message(msg) }
end
+
+ def object_storage?
+ project.export_project_object_exists?
+ end
end
end
end
diff --git a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
index 938664a95a1..dce8f89c0ab 100644
--- a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
@@ -38,14 +38,20 @@ module Gitlab
private
def send_file
- export_file = File.open(project.export_project_path)
-
- Gitlab::HTTP.public_send(http_method.downcase, url, send_file_options(export_file)) # rubocop:disable GitlabSecurity/PublicSend
+ Gitlab::HTTP.public_send(http_method.downcase, url, send_file_options) # rubocop:disable GitlabSecurity/PublicSend
ensure
- export_file.close if export_file
+ export_file.close if export_file && !object_storage?
+ end
+
+ def export_file
+ if object_storage?
+ project.import_export_upload.export_file.file.open
+ else
+ File.open(project.export_project_path)
+ end
end
- def send_file_options(export_file)
+ def send_file_options
{
body_stream: export_file,
headers: headers
@@ -53,7 +59,15 @@ module Gitlab
end
def headers
- { 'Content-Length' => File.size(project.export_project_path).to_s }
+ { 'Content-Length' => export_size.to_s }
+ end
+
+ def export_size
+ if object_storage?
+ project.import_export_upload.export_file.file.size
+ else
+ File.size(project.export_project_path)
+ end
end
end
end
diff --git a/lib/gitlab/import_export/avatar_saver.rb b/lib/gitlab/import_export/avatar_saver.rb
index 998c21e2586..31ef0490cb3 100644
--- a/lib/gitlab/import_export/avatar_saver.rb
+++ b/lib/gitlab/import_export/avatar_saver.rb
@@ -11,7 +11,12 @@ module Gitlab
def save
return true unless @project.avatar.exists?
- copy_files(avatar_path, avatar_export_path)
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared,
+ relative_export_path: 'avatar',
+ from: avatar_path
+ ).save
rescue => e
@shared.error(e)
false
@@ -19,10 +24,6 @@ module Gitlab
private
- def avatar_export_path
- File.join(@shared.export_path, 'avatar', @project.avatar_identifier)
- end
-
def avatar_path
@project.avatar.path
end
diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb
index 2f163db936b..3adc44f8044 100644
--- a/lib/gitlab/import_export/command_line_util.rb
+++ b/lib/gitlab/import_export/command_line_util.rb
@@ -18,6 +18,21 @@ module Gitlab
private
+ def download_or_copy_upload(uploader, upload_path)
+ if uploader.upload.local?
+ copy_files(uploader.path, upload_path)
+ else
+ download(uploader.url, upload_path)
+ end
+ end
+
+ def download(url, upload_path)
+ File.open(upload_path, 'w') do |file|
+ # Download (stream) file from the uploader's location
+ IO.copy_stream(URI.parse(url).open, file)
+ end
+ end
+
def tar_with_options(archive:, dir:, options:)
execute(%W(tar -#{options} #{archive} -C #{dir} .))
end
diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb
index 0f4c3498036..7fd66b4e244 100644
--- a/lib/gitlab/import_export/file_importer.rb
+++ b/lib/gitlab/import_export/file_importer.rb
@@ -4,20 +4,24 @@ module Gitlab
include Gitlab::ImportExport::CommandLineUtil
MAX_RETRIES = 8
+ IGNORED_FILENAMES = %w(. ..).freeze
def self.import(*args)
new(*args).import
end
- def initialize(archive_file:, shared:)
+ def initialize(project:, archive_file:, shared:)
+ @project = project
@archive_file = archive_file
@shared = shared
end
def import
mkdir_p(@shared.export_path)
+ mkdir_p(@shared.archive_path)
- remove_symlinks!
+ remove_symlinks
+ copy_archive
wait_for_archived_file do
decompress_archive
@@ -26,7 +30,8 @@ module Gitlab
@shared.error(e)
false
ensure
- remove_symlinks!
+ remove_import_file
+ remove_symlinks
end
private
@@ -50,7 +55,15 @@ module Gitlab
result
end
- def remove_symlinks!
+ def copy_archive
+ return if @archive_file
+
+ @archive_file = File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project))
+
+ download_or_copy_upload(@project.import_export_upload.import_file, @archive_file)
+ end
+
+ def remove_symlinks
extracted_files.each do |path|
FileUtils.rm(path) if File.lstat(path).symlink?
end
@@ -58,8 +71,12 @@ module Gitlab
true
end
+ def remove_import_file
+ FileUtils.rm_rf(@archive_file)
+ end
+
def extracted_files
- Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| f =~ %r{.*/\.{1,2}$} }
+ Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| IGNORED_FILENAMES.include?(File.basename(f)) }
end
end
end
diff --git a/lib/gitlab/import_export/group_project_object_builder.rb b/lib/gitlab/import_export/group_project_object_builder.rb
new file mode 100644
index 00000000000..6c2af770119
--- /dev/null
+++ b/lib/gitlab/import_export/group_project_object_builder.rb
@@ -0,0 +1,90 @@
+module Gitlab
+ module ImportExport
+ # Given a class, it finds or creates a new object
+ # (initializes in the case of Label) at group or project level.
+ # If it does not exist in the group, it creates it at project level.
+ #
+ # Example:
+ # `GroupProjectObjectBuilder.build(Label, label_attributes)`
+ # finds or initializes a label with the given attributes.
+ #
+ # It also adds some logic around Group Labels/Milestones for edge cases.
+ class GroupProjectObjectBuilder
+ def self.build(*args)
+ Project.transaction do
+ new(*args).find
+ end
+ end
+
+ def initialize(klass, attributes)
+ @klass = klass < Label ? Label : klass
+ @attributes = attributes
+ @group = @attributes['group']
+ @project = @attributes['project']
+ end
+
+ def find
+ find_object || @klass.create(project_attributes)
+ end
+
+ private
+
+ def find_object
+ @klass.where(where_clause).first
+ end
+
+ def where_clause
+ @attributes.slice('title').map do |key, value|
+ scope_clause = table[:project_id].eq(@project.id)
+ scope_clause = scope_clause.or(table[:group_id].eq(@group.id)) if @group
+
+ table[key].eq(value).and(scope_clause)
+ end.reduce(:or)
+ end
+
+ def table
+ @table ||= @klass.arel_table
+ end
+
+ def project_attributes
+ @attributes.except('group').tap do |atts|
+ if label?
+ atts['type'] = 'ProjectLabel' # Always create project labels
+ elsif milestone?
+ if atts['group_id'] # Transform new group milestones into project ones
+ atts['iid'] = nil
+ atts.delete('group_id')
+ else
+ claim_iid
+ end
+ end
+ end
+ end
+
+ def label?
+ @klass == Label
+ end
+
+ def milestone?
+ @klass == Milestone
+ end
+
+ # If an existing group milestone used the IID
+ # claim the IID back and set the group milestone to use one available
+ # This is necessary to fix situations like the following:
+ # - Importing into a user namespace project with exported group milestones
+ # where the IID of the Group milestone could conflict with a project one.
+ def claim_iid
+ # The milestone has to be a group milestone, as it's the only case where
+ # we set the IID as the maximum. The rest of them are fixed.
+ milestone = @project.milestones.find_by(iid: @attributes['iid'])
+
+ return unless milestone
+
+ milestone.iid = nil
+ milestone.ensure_project_iid!
+ milestone.save!
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index da3667faf7a..f69f98a78a3 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -107,6 +107,7 @@ excluded_attributes:
- :storage_version
- :remote_mirror_available_overridden
- :description_html
+ - :repository_languages
snippets:
- :expired_at
merge_request_diff:
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index 63cab07324a..4e179f63d8c 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -35,7 +35,8 @@ module Gitlab
end
def import_file
- Gitlab::ImportExport::FileImporter.import(archive_file: @archive_file,
+ Gitlab::ImportExport::FileImporter.import(project: @project,
+ archive_file: @archive_file,
shared: @shared)
end
@@ -91,7 +92,14 @@ module Gitlab
end
def remove_import_file
- FileUtils.rm_rf(@archive_file)
+ return unless Gitlab::ImportExport.object_storage?
+
+ upload = @project.import_export_upload
+
+ return unless upload&.import_file&.file
+
+ upload.remove_import_file!
+ upload.save!
end
def overwrite_project
diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb
index 8b8e48aac76..ac827cbe1ca 100644
--- a/lib/gitlab/import_export/members_mapper.rb
+++ b/lib/gitlab/import_export/members_mapper.rb
@@ -47,7 +47,7 @@ module Gitlab
def ensure_default_member!
@project.project_members.destroy_all
- ProjectMember.create!(user: @user, access_level: ProjectMember::MASTER, source_id: @project.id, importing: true)
+ ProjectMember.create!(user: @user, access_level: ProjectMember::MAINTAINER, source_id: @project.id, importing: true)
end
def add_team_member(member, existing_user = nil)
diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb
index f3d7407383c..62a1833b39c 100644
--- a/lib/gitlab/import_export/merge_request_parser.rb
+++ b/lib/gitlab/import_export/merge_request_parser.rb
@@ -25,8 +25,13 @@ module Gitlab
@project.repository.create_branch(@merge_request.target_branch, @merge_request.target_branch_sha)
end
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1295
def fetch_ref
- @project.repository.fetch_ref(@project.repository, source_ref: @diff_head_sha, target_ref: @merge_request.source_branch)
+ target_ref = Gitlab::Git::BRANCH_REF_PREFIX + @merge_request.source_branch
+
+ unless @project.repository.fetch_source_branch!(@project.repository, @diff_head_sha, target_ref)
+ Rails.logger.warn("Import/Export warning: Failed to create #{target_ref} for MR: #{@merge_request.iid}")
+ end
end
def branch_exists?(branch_name)
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index 4eb67fbe11e..76b99b1de16 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -1,8 +1,8 @@
module Gitlab
module ImportExport
class ProjectTreeRestorer
- # Relations which cannot have both group_id and project_id at the same time
- RESTRICT_PROJECT_AND_GROUP = %i(milestone milestones).freeze
+ # Relations which cannot be saved at project level (and have a group assigned)
+ GROUP_MODELS = [GroupLabel, Milestone].freeze
def initialize(user:, shared:, project:)
@path = File.join(shared.export_path, 'project.json')
@@ -70,12 +70,23 @@ module Gitlab
def save_relation_hash(relation_hash_batch, relation_key)
relation_hash = create_relation(relation_key, relation_hash_batch)
+ remove_group_models(relation_hash) if relation_hash.is_a?(Array)
+
@saved = false unless restored_project.append_or_update_attribute(relation_key, relation_hash)
# Restore the project again, extra query that skips holding the AR objects in memory
@restored_project = Project.find(@project_id)
end
+ # Remove project models that became group models as we found them at group level.
+ # This no longer required saving them at the root project level.
+ # For example, in the case of an existing group label that matched the title.
+ def remove_group_models(relation_hash)
+ relation_hash.reject! do |value|
+ GROUP_MODELS.include?(value.class) && value.group_id
+ end
+ end
+
def default_relation_list
reader.tree.reject do |model|
model.is_a?(Hash) && model[:project_members]
@@ -170,7 +181,7 @@ module Gitlab
def create_relation(relation, relation_hash_list)
relation_array = [relation_hash_list].flatten.map do |relation_hash|
Gitlab::ImportExport::RelationFactory.create(relation_sym: relation.to_sym,
- relation_hash: parsed_relation_hash(relation_hash, relation.to_sym),
+ relation_hash: relation_hash,
members_mapper: members_mapper,
user: @user,
project: @restored_project,
@@ -180,18 +191,6 @@ module Gitlab
relation_hash_list.is_a?(Array) ? relation_array : relation_array.first
end
- def parsed_relation_hash(relation_hash, relation_type)
- if RESTRICT_PROJECT_AND_GROUP.include?(relation_type)
- params = {}
- params['group_id'] = restored_project.group.try(:id) if relation_hash['group_id']
- params['project_id'] = restored_project.id if relation_hash['project_id']
- else
- params = { 'group_id' => restored_project.group.try(:id), 'project_id' => restored_project.id }
- end
-
- relation_hash.merge(params)
- end
-
def reader
@reader ||= Gitlab::ImportExport::Reader.new(shared: @shared)
end
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index c5cf290f191..091e81028bb 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -54,6 +54,8 @@ module Gitlab
@project = project
@imported_object_retries = 0
+ @relation_hash['project_id'] = @project.id
+
# Remove excluded keys from relation_hash
# We don't do this in the parsed_relation_hash because of the 'transformed attributes'
# For example, MergeRequestDiffFiles exports its diff attribute as utf8_diff. Then,
@@ -80,15 +82,12 @@ module Gitlab
case @relation_name
when :merge_request_diff_files then setup_diff
when :notes then setup_note
- when :project_label, :project_labels then setup_label
- when :milestone, :milestones then setup_milestone
when 'Ci::Pipeline' then setup_pipeline
- else
- @relation_hash['project_id'] = @project.id
end
update_user_references
update_project_references
+ update_group_references
remove_duplicate_assignees
reset_tokens!
@@ -151,39 +150,23 @@ module Gitlab
end
def update_project_references
- project_id = @relation_hash.delete('project_id')
-
# If source and target are the same, populate them with the new project ID.
if @relation_hash['source_project_id']
- @relation_hash['source_project_id'] = same_source_and_target? ? project_id : MergeRequestParser::FORKED_PROJECT_ID
+ @relation_hash['source_project_id'] = same_source_and_target? ? @relation_hash['project_id'] : MergeRequestParser::FORKED_PROJECT_ID
end
- # project_id may not be part of the export, but we always need to populate it if required.
- @relation_hash['project_id'] = project_id
- @relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id']
+ @relation_hash['target_project_id'] = @relation_hash['project_id'] if @relation_hash['target_project_id']
end
def same_source_and_target?
@relation_hash['target_project_id'] && @relation_hash['target_project_id'] == @relation_hash['source_project_id']
end
- def setup_label
- # If there's no group, move the label to a project label
- if @relation_hash['type'] == 'GroupLabel' && @relation_hash['group_id']
- @relation_hash['project_id'] = nil
- @relation_name = :group_label
- else
- @relation_hash['group_id'] = nil
- @relation_hash['type'] = 'ProjectLabel'
- end
- end
+ def update_group_references
+ return unless EXISTING_OBJECT_CHECK.include?(@relation_name)
+ return unless @relation_hash['group_id']
- def setup_milestone
- if @relation_hash['group_id']
- @relation_hash['group_id'] = @project.group.id
- else
- @relation_hash['project_id'] = @project.id
- end
+ @relation_hash['group_id'] = @project.group&.id
end
def reset_tokens!
@@ -271,15 +254,7 @@ module Gitlab
end
def existing_object
- @existing_object ||=
- begin
- existing_object = find_or_create_object!
-
- # Done in two steps, as MySQL behaves differently than PostgreSQL using
- # the +find_or_create_by+ method and does not return the ID the second time.
- existing_object.update!(parsed_relation_hash)
- existing_object
- end
+ @existing_object ||= find_or_create_object!
end
def unknown_service?
@@ -288,29 +263,16 @@ module Gitlab
end
def find_or_create_object!
- finder_attributes = if @relation_name == :group_label
- %w[title group_id]
- elsif parsed_relation_hash['project_id']
- %w[title project_id]
- else
- %w[title group_id]
- end
-
- finder_hash = parsed_relation_hash.slice(*finder_attributes)
-
- if label?
- label = relation_class.find_or_initialize_by(finder_hash)
- parsed_relation_hash.delete('priorities') if label.persisted?
-
- label.save!
- label
- else
- relation_class.find_or_create_by(finder_hash)
+ return relation_class.find_or_create_by(project_id: @project.id) if @relation_name == :project_feature
+
+ # Can't use IDs as validation exists calling `group` or `project` attributes
+ finder_hash = parsed_relation_hash.tap do |hash|
+ hash['group'] = @project.group if relation_class.attribute_method?('group_id')
+ hash['project'] = @project
+ hash.delete('project_id')
end
- end
- def label?
- @relation_name.to_s.include?('label')
+ GroupProjectObjectBuilder.build(relation_class, finder_hash)
end
end
end
diff --git a/lib/gitlab/import_export/saver.rb b/lib/gitlab/import_export/saver.rb
index 2daeba90a51..3cd153a4fd2 100644
--- a/lib/gitlab/import_export/saver.rb
+++ b/lib/gitlab/import_export/saver.rb
@@ -15,15 +15,22 @@ module Gitlab
def save
if compress_and_save
remove_export_path
+
Rails.logger.info("Saved project export #{archive_file}")
- archive_file
+
+ save_on_object_storage if use_object_storage?
else
- @shared.error(Gitlab::ImportExport::Error.new("Unable to save #{archive_file} into #{@shared.export_path}"))
+ @shared.error(Gitlab::ImportExport::Error.new(error_message))
false
end
rescue => e
@shared.error(e)
false
+ ensure
+ if use_object_storage?
+ remove_archive
+ remove_export_path
+ end
end
private
@@ -36,9 +43,29 @@ module Gitlab
FileUtils.rm_rf(@shared.export_path)
end
+ def remove_archive
+ FileUtils.rm_rf(@shared.archive_path)
+ end
+
def archive_file
@archive_file ||= File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project))
end
+
+ def save_on_object_storage
+ upload = ImportExportUpload.find_or_initialize_by(project: @project)
+
+ File.open(archive_file) { |file| upload.export_file = file }
+
+ upload.save!
+ end
+
+ def use_object_storage?
+ Gitlab::ImportExport.object_storage?
+ end
+
+ def error_message
+ "Unable to save #{archive_file} into #{@shared.export_path}. Object storage enabled: #{use_object_storage?}"
+ end
end
end
end
diff --git a/lib/gitlab/import_export/uploads_manager.rb b/lib/gitlab/import_export/uploads_manager.rb
new file mode 100644
index 00000000000..07875ebb56a
--- /dev/null
+++ b/lib/gitlab/import_export/uploads_manager.rb
@@ -0,0 +1,98 @@
+module Gitlab
+ module ImportExport
+ class UploadsManager
+ include Gitlab::ImportExport::CommandLineUtil
+
+ UPLOADS_BATCH_SIZE = 100
+
+ def initialize(project:, shared:, relative_export_path: 'uploads', from: nil)
+ @project = project
+ @shared = shared
+ @relative_export_path = relative_export_path
+ @from = from || default_uploads_path
+ end
+
+ def save
+ copy_files(@from, uploads_export_path) if File.directory?(@from)
+
+ if File.file?(@from) && @relative_export_path == 'avatar'
+ copy_files(@from, File.join(uploads_export_path, @project.avatar.filename))
+ end
+
+ copy_from_object_storage
+
+ true
+ rescue => e
+ @shared.error(e)
+ false
+ end
+
+ def restore
+ Dir["#{uploads_export_path}/**/*"].each do |upload|
+ next if File.directory?(upload)
+
+ add_upload(upload)
+ end
+
+ true
+ rescue => e
+ @shared.error(e)
+ false
+ end
+
+ private
+
+ def add_upload(upload)
+ uploader_context = FileUploader.extract_dynamic_path(upload).named_captures.symbolize_keys
+
+ UploadService.new(@project, File.open(upload, 'r'), FileUploader, uploader_context).execute
+ end
+
+ def copy_from_object_storage
+ return unless Gitlab::ImportExport.object_storage?
+
+ each_uploader do |uploader|
+ next unless uploader.file
+ next if uploader.upload.local? # Already copied, using the old method
+
+ download_and_copy(uploader)
+ end
+ end
+
+ def default_uploads_path
+ FileUploader.absolute_base_dir(@project)
+ end
+
+ def uploads_export_path
+ @uploads_export_path ||= File.join(@shared.export_path, @relative_export_path)
+ end
+
+ def each_uploader
+ avatar_path = @project.avatar&.upload&.path
+
+ if @relative_export_path == 'avatar'
+ yield(@project.avatar)
+ else
+ project_uploads_except_avatar(avatar_path).find_each(batch_size: UPLOADS_BATCH_SIZE) do |upload|
+ yield(upload.build_uploader)
+ end
+ end
+ end
+
+ def project_uploads_except_avatar(avatar_path)
+ return @project.uploads unless avatar_path
+
+ @project.uploads.where("path != ?", avatar_path)
+ end
+
+ def download_and_copy(upload)
+ secret = upload.try(:secret) || ''
+ upload_path = File.join(uploads_export_path, secret, upload.filename)
+
+ mkdir_p(File.join(uploads_export_path, secret))
+
+ download_or_copy_upload(upload, upload_path)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/uploads_restorer.rb b/lib/gitlab/import_export/uploads_restorer.rb
index df19354b76e..25f85936227 100644
--- a/lib/gitlab/import_export/uploads_restorer.rb
+++ b/lib/gitlab/import_export/uploads_restorer.rb
@@ -2,13 +2,30 @@ module Gitlab
module ImportExport
class UploadsRestorer < UploadsSaver
def restore
- return true unless File.directory?(uploads_export_path)
+ if Gitlab::ImportExport.object_storage?
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared
+ ).restore
+ elsif File.directory?(uploads_export_path)
+ copy_files(uploads_export_path, uploads_path)
- copy_files(uploads_export_path, uploads_path)
+ true
+ else
+ true # Proceed without uploads
+ end
rescue => e
@shared.error(e)
false
end
+
+ def uploads_path
+ FileUploader.absolute_base_dir(@project)
+ end
+
+ def uploads_export_path
+ @uploads_export_path ||= File.join(@shared.export_path, 'uploads')
+ end
end
end
end
diff --git a/lib/gitlab/import_export/uploads_saver.rb b/lib/gitlab/import_export/uploads_saver.rb
index 2f08dda55fd..b3f17af5661 100644
--- a/lib/gitlab/import_export/uploads_saver.rb
+++ b/lib/gitlab/import_export/uploads_saver.rb
@@ -9,21 +9,14 @@ module Gitlab
end
def save
- return true unless File.directory?(uploads_path)
-
- copy_files(uploads_path, uploads_export_path)
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared
+ ).save
rescue => e
@shared.error(e)
false
end
-
- def uploads_path
- FileUploader.absolute_base_dir(@project)
- end
-
- def uploads_export_path
- File.join(@shared.export_path, 'uploads')
- end
end
end
end
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 60d5fa4d29a..f7f5c5787f6 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -9,36 +9,42 @@ module Gitlab
# We exclude `bare_repository` here as it has no import class associated
ImportTable = [
- ImportSource.new('github', 'GitHub', Gitlab::GithubImport::ParallelImporter),
- ImportSource.new('bitbucket', 'Bitbucket', Gitlab::BitbucketImport::Importer),
- ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer),
- ImportSource.new('google_code', 'Google Code', Gitlab::GoogleCodeImport::Importer),
- ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer),
- ImportSource.new('git', 'Repo by URL', nil),
- ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer),
- ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer)
+ ImportSource.new('github', 'GitHub', Gitlab::GithubImport::ParallelImporter),
+ 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('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer),
+ ImportSource.new('git', 'Repo by URL', nil),
+ ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer),
+ ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer),
+ ImportSource.new('manifest', 'Manifest file', nil)
].freeze
class << self
def options
- @options ||= Hash[ImportTable.map { |importer| [importer.title, importer.name] }]
+ Hash[import_table.map { |importer| [importer.title, importer.name] }]
end
def values
- @values ||= ImportTable.map(&:name)
+ import_table.map(&:name)
end
def importer_names
- @importer_names ||= ImportTable.select(&:importer).map(&:name)
+ import_table.select(&:importer).map(&:name)
end
def importer(name)
- ImportTable.find { |import_source| import_source.name == name }.importer
+ import_table.find { |import_source| import_source.name == name }.importer
end
def title(name)
options.key(name)
end
+
+ def import_table
+ ImportTable
+ end
end
end
end
diff --git a/lib/gitlab/json_logger.rb b/lib/gitlab/json_logger.rb
new file mode 100644
index 00000000000..28e258196ca
--- /dev/null
+++ b/lib/gitlab/json_logger.rb
@@ -0,0 +1,22 @@
+module Gitlab
+ class JsonLogger < ::Gitlab::Logger
+ def self.file_name_noext
+ raise NotImplementedError
+ end
+
+ def format_message(severity, timestamp, progname, message)
+ data = {}
+ data[:severity] = severity
+ data[:time] = timestamp.utc.iso8601(3)
+
+ case message
+ when String
+ data[:message] = message
+ when Hash
+ data.merge!(message)
+ end
+
+ data.to_json + "\n"
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb
index da43bd0af4b..15c5ece2350 100644
--- a/lib/gitlab/kubernetes.rb
+++ b/lib/gitlab/kubernetes.rb
@@ -1,6 +1,10 @@
module Gitlab
# Helper methods to do with Kubernetes network services & resources
module Kubernetes
+ def self.build_header_hash
+ Hash.new { |h, k| h[k] = [] }
+ end
+
# This is the comand that is run to start a terminal session. Kubernetes
# expects `command=foo&command=bar, not `command[]=foo&command[]=bar`
EXEC_COMMAND = URI.encode_www_form(
@@ -37,13 +41,14 @@ module Gitlab
selectors: { pod: pod_name, container: container["name"] },
url: container_exec_url(api_url, namespace, pod_name, container["name"]),
subprotocols: ['channel.k8s.io'],
- headers: Hash.new { |h, k| h[k] = [] },
+ headers: ::Gitlab::Kubernetes.build_header_hash,
created_at: created_at
}
end
end
def add_terminal_auth(terminal, token:, max_session_time:, ca_pem: nil)
+ terminal[:headers] ||= ::Gitlab::Kubernetes.build_header_hash
terminal[:headers]['Authorization'] << "Bearer #{token}"
terminal[:max_session_time] = max_session_time
terminal[:ca_pem] = ca_pem if ca_pem.present?
diff --git a/lib/gitlab/kubernetes/config_map.rb b/lib/gitlab/kubernetes/config_map.rb
index 95e1054919d..9e55dae137c 100644
--- a/lib/gitlab/kubernetes/config_map.rb
+++ b/lib/gitlab/kubernetes/config_map.rb
@@ -1,21 +1,25 @@
module Gitlab
module Kubernetes
class ConfigMap
- def initialize(name, values)
+ def initialize(name, files)
@name = name
- @values = values
+ @files = files
end
def generate
resource = ::Kubeclient::Resource.new
resource.metadata = metadata
- resource.data = { values: values }
+ resource.data = files
resource
end
+ def config_map_name
+ "values-content-configuration-#{name}"
+ end
+
private
- attr_reader :name, :values
+ attr_reader :name, :files
def metadata
{
@@ -25,10 +29,6 @@ module Gitlab
}
end
- def config_map_name
- "values-content-configuration-#{name}"
- end
-
def namespace
Gitlab::Kubernetes::Helm::NAMESPACE
end
diff --git a/lib/gitlab/kubernetes/helm.rb b/lib/gitlab/kubernetes/helm.rb
index 0f0588b8b23..530ccf88053 100644
--- a/lib/gitlab/kubernetes/helm.rb
+++ b/lib/gitlab/kubernetes/helm.rb
@@ -1,7 +1,7 @@
module Gitlab
module Kubernetes
module Helm
- HELM_VERSION = '2.7.0'.freeze
+ HELM_VERSION = '2.7.2'.freeze
NAMESPACE = 'gitlab-managed-apps'.freeze
end
end
diff --git a/lib/gitlab/kubernetes/helm/api.rb b/lib/gitlab/kubernetes/helm/api.rb
index 2edd34109ba..d65374cc23b 100644
--- a/lib/gitlab/kubernetes/helm/api.rb
+++ b/lib/gitlab/kubernetes/helm/api.rb
@@ -8,9 +8,9 @@ module Gitlab
end
def install(command)
- @namespace.ensure_exists!
- create_config_map(command) if command.config_map?
- @kubeclient.create_pod(command.pod_resource)
+ namespace.ensure_exists!
+ create_config_map(command)
+ kubeclient.create_pod(command.pod_resource)
end
##
@@ -20,23 +20,25 @@ module Gitlab
#
# values: "Pending", "Running", "Succeeded", "Failed", "Unknown"
#
- def installation_status(pod_name)
- @kubeclient.get_pod(pod_name, @namespace.name).status.phase
+ def status(pod_name)
+ kubeclient.get_pod(pod_name, namespace.name).status.phase
end
- def installation_log(pod_name)
- @kubeclient.get_pod_log(pod_name, @namespace.name).body
+ def log(pod_name)
+ kubeclient.get_pod_log(pod_name, namespace.name).body
end
- def delete_installation_pod!(pod_name)
- @kubeclient.delete_pod(pod_name, @namespace.name)
+ def delete_pod!(pod_name)
+ kubeclient.delete_pod(pod_name, namespace.name)
end
private
+ attr_reader :kubeclient, :namespace
+
def create_config_map(command)
command.config_map_resource.tap do |config_map_resource|
- @kubeclient.create_config_map(config_map_resource)
+ kubeclient.create_config_map(config_map_resource)
end
end
end
diff --git a/lib/gitlab/kubernetes/helm/base_command.rb b/lib/gitlab/kubernetes/helm/base_command.rb
index 3d778da90c7..afcfd109de0 100644
--- a/lib/gitlab/kubernetes/helm/base_command.rb
+++ b/lib/gitlab/kubernetes/helm/base_command.rb
@@ -1,13 +1,7 @@
module Gitlab
module Kubernetes
module Helm
- class BaseCommand
- attr_reader :name
-
- def initialize(name)
- @name = name
- end
-
+ module BaseCommand
def pod_resource
Gitlab::Kubernetes::Helm::Pod.new(self, namespace).generate
end
@@ -18,22 +12,38 @@ module Gitlab
ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
- apk add -U ca-certificates openssl >/dev/null
+ apk add -U wget ca-certificates openssl >/dev/null
wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{Gitlab::Kubernetes::Helm::HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
mv /tmp/linux-amd64/helm /usr/bin/
HEREDOC
end
- def config_map?
- false
- end
-
def pod_name
"install-#{name}"
end
+ def config_map_resource
+ Gitlab::Kubernetes::ConfigMap.new(name, files).generate
+ end
+
+ def file_names
+ files.keys
+ end
+
+ def name
+ raise "Not implemented"
+ end
+
+ def files
+ raise "Not implemented"
+ end
+
private
+ def files_dir
+ "/data/helm/#{name}/config"
+ end
+
def namespace
Gitlab::Kubernetes::Helm::NAMESPACE
end
diff --git a/lib/gitlab/kubernetes/helm/certificate.rb b/lib/gitlab/kubernetes/helm/certificate.rb
new file mode 100644
index 00000000000..598714e0874
--- /dev/null
+++ b/lib/gitlab/kubernetes/helm/certificate.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+module Gitlab
+ module Kubernetes
+ module Helm
+ class Certificate
+ INFINITE_EXPIRY = 1000.years
+ SHORT_EXPIRY = 30.minutes
+
+ attr_reader :key, :cert
+
+ def key_string
+ @key.to_s
+ end
+
+ def cert_string
+ @cert.to_pem
+ end
+
+ def self.from_strings(key_string, cert_string)
+ key = OpenSSL::PKey::RSA.new(key_string)
+ cert = OpenSSL::X509::Certificate.new(cert_string)
+ new(key, cert)
+ end
+
+ def self.generate_root
+ _issue(signed_by: nil, expires_in: INFINITE_EXPIRY, certificate_authority: true)
+ end
+
+ def issue(expires_in: SHORT_EXPIRY)
+ self.class._issue(signed_by: self, expires_in: expires_in, certificate_authority: false)
+ end
+
+ private
+
+ def self._issue(signed_by:, expires_in:, certificate_authority:)
+ key = OpenSSL::PKey::RSA.new(4096)
+ public_key = key.public_key
+
+ subject = OpenSSL::X509::Name.parse("/C=US")
+
+ cert = OpenSSL::X509::Certificate.new
+ cert.subject = subject
+
+ cert.issuer = signed_by&.cert&.subject || subject
+
+ cert.not_before = Time.now
+ cert.not_after = expires_in.from_now
+ cert.public_key = public_key
+ cert.serial = 0x0
+ cert.version = 2
+
+ if certificate_authority
+ extension_factory = OpenSSL::X509::ExtensionFactory.new
+ extension_factory.subject_certificate = cert
+ extension_factory.issuer_certificate = cert
+ cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
+ cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:TRUE', true))
+ cert.add_extension(extension_factory.create_extension('keyUsage', 'cRLSign,keyCertSign', true))
+ end
+
+ cert.sign(signed_by&.key || key, OpenSSL::Digest::SHA256.new)
+
+ new(key, cert)
+ end
+
+ def initialize(key, cert)
+ @key = key
+ @cert = cert
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/helm/init_command.rb b/lib/gitlab/kubernetes/helm/init_command.rb
index a02e64561f6..a4546509515 100644
--- a/lib/gitlab/kubernetes/helm/init_command.rb
+++ b/lib/gitlab/kubernetes/helm/init_command.rb
@@ -1,7 +1,16 @@
module Gitlab
module Kubernetes
module Helm
- class InitCommand < BaseCommand
+ class InitCommand
+ include BaseCommand
+
+ attr_reader :name, :files
+
+ def initialize(name:, files:)
+ @name = name
+ @files = files
+ end
+
def generate_script
super + [
init_helm_command
@@ -11,7 +20,12 @@ module Gitlab
private
def init_helm_command
- "helm init >/dev/null"
+ tls_flags = "--tiller-tls" \
+ " --tiller-tls-verify --tls-ca-cert #{files_dir}/ca.pem" \
+ " --tiller-tls-cert #{files_dir}/cert.pem" \
+ " --tiller-tls-key #{files_dir}/key.pem"
+
+ "helm init #{tls_flags} >/dev/null"
end
end
end
diff --git a/lib/gitlab/kubernetes/helm/install_command.rb b/lib/gitlab/kubernetes/helm/install_command.rb
index d2133a6d65b..9672f80687e 100644
--- a/lib/gitlab/kubernetes/helm/install_command.rb
+++ b/lib/gitlab/kubernetes/helm/install_command.rb
@@ -1,14 +1,16 @@
module Gitlab
module Kubernetes
module Helm
- class InstallCommand < BaseCommand
- attr_reader :name, :chart, :version, :repository, :values
+ class InstallCommand
+ include BaseCommand
- def initialize(name, chart:, values:, version: nil, repository: nil)
+ attr_reader :name, :files, :chart, :version, :repository
+
+ def initialize(name:, chart:, files:, version: nil, repository: nil)
@name = name
@chart = chart
@version = version
- @values = values
+ @files = files
@repository = repository
end
@@ -20,14 +22,6 @@ module Gitlab
].compact.join("\n")
end
- def config_map?
- true
- end
-
- def config_map_resource
- Gitlab::Kubernetes::ConfigMap.new(name, values).generate
- end
-
private
def init_command
@@ -39,14 +33,25 @@ module Gitlab
end
def script_command
- <<~HEREDOC
- helm install #{chart} --name #{name}#{optional_version_flag} --namespace #{Gitlab::Kubernetes::Helm::NAMESPACE} -f /data/helm/#{name}/config/values.yaml >/dev/null
- HEREDOC
+ init_flags = "--name #{name}#{optional_tls_flags}#{optional_version_flag}" \
+ " --namespace #{Gitlab::Kubernetes::Helm::NAMESPACE}" \
+ " -f /data/helm/#{name}/config/values.yaml"
+
+ "helm install #{chart} #{init_flags} >/dev/null\n"
end
def optional_version_flag
" --version #{version}" if version
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/pod.rb b/lib/gitlab/kubernetes/helm/pod.rb
index 1e12299eefd..6e5d3388405 100644
--- a/lib/gitlab/kubernetes/helm/pod.rb
+++ b/lib/gitlab/kubernetes/helm/pod.rb
@@ -10,10 +10,8 @@ module Gitlab
def generate
spec = { containers: [container_specification], restartPolicy: 'Never' }
- if command.config_map?
- spec[:volumes] = volumes_specification
- spec[:containers][0][:volumeMounts] = volume_mounts_specification
- end
+ spec[:volumes] = volumes_specification
+ spec[:containers][0][:volumeMounts] = volume_mounts_specification
::Kubeclient::Resource.new(metadata: metadata, spec: spec)
end
@@ -61,7 +59,7 @@ module Gitlab
name: 'configuration-volume',
configMap: {
name: "values-content-configuration-#{command.name}",
- items: [{ key: 'values', path: 'values.yaml' }]
+ items: command.file_names.map { |name| { key: name, path: name } }
}
}
]
diff --git a/lib/gitlab/language_detection.rb b/lib/gitlab/language_detection.rb
new file mode 100644
index 00000000000..a41435fdb79
--- /dev/null
+++ b/lib/gitlab/language_detection.rb
@@ -0,0 +1,68 @@
+module Gitlab
+ class LanguageDetection
+ MAX_LANGUAGES = 5
+
+ def initialize(repository, repository_languages)
+ @repository = repository
+ @repository_languages = repository_languages
+ end
+
+ def languages
+ detection.keys
+ end
+
+ def language_color(name)
+ detection.dig(name, :color)
+ end
+
+ # Newly detected languages, returned in a structure accepted by
+ # Gitlab::Database.bulk_insert
+ def insertions(programming_languages)
+ lang_to_id = programming_languages.map { |p| [p.name, p.id] }.to_h
+
+ (languages - previous_language_names).map do |new_lang|
+ {
+ project_id: @repository.project.id,
+ share: detection[new_lang][:value],
+ programming_language_id: lang_to_id[new_lang]
+ }
+ end
+ end
+
+ # updates analyses which records only require updating of their share
+ def updates
+ to_update = @repository_languages.select do |lang|
+ detection.key?(lang.name) && detection[lang.name][:value] != lang.share
+ end
+
+ to_update.map do |lang|
+ { programming_language_id: lang.programming_language_id, share: detection[lang.name][:value] }
+ end
+ end
+
+ # Returns the ids of the programming languages that do not occur in the detection
+ # as current repository languages
+ def deletions
+ @repository_languages.map do |repo_lang|
+ next if detection.key?(repo_lang.name)
+
+ repo_lang.programming_language_id
+ end.compact
+ end
+
+ private
+
+ def previous_language_names
+ @previous_language_names ||= @repository_languages.map(&:name)
+ end
+
+ def detection
+ @detection ||=
+ @repository
+ .languages
+ .first(MAX_LANGUAGES)
+ .map { |l| [l[:label], l] }
+ .to_h
+ end
+ end
+end
diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb
index a42e312b5d3..e58927a40b9 100644
--- a/lib/gitlab/logger.rb
+++ b/lib/gitlab/logger.rb
@@ -4,10 +4,18 @@ module Gitlab
file_name_noext + '.log'
end
+ def self.debug(message)
+ build.debug(message)
+ end
+
def self.error(message)
build.error(message)
end
+ def self.warn(message)
+ build.warn(message)
+ end
+
def self.info(message)
build.info(message)
end
diff --git a/lib/gitlab/mail_room.rb b/lib/gitlab/mail_room.rb
index 344784c866f..db04356a5e9 100644
--- a/lib/gitlab/mail_room.rb
+++ b/lib/gitlab/mail_room.rb
@@ -53,7 +53,7 @@ module Gitlab
end
def config_file
- ENV['MAIL_ROOM_GITLAB_CONFIG_FILE'] || File.expand_path('../../../config/gitlab.yml', __FILE__)
+ ENV['MAIL_ROOM_GITLAB_CONFIG_FILE'] || File.expand_path('../../config/gitlab.yml', __dir__)
end
end
end
diff --git a/lib/gitlab/manifest_import/manifest.rb b/lib/gitlab/manifest_import/manifest.rb
new file mode 100644
index 00000000000..4d6034fb956
--- /dev/null
+++ b/lib/gitlab/manifest_import/manifest.rb
@@ -0,0 +1,81 @@
+# Class to parse manifest file and build a list of repositories for import
+#
+# <manifest>
+# <remote review="https://android-review.googlesource.com/" />
+# <project path="platform-common" name="platform" />
+# <project path="platform/art" name="platform/art" />
+# <project path="platform/device" name="platform/device" />
+# </manifest>
+#
+# 1. Project path must be uniq and can't be part of other project path.
+# For example, you can't have projects with 'foo' and 'foo/bar' paths.
+# 2. Remote must be present with review attribute so GitLab knows
+# where to fetch source code
+module Gitlab
+ module ManifestImport
+ class Manifest
+ attr_reader :parsed_xml, :errors
+
+ def initialize(file)
+ @parsed_xml = Nokogiri::XML(file) { |config| config.strict }
+ @errors = []
+ rescue Nokogiri::XML::SyntaxError
+ @errors = ['The uploaded file is not a valid XML file.']
+ end
+
+ def projects
+ raw_projects.each_with_index.map do |project, i|
+ {
+ id: i,
+ name: project['name'],
+ path: project['path'],
+ url: repository_url(project['name'])
+ }
+ end
+ end
+
+ def valid?
+ return false if @errors.any?
+
+ unless validate_remote
+ @errors << 'Make sure a <remote> tag is present and is valid.'
+ end
+
+ unless validate_projects
+ @errors << 'Make sure every <project> tag has name and path attributes.'
+ end
+
+ @errors.empty?
+ end
+
+ private
+
+ def validate_remote
+ remote.present? && URI.parse(remote).host
+ rescue URI::Error
+ false
+ end
+
+ def validate_projects
+ raw_projects.all? do |project|
+ project['name'] && project['path']
+ end
+ end
+
+ def repository_url(name)
+ URI.join(remote, name).to_s
+ end
+
+ def remote
+ return @remote if defined?(@remote)
+
+ remote_tag = parsed_xml.css('manifest > remote').first
+ @remote = remote_tag['review'] if remote_tag
+ end
+
+ def raw_projects
+ @raw_projects ||= parsed_xml.css('manifest > project')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/manifest_import/project_creator.rb b/lib/gitlab/manifest_import/project_creator.rb
new file mode 100644
index 00000000000..b5967c93735
--- /dev/null
+++ b/lib/gitlab/manifest_import/project_creator.rb
@@ -0,0 +1,41 @@
+module Gitlab
+ module ManifestImport
+ class ProjectCreator
+ attr_reader :repository, :destination, :current_user
+
+ def initialize(repository, destination, current_user)
+ @repository = repository
+ @destination = destination
+ @current_user = current_user
+ end
+
+ def execute
+ group_full_path, _, project_path = repository[:path].rpartition('/')
+ group_full_path = File.join(destination.full_path, group_full_path) if destination
+ group = create_group_with_parents(group_full_path)
+
+ params = {
+ import_url: repository[:url],
+ import_type: 'manifest',
+ namespace_id: group.id,
+ path: project_path,
+ name: project_path,
+ visibility_level: destination.visibility_level
+ }
+
+ Projects::CreateService.new(current_user, params).execute
+ end
+
+ private
+
+ def create_group_with_parents(full_path)
+ params = {
+ group_path: full_path,
+ visibility_level: destination.visibility_level
+ }
+
+ Groups::NestedCreateService.new(current_user, params).execute
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb
index 66f30e3b397..04135dac4ff 100644
--- a/lib/gitlab/metrics/influx_db.rb
+++ b/lib/gitlab/metrics/influx_db.rb
@@ -162,7 +162,6 @@ module Gitlab
# When enabled this should be set before being used as the usual pattern
# "@foo ||= bar" is _not_ thread-safe.
- # rubocop:disable Gitlab/ModuleWithInstanceVariables
def pool
if influx_metrics_enabled?
if @pool.nil?
@@ -180,7 +179,6 @@ module Gitlab
@pool
end
end
- # rubocop:enable Gitlab/ModuleWithInstanceVariables
end
end
end
diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb
index b11520a79bb..f3290e3149c 100644
--- a/lib/gitlab/metrics/method_call.rb
+++ b/lib/gitlab/metrics/method_call.rb
@@ -1,5 +1,3 @@
-# rubocop:disable Style/ClassVars
-
module Gitlab
module Metrics
# Class for tracking timing information about method calls
diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb
index 38f119cf06d..c205f348023 100644
--- a/lib/gitlab/metrics/subscribers/active_record.rb
+++ b/lib/gitlab/metrics/subscribers/active_record.rb
@@ -20,7 +20,7 @@ module Gitlab
define_histogram :gitlab_sql_duration_seconds do
docstring 'SQL time'
base_labels Transaction::BASE_LABELS
- buckets [0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
+ buckets [0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
end
def current_transaction
diff --git a/lib/gitlab/middleware/basic_health_check.rb b/lib/gitlab/middleware/basic_health_check.rb
new file mode 100644
index 00000000000..f2a03217098
--- /dev/null
+++ b/lib/gitlab/middleware/basic_health_check.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+# This middleware provides a health check that does not hit the database. Its purpose
+# is to notify the prober that the application server is handling requests, but a 200
+# response does not signify that the database or other services are ready.
+#
+# See https://thisdata.com/blog/making-a-rails-health-check-that-doesnt-hit-the-database/ for
+# more details.
+
+module Gitlab
+ module Middleware
+ class BasicHealthCheck
+ # This can't be frozen because Rails::Rack::Logger wraps the body
+ # rubocop:disable Style/MutableConstant
+ OK_RESPONSE = [200, { 'Content-Type' => 'text/plain' }, ["GitLab OK"]]
+ EMPTY_RESPONSE = [404, { 'Content-Type' => 'text/plain' }, [""]]
+ # rubocop:enable Style/MutableConstant
+ HEALTH_PATH = '/-/health'
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ return @app.call(env) unless env['PATH_INFO'] == HEALTH_PATH
+
+ request = Rack::Request.new(env)
+
+ return OK_RESPONSE if client_ip_whitelisted?(request)
+
+ EMPTY_RESPONSE
+ end
+
+ def client_ip_whitelisted?(request)
+ ip_whitelist.any? { |e| e.include?(request.ip) }
+ end
+
+ def ip_whitelist
+ @ip_whitelist ||= Settings.monitoring.ip_whitelist.map(&IPAddr.method(:new))
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/middleware/multipart.rb b/lib/gitlab/middleware/multipart.rb
index a5f5d719cc1..18f91db98fc 100644
--- a/lib/gitlab/middleware/multipart.rb
+++ b/lib/gitlab/middleware/multipart.rb
@@ -42,10 +42,10 @@ module Gitlab
key, value = parsed_field.first
if value.nil?
- value = open_file(tmp_path, @request.params["#{key}.name"])
+ value = open_file(@request.params, key)
@open_files << value
else
- value = decorate_params_value(value, @request.params[key], tmp_path)
+ value = decorate_params_value(value, @request.params[key])
end
@request.update_param(key, value)
@@ -57,7 +57,7 @@ module Gitlab
end
# This function calls itself recursively
- def decorate_params_value(path_hash, value_hash, tmp_path)
+ def decorate_params_value(path_hash, value_hash)
unless path_hash.is_a?(Hash) && path_hash.count == 1
raise "invalid path: #{path_hash.inspect}"
end
@@ -70,19 +70,21 @@ module Gitlab
case path_value
when nil
- value_hash[path_key] = open_file(tmp_path, value_hash.dig(path_key, '.name'))
+ value_hash[path_key] = open_file(value_hash.dig(path_key), '')
@open_files << value_hash[path_key]
value_hash
when Hash
- decorate_params_value(path_value, value_hash[path_key], tmp_path)
+ decorate_params_value(path_value, value_hash[path_key])
value_hash
else
raise "unexpected path value: #{path_value.inspect}"
end
end
- def open_file(path, name)
- ::UploadedFile.new(path, filename: name || File.basename(path), content_type: 'application/octet-stream')
+ def open_file(params, key)
+ ::UploadedFile.from_params(
+ params, key,
+ [FileUploader.root, Gitlab.config.uploads.storage_path])
end
end
diff --git a/lib/gitlab/middleware/read_only/controller.rb b/lib/gitlab/middleware/read_only/controller.rb
index 45b644e6510..8dca431c005 100644
--- a/lib/gitlab/middleware/read_only/controller.rb
+++ b/lib/gitlab/middleware/read_only/controller.rb
@@ -4,8 +4,18 @@ module Gitlab
class Controller
DISALLOWED_METHODS = %w(POST PATCH PUT DELETE).freeze
APPLICATION_JSON = 'application/json'.freeze
+ APPLICATION_JSON_TYPES = %W{#{APPLICATION_JSON} application/vnd.git-lfs+json}.freeze
ERROR_MESSAGE = 'You cannot perform write operations on a read-only instance'.freeze
+ WHITELISTED_GIT_ROUTES = {
+ 'projects/git_http' => %w{git_upload_pack git_receive_pack}
+ }.freeze
+
+ WHITELISTED_GIT_LFS_ROUTES = {
+ 'projects/lfs_api' => %w{batch},
+ 'projects/lfs_locks_api' => %w{verify create unlock}
+ }.freeze
+
def initialize(app, env)
@app = app
@env = env
@@ -36,7 +46,7 @@ module Gitlab
end
def json_request?
- request.media_type == APPLICATION_JSON
+ APPLICATION_JSON_TYPES.include?(request.media_type)
end
def rack_flash
@@ -59,26 +69,32 @@ module Gitlab
@route_hash ||= Rails.application.routes.recognize_path(request.url, { method: request.request_method }) rescue {}
end
+ # Overridden in EE module
def whitelisted_routes
grack_route || ReadOnly.internal_routes.any? { |path| request.path.include?(path) } || lfs_route || sidekiq_route
end
- def sidekiq_route
- request.path.start_with?('/admin/sidekiq')
- end
-
def grack_route
# Calling route_hash may be expensive. Only do it if we think there's a possible match
- return false unless request.path.end_with?('.git/git-upload-pack')
+ return false unless
+ request.path.end_with?('.git/git-upload-pack', '.git/git-receive-pack')
- route_hash[:controller] == 'projects/git_http' && route_hash[:action] == 'git_upload_pack'
+ WHITELISTED_GIT_ROUTES[route_hash[:controller]]&.include?(route_hash[:action])
end
def lfs_route
# Calling route_hash may be expensive. Only do it if we think there's a possible match
- return false unless request.path.end_with?('/info/lfs/objects/batch')
+ unless request.path.end_with?('/info/lfs/objects/batch',
+ '/info/lfs/locks', '/info/lfs/locks/verify') ||
+ %r{/info/lfs/locks/\d+/unlock\z}.match?(request.path)
+ return false
+ end
+
+ WHITELISTED_GIT_LFS_ROUTES[route_hash[:controller]]&.include?(route_hash[:action])
+ end
- route_hash[:controller] == 'projects/lfs_api' && route_hash[:action] == 'batch'
+ def sidekiq_route
+ request.path.start_with?('/admin/sidekiq')
end
end
end
diff --git a/lib/gitlab/omniauth_initializer.rb b/lib/gitlab/omniauth_initializer.rb
index 35ed3a5ac05..f33ea0880df 100644
--- a/lib/gitlab/omniauth_initializer.rb
+++ b/lib/gitlab/omniauth_initializer.rb
@@ -6,13 +6,16 @@ module Gitlab
def execute(providers)
providers.each do |provider|
- add_provider(provider['name'].to_sym, *arguments_for(provider))
+ name = provider['name'].to_sym
+
+ add_provider_to_devise(name, *arguments_for(provider))
+ setup_provider(name)
end
end
private
- def add_provider(*args)
+ def add_provider_to_devise(*args)
@devise_config.omniauth(*args)
end
@@ -71,5 +74,23 @@ module Gitlab
end
end
end
+
+ def omniauth_customized_providers
+ @omniauth_customized_providers ||= build_omniauth_customized_providers
+ end
+
+ # We override this in EE
+ def build_omniauth_customized_providers
+ %i[bitbucket jwt]
+ end
+
+ def setup_provider(provider)
+ case provider
+ when :kerberos
+ require 'omniauth-kerberos'
+ when *omniauth_customized_providers
+ require_dependency "omni_auth/strategies/#{provider}"
+ end
+ end
end
end
diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb
index b9832a724c4..d0cb7c1a7cf 100644
--- a/lib/gitlab/popen.rb
+++ b/lib/gitlab/popen.rb
@@ -34,11 +34,16 @@ module Gitlab
start = Time.now
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
+ # stderr and stdout pipes can block if stderr/stdout aren't drained: https://bugs.ruby-lang.org/issues/9082
+ # Mimic what Ruby does with capture3: https://github.com/ruby/ruby/blob/1ec544695fa02d714180ef9c34e755027b6a2103/lib/open3.rb#L257-L273
+ out_reader = Thread.new { stdout.read }
+ err_reader = Thread.new { stderr.read }
+
yield(stdin) if block_given?
stdin.close
- cmd_stdout = stdout.read
- cmd_stderr = stderr.read
+ cmd_stdout = out_reader.value
+ cmd_stderr = err_reader.value
cmd_status = wait_thr.value
end
diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb
index ecff6ab5d5e..c5bb4648572 100644
--- a/lib/gitlab/profiler.rb
+++ b/lib/gitlab/profiler.rb
@@ -146,5 +146,11 @@ module Gitlab
logger.info("#{model} total (#{query_count}): #{time.round(2)}ms")
end
end
+
+ def self.print_by_total_time(result, options = {})
+ default_options = { sort_method: :total_time }
+
+ Gitlab::Profiler::TotalTimeFlatPrinter.new(result).print(STDOUT, default_options.merge(options))
+ end
end
end
diff --git a/lib/gitlab/profiler/total_time_flat_printer.rb b/lib/gitlab/profiler/total_time_flat_printer.rb
new file mode 100644
index 00000000000..2fd0ec10ba8
--- /dev/null
+++ b/lib/gitlab/profiler/total_time_flat_printer.rb
@@ -0,0 +1,39 @@
+module Gitlab
+ module Profiler
+ class TotalTimeFlatPrinter < RubyProf::FlatPrinter
+ def max_percent
+ @options[:max_percent] || 100
+ end
+
+ # Copied from:
+ # <https://github.com/ruby-prof/ruby-prof/blob/master/lib/ruby-prof/printers/flat_printer.rb>
+ #
+ # The changes are just to filter by total time, not self time, and add a
+ # max_percent option as well.
+ def print_methods(thread)
+ total_time = thread.total_time
+ methods = thread.methods.sort_by(&sort_method).reverse
+
+ sum = 0
+ methods.each do |method|
+ total_percent = (method.total_time / total_time) * 100
+ next if total_percent < min_percent
+ next if total_percent > max_percent
+
+ sum += method.self_time
+
+ @output << "%6.2f %9.3f %9.3f %9.3f %9.3f %8d %s%s\n" % [
+ method.self_time / total_time * 100, # %self
+ method.total_time, # total
+ method.self_time, # self
+ method.wait_time, # wait
+ method.children_time, # children
+ method.called, # calls
+ method.recursive? ? "*" : " ", # cycle
+ method_name(method) # name
+ ]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/project_authorizations/with_nested_groups.rb b/lib/gitlab/project_authorizations/with_nested_groups.rb
index 15b8beacf60..e3da1634fa5 100644
--- a/lib/gitlab/project_authorizations/with_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/with_nested_groups.rb
@@ -24,7 +24,7 @@ module Gitlab
user.projects.select_for_project_authorization,
# The personal projects of the user.
- user.personal_projects.select_as_master_for_project_authorization,
+ user.personal_projects.select_as_maintainer_for_project_authorization,
# Projects that belong directly to any of the groups the user has
# access to.
diff --git a/lib/gitlab/project_authorizations/without_nested_groups.rb b/lib/gitlab/project_authorizations/without_nested_groups.rb
index ad87540e6c2..7d0c00c7f36 100644
--- a/lib/gitlab/project_authorizations/without_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/without_nested_groups.rb
@@ -15,7 +15,7 @@ module Gitlab
user.projects.select_for_project_authorization,
# Personal projects
- user.personal_projects.select_as_master_for_project_authorization,
+ user.personal_projects.select_as_maintainer_for_project_authorization,
# Projects of groups the user is a member of
user.groups_projects.select_for_project_authorization,
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 38bdc61d8ab..62f9e538c04 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -5,7 +5,7 @@ module Gitlab
def initialize(current_user, project, query, repository_ref = nil, per_page: 20)
@current_user = current_user
@project = project
- @repository_ref = repository_ref.presence || project.default_branch
+ @repository_ref = repository_ref.presence
@query = query
@per_page = per_page
end
@@ -95,7 +95,7 @@ module Gitlab
def blobs
return [] unless Ability.allowed?(@current_user, :download_code, @project)
- @blobs ||= Gitlab::FileFinder.new(project, repository_ref).find(query)
+ @blobs ||= Gitlab::FileFinder.new(project, repository_project_ref).find(query)
end
def wiki_blobs
@@ -103,11 +103,8 @@ module Gitlab
@wiki_blobs ||= begin
if project.wiki_enabled? && query.present?
- project_wiki = ProjectWiki.new(project)
-
- unless project_wiki.empty?
- ref = repository_ref || project.wiki.default_branch
- Gitlab::WikiFileFinder.new(project, ref).find(query)
+ unless project.wiki.empty?
+ Gitlab::WikiFileFinder.new(project, repository_wiki_ref).find(query)
else
[]
end
@@ -150,5 +147,13 @@ module Gitlab
def project_ids_relation
project
end
+
+ def repository_project_ref
+ @repository_project_ref ||= repository_ref || project.default_branch
+ end
+
+ def repository_wiki_ref
+ @repository_wiki_ref ||= repository_ref || project.wiki.default_branch
+ end
end
end
diff --git a/lib/gitlab/prometheus/metric.rb b/lib/gitlab/prometheus/metric.rb
index f54b2c6aaff..13cc59df795 100644
--- a/lib/gitlab/prometheus/metric.rb
+++ b/lib/gitlab/prometheus/metric.rb
@@ -3,7 +3,7 @@ module Gitlab
class Metric
include ActiveModel::Model
- attr_accessor :title, :required_metrics, :weight, :y_label, :queries
+ attr_accessor :id, :title, :required_metrics, :weight, :y_label, :queries
validates :title, :required_metrics, :weight, :y_label, :queries, presence: true
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
index e677ec84cd4..8534afcc849 100644
--- a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
+++ b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
@@ -8,6 +8,7 @@ module Gitlab
Deployment.find_by(id: deployment_id).try do |deployment|
query_metrics(
deployment.project,
+ deployment.environment,
common_query_context(
deployment.environment,
timeframe_start: (deployment.created_at - 30.minutes).to_f,
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
index 9273e69e158..e3af217b202 100644
--- a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
+++ b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
@@ -8,6 +8,7 @@ module Gitlab
::Environment.find_by(id: environment_id).try do |environment|
query_metrics(
environment.project,
+ environment,
common_query_context(environment, timeframe_start: 8.hours.ago.to_f, timeframe_end: Time.now.to_f)
)
end
diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
index f5879de1e94..3be35f189d0 100644
--- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb
+++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
@@ -2,7 +2,7 @@ module Gitlab
module Prometheus
module Queries
module QueryAdditionalMetrics
- def query_metrics(project, query_context)
+ def query_metrics(project, environment, query_context)
matched_metrics(project).map(&query_group(query_context))
.select(&method(:group_with_any_metrics))
end
@@ -14,12 +14,16 @@ module Gitlab
lambda do |group|
metrics = group.metrics.map do |metric|
- {
+ metric_hsh = {
title: metric.title,
weight: metric.weight,
y_label: metric.y_label,
queries: metric.queries.map(&query_processor).select(&method(:query_with_result))
}
+
+ metric_hsh[:id] = metric.id if metric.id
+
+ metric_hsh
end
{
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index ac3de2a8f71..0f26fcfe8cb 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -73,5 +73,35 @@ module Gitlab
def build_trace_section_regex
@build_trace_section_regexp ||= /section_((?:start)|(?:end)):(\d+):([a-zA-Z0-9_.-]+)\r\033\[0K/.freeze
end
+
+ def markdown_code_or_html_blocks
+ @markdown_code_or_html_blocks ||= %r{
+ (?<code>
+ # Code blocks:
+ # ```
+ # Anything, including `>>>` blocks which are ignored by this filter
+ # ```
+
+ ^```
+ .+?
+ \n```\ *$
+ )
+ |
+ (?<html>
+ # HTML block:
+ # <tag>
+ # Anything, including `>>>` blocks which are ignored by this filter
+ # </tag>
+
+ ^<[^>]+?>\ *\n
+ .+?
+ \n<\/[^>]+?>\ *$
+ )
+ }mx
+ end
+
+ def jira_transition_id_regex
+ @jira_transition_id_regex ||= /\d+/
+ end
end
end
diff --git a/lib/gitlab/repository_cache_adapter.rb b/lib/gitlab/repository_cache_adapter.rb
index 7f64a8c9e46..2ec871f0754 100644
--- a/lib/gitlab/repository_cache_adapter.rb
+++ b/lib/gitlab/repository_cache_adapter.rb
@@ -25,6 +25,11 @@ module Gitlab
raise NotImplementedError
end
+ # List of cached methods. Should be overridden by the including class
+ def cached_methods
+ raise NotImplementedError
+ end
+
# Caches the supplied block both in a cache and in an instance variable.
#
# The cache key and instance variable are named the same way as the value of
@@ -67,6 +72,11 @@ module Gitlab
# Expires the caches of a specific set of methods
def expire_method_caches(methods)
methods.each do |key|
+ unless cached_methods.include?(key.to_sym)
+ Rails.logger.error "Requested to expire non-existent method '#{key}' for Repository"
+ next
+ end
+
cache.expire(key)
ivar = cache_instance_variable_name(key)
diff --git a/lib/gitlab/serializer/ci/variables.rb b/lib/gitlab/serializer/ci/variables.rb
index c059c454eac..292c8de6229 100644
--- a/lib/gitlab/serializer/ci/variables.rb
+++ b/lib/gitlab/serializer/ci/variables.rb
@@ -13,8 +13,9 @@ module Gitlab
object = YAML.safe_load(string, [Symbol])
object.map do |variable|
- variable[:key] = variable[:key].to_s
- variable
+ variable.symbolize_keys.tap do |variable|
+ variable[:key] = variable[:key].to_s
+ end
end
end
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 5cedd9e84c2..a17cd27e82d 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -74,17 +74,10 @@ module Gitlab
relative_path = name.dup
relative_path << '.git' unless relative_path.end_with?('.git')
- gitaly_migrate(:create_repository,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- repository = Gitlab::Git::Repository.new(storage, relative_path, '')
- repository.gitaly_repository_client.create_repository
- true
- else
- repo_path = File.join(Gitlab.config.repositories.storages[storage].legacy_disk_path, relative_path)
- Gitlab::Git::Repository.create(repo_path, bare: true, symlink_hooks_to: gitlab_shell_hooks_path)
- end
- end
+ repository = Gitlab::Git::Repository.new(storage, relative_path, '')
+ wrapped_gitaly_errors { repository.gitaly_repository_client.create_repository }
+
+ true
rescue => err # Once the Rugged codes gets removes this can be improved
Rails.logger.error("Failed to add repository #{storage}/#{name}: #{err}")
false
@@ -99,21 +92,13 @@ module Gitlab
# Ex.
# import_repository("nfs-file06", "gitlab/gitlab-ci", "https://gitlab.com/gitlab-org/gitlab-test.git")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/874
def import_repository(storage, name, url)
if url.start_with?('.', '/')
raise Error.new("don't use disk paths with import_repository: #{url.inspect}")
end
relative_path = "#{name}.git"
- cmd = gitaly_migrate(:import_repository, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- GitalyGitlabProjects.new(storage, relative_path)
- else
- # The timeout ensures the subprocess won't hang forever
- gitlab_projects(storage, relative_path)
- end
- end
+ cmd = GitalyGitlabProjects.new(storage, relative_path)
success = cmd.import_project(url, git_timeout)
raise Error, cmd.output unless success
@@ -133,12 +118,8 @@ module Gitlab
# fetch_remote(my_repo, "upstream")
#
def fetch_remote(repository, remote, ssh_auth: nil, forced: false, no_tags: false, prune: true)
- gitaly_migrate(:fetch_remote) do |is_enabled|
- if is_enabled
- repository.gitaly_repository_client.fetch_remote(remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, timeout: git_timeout, prune: prune)
- else
- local_fetch_remote(repository.storage, repository.relative_path, remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, prune: prune)
- end
+ wrapped_gitaly_errors do
+ repository.gitaly_repository_client.fetch_remote(remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, timeout: git_timeout, prune: prune)
end
end
@@ -396,28 +377,6 @@ module Gitlab
)
end
- def local_fetch_remote(storage_name, repository_relative_path, remote, ssh_auth: nil, forced: false, no_tags: false, prune: true)
- vars = { force: forced, tags: !no_tags, prune: prune }
-
- if ssh_auth&.ssh_import?
- if ssh_auth.ssh_key_auth? && ssh_auth.ssh_private_key.present?
- vars[:ssh_key] = ssh_auth.ssh_private_key
- end
-
- if ssh_auth.ssh_known_hosts.present?
- vars[:known_hosts] = ssh_auth.ssh_known_hosts
- end
- end
-
- cmd = gitlab_projects(storage_name, repository_relative_path)
-
- success = cmd.fetch_remote(remote, git_timeout, vars)
-
- raise Error, cmd.output unless success
-
- success
- end
-
def gitlab_shell_fast_execute(cmd)
output, status = gitlab_shell_fast_execute_helper(cmd)
@@ -447,8 +406,8 @@ module Gitlab
Gitlab.config.gitlab_shell.git_timeout
end
- def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
- Gitlab::GitalyClient.migrate(method, status: status, &block)
+ def wrapped_gitaly_errors
+ yield
rescue GRPC::NotFound, GRPC::BadStatus => e
# Old Popen code returns [Error, output] to the caller, so we
# need to do the same here...
diff --git a/lib/gitlab/template_helper.rb b/lib/gitlab/template_helper.rb
new file mode 100644
index 00000000000..f24a01e6cf5
--- /dev/null
+++ b/lib/gitlab/template_helper.rb
@@ -0,0 +1,28 @@
+module Gitlab
+ module TemplateHelper
+ include Gitlab::Utils::StrongMemoize
+
+ def prepare_template_environment(file)
+ return unless file
+
+ if Gitlab::ImportExport.object_storage?
+ params[:import_export_upload] = ImportExportUpload.new(import_file: file)
+ else
+ FileUtils.mkdir_p(File.dirname(import_upload_path))
+ FileUtils.copy_entry(file.path, import_upload_path)
+
+ params[:import_source] = import_upload_path
+ end
+ end
+
+ def import_upload_path
+ strong_memoize(:import_upload_path) do
+ Gitlab::ImportExport.import_upload_path(filename: tmp_filename)
+ end
+ end
+
+ def tmp_filename
+ SecureRandom.hex
+ end
+ end
+end
diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb
index 59331c827af..308a95d2f09 100644
--- a/lib/gitlab/url_sanitizer.rb
+++ b/lib/gitlab/url_sanitizer.rb
@@ -58,7 +58,7 @@ module Gitlab
if raw_credentials.present?
url.sub!("#{raw_credentials}@", '')
- user, password = raw_credentials.split(':')
+ user, _, password = raw_credentials.partition(':')
@credentials ||= { user: user.presence, password: password.presence }
end
@@ -71,12 +71,10 @@ module Gitlab
def generate_full_url
return @url unless valid_credentials?
- @full_url = @url.dup
-
- @full_url.password = credentials[:password] if credentials[:password].present?
- @full_url.user = credentials[:user] if credentials[:user].present?
-
- @full_url
+ @url.dup.tap do |generated|
+ generated.password = encode_percent(credentials[:password]) if credentials[:password].present?
+ generated.user = encode_percent(credentials[:user]) if credentials[:user].present?
+ end
end
def safe_url
@@ -89,5 +87,10 @@ module Gitlab
def valid_credentials?
credentials && credentials.is_a?(Hash) && credentials.any?
end
+
+ def encode_percent(string)
+ # CGI.escape converts spaces to +, but this doesn't work for git clone
+ CGI.escape(string).gsub('+', '%20')
+ end
end
end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index dff0c97eeb4..22c9638ecc0 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -95,7 +95,7 @@ module Gitlab
gravatar_enabled: Gitlab::CurrentSettings.gravatar_enabled?,
ldap_enabled: Gitlab.config.ldap.enabled,
mattermost_enabled: Gitlab.config.mattermost.enabled,
- omniauth_enabled: Gitlab.config.omniauth.enabled,
+ omniauth_enabled: Gitlab::Auth.omniauth_enabled?,
reply_by_email_enabled: Gitlab::IncomingEmail.enabled?,
signup_enabled: Gitlab::CurrentSettings.allow_signup?
}
diff --git a/lib/gitlab/user_activities.rb b/lib/gitlab/user_activities.rb
deleted file mode 100644
index 125488536e1..00000000000
--- a/lib/gitlab/user_activities.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-module Gitlab
- class UserActivities
- include Enumerable
-
- KEY = 'users:activities'.freeze
- BATCH_SIZE = 500
-
- def self.record(key, time = Time.now)
- Gitlab::Redis::SharedState.with do |redis|
- redis.hset(KEY, key, time.to_i)
- end
- end
-
- def delete(*keys)
- Gitlab::Redis::SharedState.with do |redis|
- redis.hdel(KEY, keys)
- end
- end
-
- def each
- cursor = 0
- loop do
- cursor, pairs =
- Gitlab::Redis::SharedState.with do |redis|
- redis.hscan(KEY, cursor, count: BATCH_SIZE)
- end
-
- Hash[pairs].each { |pair| yield pair }
-
- break if cursor == '0'
- end
- end
- end
-end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index e893e46ee86..a9629a92a50 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -37,21 +37,14 @@ module Gitlab
end
def send_git_blob(repository, blob)
- params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_raw_show, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT)
- {
- 'GitalyServer' => gitaly_server_hash(repository),
- 'GetBlobRequest' => {
- repository: repository.gitaly_repository.to_h,
- oid: blob.id,
- limit: -1
- }
- }
- else
- {
- 'RepoPath' => repository.path_to_repo,
- 'BlobId' => blob.id
- }
- end
+ params = {
+ 'GitalyServer' => gitaly_server_hash(repository),
+ 'GetBlobRequest' => {
+ repository: repository.gitaly_repository.to_h,
+ oid: blob.id,
+ limit: -1
+ }
+ }
[
SEND_DATA_HEADER,
@@ -91,16 +84,12 @@ module Gitlab
end
def send_git_diff(repository, diff_refs)
- params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_send_git_diff, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT)
- {
- 'GitalyServer' => gitaly_server_hash(repository),
- 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
- gitaly_diff_or_patch_hash(repository, diff_refs)
- ).to_json
- }
- else
- workhorse_diff_or_patch_hash(repository, diff_refs)
- end
+ params = {
+ 'GitalyServer' => gitaly_server_hash(repository),
+ 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
+ gitaly_diff_or_patch_hash(repository, diff_refs)
+ ).to_json
+ }
[
SEND_DATA_HEADER,
@@ -109,16 +98,12 @@ module Gitlab
end
def send_git_patch(repository, diff_refs)
- params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_send_git_patch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT)
- {
- 'GitalyServer' => gitaly_server_hash(repository),
- 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
- gitaly_diff_or_patch_hash(repository, diff_refs)
- ).to_json
- }
- else
- workhorse_diff_or_patch_hash(repository, diff_refs)
- end
+ params = {
+ 'GitalyServer' => gitaly_server_hash(repository),
+ 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
+ gitaly_diff_or_patch_hash(repository, diff_refs)
+ ).to_json
+ }
[
SEND_DATA_HEADER,
@@ -231,14 +216,6 @@ module Gitlab
}
end
- def workhorse_diff_or_patch_hash(repository, diff_refs)
- {
- 'RepoPath' => repository.path_to_repo,
- 'ShaFrom' => diff_refs.base_sha,
- 'ShaTo' => diff_refs.head_sha
- }
- end
-
def gitaly_diff_or_patch_hash(repository, diff_refs)
{
repository: repository.gitaly_repository,
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
index be0d97370d0..e877ab10248 100644
--- a/lib/rouge/formatters/html_gitlab.rb
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -11,7 +11,7 @@ module Rouge
@tag = tag
end
- def stream(tokens, &b)
+ def stream(tokens)
is_first = true
token_lines(tokens) do |line|
yield "\n" unless is_first
diff --git a/lib/support/nginx/registry-ssl b/lib/support/nginx/registry-ssl
index 92511e26861..908d26a0da2 100644
--- a/lib/support/nginx/registry-ssl
+++ b/lib/support/nginx/registry-ssl
@@ -10,7 +10,7 @@ server {
listen *:80;
server_name registry.gitlab.example.com;
server_tokens off; ## Don't show the nginx version number, a security best practice
- return 301 https://$http_host:$request_uri;
+ return 301 https://$http_host$request_uri;
access_log /var/log/nginx/gitlab_registry_access.log gitlab_access;
error_log /var/log/nginx/gitlab_registry_error.log;
}
diff --git a/lib/tasks/gettext.rake b/lib/tasks/gettext.rake
index 21998dd2f5b..f431352b61e 100644
--- a/lib/tasks/gettext.rake
+++ b/lib/tasks/gettext.rake
@@ -19,6 +19,29 @@ namespace :gettext do
Rake::Task['gettext:po_to_json'].invoke
end
+ task :regenerate do
+ pot_file = 'locale/gitlab.pot'
+ # Remove all translated files, this speeds up finding
+ FileUtils.rm Dir['locale/**/gitlab.*']
+ # remove the `pot` file to ensure it's completely regenerated
+ FileUtils.rm_f pot_file
+
+ Rake::Task['gettext:find'].invoke
+
+ # leave only the required changes.
+ `git checkout -- locale/*/gitlab.po`
+
+ # Remove timestamps from the pot file
+ pot_content = File.read pot_file
+ pot_content.gsub!(/^"POT?\-(?:Creation|Revision)\-Date\:.*\n/, '')
+ File.write pot_file, pot_content
+
+ puts <<~MSG
+ All done. Please commit the changes to `locale/gitlab.pot`.
+
+ MSG
+ end
+
desc 'Lint all po files in `locale/'
task lint: :environment do
require 'simple_po_parser'
@@ -50,13 +73,12 @@ 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.*']
-
- # Make sure we start out with a clean pot.file
- `git checkout -- locale/gitlab.pot`
+ FileUtils.rm_f pot_file
# `gettext:find` writes touches to temp files to `stderr` which would cause
# `static-analysis` to report failures. We can ignore these.
@@ -64,18 +86,18 @@ namespace :gettext do
Rake::Task['gettext:find'].invoke
end
- pot_diff = `git diff -- locale/gitlab.pot`.strip
+ pot_diff = `git diff -- #{pot_file} | grep -E '^(\\+|-)msgid'`.strip
# reset the locale folder for potential next tasks
`git checkout -- locale`
if pot_diff.present?
raise <<~MSG
- Newly translated strings found, please add them to `gitlab.pot` by running:
+ Newly translated strings found, please add them to `#{pot_file}` by running:
- rm locale/**/gitlab.*; bin/rake gettext:find; git checkout -- locale/*/gitlab.po
+ bin/rake gettext:regenerate
- Then commit and push the resulting changes to `locale/gitlab.pot`.
+ Then commit and push the resulting changes to `#{pot_file}`.
The diff was:
diff --git a/lib/tasks/gitlab/bulk_add_permission.rake b/lib/tasks/gitlab/bulk_add_permission.rake
index 83dd870fa31..26cbf0740b6 100644
--- a/lib/tasks/gitlab/bulk_add_permission.rake
+++ b/lib/tasks/gitlab/bulk_add_permission.rake
@@ -1,6 +1,6 @@
namespace :gitlab do
namespace :import do
- desc "GitLab | Add all users to all projects (admin users are added as masters)"
+ desc "GitLab | Add all users to all projects (admin users are added as maintainers)"
task all_users_to_all_projects: :environment do |t, args|
user_ids = User.where(admin: false).pluck(:id)
admin_ids = User.where(admin: true).pluck(:id)
@@ -10,7 +10,7 @@ namespace :gitlab do
ProjectMember.add_users_to_projects(project_ids, user_ids, ProjectMember::DEVELOPER)
puts "Importing #{admin_ids.size} admins into #{project_ids.size} projects"
- ProjectMember.add_users_to_projects(project_ids, admin_ids, ProjectMember::MASTER)
+ ProjectMember.add_users_to_projects(project_ids, admin_ids, ProjectMember::MAINTAINER)
end
desc "GitLab | Add a specific user to all projects (as a developer)"
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index a8acafa9cd9..e5b5f3548e4 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -385,14 +385,6 @@ namespace :gitlab do
end
end
- namespace :repo do
- desc "GitLab | Check the integrity of the repositories managed by GitLab"
- task check: :gitlab_environment do
- puts "This task is deprecated. Please use gitlab:git:fsck instead".color(:red)
- Rake::Task["gitlab:git:fsck"].execute
- end
- end
-
namespace :orphans do
desc 'Gitlab | Check for orphaned namespaces and repositories'
task check: :gitlab_environment do
@@ -422,23 +414,6 @@ namespace :gitlab do
end
end
- namespace :user do
- desc "GitLab | Check the integrity of a specific user's repositories"
- task :check_repos, [:username] => :gitlab_environment do |t, args|
- username = args[:username] || prompt("Check repository integrity for username? ".color(:blue))
- user = User.find_by(username: username)
- if user
- repo_dirs = user.authorized_projects.map do |p|
- p.repository.path_to_repo
- end
-
- repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) }
- else
- puts "\nUser '#{username}' not found".color(:red)
- end
- end
- end
-
# Helper methods
##########################
diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake
index 52ae1330d7f..c8a8863443e 100644
--- a/lib/tasks/gitlab/cleanup.rake
+++ b/lib/tasks/gitlab/cleanup.rake
@@ -7,9 +7,8 @@ namespace :gitlab do
desc "GitLab | Cleanup | Clean namespaces"
task dirs: :gitlab_environment do
warn_user_is_not_gitlab
- remove_flag = ENV['REMOVE']
- namespaces = Namespace.pluck(:path)
+ namespaces = Namespace.pluck(:path)
namespaces << HASHED_REPOSITORY_NAME # add so that it will be ignored
Gitlab.config.repositories.storages.each do |name, repository_storage|
git_base_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository_storage.legacy_disk_path }
@@ -31,7 +30,7 @@ namespace :gitlab do
end
all_dirs.each do |dir_path|
- if remove_flag
+ if remove?
if FileUtils.rm_rf dir_path
puts "Removed...#{dir_path}".color(:red)
else
@@ -43,7 +42,7 @@ namespace :gitlab do
end
end
- unless remove_flag
+ unless remove?
puts "To cleanup this directories run this command with REMOVE=true".color(:yellow)
end
end
@@ -105,27 +104,46 @@ namespace :gitlab do
end
end
- # This is a rake task which removes faulty refs. These refs where only
- # created in the 8.13.RC cycle, and fixed in the stable builds which were
- # released. So likely this should only be run once on gitlab.com
- # Faulty refs are moved so they are kept around, else some features break.
- desc 'GitLab | Cleanup | Remove faulty deployment refs'
- task move_faulty_deployment_refs: :gitlab_environment do
- projects = Project.where(id: Deployment.select(:project_id).distinct)
+ desc "GitLab | Cleanup | Clean orphaned project uploads"
+ task project_uploads: :gitlab_environment do
+ warn_user_is_not_gitlab
- projects.find_each do |project|
- rugged = project.repository.rugged
+ cleaner = Gitlab::Cleanup::ProjectUploads.new(logger: logger)
+ cleaner.run!(dry_run: dry_run?)
- max_iid = project.deployments.maximum(:iid)
+ if dry_run?
+ logger.info "To clean up these files run this command with DRY_RUN=false".color(:yellow)
+ end
+ end
- rugged.references.each('refs/environments/**/*') do |ref|
- id = ref.name.split('/').last.to_i
- next unless id > max_iid
+ desc 'GitLab | Cleanup | Clean orphan remote upload files that do not exist in the db'
+ task remote_upload_files: :environment do
+ cleaner = Gitlab::Cleanup::RemoteUploads.new(logger: logger)
+ cleaner.run!(dry_run: dry_run?)
- project.deployments.find(id).create_ref
- project.repository.delete_refs(ref)
- end
+ if dry_run?
+ logger.info "To cleanup these files run this command with DRY_RUN=false".color(:yellow)
end
end
+
+ def remove?
+ ENV['REMOVE'] == 'true'
+ end
+
+ def dry_run?
+ ENV['DRY_RUN'] != 'false'
+ end
+
+ def logger
+ return @logger if defined?(@logger)
+
+ @logger = if Rails.env.development? || Rails.env.production?
+ Logger.new(STDOUT).tap do |stdout_logger|
+ stdout_logger.extend(ActiveSupport::Logger.broadcast(Rails.logger))
+ end
+ else
+ Rails.logger
+ end
+ end
end
end
diff --git a/lib/tasks/gitlab/git.rake b/lib/tasks/gitlab/git.rake
index cb4f7e5c8a8..8a53b51d4fe 100644
--- a/lib/tasks/gitlab/git.rake
+++ b/lib/tasks/gitlab/git.rake
@@ -1,87 +1,24 @@
namespace :gitlab do
namespace :git do
- desc "GitLab | Git | Repack"
- task repack: :gitlab_environment do
- failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} repack -a --quiet), "Repacking repo")
- if failures.empty?
- puts "Done".color(:green)
- else
- output_failures(failures)
- end
- end
-
- desc "GitLab | Git | Run garbage collection on all repos"
- task gc: :gitlab_environment do
- failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} gc --auto --quiet), "Garbage Collecting")
- if failures.empty?
- puts "Done".color(:green)
- else
- output_failures(failures)
- end
- end
-
- desc "GitLab | Git | Prune all repos"
- task prune: :gitlab_environment do
- failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} prune), "Git Prune")
- if failures.empty?
- puts "Done".color(:green)
- else
- output_failures(failures)
- end
- end
-
desc 'GitLab | Git | Check all repos integrity'
task fsck: :gitlab_environment do
- failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} fsck --name-objects --no-progress), "Checking integrity") do |repo|
- check_config_lock(repo)
- check_ref_locks(repo)
- end
-
- if failures.empty?
- puts "Done".color(:green)
- else
- output_failures(failures)
- end
- end
-
- def perform_git_cmd(cmd, message)
- puts "Starting #{message} on all repositories"
-
failures = []
- all_repos do |repo|
- if system(*cmd, chdir: repo)
- puts "Performed #{message} at #{repo}"
- else
- failures << repo
+ Project.find_each(batch_size: 100) do |project|
+ begin
+ project.repository.fsck
+
+ rescue => e
+ failures << "#{project.full_path} on #{project.repository_storage}: #{e}"
end
- yield(repo) if block_given?
+ puts "Performed integrity check for #{project.repository.full_path}"
end
- failures
- end
-
- def output_failures(failures)
- puts "The following repositories reported errors:".color(:red)
- failures.each { |f| puts "- #{f}" }
- end
-
- def check_config_lock(repo_dir)
- config_exists = File.exist?(File.join(repo_dir, 'config.lock'))
- config_output = config_exists ? 'yes'.color(:red) : 'no'.color(:green)
-
- puts "'config.lock' file exists?".color(:yellow) + " ... #{config_output}"
- end
-
- def check_ref_locks(repo_dir)
- lock_files = Dir.glob(File.join(repo_dir, 'refs/heads/*.lock'))
-
- if lock_files.present?
- puts "Ref lock files exist:".color(:red)
-
- lock_files.each { |lock_file| puts " #{lock_file}" }
+ if failures.empty?
+ puts "Done".color(:green)
else
- puts "No ref lock files exist".color(:green)
+ puts "The following repositories reported errors:".color(:red)
+ failures.each { |f| puts "- #{f}" }
end
end
end
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index 6de739e9515..e97d77d20e0 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -54,8 +54,8 @@ namespace :gitlab do
puts "HTTP Clone URL:\t#{http_clone_url}"
puts "SSH Clone URL:\t#{ssh_clone_url}"
puts "Using LDAP:\t#{Gitlab.config.ldap.enabled ? "yes".color(:green) : "no"}"
- puts "Using Omniauth:\t#{Gitlab.config.omniauth.enabled ? "yes".color(:green) : "no"}"
- puts "Omniauth Providers: #{omniauth_providers.join(', ')}" if Gitlab.config.omniauth.enabled
+ puts "Using Omniauth:\t#{Gitlab::Auth.omniauth_enabled? ? "yes".color(:green) : "no"}"
+ puts "Omniauth Providers: #{omniauth_providers.join(', ')}" if Gitlab::Auth.omniauth_enabled?
# check Gitolite version
gitlab_shell_version_file = "#{Gitlab.config.gitlab_shell.hooks_path}/../VERSION"
diff --git a/lib/tasks/gitlab/uploads/migrate.rake b/lib/tasks/gitlab/uploads/migrate.rake
index 78e18992a8e..f548a266b99 100644
--- a/lib/tasks/gitlab/uploads/migrate.rake
+++ b/lib/tasks/gitlab/uploads/migrate.rake
@@ -8,7 +8,7 @@ namespace :gitlab do
@uploader_class = args.uploader_class.constantize
@model_class = args.model_class.constantize
- uploads.each_batch(of: batch_size, &method(:enqueue_batch)) # rubocop: disable Cop/InBatches
+ uploads.each_batch(of: batch_size, &method(:enqueue_batch))
end
def enqueue_batch(batch, index)
diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb
index 5dc85b2baea..53e5ac02e42 100644
--- a/lib/uploaded_file.rb
+++ b/lib/uploaded_file.rb
@@ -21,14 +21,14 @@ class UploadedFile
raise InvalidPathError, "#{path} file does not exist" unless ::File.exist?(path)
@content_type = content_type
- @original_filename = filename || ::File.basename(path)
+ @original_filename = sanitize_filename(filename || path)
@content_type = content_type
@sha256 = sha256
@remote_id = remote_id
@tempfile = File.new(path, 'rb')
end
- def self.from_params(params, field, upload_path)
+ def self.from_params(params, field, upload_paths)
unless params["#{field}.path"]
raise InvalidPathError, "file is invalid" if params["#{field}.remote_id"]
@@ -37,7 +37,8 @@ class UploadedFile
file_path = File.realpath(params["#{field}.path"])
- unless self.allowed_path?(file_path, [upload_path, Dir.tmpdir].compact)
+ paths = Array(upload_paths) << Dir.tmpdir
+ unless self.allowed_path?(file_path, paths.compact)
raise InvalidPathError, "insecure path used '#{file_path}'"
end
@@ -54,6 +55,16 @@ class UploadedFile
end
end
+ # copy-pasted from CarrierWave::SanitizedFile
+ def sanitize_filename(name)
+ name = name.tr("\\", "/") # work-around for IE
+ name = ::File.basename(name)
+ name = name.gsub(CarrierWave::SanitizedFile.sanitize_regexp, "_")
+ name = "_#{name}" if name =~ /\A\.+\z/
+ name = "unnamed" if name.empty?
+ name.mb_chars.to_s
+ end
+
def path
@tempfile.path
end
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index a170014844f..fe79729d6dd 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:34-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Bulgarian\n"
"Language: bg_BG\n"
@@ -15,10 +13,26 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: bg\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:39\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d подаване"
@@ -54,6 +68,26 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+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] "%s подаване беше пропуÑнато, за да не Ñе натоварва ÑиÑтемата."
@@ -65,17 +99,32 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
msgstr[1] ""
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
@@ -88,23 +137,80 @@ msgstr ""
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+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 "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr ""
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "1 Ñхема"
@@ -116,9 +222,51 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Ðабор от графики отноÑно непрекъÑнатата интеграциÑ"
@@ -128,33 +276,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr "ОтноÑно автоматичното внедрÑване"
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr ""
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr ""
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "Ðктивно"
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr "ДейноÑÑ‚"
@@ -179,12 +357,39 @@ msgstr "ДобавÑне на лиценз"
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "ДобавÑне на нова папка"
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -251,7 +456,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -275,6 +483,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -290,6 +540,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -305,13 +558,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -347,18 +600,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr ""
@@ -368,12 +645,21 @@ msgstr ""
msgid "April"
msgstr ""
-msgid "Archived project! Repository is read-only"
-msgstr "Ðрхивиран проект! Хранилището е Ñамо за четене"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ÐаиÑтина ли иÑкате да изтриете този план за Ñхема?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -389,6 +675,12 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -413,12 +705,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Прикачете файл чрез влачене и пуÑкане или %{upload_link}"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -428,12 +735,36 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -449,7 +780,10 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
@@ -470,12 +804,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr ""
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -485,12 +825,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -509,6 +930,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -533,6 +957,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -545,6 +978,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -622,7 +1067,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -703,7 +1148,7 @@ msgstr "Преглед на файловете"
msgid "Browse files"
msgstr "Разглеждане на файловете"
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -712,6 +1157,9 @@ msgstr "от"
msgid "CI / CD"
msgstr ""
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -721,12 +1169,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "Отказ"
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -784,15 +1292,30 @@ msgstr "Подбиране на това подаване"
msgid "Cherry-pick this merge request"
msgstr "Подбиране на тази заÑвка за Ñливане"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -898,9 +1421,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -913,6 +1457,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -922,6 +1469,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -931,10 +1481,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -949,9 +1502,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -967,6 +1517,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -976,22 +1529,25 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -1000,7 +1556,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1012,6 +1568,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1039,6 +1601,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1075,7 +1643,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1102,6 +1676,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -1114,9 +1700,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1144,19 +1727,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select project"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1189,6 +1790,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1219,13 +1823,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1292,6 +1905,9 @@ msgstr ""
msgid "Committed by"
msgstr "Подадено от"
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr "Сравнение"
@@ -1340,6 +1956,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1406,15 +2025,30 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr "РъководÑтво за ÑътрудничеÑтво"
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr "Сътрудници"
@@ -1430,12 +2064,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1451,9 +2094,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr "Копиране на идентификатора на подаването в буфера за обмен"
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1466,12 +2121,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Създайте Ñи личен жетон за доÑтъп в акаунта Ñи, за да можете да изтеглÑте и изпращате промени чрез %{protocol}."
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr "Създаване на папка"
@@ -1484,9 +2145,15 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1505,6 +2172,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1523,10 +2193,16 @@ msgstr "Етикет"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "Ñи Ñъздадете личен жетон за доÑтъп"
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1541,6 +2217,15 @@ msgstr "СинтакÑÐ¸Ñ Ð½Ð° „Cron“"
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr "ПерÑонализирани ÑÑŠÐ±Ð¸Ñ‚Ð¸Ñ Ð·Ð° извеÑÑ‚Ñване"
@@ -1550,6 +2235,12 @@ msgstr "ПерÑонализираните нива на извеÑÑ‚Ñване
msgid "Customize colors"
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 "Cycle Analytics"
msgstr "Ðнализ на циклите"
@@ -1574,6 +2265,9 @@ msgstr "Подготовка за издаване"
msgid "CycleAnalyticsStage|Test"
msgstr "ТеÑтване"
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1586,15 +2280,36 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr "Задайте потребителÑки шаблон, използвайки ÑинтакÑиÑа на „Cron“"
msgid "Delete"
msgstr "Изтриване"
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "ВнедрÑване"
@@ -1603,39 +2318,195 @@ msgstr[1] "ВнедрÑваниÑ"
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr "ОпиÑание"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr "Име на папката"
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr "Да не Ñе показва повече"
@@ -1678,16 +2549,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr "Редактиране"
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "Редактиране на плана %{id} за Ñхема"
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1699,15 +2585,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1723,6 +2618,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1735,15 +2642,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1756,33 +2678,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1798,12 +2741,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1822,6 +2759,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1834,6 +2786,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1861,9 +2816,30 @@ msgstr "Ð’Ñеки меÑец (на 1-во чиÑло, в 4 ч. Ñутринта
msgid "Every week (Sundays at 4:00am)"
msgstr "Ð’ÑÑка Ñедмица (в неделÑ, в 4 ч. Ñутринта)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1891,6 +2867,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1900,6 +2879,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr "СобÑтвеникът не може да бъде променен"
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1909,6 +2891,12 @@ msgstr "Планът за Ñхема не може да бъде премахнÐ
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1918,9 +2906,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr "Файлове"
@@ -1930,6 +2915,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Филтриране по Ñъобщение"
@@ -1939,6 +2927,12 @@ msgstr "ТърÑене по път"
msgid "Find file"
msgstr "ТърÑене на файл"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1948,12 +2942,39 @@ msgstr "Първо"
msgid "FirstPushedBy|pushed by"
msgstr "изпращане на промени от"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "Разклонение"
@@ -1971,9 +2992,24 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr "От Ñъздаването на проблема до внедрÑването в крайната верÑиÑ"
@@ -1986,6 +3022,12 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -2004,7 +3046,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2019,31 +3064,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2064,19 +3121,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2085,16 +3148,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2112,13 +3178,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2148,6 +3220,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2157,6 +3235,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2169,34 +3250,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "Към Вашето разклонение"
msgid "GoToYourFork|Fork"
msgstr "Разклонение"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2208,7 +3355,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2238,6 +3406,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2315,15 +3510,54 @@ msgid_plural "Hide values"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr ""
msgid "Housekeeping successfully started"
msgstr "ОÑвежаването започна уÑпешно"
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2336,15 +3570,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2363,10 +3636,22 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance"
@@ -2380,6 +3665,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2395,6 +3683,9 @@ msgstr "Шаблон за интервала"
msgid "Introducing Cycle Analytics"
msgstr "ПредÑтавÑме Ви анализа на циклите"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2413,12 +3704,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2437,6 +3737,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2461,6 +3764,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Изключено"
@@ -2470,12 +3776,21 @@ msgstr "Включено"
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2485,6 +3800,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2520,6 +3838,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2544,18 +3865,36 @@ msgstr "ÐапуÑкане на групата"
msgid "Leave project"
msgstr "ÐапуÑкане на проекта"
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2565,24 +3904,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2595,13 +3955,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2619,24 +4000,60 @@ msgstr ""
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2646,18 +4063,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2670,6 +4096,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2682,9 +4111,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2715,6 +4162,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2748,6 +4198,15 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2757,6 +4216,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2766,23 +4228,62 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Ðов проблем"
msgstr[1] "Ðови проблема"
-msgid "New Kubernetes Cluster"
-msgstr ""
-
-msgid "New Kubernetes cluster"
+msgid "New Label"
msgstr ""
msgid "New Pipeline Schedule"
msgstr "Ðов план за Ñхема"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "Ðов клон"
@@ -2801,6 +4302,9 @@ msgstr "Ðов файл"
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr "Ðов проблем"
@@ -2810,6 +4314,9 @@ msgstr ""
msgid "New merge request"
msgstr "Ðова заÑвка за Ñливане"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2825,6 +4332,12 @@ msgstr ""
msgid "New tag"
msgstr "Ðов етикет"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2846,7 +4359,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2855,6 +4398,9 @@ msgstr "ÐÑма хранилище"
msgid "No schedules"
msgstr "ÐÑма планове"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -2891,6 +4437,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "Ð¡ÑŠÐ±Ð¸Ñ‚Ð¸Ñ Ð·Ð° извеÑÑ‚Ñване"
@@ -2978,27 +4527,72 @@ msgstr "Филтър"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Отворен"
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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 "Опции"
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3032,12 +4626,30 @@ msgstr ""
msgid "Password"
msgstr ""
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3056,6 +4668,9 @@ msgstr "Планове за Ñхема"
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr "ÐеуÑпешни:"
@@ -3152,10 +4767,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3164,7 +4791,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3182,18 +4809,42 @@ msgstr "Ñ ÐµÑ‚Ð°Ð¿"
msgid "Pipeline|with stages"
msgstr "Ñ ÐµÑ‚Ð°Ð¿Ð¸"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3203,9 +4854,24 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3218,9 +4884,21 @@ msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3239,9 +4917,27 @@ msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3251,6 +4947,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3260,6 +4959,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -3272,6 +4977,9 @@ msgstr "Проектът „%{project_name}“ беше Ñъздаден уÑпÐ
msgid "Project '%{project_name}' was successfully updated."
msgstr "Проектът „%{project_name}“ беше обновен уÑпешно."
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "ДоÑтъпът до проекта Ñ‚Ñ€Ñбва да бъде даван поотделно на вÑеки потребител."
@@ -3296,6 +5004,9 @@ msgstr "Връзката към изнеÑените данни на проекÑ
msgid "Project export started. A download link will be sent by email."
msgstr "ИзнаÑÑнето на проекта започна. Ще получите връзка към данните по е-поща."
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -3305,24 +5016,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr "Изключено"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "Ð’Ñеки Ñ Ð´Ð¾Ñтъп"
-
-msgid "ProjectFeature|Only team members"
-msgstr "Само членовете на екипа"
-
msgid "ProjectFileTree|Name"
msgstr "Име"
@@ -3332,12 +5034,18 @@ msgstr "Ðикога"
msgid "ProjectLifecycle|Stage"
msgstr "Етап"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "Графика"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3356,6 +5064,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3374,7 +5085,37 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3455,21 +5196,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3485,30 +5250,45 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr "Прочетете повече"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "ПрочетиМе"
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
-msgstr "Клони"
-
-msgid "RefSwitcher|Tags"
-msgstr "Етикети"
-
msgid "Reference:"
msgstr ""
+msgid "Refresh"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -3539,36 +5319,60 @@ msgstr "ÐапомнÑне по-къÑно"
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr "Премахване на проекта"
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr "ЗаÑвка за доÑтъп"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr ""
@@ -3578,10 +5382,28 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3595,6 +5417,9 @@ msgstr "ОтмÑна на това подаване"
msgid "Revert this merge request"
msgstr "ОтмÑна на тази заÑвка за Ñливане"
+msgid "Review"
+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 ""
@@ -3604,18 +5429,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3628,6 +5471,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3649,15 +5501,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr "Планиране на Ñхемите"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr "ТърÑете в клоните и етикетите"
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3673,15 +5549,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Изберете формата на архива"
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr "Изберете чаÑова зона"
@@ -3694,9 +5585,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr "Изберете целеви клон"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3712,6 +5621,9 @@ msgstr ""
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3754,9 +5666,15 @@ msgstr ""
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3766,32 +5684,65 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Показване на %d Ñъбитие"
msgstr[1] "Показване на %d ÑъбитиÑ"
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3804,6 +5755,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3813,13 +5767,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3828,9 +5788,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -3948,9 +5917,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr "Звезда"
@@ -3972,33 +5968,66 @@ msgstr ""
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr "Преминаване към клон/етикет"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4007,6 +6036,9 @@ msgstr[1] ""
msgid "Tags"
msgstr "Етикети"
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4070,7 +6102,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4085,6 +6117,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4124,12 +6165,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "Етапът от цикъла на разработка"
@@ -4148,6 +6192,9 @@ msgstr "Ð’Ñеки впиÑан потребител има доÑтъп до п
msgid "The project can be accessed without any authentication."
msgstr "Ð’Ñеки може да има доÑтъп до проекта, без нужда от удоÑтоверÑване."
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "Хранилището за този проект не ÑъщеÑтвува."
@@ -4163,6 +6210,9 @@ msgstr "Етапът на преглед и одобрение показва в
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "Етапът на подготовка за издаване показва времето между прилагането на заÑвката за Ñливане и внедрÑването на кода в Ñредата на работещата крайна верÑиÑ. Данните ще бъдат добавени автоматично Ñлед като направите първото Ñи внедрÑване в крайната верÑиÑ."
@@ -4175,25 +6225,31 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 "СтойноÑтта, коÑто Ñе намира в Ñредата на поÑледователноÑтта от наблюдавани данни. Ðапример: медианата на 3, 5 и 9 е 5, а медианата на 3, 5, 7 и 8 е (5+7)/2 = 6."
msgid "There are no issues to show"
msgstr ""
-msgid "There are no merge requests to show"
+msgid "There are no labels yet"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no merge requests to show"
msgstr ""
-msgid "There was an error loading results"
+msgid "There are problems accessing Git storage: "
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -4214,12 +6270,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4241,6 +6324,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4259,15 +6351,30 @@ msgstr "Това означава, че нÑма да можете да изпр
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4283,10 +6390,13 @@ msgstr "Време преди работата по проблем да запо
msgid "Time between merge request creation and merge/close"
msgstr "Време между Ñъздаване на заÑвка за Ñливане и прилагането/отхвърлÑнето Ñ"
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4310,6 +6420,9 @@ msgstr "преди %s дни"
msgid "Timeago|%s days remaining"
msgstr "оÑтават %s дни"
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr "оÑтават %s чаÑа"
@@ -4325,6 +6438,9 @@ msgstr "преди %s меÑеца"
msgid "Timeago|%s months remaining"
msgstr "оÑтават %s меÑеца"
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr "оÑтават %s Ñекунди"
@@ -4340,48 +6456,45 @@ msgstr "преди %s години"
msgid "Timeago|%s years remaining"
msgstr "оÑтават %s години"
+msgid "Timeago|1 day ago"
+msgstr ""
+
msgid "Timeago|1 day remaining"
msgstr "оÑтава 1 ден"
+msgid "Timeago|1 hour ago"
+msgstr ""
+
msgid "Timeago|1 hour remaining"
msgstr "оÑтава 1 чаÑ"
+msgid "Timeago|1 minute ago"
+msgstr ""
+
msgid "Timeago|1 minute remaining"
msgstr "оÑтава 1 минута"
+msgid "Timeago|1 month ago"
+msgstr ""
+
msgid "Timeago|1 month remaining"
msgstr "оÑтава 1 меÑец"
+msgid "Timeago|1 week ago"
+msgstr ""
+
msgid "Timeago|1 week remaining"
msgstr "оÑтава 1 Ñедмица"
+msgid "Timeago|1 year ago"
+msgstr ""
+
msgid "Timeago|1 year remaining"
msgstr "оÑтава 1 година"
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|a year ago"
-msgstr "преди една година"
-
-msgid "Timeago|about %s hours ago"
-msgstr "преди около %s чаÑа"
-
-msgid "Timeago|about a minute ago"
-msgstr "преди около една минута"
-
-msgid "Timeago|about an hour ago"
-msgstr "преди около един чаÑ"
-
msgid "Timeago|in %s days"
msgstr "Ñлед %s дни"
@@ -4421,11 +6534,14 @@ msgstr "Ñлед 1 Ñедмица"
msgid "Timeago|in 1 year"
msgstr "Ñлед 1 година"
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
msgstr ""
-msgid "Timeago|less than a minute ago"
-msgstr "преди по-малко от минута"
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4449,6 +6565,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4458,6 +6577,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4467,21 +6592,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4491,6 +6640,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr "Общо време"
@@ -4509,12 +6664,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4527,12 +6700,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr "Без звезда"
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4548,6 +6754,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr "Качване на нов файл"
@@ -4566,9 +6775,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4578,9 +6796,21 @@ msgstr "Използване на глобалната Ви наÑтройка Ð
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4593,7 +6823,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4605,9 +6838,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr "Преглед на отворената заÑвка за Ñливане"
@@ -4620,6 +6865,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "Вътрешен"
@@ -4635,7 +6886,7 @@ msgstr "ÐеизвеÑтно"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "ИÑкате ли да видите данните? Помолете админиÑтратор за доÑтъп."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4653,10 +6904,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4683,7 +6946,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4719,6 +7006,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4734,7 +7027,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4746,9 +7039,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4773,7 +7063,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "ОттеглÑне на заÑвката за доÑтъп"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4791,7 +7090,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4800,6 +7102,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4812,22 +7120,40 @@ msgstr "Можете да добавÑте файлове Ñамо когато
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Ðе можете да Ñъздавате повече проекти"
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4836,6 +7162,9 @@ msgstr "ТрÑбва да Ñе впишете, за да отбележите п
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "Ðуждаете Ñе от разрешение."
@@ -4866,9 +7195,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4884,6 +7222,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4902,10 +7246,13 @@ msgstr "Вашето име"
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4919,45 +7266,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4967,19 +7407,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST detected no security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4988,15 +7425,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5006,11 +7482,17 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "ден"
msgstr[1] "дни"
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5024,16 +7506,28 @@ msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5045,6 +7539,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5068,24 +7565,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5113,6 +7604,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5146,9 +7640,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5209,6 +7718,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5218,9 +7730,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5262,15 +7771,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5283,6 +7801,14 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/cs_CZ/gitlab.po b/locale/cs_CZ/gitlab.po
new file mode 100644
index 00000000000..5e433cec164
--- /dev/null
+++ b/locale/cs_CZ/gitlab.po
@@ -0,0 +1,7910 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Czech\n"
+"Language: cs_CZ\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: cs\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+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] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+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 not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+msgstr ""
+
+msgid "%{storage_name}: failed storage access attempt on host:"
+msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add License"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: ${details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+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 you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Background jobs"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+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 "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+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 ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+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 "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about environments"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about security configuration"
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|Security"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|installing applications"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure Sidekiq job throttling."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push and pull mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateNewFork|Fork"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+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 "Customize colors"
+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 "Cycle Analytics"
+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 "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default classification label"
+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 ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+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 "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+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 "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Go to your fork"
+msgstr ""
+
+msgid "GoToYourFork|Fork"
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the 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 ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Filter by name..."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+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 "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+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 already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Koding"
+msgstr ""
+
+msgid "Koding Dashboard"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+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 "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+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 ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+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|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+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 "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+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 Badges"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+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 name"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+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 "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+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 ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "Security report"
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up Koding"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Setup a specific Runner automatically"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+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 ""
+
+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 connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+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 maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+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 private key to use when a client certificate is provided. This value is encrypted at rest."
+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 pseudonymizer data collection is disabled. 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."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+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 roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+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 in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+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>:</code>. 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 ""
+
+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 no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+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 in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+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 ago"
+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 ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+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|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Time|s"
+msgstr ""
+
+msgid "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "Upload new avatar"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+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 detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+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_full_name}. 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_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have not created any merge requests"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You must sign in to star a project"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+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 "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 need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "and 1 fixed vulnerability"
+msgid_plural "and %d fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{type} detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|SAST is loading"
+msgstr ""
+
+msgid "ciReport|SAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "detected %d fixed vulnerability"
+msgid_plural "detected %d fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "detected %d new vulnerability"
+msgid_plural "detected %d new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "detected no vulnerabilities"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 5a5cf1a19a3..53335658c82 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:37-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -15,10 +13,26 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d Commit"
@@ -54,6 +68,26 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+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] "%s zusätzlicher Commit wurde ausgelassen um Leistungsprobleme zu verhindern."
@@ -65,17 +99,32 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
msgstr[1] ""
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
@@ -88,23 +137,80 @@ msgstr "%{number_of_failures} von %{maximum_failures} Fehlschlägen. GitLab wird
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+msgstr ""
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] "%{storage_name}: fehlgeschlagener Speicherzugriff auf Host:"
msgstr[1] "%{storage_name}: %{failed_attempts} fehlgeschlagene Speicherzugriffe:"
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(beachte die Informationen zur Installation auf %{link})."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr ""
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] ""
@@ -116,9 +222,51 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Eine Sammlung von Graphen bezüglich kontinuierlicher Integration"
@@ -128,33 +276,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr "Ãœber automatische Bereitstellung "
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr ""
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Zugriff auf fehlerhafte Speicher wurde vorübergehend deaktiviert, um die Wiederherstellung zu ermöglichen. Für den zukünftigen Zugriff, behebe bitte das Problem und setze danach die Speicherinformationen zurück."
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr "Konto"
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "Aktiv"
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr "Aktivität"
@@ -179,12 +357,39 @@ msgstr "Lizenz hinzufügen"
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "Erstelle eine neues Verzeichnis"
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -251,7 +456,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -275,6 +483,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -290,6 +540,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -305,13 +558,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -347,18 +600,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr "Anwendungen"
@@ -368,12 +645,21 @@ msgstr ""
msgid "April"
msgstr ""
-msgid "Archived project! Repository is read-only"
-msgstr "Archiviertes Projekt! Repository ist nicht änderbar."
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Bist Du sicher, dass Du diesen Pipeline-Zeitplan löschen möchtest?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Bist Du sicher, dass Du den Registrierungstoken zurücksetzen willst?"
@@ -389,6 +675,12 @@ msgstr "Bist Du sicher?"
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -413,12 +705,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Datei mittels Drag &amp; Drop oder %{upload_link} hinzufügen"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -428,12 +735,36 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -449,7 +780,10 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
@@ -470,12 +804,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr ""
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -485,12 +825,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -509,6 +930,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -533,6 +957,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -545,6 +978,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -622,7 +1067,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -703,7 +1148,7 @@ msgstr "Dateien durchsuchen"
msgid "Browse files"
msgstr "Dateien durchsuchen"
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -712,6 +1157,9 @@ msgstr "von"
msgid "CI / CD"
msgstr "CI / CD"
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -721,12 +1169,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "Abbrechen"
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -784,15 +1292,30 @@ msgstr "Diesen Commit herauspicken "
msgid "Cherry-pick this merge request"
msgstr "Diesen Merge Request herauspicken"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -898,9 +1421,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -913,6 +1457,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -922,6 +1469,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -931,10 +1481,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -949,9 +1502,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -967,6 +1517,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -976,22 +1529,25 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -1000,7 +1556,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1012,6 +1568,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1039,6 +1601,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1075,7 +1643,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1102,6 +1676,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -1114,9 +1700,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1144,19 +1727,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select zone"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1189,6 +1790,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1219,13 +1823,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1292,6 +1905,9 @@ msgstr ""
msgid "Committed by"
msgstr "Committed von"
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr "Vergleichen"
@@ -1340,6 +1956,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1406,15 +2025,30 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr "Mitarbeitsanleitung"
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr "Mitarbeiter"
@@ -1430,12 +2064,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1451,9 +2094,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr "Kopiere Commit SHA in die Zwischenablage"
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1466,12 +2121,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Erstelle einen persönlichen Zugriffstoken in Deinem Konto um mittels %{protocol} zu übertragen (push) oder abzurufen (pull)."
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr "Erstelle Verzeichnis"
@@ -1484,9 +2145,15 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1505,6 +2172,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1523,10 +2193,16 @@ msgstr "Tag "
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "Erstelle einen persönlichen Zugriffstoken"
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1541,6 +2217,15 @@ msgstr "Cron Syntax"
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Individuelle Benachrichtigungsereignisse"
@@ -1550,6 +2235,12 @@ msgstr "Individuelle Benachrichtigungsstufen sind identisch mit den Beteiligungs
msgid "Customize colors"
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 "Cycle Analytics"
msgstr "Arbeitsablaufsanalysen"
@@ -1574,6 +2265,9 @@ msgstr "Staging"
msgid "CycleAnalyticsStage|Test"
msgstr "Test"
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1586,15 +2280,36 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr "Erstelle ein individuelles Muster mittels Cron Syntax"
msgid "Delete"
msgstr "Löschen"
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "Bereitstellung"
@@ -1603,39 +2318,195 @@ msgstr[1] "Bereitstellungen"
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr "Beschreibung"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr "Details"
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr "Verzeichnisname"
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr "Nicht erneut anzeigen"
@@ -1678,16 +2549,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr "Bearbeiten"
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "Pipeline Zeitplan bearbeiten %{id}"
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1699,15 +2585,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr "E-Mails"
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1723,6 +2618,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1735,15 +2642,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1756,33 +2678,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1798,12 +2741,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1822,6 +2759,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1834,6 +2786,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "Filtere alle"
@@ -1861,9 +2816,30 @@ msgstr "Monatlich (am Ersten um 4:00 Uhr)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Wöchentlich (Sonntags um 4:00 Uhr)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1891,6 +2867,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1900,6 +2879,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr "Wechsel des Besitzers fehlgeschlagen"
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1909,6 +2891,12 @@ msgstr "Entfernung der Pipelineplanung fehlgeschlagen"
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1918,9 +2906,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr "Dateien"
@@ -1930,6 +2915,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Filter nach Commit Nachricht"
@@ -1939,6 +2927,12 @@ msgstr "Finde über den Pfad"
msgid "Find file"
msgstr "Finde Datei"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1948,12 +2942,39 @@ msgstr "Erster"
msgid "FirstPushedBy|pushed by"
msgstr "übertragen von"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "Ableger"
@@ -1971,9 +2992,24 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr "Von der Ticketbeschreibung bis zur Bereitstellung"
@@ -1986,6 +3022,12 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -2004,7 +3046,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2019,31 +3064,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2064,19 +3121,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2085,16 +3148,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2112,13 +3178,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2148,6 +3220,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2157,6 +3235,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr "Informationen über den Speicherzustand von Gitlab wurden zurückgesetzt."
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2169,34 +3250,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "GitLab Runner Bereich"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "Gehe zu Deinem Ableger"
msgid "GoToYourFork|Fork"
msgstr "Ableger"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2208,7 +3355,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2238,6 +3406,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2315,15 +3510,54 @@ msgid_plural "Hide values"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr ""
msgid "Housekeeping successfully started"
msgstr "Aufräumen erfolgreich gestartet"
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2336,15 +3570,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2363,11 +3636,23 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "Installiere einen Runner der mit GitLab CI kompatibel ist"
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
msgid "Instance"
msgid_plural "Instances"
@@ -2380,6 +3665,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2395,6 +3683,9 @@ msgstr "Intervallmuster"
msgid "Introducing Cycle Analytics"
msgstr "Arbeitsablaufsanalysen vorgestellt"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2413,12 +3704,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2437,6 +3737,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2461,6 +3764,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Deaktiviert"
@@ -2470,12 +3776,21 @@ msgstr "Aktiviert"
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2485,6 +3800,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2520,6 +3838,9 @@ msgstr "Du übertrugst an"
msgid "LastPushEvent|at"
msgstr "am"
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2544,18 +3865,36 @@ 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 ""
+
msgid "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2565,24 +3904,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2595,13 +3955,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2619,24 +4000,60 @@ msgstr "Mitglieder"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr "Ereignisse zusammenführen"
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr "Nachrichten"
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2646,18 +4063,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2670,6 +4096,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2682,9 +4111,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2715,6 +4162,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2748,6 +4198,15 @@ msgstr ""
msgid "Monitoring"
msgstr "Ãœberwachung"
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2757,6 +4216,9 @@ msgstr ""
msgid "More information is available|here"
msgstr "hier"
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2766,23 +4228,62 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Neues Ticket"
msgstr[1] "Neue Tickets"
-msgid "New Kubernetes Cluster"
-msgstr ""
-
-msgid "New Kubernetes cluster"
+msgid "New Label"
msgstr ""
msgid "New Pipeline Schedule"
msgstr "Neuer Pipeline Zeitplan"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "Neuer Branch"
@@ -2801,6 +4302,9 @@ msgstr "Neue Datei"
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr "Neues Ticket"
@@ -2810,6 +4314,9 @@ msgstr ""
msgid "New merge request"
msgstr "Neuer Merge Request"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2825,6 +4332,12 @@ msgstr ""
msgid "New tag"
msgstr "Neuer Tag"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2846,7 +4359,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2855,6 +4398,9 @@ msgstr "Kein Repository"
msgid "No schedules"
msgstr "Keine Zeitpläne"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -2891,6 +4437,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "Benachrichtigungsereignisse"
@@ -2978,27 +4527,72 @@ msgstr "Filter"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Ungelöst"
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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 "Optionen"
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3032,12 +4626,30 @@ msgstr ""
msgid "Password"
msgstr "Passwort"
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3056,6 +4668,9 @@ msgstr "Zustände der Pipeline"
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr "Fehlgeschlagen:"
@@ -3152,10 +4767,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3164,7 +4791,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3182,18 +4809,42 @@ msgstr "mit Stage"
msgid "Pipeline|with stages"
msgstr "mit Stages"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3203,9 +4854,24 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3218,9 +4884,21 @@ msgstr ""
msgid "Profile"
msgstr "Profil"
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3239,9 +4917,27 @@ msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3251,6 +4947,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3260,6 +4959,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -3272,6 +4977,9 @@ msgstr "Das Projekt '%{project_name}' wurde erfolgreich erstellt."
msgid "Project '%{project_name}' was successfully updated."
msgstr "Das Projekt '%{project_name}' wurde erfolgreich aktualisiert."
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "Jedem Nutzer muss explizit der Zugriff auf das Projekt gewährt werden."
@@ -3296,6 +5004,9 @@ msgstr "Der Link für den Export des Projektes ist abgelaufen. Bitte generiere e
msgid "Project export started. A download link will be sent by email."
msgstr "Export des Projektes gestartet. Ein Link zum herunterladen wir Dir per E-Mail zugesandt."
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "Abonnieren"
@@ -3305,24 +5016,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr "Dekativiert"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "Jeder mit Zugriff"
-
-msgid "ProjectFeature|Only team members"
-msgstr "Nur Teammitglieder"
-
msgid "ProjectFileTree|Name"
msgstr "Name"
@@ -3332,12 +5034,18 @@ msgstr "Niemals"
msgid "ProjectLifecycle|Stage"
msgstr "Stage"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "Diagramm"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3356,6 +5064,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3374,7 +5085,37 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3455,21 +5196,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3485,30 +5250,45 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr "Mehr lesen"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "Lies mich"
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
-msgstr "Branches"
-
-msgid "RefSwitcher|Tags"
-msgstr "Tags"
-
msgid "Reference:"
msgstr ""
+msgid "Refresh"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -3539,36 +5319,60 @@ msgstr "Später erinnern"
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr "Projekt entfernen"
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr "Anfrage auf Zugriff"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr "Informationen über Speicherzustand zurücksetzen"
@@ -3578,10 +5382,28 @@ msgstr "Zugriffstoken für Systemzustand zurücksetzen"
msgid "Reset runners registration token"
msgstr "Registrierungstoken für Runner zurücksetzen"
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3595,6 +5417,9 @@ msgstr "Commit zurücksetzen"
msgid "Revert this merge request"
msgstr "Merge Request zurücksetzen"
+msgid "Review"
+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 ""
@@ -3604,18 +5429,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3628,6 +5471,15 @@ msgstr ""
msgid "SSH Keys"
msgstr "SSH-Schlüssel"
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3649,15 +5501,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr "Pipelines planen"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr "Suche nach Branches und Tags"
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3673,15 +5549,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Archivierungsformat auswählen"
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr "Zeitzone auswählen"
@@ -3694,9 +5585,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr "Zielbranch auswählen"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3712,6 +5621,9 @@ msgstr ""
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3754,9 +5666,15 @@ msgstr "Einstellungen"
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3766,32 +5684,65 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Zeige %d Ereignis"
msgstr[1] "Zeige %d Ereignisse"
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3804,6 +5755,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3813,13 +5767,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3828,9 +5788,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -3948,9 +5917,36 @@ msgstr "Spam-Protokolle"
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "Lege die folgende URL während des Runner Setups fest:"
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr "Favorisieren"
@@ -3972,33 +5968,66 @@ msgstr "Starte den Runner!"
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr "Zu Branch/Tag wechseln"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4007,6 +6036,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4070,7 +6102,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4085,6 +6117,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4124,12 +6165,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "Die Phase des Entwicklungslebenszyklus."
@@ -4148,6 +6192,9 @@ msgstr "Auf das Projekt kann jeder angemeldete Nutzer zugreifen."
msgid "The project can be accessed without any authentication."
msgstr "Auf das Projekt kann ohne Authentifizierung zugegriffen werden."
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "Das Repository für das Projekt existiert nicht."
@@ -4163,6 +6210,9 @@ msgstr "Die Überprüfungsphase stellt die Zeit vom Anlegen eines Merge Requests
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "Die Staging-Phase stellt die Zeit zwischen der Umsetzung eines Merge Requests und der Bereitstellung des Codes auf dem Produktivsystem dar. Sobald Du das erste Mal auf das Produktivsystem ausgeliefert hast, werden dessen Daten hier automatisch angezeigt."
@@ -4175,27 +6225,33 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "Zeit, die für das jeweilige Ereignis in der Phase ermittelt wurde."
+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>:</code>. 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 ""
+
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 "Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Median 5. Bei 3, 5, 7, 8 ist der Median (5+7)/2 = 6."
msgid "There are no issues to show"
msgstr ""
+msgid "There are no labels yet"
+msgstr ""
+
msgid "There are no merge requests to show"
msgstr ""
msgid "There are problems accessing Git storage: "
msgstr "Es gibt ein Problem beim Zugriff auf den Gitspeicher:"
-msgid "There was an error loading results"
-msgstr ""
-
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -4214,12 +6270,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4241,6 +6324,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4259,15 +6351,30 @@ msgstr "Dies bedeutet, dass Du keinen Code übertragen kannst, bevor Du kein lee
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4283,10 +6390,13 @@ msgstr "Zeit bis die Implementierung für ein Ticket beginnt"
msgid "Time between merge request creation and merge/close"
msgstr "Zeit zwischen einem Merge Request und dessen Umsetzung / Schließung"
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4310,6 +6420,9 @@ msgstr "vor %s Tagen"
msgid "Timeago|%s days remaining"
msgstr "%s Tage verbleibend"
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr "%s Stunden verbleibend"
@@ -4325,6 +6438,9 @@ msgstr ""
msgid "Timeago|%s months remaining"
msgstr "%s Monate verbleibend"
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr "%s Sekunden verbleibend"
@@ -4340,48 +6456,45 @@ msgstr ""
msgid "Timeago|%s years remaining"
msgstr "%s Jahre verbleibend"
+msgid "Timeago|1 day ago"
+msgstr ""
+
msgid "Timeago|1 day remaining"
msgstr "1 Tag verbleibend"
+msgid "Timeago|1 hour ago"
+msgstr ""
+
msgid "Timeago|1 hour remaining"
msgstr "1 Stunde verbleibend"
+msgid "Timeago|1 minute ago"
+msgstr ""
+
msgid "Timeago|1 minute remaining"
msgstr "1 Minute verbleibend"
+msgid "Timeago|1 month ago"
+msgstr ""
+
msgid "Timeago|1 month remaining"
msgstr "1 Monat verbleibend"
+msgid "Timeago|1 week ago"
+msgstr ""
+
msgid "Timeago|1 week remaining"
msgstr "1 Woche verbleibend"
+msgid "Timeago|1 year ago"
+msgstr ""
+
msgid "Timeago|1 year remaining"
msgstr "1 Jahr verbleibend"
msgid "Timeago|Past due"
msgstr "Fällig"
-msgid "Timeago|a day ago"
-msgstr "vor einem Tag"
-
-msgid "Timeago|a month ago"
-msgstr "vor einem Monat"
-
-msgid "Timeago|a week ago"
-msgstr "vor einer Woche"
-
-msgid "Timeago|a year ago"
-msgstr "vor einem Jahr"
-
-msgid "Timeago|about %s hours ago"
-msgstr "vor ungefähr %s Stunden"
-
-msgid "Timeago|about a minute ago"
-msgstr "vor ungefähr einer Minute"
-
-msgid "Timeago|about an hour ago"
-msgstr "vor ungefähr einer Stunde"
-
msgid "Timeago|in %s days"
msgstr "in %s Tagen"
@@ -4421,11 +6534,14 @@ msgstr "in 1 Woche"
msgid "Timeago|in 1 year"
msgstr "in 1 Jahr"
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
msgstr ""
-msgid "Timeago|less than a minute ago"
-msgstr "vor weniger als einer Minute"
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4449,6 +6565,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4458,6 +6577,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4467,21 +6592,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4491,6 +6640,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr "Gesamtzeit"
@@ -4509,12 +6664,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4527,12 +6700,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr "Entfavorisieren"
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4548,6 +6754,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr "Eine Neue Datei hochladen"
@@ -4566,9 +6775,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "Benutze den folgenden Registrierungstoken während des Setups:"
@@ -4578,9 +6796,21 @@ msgstr "Benutze Deine globalen Benachrichtigungseinstellungen"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4593,7 +6823,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4605,9 +6838,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr "Zeige offene Merge Requests."
@@ -4620,6 +6865,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "Intern"
@@ -4635,7 +6886,7 @@ msgstr "Unbekannt"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Du möchtest diese Daten sehen? Bitte frage einen Administrator nach dem Zugang."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4653,10 +6904,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4683,7 +6946,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4719,6 +7006,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4734,7 +7027,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4746,9 +7039,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4773,7 +7063,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "Zugriffsanfrage widerrufen"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4791,7 +7090,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4800,6 +7102,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4812,22 +7120,40 @@ msgstr "Du kannst Dateien nur hinzufügen, wenn Du dich auf einem Branch befinde
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Du hast die Projektbegrenzung erreicht."
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4836,6 +7162,9 @@ msgstr "Du musst angemeldet sein, um ein Projekt zu favorisieren."
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "Du brauchst eine Genehmigung."
@@ -4866,9 +7195,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4884,6 +7222,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4902,10 +7246,13 @@ msgstr "Dein Name"
msgid "Your projects"
msgstr "Deine Projekte"
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4919,45 +7266,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4967,19 +7407,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST detected no security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4988,15 +7425,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5006,11 +7482,17 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "Tag"
msgstr[1] "Tage"
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5024,16 +7506,28 @@ msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5045,6 +7539,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5068,24 +7565,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5113,6 +7604,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5146,9 +7640,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5209,6 +7718,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5218,9 +7730,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5262,15 +7771,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5283,6 +7801,14 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index 009ad31d28b..1da61800729 100644
--- a/locale/eo/gitlab.po
+++ b/locale/eo/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:38-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Esperanto\n"
"Language: eo_UY\n"
@@ -15,10 +13,26 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: eo\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d enmetado"
@@ -54,6 +68,26 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+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] "%s enmetado estis transsaltita, por ne troÅarÄi la sistemon."
@@ -65,17 +99,32 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
msgstr[1] ""
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
@@ -88,23 +137,80 @@ msgstr ""
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+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 "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr ""
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "1 ĉenstablo"
@@ -116,9 +222,51 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Aro da diagramoj pri la seninterrompa integrado"
@@ -128,33 +276,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr "Pri la aÅ­tomata disponigado"
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr ""
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr ""
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "Aktiva"
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr "Aktiveco"
@@ -179,12 +357,39 @@ msgstr "Aldoni rajtigilon"
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "Aldoni novan dosierujon"
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -251,7 +456,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -275,6 +483,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -290,6 +540,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -305,13 +558,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -347,18 +600,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr ""
@@ -368,12 +645,21 @@ msgstr ""
msgid "April"
msgstr ""
-msgid "Archived project! Repository is read-only"
-msgstr "Arkivita projekto! La deponejo permesas nur legadon"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ĉu vi certe volas forigi ĉi tiun ĉenstablan planon?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -389,6 +675,12 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -413,12 +705,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Alkroĉu dosieron per Åovmetado aÅ­ %{upload_link}"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -428,12 +735,36 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -449,7 +780,10 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
@@ -470,12 +804,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr ""
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -485,12 +825,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -509,6 +930,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -533,6 +957,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -545,6 +978,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -622,7 +1067,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -703,7 +1148,7 @@ msgstr "Foliumi dosierojn"
msgid "Browse files"
msgstr "Elekti dosierojn"
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -712,6 +1157,9 @@ msgstr "de"
msgid "CI / CD"
msgstr ""
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -721,12 +1169,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "Nuligi"
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -784,15 +1292,30 @@ msgstr "Precize elekti ĉi tiun kunmetadon"
msgid "Cherry-pick this merge request"
msgstr "Precize elekti ĉi tiun peton pri kunfando"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -898,9 +1421,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -913,6 +1457,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -922,6 +1469,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -931,10 +1481,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -949,9 +1502,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -967,6 +1517,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -976,22 +1529,25 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -1000,7 +1556,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1012,6 +1568,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1039,6 +1601,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1075,7 +1643,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1102,6 +1676,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -1114,9 +1700,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1144,19 +1727,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select project"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1189,6 +1790,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1219,13 +1823,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1292,6 +1905,9 @@ msgstr ""
msgid "Committed by"
msgstr "Enmetita de"
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr "Kompari"
@@ -1340,6 +1956,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1406,15 +2025,30 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr "Gvidlinioj por kontribuado"
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr "Kontribuantoj"
@@ -1430,12 +2064,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1451,9 +2094,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr "Kopii la identigilon de la enmetado"
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1466,12 +2121,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Kreu propran atingoĵetonon en via konto por ebligi al vi eltiri kaj alpuÅi per %{protocol}."
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr "Krei dosierujon"
@@ -1484,9 +2145,15 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1505,6 +2172,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1523,10 +2193,16 @@ msgstr "Etikedo"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "kreos propran atingoĵetonon"
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1541,6 +2217,15 @@ msgstr "La sintakso de Cron"
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Propraj sciigaj eventoj"
@@ -1550,6 +2235,12 @@ msgstr "La propraj sciigaj niveloj estas la samaj kiel la niveloj de partoprenad
msgid "Customize colors"
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 "Cycle Analytics"
msgstr "Cikla analizo"
@@ -1574,6 +2265,9 @@ msgstr "Preparo por eldono"
msgid "CycleAnalyticsStage|Test"
msgstr "Testado"
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1586,15 +2280,36 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr "Difini propran Åablonon, uzante la sintakson de Cron"
msgid "Delete"
msgstr "Forigi"
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "Disponigado"
@@ -1603,39 +2318,195 @@ msgstr[1] "Disponigadoj"
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr "Priskribo"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr "Nomo de dosierujo"
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr "Ne montru denove"
@@ -1678,16 +2549,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr "Redakti"
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "Redakti ĉenstablan planon %{id}"
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1699,15 +2585,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1723,6 +2618,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1735,15 +2642,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1756,33 +2678,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1798,12 +2741,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1822,6 +2759,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1834,6 +2786,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1861,9 +2816,30 @@ msgstr "Ĉiumonate (en la 1a de la monato, je 4:00)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Ĉiusemajne (en dimanĉo, je 4:00)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1891,6 +2867,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1900,6 +2879,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr "Ne eblas ÅanÄi la posedanton"
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1909,6 +2891,12 @@ msgstr "Ne eblas forigi la ĉenstablan planon"
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1918,9 +2906,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr "Dosieroj"
@@ -1930,6 +2915,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Filtri per mesaÄo"
@@ -1939,6 +2927,12 @@ msgstr "Trovi per dosierindiko"
msgid "Find file"
msgstr "Trovi dosieron"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1948,12 +2942,39 @@ msgstr "Unue"
msgid "FirstPushedBy|pushed by"
msgstr "alpuÅita de"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "Disbranĉigo"
@@ -1971,9 +2992,24 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+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"
@@ -1986,6 +3022,12 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -2004,7 +3046,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2019,31 +3064,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2064,19 +3121,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2085,16 +3148,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2112,13 +3178,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2148,6 +3220,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2157,6 +3235,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2169,34 +3250,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "Al via disbranĉigo"
msgid "GoToYourFork|Fork"
msgstr "Disbranĉigo"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2208,7 +3355,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2238,6 +3406,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2315,15 +3510,54 @@ msgid_plural "Hide values"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr ""
msgid "Housekeeping successfully started"
msgstr "La refreÅigo komenciÄis sukcese"
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2336,15 +3570,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2363,10 +3636,22 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance"
@@ -2380,6 +3665,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2395,6 +3683,9 @@ msgstr "Intervala Åablono"
msgid "Introducing Cycle Analytics"
msgstr "Ni prezentas al vi la ciklan analizon"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2413,12 +3704,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2437,6 +3737,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2461,6 +3764,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "MalÅaltita"
@@ -2470,12 +3776,21 @@ msgstr "Åœaltita"
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2485,6 +3800,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2520,6 +3838,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2544,18 +3865,36 @@ 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 "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2565,24 +3904,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2595,13 +3955,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2619,24 +4000,60 @@ msgstr ""
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2646,18 +4063,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2670,6 +4096,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2682,9 +4111,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2715,6 +4162,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2748,6 +4198,15 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2757,6 +4216,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2766,23 +4228,62 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nova problemo"
msgstr[1] "Novaj problemoj"
-msgid "New Kubernetes Cluster"
-msgstr ""
-
-msgid "New Kubernetes cluster"
+msgid "New Label"
msgstr ""
msgid "New Pipeline Schedule"
msgstr "Nova ĉenstabla plano"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "Nova branĉo"
@@ -2801,6 +4302,9 @@ msgstr "Nova dosiero"
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr "Nova problemo"
@@ -2810,6 +4314,9 @@ msgstr ""
msgid "New merge request"
msgstr "Nova peto pri kunfando"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2825,6 +4332,12 @@ msgstr ""
msgid "New tag"
msgstr "Nova etikedo"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2846,7 +4359,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2855,6 +4398,9 @@ msgstr "Ne estas deponejo"
msgid "No schedules"
msgstr "Ne estas planoj"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -2891,6 +4437,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "Sciigaj eventoj"
@@ -2978,27 +4527,72 @@ msgstr "Filtrilo"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Malfermita"
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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"
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3032,12 +4626,30 @@ msgstr ""
msgid "Password"
msgstr ""
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3056,6 +4668,9 @@ msgstr "Ĉenstablaj planoj"
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr "Malsukcesaj:"
@@ -3152,10 +4767,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3164,7 +4791,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3182,18 +4809,42 @@ msgstr "kun etapo"
msgid "Pipeline|with stages"
msgstr "kun etapoj"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3203,9 +4854,24 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3218,9 +4884,21 @@ msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3239,9 +4917,27 @@ msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3251,6 +4947,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3260,6 +4959,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -3272,6 +4977,9 @@ msgstr "La projekto „%{project_name}“ estis sukcese kreita."
msgid "Project '%{project_name}' was successfully updated."
msgstr "La projekto „%{project_name}“ estis sukcese Äisdatigita."
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "Ĉiu uzanto devas akiri propran atingon al la projekto."
@@ -3296,6 +5004,9 @@ msgstr "La ligilo por la projekta elporto eksvalidiÄis. Bonvolu krei novan elpo
msgid "Project export started. A download link will be sent by email."
msgstr "La elporto de la projekto komenciÄis. Vi ricevos ligilon per retpoÅto por elÅuti la datenoj."
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -3305,24 +5016,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr "MalÅaltita"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "Ĉiu, kiu havas atingon"
-
-msgid "ProjectFeature|Only team members"
-msgstr "Nur skipanoj"
-
msgid "ProjectFileTree|Name"
msgstr "Nomo"
@@ -3332,12 +5034,18 @@ msgstr "Neniam"
msgid "ProjectLifecycle|Stage"
msgstr "Etapo"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "Grafeo"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3356,6 +5064,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3374,7 +5085,37 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3455,21 +5196,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3485,30 +5250,45 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr "Legu pli"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "LeguMin"
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
-msgstr "Branĉoj"
-
-msgid "RefSwitcher|Tags"
-msgstr "Etikedoj"
-
msgid "Reference:"
msgstr ""
+msgid "Refresh"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -3539,36 +5319,60 @@ msgstr "Rememorigu denove"
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr "Forigi la projekton"
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr "Peti atingeblon"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr ""
@@ -3578,10 +5382,28 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3595,6 +5417,9 @@ msgstr "Malfari ĉi tiun enmetadon"
msgid "Revert this merge request"
msgstr "Malfari ĉi tiun peton pri kunfando"
+msgid "Review"
+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 ""
@@ -3604,18 +5429,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3628,6 +5471,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3649,15 +5501,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr "Planado de la ĉenstabloj"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr "Serĉu branĉon aŭ etikedon"
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3673,15 +5549,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Elektu formaton de arkivo"
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr "Elektu horzonon"
@@ -3694,9 +5585,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr "Elektu celan branĉon"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3712,6 +5621,9 @@ msgstr ""
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3754,9 +5666,15 @@ msgstr ""
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3766,32 +5684,65 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Estas montrata %d evento"
msgstr[1] "Estas montrataj %d eventoj"
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3804,6 +5755,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3813,13 +5767,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3828,9 +5788,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -3948,9 +5917,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr "Steligi"
@@ -3972,33 +5968,66 @@ msgstr ""
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr "Iri al branĉo/etikedo"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4007,6 +6036,9 @@ msgstr[1] ""
msgid "Tags"
msgstr "Etikedoj"
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4070,7 +6102,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4085,6 +6117,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4124,12 +6165,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "La etapo de la disvolva ciklo."
@@ -4148,6 +6192,9 @@ msgstr "Ĉiu ensalutita uzanto havas atingon al la projekto"
msgid "The project can be accessed without any authentication."
msgstr "Ĉiu povas havi atingon al la projekto, sen ensaluti"
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "La deponejo por ĉi tiu projekto ne ekzistas."
@@ -4163,6 +6210,9 @@ msgstr "La etapo de la kontrolo montras la tempon de la kreado de la peto pri ku
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "La etapo de preparo por eldono montras la tempon inter la aplikado de la peto pri kunfando kaj la disponigado de la kodo en la publika versio. La datenoj aldoniÄos aÅ­tomate post kiam vi faros la unuan disponigadon en la publika versio."
@@ -4175,25 +6225,31 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "La tempo, kiu estas necesa por ĉiu dateno kolektita de la etapo."
+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>:</code>. 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 ""
+
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 "La valoro, kiu troviÄas en la mezo de aro da rigardataj valoroj. Ekzemple: inter 3, 5 kaj 9, la mediano estas 5. Inter 3, 5, 7 kaj 8, la mediano estas (5+7)/2 = 6."
msgid "There are no issues to show"
msgstr ""
-msgid "There are no merge requests to show"
+msgid "There are no labels yet"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no merge requests to show"
msgstr ""
-msgid "There was an error loading results"
+msgid "There are problems accessing Git storage: "
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -4214,12 +6270,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4241,6 +6324,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4259,15 +6351,30 @@ msgstr "Ĉi tiu signifas, ke vi ne povos alpuÅi kodon, antaÅ­ ol vi kreos malpl
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4283,10 +6390,13 @@ msgstr "Tempo antaÅ­ la komenco de laboro super problemo"
msgid "Time between merge request creation and merge/close"
msgstr "Tempo inter la kreado de poeto pri kunfando kaj Äia aplikado/fermado"
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4310,6 +6420,9 @@ msgstr "antaÅ­ %s tagoj"
msgid "Timeago|%s days remaining"
msgstr "restas %s tagoj"
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr "restas %s horoj"
@@ -4325,6 +6438,9 @@ msgstr "antaÅ­ %s monatoj"
msgid "Timeago|%s months remaining"
msgstr "restas %s monatoj"
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr "restas %s sekundoj"
@@ -4340,48 +6456,45 @@ msgstr "antaÅ­ %s jaroj"
msgid "Timeago|%s years remaining"
msgstr "restas %s jaroj"
+msgid "Timeago|1 day ago"
+msgstr ""
+
msgid "Timeago|1 day remaining"
msgstr "restas 1 tago"
+msgid "Timeago|1 hour ago"
+msgstr ""
+
msgid "Timeago|1 hour remaining"
msgstr "restas 1 horo"
+msgid "Timeago|1 minute ago"
+msgstr ""
+
msgid "Timeago|1 minute remaining"
msgstr "restas 1 minuto"
+msgid "Timeago|1 month ago"
+msgstr ""
+
msgid "Timeago|1 month remaining"
msgstr "restas 1 monato"
+msgid "Timeago|1 week ago"
+msgstr ""
+
msgid "Timeago|1 week remaining"
msgstr "restas 1 semajno"
+msgid "Timeago|1 year ago"
+msgstr ""
+
msgid "Timeago|1 year remaining"
msgstr "restas 1 jaro"
msgid "Timeago|Past due"
msgstr "MalfruiÄis"
-msgid "Timeago|a day ago"
-msgstr "antaÅ­ unu tago"
-
-msgid "Timeago|a month ago"
-msgstr "antaÅ­ unu monato"
-
-msgid "Timeago|a week ago"
-msgstr "antaÅ­ unu semajno"
-
-msgid "Timeago|a year ago"
-msgstr "antaÅ­ unu jaro"
-
-msgid "Timeago|about %s hours ago"
-msgstr "antaŭ ĉirkaŭ %s horoj"
-
-msgid "Timeago|about a minute ago"
-msgstr "antaŭ ĉirkaŭ unu minuto"
-
-msgid "Timeago|about an hour ago"
-msgstr "antaŭ ĉirkaŭ unu horo"
-
msgid "Timeago|in %s days"
msgstr "post %s tagoj"
@@ -4421,11 +6534,14 @@ msgstr "post 1 semajno"
msgid "Timeago|in 1 year"
msgstr "post 1 jaro"
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
msgstr ""
-msgid "Timeago|less than a minute ago"
-msgstr "antaÅ­ malpli ol minuto"
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4449,6 +6565,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4458,6 +6577,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4467,21 +6592,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4491,6 +6640,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr "Totala tempo"
@@ -4509,12 +6664,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4527,12 +6700,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr "Malsteligi"
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4548,6 +6754,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr "AlÅuti novan dosieron"
@@ -4566,9 +6775,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4578,9 +6796,21 @@ msgstr "Uzi vian Äeneralan agordon pri la sciigoj"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4593,7 +6823,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4605,9 +6838,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr "Vidi la malfermitan peton pri kunfando"
@@ -4620,6 +6865,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "Interna"
@@ -4635,7 +6886,7 @@ msgstr "Nekonata"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Ĉu vi volas vidi la datenojn? Bonvolu peti atingeblon de administranto."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4653,10 +6904,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4683,7 +6946,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4719,6 +7006,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4734,7 +7027,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4746,9 +7039,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4773,7 +7063,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "Nuligi la peton pri atingeblo"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4791,7 +7090,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4800,6 +7102,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4812,22 +7120,40 @@ msgstr "Oni povas aldoni dosierojn nur kiam oni estas en branĉo"
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Vi ne povas krei pliajn projektojn"
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4836,6 +7162,9 @@ msgstr "Oni devas ensaluti por steligi projekton"
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "VI bezonas permeson."
@@ -4866,9 +7195,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4884,6 +7222,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4902,10 +7246,13 @@ msgstr "Via nomo"
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4919,45 +7266,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4967,19 +7407,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST detected no security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4988,15 +7425,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5006,11 +7482,17 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "tago"
msgstr[1] "tagoj"
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5024,16 +7506,28 @@ msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5045,6 +7539,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5068,24 +7565,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5113,6 +7604,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5146,9 +7640,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5209,6 +7718,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5218,9 +7730,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5262,15 +7771,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5283,6 +7801,14 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index e2af97f1b83..cbcfb392d0c 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:35-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
@@ -15,10 +13,26 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: es-ES\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:39\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d cambio"
@@ -54,6 +68,26 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+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] "%s cambio adicional ha sido omitido para evitar problemas de rendimiento."
@@ -65,17 +99,32 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} participante"
msgstr[1] "%{count} participantes"
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr "%{number_commits_behind} commits detrás de %{default_branch}, %{number_commits_ahead} commits por delante"
@@ -88,23 +137,80 @@ msgstr "%{number_of_failures} de %{maximum_failures} intentos fallidos. GitLab n
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+msgstr ""
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] "%{storage_name}: intento de acceso fallido al almacenamiento en host:"
msgstr[1] "%{storage_name}: %{failed_attempts} intentos de acceso fallido al almacenamiento:"
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr "%{text} esta disponible"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(para obtener información sobre cómo instalarlo visite %{link})."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} más"
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr "- mostrar menos"
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "1 pipeline"
@@ -116,9 +222,51 @@ msgstr "¡1ra contribución!"
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Una colección de gráficos sobre Integración Continua"
@@ -128,38 +276,68 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr "Acerca del auto despliegue"
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr "Reportes de abuso"
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr "Tokens de acceso"
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr "Cuenta"
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "Activo"
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr "Actividad"
msgid "Add"
-msgstr "Añadir"
+msgstr ""
msgid "Add Changelog"
msgstr "Agregar Changelog"
@@ -168,7 +346,7 @@ msgid "Add Contribution guide"
msgstr "Agregar guía de contribución"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr "Añadir Webhooks Grupales y Gitlab Enterprise Edition."
+msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
@@ -179,12 +357,39 @@ msgstr "Agregar Licencia"
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "Agregar nuevo directorio"
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "Detener todos los trabajos"
@@ -237,7 +442,7 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
msgid "Advanced"
-msgstr "Avanzado"
+msgstr ""
msgid "Advanced settings"
msgstr "Configuración avanzada"
@@ -251,7 +456,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -275,6 +483,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr "Ha ocurrido un error visualizando el blob"
@@ -282,7 +532,7 @@ msgid "An error occurred when toggling the notification subscription"
msgstr "Se produjo un error al activar/desactivar la suscripción de notificación"
msgid "An error occurred when updating the issue weight"
-msgstr "Se produjo un error al actualizar el peso de la incidencia"
+msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
@@ -290,6 +540,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -305,13 +558,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -347,18 +600,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr "Se produjo un error. Por favor inténtelo de nuevo."
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr "Apariencia"
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr "Aplicaciones"
@@ -368,12 +645,21 @@ msgstr ""
msgid "April"
msgstr "Abril"
-msgid "Archived project! Repository is read-only"
-msgstr "¡Proyecto archivado! El repositorio es de solo lectura"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "¿Estás seguro que deseas eliminar esta programación del pipeline?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "¿Está seguro que desea reinicializar el token de registro?"
@@ -389,6 +675,12 @@ msgstr "¿Estás seguro?"
msgid "Artifacts"
msgstr "Artefactos"
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -413,12 +705,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr "Asignado a"
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Adjunte un archivo arrastrando &amp; soltando o %{upload_link}"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr "Ago"
@@ -428,12 +735,36 @@ msgstr "Agosto"
msgid "Authentication Log"
msgstr "Registro de Autenticación"
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr "Autores: %{authors}"
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -449,8 +780,11 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Tanto las Auto Review Apps como Auto Deploy necesitan un dominio para funcionar correctamente."
-msgid "AutoDevOps|Auto DevOps (Beta)"
-msgstr "Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
msgstr "Documentación de Auto DevOps"
@@ -470,12 +804,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr "Disponible"
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -485,65 +825,170 @@ msgstr "Promedio por día: %{average}"
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr "Iniciar con el commit seleccionado"
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
-msgstr "Facturación"
+msgstr ""
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
-msgstr "%{group_name} está actualmente en el plan %{plan_link}."
+msgstr ""
msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
-msgstr "Aumentar o disminuir automáticamente las características de algunos Planes no está disponible actualmente."
+msgstr ""
msgid "BillingPlans|Current plan"
-msgstr "Plan actual"
+msgstr ""
msgid "BillingPlans|Customer Support"
-msgstr "Atención al cliente"
+msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
-msgstr "Obtenga más información sobre cada plan al leer nuestro %{faq_link}."
+msgstr ""
msgid "BillingPlans|Manage plan"
-msgstr "Gestionar plan"
+msgstr ""
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
-msgstr "Por favor contacte a %{customer_support_link} en ese caso."
+msgstr ""
msgid "BillingPlans|See all %{plan_name} features"
-msgstr "Ver todas la funcionalidades del plan %{plan_name}"
+msgstr ""
msgid "BillingPlans|This group uses the plan associated with its parent group."
msgstr ""
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
-msgstr "Para gestionar el plan para este grupo, visite la sección de facturación de %{parent_billing_page_link}."
+msgstr ""
msgid "BillingPlans|Upgrade"
msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
-msgstr "Actualmente estás en el plan %{plan_link}."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
msgid "BillingPlans|frequently asked questions"
-msgstr "preguntas frecuentes"
+msgstr ""
msgid "BillingPlans|monthly"
-msgstr "mensual"
+msgstr ""
msgid "BillingPlans|paid annually at %{price_per_year}"
-msgstr "%{price_per_year} pagados anualmente"
+msgstr ""
msgid "BillingPlans|per user"
-msgstr "por usuario"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
@@ -622,7 +1067,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -703,7 +1148,7 @@ msgstr "Examinar archivos"
msgid "Browse files"
msgstr "Examinar archivos"
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -712,6 +1157,9 @@ msgstr "por"
msgid "CI / CD"
msgstr "CI / CD"
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -721,12 +1169,72 @@ msgstr "Configuración de CI/CD"
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "Cancelar"
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -737,7 +1245,7 @@ msgid "Certificate fingerprint"
msgstr ""
msgid "Change Weight"
-msgstr "Cambiar peso"
+msgstr ""
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -784,17 +1292,32 @@ msgstr "Escoger este cambio"
msgid "Cherry-pick this merge request"
msgstr "Escoger esta solicitud de fusión"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr "Elegir archivo..."
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
-msgstr "Elija qué grupos desea sincronizar a este nodo secundario."
+msgstr ""
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr ""
@@ -803,7 +1326,7 @@ msgid "Choose which repositories you want to import."
msgstr ""
msgid "Choose which shards you wish to synchronize to this secondary node."
-msgstr "Elija qué fragmentos desea sincronizar a este nodo secundario."
+msgstr ""
msgid "CiStatusLabel|canceled"
msgstr "cancelado"
@@ -898,9 +1421,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr "Haga clic para expandir el texto"
@@ -913,6 +1457,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -920,7 +1467,10 @@ msgid "Close"
msgstr "Cerrar"
msgid "Closed"
-msgstr "Cerrado"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -931,10 +1481,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -949,9 +1502,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -967,6 +1517,9 @@ msgstr "Copiar Certificado CA"
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -976,22 +1529,25 @@ msgstr "Copiar Token"
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr "Crear cluster de Kubernetes"
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
-msgstr "Crear en GKE"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr "Ingrese los detalles para un cluster Kubernetes existente"
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching projects"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -1000,8 +1556,8 @@ msgstr "Integración GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
-msgstr "ID del proyecto de Google Cloud Platform"
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
msgstr "Google Kubernetes Engine"
@@ -1012,6 +1568,12 @@ msgstr "Proyecto Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1039,6 +1601,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr "Estado de integración"
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "cluster de Kubernetes"
@@ -1075,8 +1643,14 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
-msgstr "Conozca más sobre %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
msgid "ClusterIntegration|Learn more about environments"
msgstr "Conozca más sobre los entornos"
@@ -1102,6 +1676,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr "Nota:"
@@ -1114,9 +1700,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Asegúrese de que su cuenta de Google cumpla con los siguientes requisitos:"
-msgid "ClusterIntegration|Project ID"
-msgstr "ID de Proyecto"
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1144,20 +1727,38 @@ msgstr "Falló la solicitud para iniciar la instalación"
msgid "ClusterIntegration|Save changes"
msgstr "Guardar cambios"
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
-msgstr "Ver tipos de máquina"
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
-msgid "ClusterIntegration|See your projects"
-msgstr "Ver tus proyectos"
+msgid "ClusterIntegration|Select project"
+msgstr ""
-msgid "ClusterIntegration|See zones"
-msgstr "Ver zonas"
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
msgid "ClusterIntegration|Service token"
msgstr ""
@@ -1189,6 +1790,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr "Token"
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1219,13 +1823,22 @@ msgstr "cumple con los requisitos"
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr "Contraer"
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1292,6 +1905,9 @@ msgstr ""
msgid "Committed by"
msgstr "Enviado por"
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr "Comparar"
@@ -1340,6 +1956,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1406,15 +2025,30 @@ msgstr "Usar nombres de imagen diferentes"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr "Guía de contribución"
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr "Contribuidores"
@@ -1430,14 +2064,23 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
-msgstr "Copiar la clave pública SSH al portapapeles"
+msgstr ""
msgid "Copy URL to clipboard"
msgstr "Copiar URL al portapapeles"
@@ -1451,9 +2094,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr "Copiar SHA del cambio al portapapeles"
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr "Crear"
@@ -1466,12 +2121,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Crear un token de acceso personal en tu cuenta para actualizar o enviar a través de %{protocol}."
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr "Crear directorio"
@@ -1479,14 +2140,20 @@ msgid "Create empty repository"
msgstr ""
msgid "Create epic"
-msgstr "Crear epic"
+msgstr ""
msgid "Create file"
msgstr "Crear archivo"
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1505,6 +2172,9 @@ msgstr "Crear nuevo directorio"
msgid "Create new file"
msgstr "Crear nuevo archivo"
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr "Crear nueva etiqueta"
@@ -1523,14 +2193,20 @@ msgstr "Etiqueta"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "crear un token de acceso personal"
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
-msgstr "Creando epic"
+msgstr ""
msgid "Cron Timezone"
msgstr "Zona horaria del Cron"
@@ -1539,7 +2215,16 @@ msgid "Cron syntax"
msgstr "Sintaxis de Cron"
msgid "Current node"
-msgstr "Nodo actual"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
msgid "Custom notification events"
msgstr "Eventos de notificaciones personalizadas"
@@ -1550,6 +2235,12 @@ msgstr "Los niveles de notificación personalizados son los mismos que los nivel
msgid "Customize colors"
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 "Cycle Analytics"
msgstr ""
@@ -1574,6 +2265,9 @@ msgstr "Puesta en escena"
msgid "CycleAnalyticsStage|Test"
msgstr "Pruebas"
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr "Todos"
@@ -1586,15 +2280,36 @@ msgstr "Dic"
msgid "December"
msgstr "Diciembre"
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr "Definir un patrón personalizado con la sintaxis de cron"
msgid "Delete"
msgstr "Eliminar"
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "Despliegue"
@@ -1603,39 +2318,195 @@ msgstr[1] "Despliegues"
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr "Descripción"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr "Detalles"
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr "Nombre del directorio"
msgid "Disable"
msgstr "Deshabilitar"
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr "No mostrar de nuevo"
@@ -1678,16 +2549,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr "Editar"
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "Editar Programación del Pipeline %{id}"
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1699,15 +2585,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr "Habilitar"
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1723,6 +2618,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1735,15 +2642,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1756,38 +2678,59 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Actualizado"
msgid "Environments|You don't have any environments right now."
msgstr "No tiene ningún entorno en este momento."
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
msgid "Epics"
-msgstr "Epics"
+msgstr ""
msgid "Epics Roadmap"
msgstr ""
@@ -1798,14 +2741,8 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
-msgstr "Error creando epic"
+msgstr ""
msgid "Error fetching contributors data."
msgstr ""
@@ -1822,6 +2759,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Se produjo un error al activar/desactivar la suscripción de notificación"
@@ -1834,6 +2786,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1861,9 +2816,30 @@ msgstr "Todos los meses (el día 1 a las 4:00 am)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Todas las semanas (domingos a las 4:00 am)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr "Expandir"
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr "Explorar proyectos"
@@ -1891,6 +2867,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1900,6 +2879,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr "Error al cambiar el propietario"
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1909,6 +2891,12 @@ msgstr "Error al eliminar la programación del pipeline"
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1918,9 +2906,6 @@ msgstr "Febrero"
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr "Nombre de archivo"
-
msgid "Files"
msgstr "Archivos"
@@ -1930,6 +2915,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Filtrar por mensaje del cambio"
@@ -1939,6 +2927,12 @@ msgstr "Buscar por ruta"
msgid "Find file"
msgstr "Buscar archivo"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1948,12 +2942,39 @@ msgstr "Primer"
msgid "FirstPushedBy|pushed by"
msgstr "enviado por"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "Bifurcación"
@@ -1971,9 +2992,24 @@ msgstr ""
msgid "Format"
msgstr "Formato"
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr "Desde la creación de la incidencia hasta el despliegue a producción"
@@ -1986,6 +3022,12 @@ msgstr ""
msgid "GPG Keys"
msgstr "Llaves GPG"
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -1996,15 +3038,18 @@ msgid "Geo allows you to replicate your GitLab instance to other geographical lo
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
-msgstr "El nodo está fallando o dañado."
+msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
-msgstr "El nodo está lento, sobrecargado o se esta recuperando de una interrupción."
+msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2019,31 +3064,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2064,19 +3121,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Replication slots"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2085,16 +3148,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2112,13 +3178,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2128,7 +3200,7 @@ msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
-msgstr "Capacidad de sincronización de archivos"
+msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
@@ -2140,14 +3212,20 @@ msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
-msgstr "Capacidad de sincronización del Repositorio"
+msgstr ""
msgid "Geo|Select groups to replicate."
-msgstr "Seleccionar grupos a replicar."
+msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2157,6 +3235,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2169,34 +3250,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "Sección GitLab Runner"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "Ir a tu bifurcación"
msgid "GoToYourFork|Fork"
msgstr "Bifurcación"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "La autenticación de Google no se encuentra %{link_to_documentation}. Pregúntale a tu administrador de GitLab si quieres usar este servicio."
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2208,7 +3355,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2238,6 +3406,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2315,15 +3510,54 @@ msgid_plural "Hide values"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr "Historial"
msgid "Housekeeping successfully started"
msgstr "Servicio de limpieza iniciado con éxito"
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2336,15 +3570,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2355,24 +3628,36 @@ msgid "ImportButtons|Connect repositories from"
msgstr ""
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr "Mejore los tableros de Incidencias con GitLab Enterprise Edition."
+msgstr ""
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
-msgstr "Mejore la gestión de incidencias con Peso de Incidencias y GitLab Enterprise Edition."
+msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "Instala un Runner compatible con GitLab CI"
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
msgid "Instance"
msgid_plural "Instances"
-msgstr[0] "Instancia"
-msgstr[1] "Instancias"
+msgstr[0] ""
+msgstr[1] ""
msgid "Instance does not support multiple Kubernetes clusters"
msgstr ""
@@ -2380,6 +3665,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2395,8 +3683,11 @@ msgstr "Patrón de intervalo"
msgid "Introducing Cycle Analytics"
msgstr "Introducción a Cycle Analytics"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
-msgstr "Modo enfocado del Tablero de Incidencias"
+msgstr ""
msgid "Issue events"
msgstr "Eventos de incidencia"
@@ -2405,7 +3696,7 @@ msgid "IssueBoards|Board"
msgstr "Tablero"
msgid "IssueBoards|Boards"
-msgstr "Tableros"
+msgstr ""
msgid "Issues"
msgstr ""
@@ -2413,12 +3704,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr "Enero"
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2437,6 +3737,9 @@ msgstr "Junio"
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2461,6 +3764,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Deshabilitado"
@@ -2470,12 +3776,21 @@ msgstr "Habilitado"
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2485,6 +3800,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2520,6 +3838,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr "Conozca más"
@@ -2544,18 +3865,36 @@ msgstr "Abandonar grupo"
msgid "Leave project"
msgstr "Abandonar proyecto"
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
-msgstr "Licencia"
+msgstr ""
+
+msgid "LinkedIn"
+msgstr ""
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr "Bloquear"
@@ -2565,24 +3904,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr "Bloqueado"
msgid "Locked Files"
-msgstr "Archivos Bloqueados"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
-msgstr "Iniciar sesión"
+msgid "Logs"
+msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2595,13 +3955,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr "Marzo"
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2619,24 +4000,60 @@ msgstr "Miembros"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr "Mensajes"
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2646,18 +4063,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2670,6 +4096,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2682,9 +4111,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2715,6 +4162,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2748,6 +4198,15 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2757,6 +4216,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2766,23 +4228,62 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nueva incidencia"
msgstr[1] "Nuevas incidencias"
-msgid "New Kubernetes Cluster"
-msgstr ""
-
-msgid "New Kubernetes cluster"
+msgid "New Label"
msgstr ""
msgid "New Pipeline Schedule"
msgstr "Nueva Programación del Pipeline"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "Nueva rama"
@@ -2793,7 +4294,7 @@ msgid "New directory"
msgstr "Nuevo directorio"
msgid "New epic"
-msgstr "Nuevo epic"
+msgstr ""
msgid "New file"
msgstr "Nuevo archivo"
@@ -2801,6 +4302,9 @@ msgstr "Nuevo archivo"
msgid "New group"
msgstr "Nuevo grupo"
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr "Nueva incidencia"
@@ -2810,6 +4314,9 @@ msgstr ""
msgid "New merge request"
msgstr "Nueva solicitud de fusión"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr "Nuevo proyecto"
@@ -2825,6 +4332,12 @@ msgstr "Nuevo sub-grupo"
msgid "New tag"
msgstr "Nueva etiqueta"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2846,7 +4359,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2855,6 +4398,9 @@ msgstr "No hay repositorio"
msgid "No schedules"
msgstr "No hay programaciones"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr "Ninguno"
@@ -2891,6 +4437,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "Eventos de notificación"
@@ -2978,17 +4527,44 @@ msgstr "Filtrar"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr "Sólo los miembros de proyecto pueden comentar."
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
-msgstr "Abierto"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
msgid "OpenedNDaysAgo|Opened"
msgstr "Abierto"
@@ -2996,9 +4572,27 @@ msgstr "Abierto"
msgid "Opens in a new window"
msgstr "Abre en una nueva ventana"
+msgid "Operations"
+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 "Opciones"
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3032,12 +4626,30 @@ msgstr ""
msgid "Password"
msgstr "Contraseña"
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3056,6 +4668,9 @@ msgstr "Programaciones de los Pipelines"
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr "Fallidos:"
@@ -3152,10 +4767,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3164,7 +4791,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3182,18 +4809,42 @@ msgstr "con etapa"
msgid "Pipeline|with stages"
msgstr "con etapas"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3203,9 +4854,24 @@ msgstr ""
msgid "Preferences"
msgstr "Preferencias"
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Privado - El acceso al proyecto debe ser concedido explícitamente para cada usuario."
@@ -3218,9 +4884,21 @@ msgstr ""
msgid "Profile"
msgstr "Perfil"
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3239,9 +4917,27 @@ msgstr "Contraseña inválida"
msgid "Profiles|Invalid username"
msgstr "Nombre de usuario inválido"
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Escribe tu %{confirmationValue} para confirmar:"
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr "No tienes acceso para eliminar este usuario."
@@ -3251,6 +4947,9 @@ msgstr "Debes transferir o eliminar estos grupos antes de que puedas eliminar tu
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Actualmente su cuenta es propietaria de estos grupos:"
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr "tu cuenta"
@@ -3260,6 +4959,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Proyecto '%{project_name}' está en proceso de ser eliminado."
@@ -3272,6 +4977,9 @@ msgstr "Proyecto ‘%{project_name}’ fue creado satisfactoriamente."
msgid "Project '%{project_name}' was successfully updated."
msgstr "Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "El acceso al proyecto debe concederse explícitamente a cada usuario."
@@ -3296,6 +5004,9 @@ msgstr "El enlace de exportación del proyecto ha caducado. Por favor, genera un
msgid "Project export started. A download link will be sent by email."
msgstr "Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico."
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "Suscribirse"
@@ -3305,24 +5016,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr "Deshabilitada"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "Todos con acceso"
-
-msgid "ProjectFeature|Only team members"
-msgstr "Solo miembros del equipo"
-
msgid "ProjectFileTree|Name"
msgstr "Nombre"
@@ -3332,23 +5034,29 @@ msgstr "Nunca"
msgid "ProjectLifecycle|Stage"
msgstr "Etapa"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "Historial gráfico"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
-msgstr "Póngase en contacto con un administrador para cambiar esta opción."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
-msgstr "Solo se pueden enviar commits firmados a este repositorio."
+msgstr ""
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
-msgstr "Esta configuración se aplica a nivel del servidor y puede ser redefinida por un administrador."
+msgstr ""
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
-msgstr "Esta configuración se aplica a nivel del servidor pero se ha redefinido para este proyecto."
+msgstr ""
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
-msgstr "Esta configuración se aplicará a todos los proyectos a menos que sea redefinida por un administrador."
+msgstr ""
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
msgstr ""
@@ -3356,6 +5064,9 @@ msgstr ""
msgid "Projects"
msgstr "Proyectos"
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3374,8 +5085,38 @@ msgstr "Algo salió mal por nuestra parte."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Lo sentimos, no hay proyectos que coincidan con su búsqueda"
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr "Esta función requiere que el navegador soporte localStorage"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr ""
@@ -3455,23 +5196,47 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Público - El grupo y cualquier proyecto público puede ser visto sin autenticación."
msgid "Public - The project can be accessed without any authentication."
msgstr "Público - El proyecto puede ser accedido sin ninguna autenticación."
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
-msgstr "Reglas Push"
+msgstr ""
msgid "Push events"
msgstr "Eventos Push"
@@ -3483,7 +5248,16 @@ msgid "Push to create a project"
msgstr ""
msgid "PushRule|Committer restriction"
-msgstr "Restricción de Committer"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
@@ -3491,24 +5265,30 @@ msgstr ""
msgid "Read more"
msgstr "Leer más"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "Léeme"
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
-msgstr "Ramas"
-
-msgid "RefSwitcher|Tags"
-msgstr "Etiquetas"
-
msgid "Reference:"
msgstr ""
+msgid "Refresh"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
msgid "Registry"
msgstr "Registro"
@@ -3539,36 +5319,60 @@ msgstr "Recordar después"
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr "Eliminar proyecto"
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr "Repositorio"
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr "Solicitar acceso"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr ""
@@ -3578,10 +5382,28 @@ msgstr ""
msgid "Reset runners registration token"
msgstr "Reinicializar el token de registro del runner"
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3595,6 +5417,9 @@ msgstr "Revertir este cambio"
msgid "Revert this merge request"
msgstr "Revertir esta solicitud de fusión"
+msgid "Review"
+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 ""
@@ -3604,18 +5429,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3628,6 +5471,15 @@ msgstr ""
msgid "SSH Keys"
msgstr "Llaves SSH"
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr "Guardar los cambios"
@@ -3649,15 +5501,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr "Programación de Pipelines"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr "Buscar ramas y etiquetas"
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3673,15 +5549,30 @@ msgstr "Segundos antes de reinicializar información de fallas"
msgid "Seconds to wait for a storage access attempt"
msgstr "Segundos a esperar para intentar acceder al almacenamiento"
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Seleccionar formato de archivo"
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr "Selecciona una zona horaria"
@@ -3694,9 +5585,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr "Selecciona una rama de destino"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3712,6 +5621,9 @@ msgstr "Septiembre"
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr "Plantillas de Servicio"
@@ -3754,9 +5666,15 @@ msgstr "Configuración"
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3766,32 +5684,65 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr "Mostrar páginas padre"
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Mostrando %d evento"
msgstr[1] "Mostrando %d eventos"
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3804,6 +5755,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3813,13 +5767,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3828,9 +5788,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr "Ordenar por"
@@ -3948,9 +5917,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr "Destacar"
@@ -3972,33 +5968,66 @@ msgstr ""
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr "Detenido"
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr "Sub-grupos"
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr "Cambiar rama/etiqueta"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr "Hooks de sistema"
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4007,6 +6036,9 @@ msgstr[1] ""
msgid "Tags"
msgstr "Etiquetas"
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4070,7 +6102,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4085,8 +6117,17 @@ msgstr ""
msgid "Team"
msgstr "Equipo"
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
-msgstr "Gracias! No mostrar esto nuevamente"
+msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
@@ -4124,12 +6165,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "La etapa del ciclo de vida de desarrollo."
@@ -4148,6 +6192,9 @@ msgstr "El proyecto puede ser accedido por cualquier usuario conectado."
msgid "The project can be accessed without any authentication."
msgstr "El proyecto puede accederse sin ninguna autenticación."
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "El repositorio para este proyecto no existe."
@@ -4163,6 +6210,9 @@ msgstr "La etapa de revisión muestra el tiempo desde la creación de la solicit
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "La etapa de puesta en escena muestra el tiempo entre la fusión y el despliegue de código en el entorno de producción. Los datos se añadirán automáticamente una vez que se despliega a producción por primera vez."
@@ -4175,25 +6225,31 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "El tiempo utilizado por cada entrada de datos obtenido por esa etapa."
+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>:</code>. 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 ""
+
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 "El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."
msgid "There are no issues to show"
msgstr ""
-msgid "There are no merge requests to show"
+msgid "There are no labels yet"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no merge requests to show"
msgstr ""
-msgid "There was an error loading results"
+msgid "There are problems accessing Git storage: "
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -4214,12 +6270,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4241,6 +6324,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4259,15 +6351,30 @@ msgstr "Esto significa que no puede enviar código hasta que cree un repositorio
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4283,10 +6390,13 @@ msgstr "Tiempo antes de que empieze la implementación de una incidencia"
msgid "Time between merge request creation and merge/close"
msgstr "Tiempo entre la creación de la solicitud de fusión y la integración o cierre de ésta"
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4310,6 +6420,9 @@ msgstr "hace %s días"
msgid "Timeago|%s days remaining"
msgstr "%s días restantes"
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr "%s horas restantes"
@@ -4325,6 +6438,9 @@ msgstr "hace %s meses"
msgid "Timeago|%s months remaining"
msgstr "%s meses restantes"
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr "%s segundos restantes"
@@ -4340,48 +6456,45 @@ msgstr "hace %s años"
msgid "Timeago|%s years remaining"
msgstr "%s años restantes"
+msgid "Timeago|1 day ago"
+msgstr ""
+
msgid "Timeago|1 day remaining"
msgstr "1 día restante"
+msgid "Timeago|1 hour ago"
+msgstr ""
+
msgid "Timeago|1 hour remaining"
msgstr "1 hora restante"
+msgid "Timeago|1 minute ago"
+msgstr ""
+
msgid "Timeago|1 minute remaining"
msgstr "1 minuto restante"
+msgid "Timeago|1 month ago"
+msgstr ""
+
msgid "Timeago|1 month remaining"
msgstr "1 mes restante"
+msgid "Timeago|1 week ago"
+msgstr ""
+
msgid "Timeago|1 week remaining"
msgstr "1 semana restante"
+msgid "Timeago|1 year ago"
+msgstr ""
+
msgid "Timeago|1 year remaining"
msgstr "1 año restante"
msgid "Timeago|Past due"
msgstr "Atrasado"
-msgid "Timeago|a day ago"
-msgstr "hace un día"
-
-msgid "Timeago|a month ago"
-msgstr "hace un mes"
-
-msgid "Timeago|a week ago"
-msgstr "hace una semana"
-
-msgid "Timeago|a year ago"
-msgstr "hace un año"
-
-msgid "Timeago|about %s hours ago"
-msgstr "hace alrededor de %s horas"
-
-msgid "Timeago|about a minute ago"
-msgstr "hace alrededor de 1 minuto"
-
-msgid "Timeago|about an hour ago"
-msgstr "hace alrededor de 1 hora"
-
msgid "Timeago|in %s days"
msgstr "en %s días"
@@ -4421,11 +6534,14 @@ msgstr "en 1 semana"
msgid "Timeago|in 1 year"
msgstr "en 1 año"
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
msgstr ""
-msgid "Timeago|less than a minute ago"
-msgstr "hace menos de 1 minuto"
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4444,11 +6560,14 @@ msgid "Tip:"
msgstr ""
msgid "Title"
-msgstr "Título"
+msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4458,6 +6577,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4467,21 +6592,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4491,6 +6640,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr "Tiempo Total"
@@ -4509,12 +6664,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4527,12 +6700,45 @@ msgstr "Desbloqueado"
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr "No Destacar"
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4548,6 +6754,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr "Subir nuevo archivo"
@@ -4566,9 +6775,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4578,9 +6796,21 @@ msgstr "Utiliza tu configuración de notificación global"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4593,7 +6823,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4605,9 +6838,21 @@ msgstr "Ver archivo @ "
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr "Ver solicitud de fusión abierta"
@@ -4620,6 +6865,12 @@ msgstr "Ver archivo reemplazado @ "
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "Interno"
@@ -4635,7 +6886,7 @@ msgstr "Desconocido"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "¿Quieres ver los datos? Por favor pide acceso al administrador."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4653,10 +6904,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
-msgstr "Peso"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4683,7 +6946,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4719,6 +7006,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4734,7 +7027,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4746,9 +7039,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4773,7 +7063,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "Retirar Solicitud de Acceso"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4791,7 +7090,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4800,6 +7102,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4812,22 +7120,40 @@ msgstr "Solo puedes agregar archivos cuando estás en una rama"
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Has alcanzado el límite de tu proyecto"
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4836,6 +7162,9 @@ msgstr "Debes iniciar sesión para destacar un proyecto"
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "Necesitas permisos."
@@ -4866,9 +7195,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4884,6 +7222,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4902,10 +7246,13 @@ msgstr "Tu nombre"
msgid "Your projects"
msgstr "Tus proyectos"
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4917,7 +7264,37 @@ msgid "branch name"
msgstr "nombre de la rama"
msgid "by"
-msgstr "por"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
@@ -4925,39 +7302,102 @@ msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4967,19 +7407,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST detected no security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4988,15 +7425,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5006,11 +7482,17 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "día"
msgstr[1] "días"
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5024,16 +7506,28 @@ msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5045,6 +7539,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5068,24 +7565,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5113,6 +7604,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5146,9 +7640,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5209,6 +7718,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5218,9 +7730,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5262,20 +7771,29 @@ msgstr "token de acceso personal"
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
msgid "to help your contributors communicate effectively!"
-msgstr "para ayudar a sus colaboradores a comunicarse de manera efectiva!"
+msgstr ""
msgid "username"
msgstr "usuario"
@@ -5283,6 +7801,14 @@ msgstr "usuario"
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/fil_PH/gitlab.po b/locale/fil_PH/gitlab.po
index ed955f68381..b3275d60379 100644
--- a/locale/fil_PH/gitlab.po
+++ b/locale/fil_PH/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:34-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Filipino\n"
"Language: fil_PH\n"
@@ -15,10 +13,26 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: fil\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -54,6 +68,26 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+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] ""
@@ -65,17 +99,32 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
msgstr[1] ""
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
@@ -88,23 +137,80 @@ msgstr ""
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+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 "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr ""
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] ""
@@ -116,9 +222,51 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -128,33 +276,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr ""
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr ""
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr ""
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr ""
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr ""
@@ -179,12 +357,39 @@ msgstr ""
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr ""
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -251,7 +456,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -275,6 +483,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -290,6 +540,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -305,13 +558,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -347,18 +600,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr ""
@@ -368,12 +645,21 @@ msgstr ""
msgid "April"
msgstr ""
-msgid "Archived project! Repository is read-only"
+msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -389,6 +675,12 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -413,12 +705,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr ""
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -428,12 +735,36 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -449,7 +780,10 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
@@ -470,12 +804,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr ""
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -485,12 +825,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -509,6 +930,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -533,6 +957,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -545,6 +978,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -622,7 +1067,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -703,7 +1148,7 @@ msgstr ""
msgid "Browse files"
msgstr ""
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -712,6 +1157,9 @@ msgstr ""
msgid "CI / CD"
msgstr ""
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -721,12 +1169,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr ""
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -784,15 +1292,30 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -898,9 +1421,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -913,6 +1457,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -922,6 +1469,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -931,10 +1481,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -949,9 +1502,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -967,6 +1517,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -976,22 +1529,25 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -1000,7 +1556,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1012,6 +1568,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1039,6 +1601,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1075,7 +1643,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1102,6 +1676,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -1114,9 +1700,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1144,19 +1727,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select zone"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1189,6 +1790,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1219,13 +1823,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1292,6 +1905,9 @@ msgstr ""
msgid "Committed by"
msgstr ""
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr ""
@@ -1340,6 +1956,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1406,15 +2025,30 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr ""
@@ -1430,12 +2064,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1451,9 +2094,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr ""
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1466,12 +2121,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr ""
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr ""
@@ -1484,9 +2145,15 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1505,6 +2172,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1523,10 +2193,16 @@ msgstr ""
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr ""
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1541,6 +2217,15 @@ msgstr ""
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr ""
@@ -1550,6 +2235,12 @@ msgstr ""
msgid "Customize colors"
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 "Cycle Analytics"
msgstr ""
@@ -1574,6 +2265,9 @@ msgstr ""
msgid "CycleAnalyticsStage|Test"
msgstr ""
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1586,15 +2280,36 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] ""
@@ -1603,39 +2318,195 @@ msgstr[1] ""
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr ""
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr ""
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr ""
@@ -1678,16 +2549,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr ""
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1699,15 +2585,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1723,6 +2618,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1735,15 +2642,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1756,33 +2678,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1798,12 +2741,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1822,6 +2759,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1834,6 +2786,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1861,9 +2816,30 @@ msgstr ""
msgid "Every week (Sundays at 4:00am)"
msgstr ""
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1891,6 +2867,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1900,6 +2879,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr ""
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1909,6 +2891,12 @@ msgstr ""
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1918,9 +2906,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr ""
@@ -1930,6 +2915,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
@@ -1939,6 +2927,12 @@ msgstr ""
msgid "Find file"
msgstr ""
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1948,12 +2942,39 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] ""
@@ -1971,9 +2992,24 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
@@ -1986,6 +3022,12 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -2004,7 +3046,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2019,31 +3064,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2064,19 +3121,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2085,16 +3148,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2112,13 +3178,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2148,6 +3220,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2157,6 +3235,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2169,34 +3250,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
msgid "GoToYourFork|Fork"
msgstr ""
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2208,7 +3355,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2238,6 +3406,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2315,15 +3510,54 @@ msgid_plural "Hide values"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr ""
msgid "Housekeeping successfully started"
msgstr ""
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2336,15 +3570,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2363,10 +3636,22 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance"
@@ -2380,6 +3665,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2395,6 +3683,9 @@ msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2413,12 +3704,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2437,6 +3737,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2461,6 +3764,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr ""
@@ -2470,12 +3776,21 @@ msgstr ""
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2485,6 +3800,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2520,6 +3838,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2544,18 +3865,36 @@ msgstr ""
msgid "Leave project"
msgstr ""
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2565,24 +3904,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2595,13 +3955,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2619,24 +4000,60 @@ msgstr ""
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2646,18 +4063,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2670,6 +4096,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2682,9 +4111,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2715,6 +4162,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2748,6 +4198,15 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2757,6 +4216,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2766,21 +4228,60 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] ""
msgstr[1] ""
-msgid "New Kubernetes Cluster"
+msgid "New Label"
msgstr ""
-msgid "New Kubernetes cluster"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
msgstr ""
msgid "New branch"
@@ -2801,6 +4302,9 @@ msgstr ""
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -2810,6 +4314,9 @@ msgstr ""
msgid "New merge request"
msgstr ""
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2825,6 +4332,12 @@ msgstr ""
msgid "New tag"
msgstr ""
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2846,7 +4359,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2855,6 +4398,9 @@ msgstr ""
msgid "No schedules"
msgstr ""
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -2891,6 +4437,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -2978,27 +4527,72 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr ""
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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 ""
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3032,12 +4626,30 @@ msgstr ""
msgid "Password"
msgstr ""
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3056,6 +4668,9 @@ msgstr ""
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr ""
@@ -3152,10 +4767,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3164,7 +4791,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3182,18 +4809,42 @@ msgstr ""
msgid "Pipeline|with stages"
msgstr ""
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3203,9 +4854,24 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3218,9 +4884,21 @@ msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3239,9 +4917,27 @@ msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3251,6 +4947,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3260,6 +4959,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -3272,6 +4977,9 @@ msgstr ""
msgid "Project '%{project_name}' was successfully updated."
msgstr ""
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -3296,6 +5004,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -3305,24 +5016,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr ""
-
-msgid "ProjectFeature|Everyone with access"
-msgstr ""
-
-msgid "ProjectFeature|Only team members"
-msgstr ""
-
msgid "ProjectFileTree|Name"
msgstr ""
@@ -3332,12 +5034,18 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
-msgid "ProjectNetworkGraph|Graph"
+msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3356,6 +5064,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3374,7 +5085,37 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3455,21 +5196,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3485,28 +5250,43 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr ""
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr ""
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
+msgid "Reference:"
msgstr ""
-msgid "RefSwitcher|Tags"
+msgid "Refresh"
msgstr ""
-msgid "Reference:"
+msgid "Register / Sign In"
msgstr ""
-msgid "Register / Sign In"
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
msgstr ""
msgid "Registry"
@@ -3539,36 +5319,60 @@ msgstr ""
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr ""
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr ""
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr ""
@@ -3578,10 +5382,28 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3595,6 +5417,9 @@ msgstr ""
msgid "Revert this merge request"
msgstr ""
+msgid "Review"
+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 ""
@@ -3604,18 +5429,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3628,6 +5471,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3649,15 +5501,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr ""
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr ""
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3673,15 +5549,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr ""
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr ""
@@ -3694,9 +5585,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3712,6 +5621,9 @@ msgstr ""
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3754,9 +5666,15 @@ msgstr ""
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3766,32 +5684,65 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3804,6 +5755,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3813,13 +5767,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching assignees list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3828,9 +5788,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -3948,9 +5917,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr ""
@@ -3972,33 +5968,66 @@ msgstr ""
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr ""
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4007,6 +6036,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4070,7 +6102,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4085,6 +6117,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4124,12 +6165,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr ""
@@ -4148,6 +6192,9 @@ msgstr ""
msgid "The project can be accessed without any authentication."
msgstr ""
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr ""
@@ -4163,6 +6210,9 @@ msgstr ""
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 ""
@@ -4175,25 +6225,31 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 no issues to show"
msgstr ""
-msgid "There are no merge requests to show"
+msgid "There are no labels yet"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no merge requests to show"
msgstr ""
-msgid "There was an error loading results"
+msgid "There are problems accessing Git storage: "
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -4214,12 +6270,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4241,6 +6324,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4259,15 +6351,30 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4283,10 +6390,13 @@ msgstr ""
msgid "Time between merge request creation and merge/close"
msgstr ""
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4310,6 +6420,9 @@ msgstr ""
msgid "Timeago|%s days remaining"
msgstr ""
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr ""
@@ -4325,6 +6438,9 @@ msgstr ""
msgid "Timeago|%s months remaining"
msgstr ""
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr ""
@@ -4340,46 +6456,43 @@ msgstr ""
msgid "Timeago|%s years remaining"
msgstr ""
-msgid "Timeago|1 day remaining"
-msgstr ""
-
-msgid "Timeago|1 hour remaining"
+msgid "Timeago|1 day ago"
msgstr ""
-msgid "Timeago|1 minute remaining"
+msgid "Timeago|1 day remaining"
msgstr ""
-msgid "Timeago|1 month remaining"
+msgid "Timeago|1 hour ago"
msgstr ""
-msgid "Timeago|1 week remaining"
+msgid "Timeago|1 hour remaining"
msgstr ""
-msgid "Timeago|1 year remaining"
+msgid "Timeago|1 minute ago"
msgstr ""
-msgid "Timeago|Past due"
+msgid "Timeago|1 minute remaining"
msgstr ""
-msgid "Timeago|a day ago"
+msgid "Timeago|1 month ago"
msgstr ""
-msgid "Timeago|a month ago"
+msgid "Timeago|1 month remaining"
msgstr ""
-msgid "Timeago|a week ago"
+msgid "Timeago|1 week ago"
msgstr ""
-msgid "Timeago|a year ago"
+msgid "Timeago|1 week remaining"
msgstr ""
-msgid "Timeago|about %s hours ago"
+msgid "Timeago|1 year ago"
msgstr ""
-msgid "Timeago|about a minute ago"
+msgid "Timeago|1 year remaining"
msgstr ""
-msgid "Timeago|about an hour ago"
+msgid "Timeago|Past due"
msgstr ""
msgid "Timeago|in %s days"
@@ -4421,10 +6534,13 @@ msgstr ""
msgid "Timeago|in 1 year"
msgstr ""
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
msgstr ""
-msgid "Timeago|less than a minute ago"
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
msgstr ""
msgid "Time|hr"
@@ -4449,6 +6565,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4458,6 +6577,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4467,21 +6592,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4491,6 +6640,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr ""
@@ -4509,12 +6664,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4527,12 +6700,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr ""
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4548,6 +6754,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr ""
@@ -4566,9 +6775,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4578,9 +6796,21 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4593,7 +6823,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4605,9 +6838,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr ""
@@ -4620,6 +6865,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr ""
@@ -4635,7 +6886,7 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4653,10 +6904,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4683,7 +6946,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4719,6 +7006,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4734,7 +7027,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4746,9 +7039,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4773,7 +7063,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4791,7 +7090,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4800,6 +7102,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4812,22 +7120,40 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4836,6 +7162,9 @@ msgstr ""
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr ""
@@ -4866,9 +7195,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4884,6 +7222,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4902,10 +7246,13 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4919,45 +7266,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4967,19 +7407,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4988,15 +7425,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5006,11 +7482,17 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5024,16 +7506,28 @@ msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5045,6 +7539,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5068,24 +7565,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5113,6 +7604,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5146,9 +7640,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5209,6 +7718,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5218,9 +7730,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5262,15 +7771,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5283,6 +7801,14 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
index 3f30c5c4b87..165dc97055b 100644
--- a/locale/fr/gitlab.po
+++ b/locale/fr/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:38-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: French\n"
"Language: fr_FR\n"
@@ -15,9 +13,25 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: fr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
msgid " and"
-msgstr " et"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] "%d fichier modifié"
+msgstr[1] "%d fichiers modifiés"
msgid "%d commit"
msgid_plural "%d commits"
@@ -27,7 +41,7 @@ msgstr[1] "%d commits"
msgid "%d commit behind"
msgid_plural "%d commits behind"
msgstr[0] "%d commit de retard"
-msgstr[1] "%d commits de retard"
+msgstr[1] "%d commits de retard"
msgid "%d exporter"
msgid_plural "%d exporters"
@@ -37,27 +51,47 @@ msgstr[1] "%d exportateurs"
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] "%d ticket"
-msgstr[1] "%d tickets"
+msgstr[1] "%d tickets"
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] "%d couche"
-msgstr[1] "%d couches"
+msgstr[1] "%d couches"
msgid "%d merge request"
msgid_plural "%d merge requests"
msgstr[0] "%d demande de fusion"
-msgstr[1] "%d demandes de fusion"
+msgstr[1] "%d demandes de fusion"
msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] "%d métrique"
-msgstr[1] "%d métriques"
+msgstr[1] "%d métriques"
+
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] "%d changement à valider"
+msgstr[1] "%d changements à valider"
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] "%d changement qui ne sera pas validé"
+msgstr[1] "%d changements qui ne seront pas validés"
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+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] "%s validation supplémentaire a été masquée afin d'éviter de créer de problèmes de performances."
-msgstr[1] "%s commits supplémentaires ont été masqués afin d'éviter un problème de performance."
+msgstr[0] "%s commit supplémentaire a été ignoré afin d’éviter de causer des problèmes de performance."
+msgstr[1] "%s commits supplémentaires ont été ignorés afin d’éviter de causer des problèmes de performance."
msgid "%{actionText} & %{openOrClose} %{noteable}"
msgstr "%{actionText} et %{openOrClose} %{noteable}"
@@ -65,10 +99,19 @@ msgstr "%{actionText} et %{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr "%{commit_author_link} a créé %{commit_timeago}"
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
-msgstr[0] "%{count} participant•e"
-msgstr[1] "%{count} participant•e•s"
+msgstr[0] "%{count} participant·e"
+msgstr[1] "%{count} participant·e·s"
+
+msgid "%{filePath} deleted"
+msgstr "%{filePath} supprimé"
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr "Les %{group_docs_link_start}groupes%{group_docs_link_end} vous permettent de gérer plusieurs projets et d’y collaborer. Les membres d’un groupe ont accès à tous ses projets."
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} Démarré"
@@ -76,11 +119,17 @@ msgstr "%{loadingIcon} Démarré"
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr "%{lock_path} est verrouillé par l’utilisateur GitLab %{lock_user_id}"
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr "%{nip_domain} peut être utilisé comme alternative à un domaine personnalisé."
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
-msgstr "%{number_commits_behind} commits de retard sur %{default_branch}, %{number_commits_ahead} commits d'avance"
+msgstr "%{number_commits_behind} commits de retard sur %{default_branch}, %{number_commits_ahead} commits d’avance"
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
-msgstr "%{number_of_failures} sur %{maximum_failures} tentative(s). GitLab va vous permettre d'accéder à la prochaine tentative."
+msgstr "%{number_of_failures} sur %{maximum_failures} tentative(s). GitLab va vous permettre d’accéder à la prochaine tentative."
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "%{number_of_failures} échecs sur %{maximum_failures}. GitLab ne va plus réessayer automatiquement. Réinitialisez les informations de stockage lorsque le problème est résolu."
@@ -88,78 +137,207 @@ msgstr "%{number_of_failures} échecs sur %{maximum_failures}. GitLab ne va plus
msgid "%{openOrClose} %{noteable}"
msgstr "%{openOrClose} %{noteable}"
+msgid "%{percent}%% complete"
+msgstr "%{percent} %% effectués"
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
-msgstr[0] "%{storage_name} : la tentative d’accès au stockage a échouée sur l’hôte :"
-msgstr[1] "%{storage_name} : %{failed_attempts} tentatives d’accès au stockage ont échouées :"
+msgstr[0] "%{storage_name} : la tentative d’accès au stockage a échoué sur l’hôte :"
+msgstr[1] "%{storage_name} : %{failed_attempts} tentatives d’accès au stockage ont échoué :"
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
msgid "%{text} is available"
msgstr "%{text} est disponible"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(Lisez %{link} pour savoir comment l'installer)."
+msgid "%{title} changes"
+msgstr "Changements %{title}"
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr "%{staged} changements prêts à être validés et %{unstaged} autres changements"
msgid "+ %{moreCount} more"
-msgstr "+ %{moreCount} de plus"
+msgstr "+ %{moreCount} de plus"
+
+msgid "- Runner is active and can process any new jobs"
+msgstr "- l’exécuteur est actif et peut traiter de nouvelles tâches"
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr "- l’exécuteur est en pause et ne recevra pas de nouvelles tâches"
msgid "- show less"
msgstr "- en montrer moins"
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] "un ajout de %{type}"
+msgstr[1] "%{count} ajouts de %{type}"
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] "une modification de %{type}"
+msgstr[1] "%{count} modifications de %{type}"
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] "un ticket fermé"
+msgstr[1] "%d tickets fermés"
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] "une demande de fusion fermée"
+msgstr[1] "%d demandes de fusion fermées"
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] "une demande de fusion fusionnée"
+msgstr[1] "%d demandes de fusion fusionnées"
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] "un ticket ouvert"
+msgstr[1] "%d tickets ouverts"
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] "une demande de fusion ouverte"
+msgstr[1] "%d demandes de fusion ouvertes"
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "1 pipeline"
msgstr[1] "%d pipelines"
msgid "1st contribution!"
-msgstr "1ère contribution !"
+msgstr "1ʳᵉ contribution !"
msgid "2FA enabled"
msgstr "2FA activé"
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr "Veuillez contacter votre administrateur GitLab afin d’obtenir l’autorisation."
+
+msgid "403|You don't have the permission to access this page."
+msgstr "Vous n’avez pas l’autorisation d’accéder à cette page."
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr "Assurezâ€vous que l’adresse est correcte et que la page n’a pas été déplacée."
+
+msgid "404|Page Not Found"
+msgstr "Page introuvable"
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr "Veuillez contacter votre administrateur GitLab si vous pensez qu’il s’agit d’une erreur."
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr "<strong>%{created_count}</strong> créés, <strong>%{closed_count}</strong> clos."
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr "<strong>%{pushes}</strong> poussées Git, plus de <strong>%{commits}</strong> commits effectués par <strong>%{people}</strong> contributeurs."
+
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>Supprime</strong> la branche source"
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr "Un « exécuteur » est un processus qui exécute une tâche. Vous pouvez configurer autant d’exécuteurs que nécessaire."
+
msgid "A collection of graphs regarding Continuous Integration"
-msgstr "Un ensemble de graphiques concernant l’Intégration Continue (CI)"
+msgstr "Un ensemble de graphiques concernant l’intégration continue (CI)"
msgid "A new branch will be created in your fork and a new merge request will be started."
-msgstr "Une nouvelle branche sera créée dans votre fourche et une nouvelle demande de fusion sera lancée."
+msgstr "Une nouvelle branche sera créée dans votre dépôt divergent (fork) et une nouvelle demande de fusion sera lancée."
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr "Un projet est l’endroit où vous hébergez vos fichiers (dépôt), planifiez votre travail (tickets) et publiez votre documentation (wiki), %{among_other_things_link}."
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
-msgstr "Un•e utilisateur•rice avec un accès en écriture à la branche source a sélectionné cette option"
+msgstr "Une personne avec un accès en écriture à la branche source a sélectionné cette option"
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
msgid "About auto deploy"
-msgstr "À propos de l'auto-déploiement"
+msgstr "À propos de l’autoâ€déploiement"
+
+msgid "About this feature"
+msgstr ""
msgid "Abuse Reports"
msgstr "Rapports d’abus"
msgid "Abuse reports"
+msgstr "Rapports d’abus"
+
+msgid "Accept terms"
+msgstr "Accepter les conditions"
+
+msgid "Accepted MR"
msgstr ""
msgid "Access Tokens"
-msgstr "Jetons d'Accès"
+msgstr "Jetons d’accès"
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "L'accès aux stockages défaillants a été temporairement désactivé pour permettre au montage de récupérer. Réinitialiser les informations de stockage dès que le problème est résolu pour permettre l’accès à nouveau."
+msgstr "L’accès aux stockages défaillants a été temporairement désactivé pour permettre la récupération du montage. Réinitialisez les informations de stockage quand le problème sera résolu pour permettre à nouveau l’accès."
+
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr "Accédez à votre jeton d’exécuteur, personnalisez la configuration de votre pipeline et affichez l’état de votre pipeline et le rapport de couverture."
msgid "Account"
msgstr "Compte"
-msgid "Account and limit settings"
-msgstr "Paramètres de compte et de limitation"
+msgid "Account and limit"
+msgstr "Limitations du compte"
msgid "Active"
msgstr "Actif"
+msgid "Active Sessions"
+msgstr "Sessions actives"
+
msgid "Activity"
msgstr "Activité"
msgid "Add"
-msgstr "Ajouter"
+msgstr ""
msgid "Add Changelog"
msgstr "Ajouter un journal des modifications"
@@ -168,10 +346,10 @@ msgid "Add Contribution guide"
msgstr "Ajouter un guide de contribution"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr "Ajouter des Webhooks de groupe et GitLab Enterprise Edition."
+msgstr ""
msgid "Add Kubernetes cluster"
-msgstr "Ajouter un cluster Kubernetes"
+msgstr "Ajouter une grappe de serveurs Kubernetes"
msgid "Add License"
msgstr "Ajouter une licence"
@@ -179,26 +357,53 @@ msgstr "Ajouter une licence"
msgid "Add Readme"
msgstr "Ajouter un fichier Readme"
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "Ajouter un nouveau dossier"
+msgid "Add reaction"
+msgstr "Ajouter une réaction"
+
msgid "Add todo"
-msgstr "Ajouter à la liste à faire"
+msgstr "Ajouter à la liste « à faire »"
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
msgid "AdminArea|Stop all jobs"
-msgstr "Arrêtez tous les travaux"
+msgstr "Arrêter toutes les tâches"
msgid "AdminArea|Stop all jobs?"
-msgstr "Arrêtez tous les travaux?"
+msgstr "Arrêter toutes les tâches ?"
msgid "AdminArea|Stop jobs"
-msgstr "Arrêtez les travaux"
+msgstr "Arrêter les tâches"
msgid "AdminArea|Stopping jobs failed"
-msgstr "L’arrêt des travaux a échoué"
+msgstr "L’arrêt des tâches a échoué"
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
-msgstr "Vous êtes sur le point d’arrêter tous les travaux. Tous les travaux en cours seront interrompus."
+msgstr "Vous êtes sur le point d’arrêter toutes les tâches. Toutes les tâches en cours seront interrompues."
msgid "AdminHealthPageLink|health page"
msgstr "État des services"
@@ -207,7 +412,7 @@ msgid "AdminProjects|Delete"
msgstr "Supprimer"
msgid "AdminProjects|Delete Project %{projectName}?"
-msgstr "Supprimer le projet %{projectName} ?"
+msgstr "Supprimer le projet %{projectName} ?"
msgid "AdminProjects|Delete project"
msgstr "Supprimer le projet"
@@ -216,19 +421,19 @@ msgid "AdminSettings|Specify a domain to use by default for every project's Auto
msgstr "Spécifiez un domaine à utiliser par défaut pour les étapes Auto Review Apps et Auto Deploy de chaque projet."
msgid "AdminUsers|Block user"
-msgstr "Bloquer l’utilisateur•rice"
+msgstr "Bloquer ce compte"
msgid "AdminUsers|Delete User %{username} and contributions?"
-msgstr "Supprimer l’utilisateur•rice %{username} et ses contributions ?"
+msgstr "Supprimer le compte %{username} et ses contributions ?"
msgid "AdminUsers|Delete User %{username}?"
-msgstr "Supprimer l’utilisateur•rice %{username} ?"
+msgstr "Supprimer le compte %{username} ?"
msgid "AdminUsers|Delete user"
-msgstr "Supprimer un•e utilisateur•rice"
+msgstr "Supprimer un compte utilisateur"
msgid "AdminUsers|Delete user and contributions"
-msgstr "Supprimer l’utilisateur•rice et ses contributions"
+msgstr "Supprimer le compte et ses contributions"
msgid "AdminUsers|To confirm, type %{projectName}"
msgstr "Pour confirmer, veuillez saisir %{projectName}"
@@ -237,7 +442,7 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr "Pour confirmer, veuillez saisir %{username}"
msgid "Advanced"
-msgstr "Avancé"
+msgstr "Paramètres avancés"
msgid "Advanced settings"
msgstr "Paramètres avancés"
@@ -251,53 +456,101 @@ msgstr "Toutes les modifications sont validées"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "Toutes les fonctionnalités sont activées pour les projets vierges, à partir de modèles ou lors de l’importation, mais vous pouvez les désactiver ultérieurement dans les paramètres du projet."
-msgid "Allow edits from maintainers."
-msgstr "Autoriser les modifications par les mainteneur•se•s."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr "Autoriser les commits des membres qui peuvent fusionner dans la branche cible."
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr "Autoriser l’accès public aux pipelines et aux détails des tâches, y compris les journaux de sortie et les artefacts"
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
-msgstr ""
+msgstr "Permettre le rendu des diagrammes PlantUML dans les documents Asciidoc."
msgid "Allow requests to the local network from hooks and services."
-msgstr ""
+msgstr "Autoriser les requêtes sur le réseau local à partir de hooks et de services."
msgid "Allows you to add and manage Kubernetes clusters."
-msgstr "Vous permet d’ajouter et de gérer des clusters Kubernetes."
+msgstr "Vous permet d’ajouter et de gérer des grappes de serveurs Kubernetes."
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
-msgstr ""
+msgstr "Également appelé « émetteur » ou « identifiant du tiers de confiance »"
msgid "Also called \"Relying party service URL\" or \"Reply URL\""
-msgstr ""
+msgstr "Aussi appelée « adresse URL du service du tiers de confiance » ou « URL de réponse »"
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr "Alternativement, vous pouvez utiliser un %{personal_access_token_link}. Lorsque vous créez votre jeton d’accès, vous devrez sélectionner le champ <code>repo</code>, afin que nous puissions afficher une liste de vos dépôts publics et privés qui sont disponibles pour se connecter."
+msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
-msgstr "Alternativement, vous pouvez utiliser un %{personal_access_token_link}. Lorsque vous créez votre jeton d’accès, vous devrez sélectionner le champ <code>repo</code>, afin que nous puissions afficher une liste de vos dépôts publics et privés qui sont disponibles pour être importés."
+msgstr "Vous pouvez également utiliser un %{personal_access_token_link}. Lorsque vous créerez votre jeton d’accès, vous devrez sélectionner le champ <code>repo</code>, afin que nous puissions afficher une liste de vos dépôts publics et privés qui sont disponibles pour être importés."
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr "Une erreur est survenue lors de la création de la nouvelle branche."
+
+msgid "An error occured whilst fetching the job trace."
+msgstr "Une erreur est survenue pendant le rapatriement de la trace de la tâche."
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr "Une erreur est survenue lors du rapatriement de dernier pipeline."
+
+msgid "An error occured whilst loading all the files."
+msgstr "Une erreur est survenue lors du chargement de l’ensemble des fichiers."
+
+msgid "An error occured whilst loading the file content."
+msgstr "Une erreur est survenue lors du chargement du contenu du fichier."
+
+msgid "An error occured whilst loading the file."
+msgstr "Une erreur est survenue lors du chargement du fichier."
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr "Une erreur est survenue lors du chargement des modifications de la demande de fusion."
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr "Une erreur est survenue lors du chargement des données de version de la demande de fusion."
+
+msgid "An error occured whilst loading the merge request."
+msgstr "Une erreur est survenue lors du chargement de la demande de fusion."
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr "Une erreur est survenue lors du chargement des tâches des pipelines."
msgid "An error occurred previewing the blob"
msgstr "Une erreur s’est produite lors de la prévisualisation du blob"
msgid "An error occurred when toggling the notification subscription"
-msgstr "Une erreur s’est produite lors de l’activation/désactivation de l’abonnement aux notifications"
+msgstr "Une erreur s’est produite lors de l’activation ou la désactivation de l’abonnement aux notifications"
msgid "An error occurred when updating the issue weight"
-msgstr "Une erreur s'est produite lors de la mise à jour du poids du ticket"
+msgstr "Une erreur est survenue lors de la mise à jour du poids du ticket"
msgid "An error occurred while adding approver"
-msgstr "Une erreur s’est produite lors de l’ajout de l'approbateur•rice"
+msgstr "Une erreur s’est produite lors de l’ajout de l’approba·teur·trice"
msgid "An error occurred while detecting host keys"
-msgstr "Une erreur s’est produite lors de la détection des clés de l'hôte"
+msgstr "Une erreur est survenue lors de la détection des clefs de l’hôte"
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr "Une erreur s’est produite lors de la révocation de l’alerte. Actualisez la page et essayez à nouveau."
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr "Une erreur s’est produite lors de la révocation de la mise en avant de la fonctionnalité. Actualisez la page et essayez de la révoquer à nouveau."
msgid "An error occurred while fetching markdown preview"
-msgstr "Une erreur s'est produite lors de la prévisualisation markdown"
+msgstr "Une erreur s’est produite lors de la prévisualisation markdown"
msgid "An error occurred while fetching sidebar data"
-msgstr "Une erreur s'est produite lors de la récupération des données de la barre latérale"
+msgstr "Une erreur s’est produite lors de la récupération des données de la barre latérale"
msgid "An error occurred while fetching the pipeline."
msgstr "Une erreur est survenue pendant la récupération du pipeline."
@@ -305,14 +558,14 @@ msgstr "Une erreur est survenue pendant la récupération du pipeline."
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"
-msgstr "Une erreur s’est produite lors de l’importation du projet"
+msgid "An error occurred while importing project: ${details}"
+msgstr "Une erreur s’est produite lors de l’importation du projet : ${details}"
msgid "An error occurred while initializing path locks"
-msgstr "Une erreur s’est produite lors de l’initialisation des verrous de chemin"
+msgstr "Une erreur est survenue lors de l’initialisation des verrous des chemins d’accès"
-msgid "An error occurred while loading commits"
-msgstr "Une erreur s’est produite lors du chargement des commits"
+msgid "An error occurred while loading commit signatures"
+msgstr ""
msgid "An error occurred while loading diff"
msgstr "Une erreur s’est produite lors du chargement du diff"
@@ -327,7 +580,7 @@ msgid "An error occurred while making the request."
msgstr "Une erreur s’est produite lors de la requête."
msgid "An error occurred while removing approver"
-msgstr "Une erreur s’est produite lors de la suppression de l’approbateur•rice"
+msgstr "Une erreur est survenue lors de la suppression de l’approba·teur·trice"
msgid "An error occurred while rendering KaTeX"
msgstr "Une erreur s’est produite lors du rendu de KaTeX"
@@ -342,61 +595,100 @@ msgid "An error occurred while retrieving diff"
msgstr "Une erreur s’est produite lors de la récupération du diff"
msgid "An error occurred while saving LDAP override status. Please try again."
-msgstr "Une erreur s’est produite lors de l’enregistrement du statut de remplacement LDAP. Veuillez réessayer."
+msgstr "Une erreur est survenue lors de l’enregistrement du statut du remplacement LDAP. Veuillez réessayer."
msgid "An error occurred while saving assignees"
msgstr "Une erreur s’est produite lors de l’enregistrement des destinataires"
+msgid "An error occurred while subscribing to notifications."
+msgstr "Une erreur est survenue lors de l’abonnement aux notifications."
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr "Une erreur est survenue lors du désabonnement aux notifications."
+
msgid "An error occurred while validating username"
-msgstr "Une erreur s’est produite lors de la validation du nom d’utilisateur•rice"
+msgstr "Une erreur s’est produite lors de la validation du nom d’utilisateur·rice"
msgid "An error occurred. Please try again."
-msgstr "Une erreur s’est produite. Veuillez réessayer."
+msgstr "Une erreur est survenue. Merci de réessayer."
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
msgid "Any Label"
-msgstr "Tout label"
+msgstr ""
msgid "Appearance"
msgstr "Apparence"
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr "Applications"
msgid "Apr"
-msgstr "Avr."
+msgstr "avr."
msgid "April"
-msgstr "Avril"
+msgstr "avril"
-msgid "Archived project! Repository is read-only"
-msgstr "Projet archivé ! Le dépôt est en lecture seule"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr "Projet archivé ! Le dépôt et les autres ressources du projet sont en lecture seule"
msgid "Are you sure you want to delete this pipeline schedule?"
-msgstr "Êtes-vous sûr·e de vouloir supprimer ce pipeline programmé ?"
+msgstr "Êtesâ€vous sûr·e de vouloir supprimer ce pipeline programmé ?"
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+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 reset registration token?"
-msgstr "Êtes-vous sûr·e de vouloir réinitialiser le jeton d’inscription ?"
+msgstr "Êtesâ€vous sûr·e de vouloir réinitialiser le jeton d’inscription ?"
msgid "Are you sure you want to reset the health check token?"
-msgstr "Êtes-vous sûr de vouloir réinitialiser le jeton de bilan de santé ?"
+msgstr "Êtesâ€vous sûr·e de vouloir réinitialiser le jeton de bilan de santé ?"
msgid "Are you sure you want to unlock %{path_lock_path}?"
-msgstr "Êtes-vous sûr•e de vouloir déverrouiller %{path_lock_path} ?"
+msgstr "Êtesâ€vous sûr·e de vouloir déverrouiller %{path_lock_path} ?"
msgid "Are you sure?"
-msgstr "Êtes-vous certain ?"
+msgstr "Êtesâ€vous certain(e) ?"
msgid "Artifacts"
msgstr "Artéfacts"
-msgid "Assertion consumer service URL"
+msgid "Ascending"
msgstr ""
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr "Demandez au responsable du groupe de configurer un exécuteur de groupe."
+
+msgid "Assertion consumer service URL"
+msgstr "URL du service consommateur d’assertion"
+
msgid "Assign custom color like #FF0000"
msgstr "Attribuer une couleur personnalisée comme #FF0000"
msgid "Assign labels"
-msgstr "Attribuer des labels"
+msgstr "Attribuer des étiquettes"
msgid "Assign milestone"
msgstr "Attribuer un jalon"
@@ -411,34 +703,73 @@ msgid "Assigned Merge Requests"
msgstr "Demandes de fusion assignées"
msgid "Assigned to :name"
-msgstr "Assigné•e à :name"
+msgstr "Assigné·e à :name"
+
+msgid "Assigned to me"
+msgstr "Assigné à moi"
msgid "Assignee"
-msgstr "Assigné•e"
+msgstr "Assigné·e"
+
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr "Assigné·e(s)"
msgid "Attach a file by drag &amp; drop or %{upload_link}"
-msgstr "Attachez un fichier par glisser &amp; déposer ou %{upload_link}"
+msgstr "Attachez un fichier par glisserâ€déposer ou %{upload_link}"
+
+msgid "Audit Events"
+msgstr ""
msgid "Aug"
-msgstr "Août"
+msgstr "août"
msgid "August"
-msgstr "Août"
+msgstr "août"
msgid "Authentication Log"
-msgstr "Journal d'authentification"
+msgstr "Journal d’authentification"
+
+msgid "Authentication log"
+msgstr ""
msgid "Author"
-msgstr "Auteur"
+msgstr "Auteur·e"
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
msgid "Authors: %{authors}"
-msgstr "Auteur•e•s : %{authors}"
+msgstr "Auteur·e·s : %{authors}"
+
+msgid "Auto DevOps"
+msgstr ""
msgid "Auto DevOps enabled"
msgstr "Auto DevOps activé"
msgid "Auto DevOps, runners and job artifacts"
-msgstr ""
+msgstr "Auto DevOps, exécuteurs et artéfacts de tâches"
msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr "Auto Review Apps et Auto Deploy ont besoin d’un %{kubernetes} qui fonctionne correctement."
@@ -447,91 +778,193 @@ msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} t
msgstr "Auto Review Apps et Auto Deploy ont besoin d’un nom de domaine et d’un %{kubernetes} qui fonctionne correctement."
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
-msgstr "Les applications de révision automatique et de déploiement automatique requièrent un nom de domaine pour fonctionner correctement."
+msgstr "Auto Review Apps et Auto Deploy ont besoin d’un nom de domaine qui fonctionne correctement."
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
-msgstr "Auto DevOps (Béta)"
+msgid "AutoDevOps|Auto DevOps"
+msgstr "Auto DevOps"
msgid "AutoDevOps|Auto DevOps documentation"
-msgstr "Documentation Auto DevOps"
+msgstr "documentation Auto DevOps"
msgid "AutoDevOps|Enable in settings"
msgstr "Activer dans les paramètres"
msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
-msgstr "AutoDevOps vous permet de construire, tester et déployer automatiquement votre application à partir d'une configuration CI/CD prédéfinie."
+msgstr "Auto DevOps va automatiquement construire, tester et déployer votre application selon une configuration prédéfinie d’intégration et de livraison continues."
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
-msgstr "En savoir plus dans %{link_to_documentation}"
+msgstr "Apprenezâ€en davantage en consultant la %{link_to_documentation}"
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr "Vous pouvez automatiquement générer et tester votre application si vous %{link_to_auto_devops_settings} pour ce projet. Vous pouvez aussi la déployer automatiquement, si vous %{link_to_add_kubernetes_cluster}."
msgid "AutoDevOps|add a Kubernetes cluster"
-msgstr "ajouter un cluster Kubernetes"
+msgstr "ajoutez une grappe de serveurs Kubernetes"
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
-msgstr "activer Auto DevOps (Bêta)"
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr "activez Auto DevOps"
msgid "Available"
msgstr "Disponible"
+msgid "Available group Runners : %{runners}"
+msgstr "Exécuteurs de groupe disponibles : %{runners}"
+
+msgid "Available group Runners : %{runners}."
+msgstr "Exécuteurs de groupe disponibles : %{runners}."
+
msgid "Avatar will be removed. Are you sure?"
-msgstr "L’avatar sera supprimé. Êtes-vous sûr•e ?"
+msgstr "L’avatar sera supprimé. Êtesâ€vous sûr·e ?"
msgid "Average per day: %{average}"
-msgstr "Moyenne par jour : %{average}"
+msgstr "Moyenne par jour : %{average}"
msgid "Background Color"
+msgstr "Couleur de fond"
+
+msgid "Background Jobs"
msgstr ""
+msgid "Background color"
+msgstr "Couleur d’arrièreâ€plan"
+
msgid "Background jobs"
-msgstr ""
+msgstr "Tâches de fond"
+
+msgid "Badges"
+msgstr "Badges"
+
+msgid "Badges|A new badge was added."
+msgstr "Un nouveau badge a été ajouté."
+
+msgid "Badges|Add badge"
+msgstr "Ajouter un badge"
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr "L’ajout du badge a échoué, veuillez vérifier les URL entrées et réessayez."
+
+msgid "Badges|Badge image URL"
+msgstr "URL de l’image du badge"
+
+msgid "Badges|Badge image preview"
+msgstr "Aperçu de l’image du badge"
+
+msgid "Badges|Delete badge"
+msgstr "Supprimer le badge"
+
+msgid "Badges|Delete badge?"
+msgstr "Supprimer le badge ?"
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr "La suppression du badge a échoué, veuillez réessayer."
+
+msgid "Badges|Group Badge"
+msgstr "Badge de groupe"
+
+msgid "Badges|Link"
+msgstr "Lien"
+
+msgid "Badges|No badge image"
+msgstr "Pas d’image de badge"
+
+msgid "Badges|No image to preview"
+msgstr "Pas d’image à prévisualiser"
+
+msgid "Badges|Project Badge"
+msgstr "Badge de projet"
+
+msgid "Badges|Reload badge image"
+msgstr "Recharger l’image de badge"
+
+msgid "Badges|Save changes"
+msgstr "Enregistrer les modifications"
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr "L’enregistrement du badge a échoué. Veuillez vérifier les URL entrées et réessayer."
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr "Les %{docsLinkStart}variables%{docsLinkEnd} que GitLab prend en charge : %{placeholders}"
+
+msgid "Badges|The badge was deleted."
+msgstr "Le badge a été supprimé."
+
+msgid "Badges|The badge was saved."
+msgstr "Le badge a été enregistré."
+
+msgid "Badges|This group has no badges"
+msgstr "Ce groupe n’a pas de badge"
+
+msgid "Badges|This project has no badges"
+msgstr "Ce projet n’a pas de badge"
+
+msgid "Badges|Your badges"
+msgstr "Vos badges"
msgid "Begin with the selected commit"
msgstr "Commencer avec le commit sélectionné"
+msgid "Below are examples of regex for existing tools:"
+msgstr "Voici quelques exemples d’expressions rationnelles pour des outils existants :"
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr "Facturation"
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
-msgstr "%{group_name} est actuellement abonné au forfait %{plan_link}."
+msgstr "%{group_name} est actuellement sur le calendrier de facturation %{plan_link}."
msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
-msgstr "La mise à niveau de certains abonnements n’est actuellement pas disponible."
+msgstr "La rétrogradation automatique et la mise à niveau vers certains calendriers de facturation ne sont actuellement pas disponibles."
msgid "BillingPlans|Current plan"
-msgstr "Abonnement actuel"
+msgstr "Calendrier actuel"
msgid "BillingPlans|Customer Support"
-msgstr "Support client"
+msgstr "Service client"
msgid "BillingPlans|Downgrade"
-msgstr "Retour à un forfait inférieur"
+msgstr "Rétrograder"
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
-msgstr "En savoir plus sur chaque forfait en lisant nos %{faq_link}."
+msgstr "En savoir plus sur chaque calendrier de facturation en lisant notre %{faq_link}."
msgid "BillingPlans|Manage plan"
-msgstr "Gérer l'abonnement"
+msgstr "Gérer le calendrier"
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
-msgstr "Merci de contacter %{customer_support_link} à ce sujet."
+msgstr "Veuillez contacter le %{customer_support_link} dans ce cas."
msgid "BillingPlans|See all %{plan_name} features"
-msgstr "Voir toutes les fonctionnalités du forfait %{plan_name}"
+msgstr "Afficher toutes les caractéristiques de %{plan_name}"
msgid "BillingPlans|This group uses the plan associated with its parent group."
-msgstr "Ce groupe utilise le forfait associé à son groupe parent."
+msgstr "Ce groupe utilise le plan associé à son groupe parent."
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
-msgstr "Pour gérer l‘abonnement de ce groupe, visitez la section facturation de %{parent_billing_page_link}."
+msgstr "Pour gérer le calendrier de facturation de ce groupe, rendez vous à la section facturation de %{parent_billing_page_link}."
msgid "BillingPlans|Upgrade"
msgstr "Mise à niveau"
msgid "BillingPlans|You are currently on the %{plan_link} plan."
-msgstr "Vous êtes actuellement abonné·e au forfait %{plan_link}."
+msgstr "Vous êtes actuellement sur le calendrier %{plan_link}."
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr "caractéristiques"
msgid "BillingPlans|frequently asked questions"
msgstr "foire aux questions"
@@ -540,18 +973,30 @@ msgid "BillingPlans|monthly"
msgstr "mensuel"
msgid "BillingPlans|paid annually at %{price_per_year}"
-msgstr "payé annuellement pour %{price_per_year}"
+msgstr "au prix annuel de %{price_per_year}"
msgid "BillingPlans|per user"
msgstr "par utilisateur"
+msgid "Bitbucket import"
+msgstr "Importation de Bitbucket"
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr "Tableaux"
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr "La branche %{branchName} n’a pas été trouvée dans le dépôt de ce projet."
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] "Branche (%{branch_count})"
msgstr[1] "Branches (%{branch_count})"
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 "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}"
+msgstr "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}"
msgid "Branch has changed"
msgstr "La branche a été modifiée"
@@ -563,7 +1008,7 @@ msgid "Branch name"
msgstr "Nom de la branche"
msgid "BranchSwitcherPlaceholder|Search branches"
-msgstr "Rechercher la branche"
+msgstr "Rechercher les branches"
msgid "BranchSwitcherTitle|Switch branch"
msgstr "Changer de branche"
@@ -587,7 +1032,7 @@ msgid "Branches|Compare"
msgstr "Comparer"
msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr "Supprimer toutes les branches qui ont été fusionnées dans '%{default_branch}'"
+msgstr "Supprimer toutes les branches qui ont été fusionnées dans « %{default_branch} »"
msgid "Branches|Delete branch"
msgstr "Supprimer cette branche"
@@ -599,13 +1044,13 @@ msgid "Branches|Delete protected branch"
msgstr "Supprimer cette branche protégée"
msgid "Branches|Delete protected branch '%{branch_name}'?"
-msgstr "Supprimer la branche protégée '%{branch_name}' ?"
+msgstr "Supprimer la branche protégée « %{branch_name} » ?"
msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
-msgstr "La suppression de la branche '%{branch_name}' ne peut être annulée. Êtes-vous sûr ?"
+msgstr "La suppression de la branche « %{branch_name} » ne peut être annulée. Êtesâ€vous sûr·e ?"
msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr "La suppression des branches fusionnées ne peut être annulée. Êtes-vous sûr ?"
+msgstr "La suppression des branches fusionnées ne peut être annulée. Êtesâ€vous sûr·e ?"
msgid "Branches|Filter by branch name"
msgstr "Filtrer par nom de branche"
@@ -622,8 +1067,8 @@ msgstr "Aucune branche à afficher"
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr "Une fois que vous aurez confirmé et cliqué sur %{delete_protected_branch}, cette action ne pourra pas être annulée ou restaurée."
-msgid "Branches|Only a project master or owner can delete a protected branch"
-msgstr "Seulement un maître ou un propriétaire du projet peut supprimer une branche protégée"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr "Seul un responsable du projet ou son propriétaire peut supprimer une branche protégée"
msgid "Branches|Overview"
msgstr "Vue d’ensemble"
@@ -659,28 +1104,28 @@ msgid "Branches|Stale branches"
msgstr "Branches périmées"
msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
-msgstr "Cette branche ne peut pas être mise à jour automatiquement car elle a dévié par rapport à son dépôt en amont."
+msgstr "Cette branche ne peut pas être mise à jour automatiquement car elle a divergé de son homologue en amont."
msgid "Branches|The default branch cannot be deleted"
msgstr "La branche par défaut ne peut pas être supprimée"
msgid "Branches|This branch hasn’t been merged into %{default_branch}."
-msgstr "Cette branche n'a pas été fusionnée dans %{default_branch}."
+msgstr "Cette branche n’a pas été fusionnée dans %{default_branch}."
msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
-msgstr "Afin d'éviter de perdre des données, il est conseillé de fusionner cette branche avant de la supprimer."
+msgstr "Afin d’éviter de perdre des données, il est conseillé de fusionner cette branche avant de la supprimer."
msgid "Branches|To confirm, type %{branch_name_confirmation}:"
-msgstr "Pour confirmer, veuillez saisir %{branch_name_confirmation} :"
+msgstr "Pour confirmer, veuillez saisir %{branch_name_confirmation} :"
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
-msgstr "Pour rejeter les changements locaux et écraser la branche avec la version du dépôt en amont, veuillez la supprimer ici puis cliquez ci-dessus sur 'Mettre à jour maintenant'."
+msgstr "Pour rejeter les changements locaux et écraser la branche avec la version du dépôt en amont, veuillez la supprimer ici et cliquer sur « Mettre à jour maintenant » ciâ€dessus."
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
-msgstr "Vous êtes sur le point de supprimer définitivement la branche protégée %{branch_name}."
+msgstr "Vous êtes sur le point de supprimer définitivement la branche protégée « %{branch_name} »."
msgid "Branches|diverged from upstream"
-msgstr "a dévié du dépôt en amont"
+msgstr "en divergence du dépôt en amont"
msgid "Branches|merged"
msgstr "fusionnée"
@@ -703,44 +1148,107 @@ msgstr "Parcourir les fichiers"
msgid "Browse files"
msgstr "Parcourir les fichiers"
-msgid "Business"
-msgstr "Entreprise"
+msgid "Business metrics (Custom)"
+msgstr "Métriques commerciales (personnalisées)"
msgid "ByAuthor|by"
msgstr "par"
msgid "CI / CD"
-msgstr "Intégration continu / Déploiement continu"
+msgstr "Intégration et livraison continues"
+
+msgid "CI / CD Settings"
+msgstr "Paramètres CI / CD (intégration et livraison continues)"
msgid "CI/CD"
-msgstr "CI/CD"
+msgstr "Intégration et livraison continues"
msgid "CI/CD configuration"
-msgstr "Configuration CI/CD"
+msgstr "Configuration de l’intégration et de la livraison continues"
msgid "CI/CD for external repo"
-msgstr "CI / CD pour dépôt externe"
+msgstr "Intégration et livraison continues pour dépôt externe"
+
+msgid "CI/CD settings"
+msgstr "Paramètres de l’intégration et de la livraison continues"
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr "Un %{ci_file} explicite a besoin d’être spécifié avant que vous puissiez commencer à utiliser l’intégration et la livraison continues."
+
+msgid "CICD|Auto DevOps"
+msgstr "Auto DevOps"
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr "Auto DevOps va automatiquement construire, tester et déployer votre application selon une configuration prédéfinie d’intégration et de livraison continues."
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr "Déploiement automatique pour « staging », déploiement manuel pour la production"
+
+msgid "CICD|Continuous deployment to production"
+msgstr "Déploiement continu en production"
+
+msgid "CICD|Deployment strategy"
+msgstr "Stratégie de déploiement"
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr "La stratégie de déploiement nécessite un nom de domaine pour fonctionner correctement."
+
+msgid "CICD|Disable Auto DevOps"
+msgstr "Désactiver Auto DevOps"
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr "Ne définissez pas un domaine ici si vous mettez en place plusieurs grappes de serveurs Kubernetes avec Auto DevOps."
+
+msgid "CICD|Enable Auto DevOps"
+msgstr "Activer Auto DevOps"
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr "Utiliser la valeur par défaut de l’instance pour avoir Auto DevOps activé ou désactivé quand il n’y a pas de %{ci_file} spécifique au projet."
+
+msgid "CICD|Instance default (%{state})"
+msgstr "Valeur par défaut de l’instance (%{state})"
msgid "CICD|Jobs"
msgstr "Tâches"
+msgid "CICD|Learn more about Auto DevOps"
+msgstr "En savoir plus à propos d’Auto DevOps"
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr "La configuration de pipeline de l’Auto DevOps sera utilisée lorsqu’il n’y a pas de %{ci_file} dans le projet."
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr "Vous devez spécifier un domaine si vous voulez utiliser la revue automatique d’applications Auto Review Apps et le déploiement automatique d’étapes Auto Deploy stages."
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr "Impossible de trouver le dernier commit (HEAD) pour cette branche"
+
msgid "Cancel"
msgstr "Annuler"
+msgid "Cancel this job"
+msgstr "Annuler cette tâche"
+
msgid "Cannot be merged automatically"
msgstr "Ne peut être fusionnée automatiquement"
msgid "Cannot modify managed Kubernetes cluster"
-msgstr "Impossible de modifier le cluster géré par Kubernetes"
+msgstr "Impossible de modifier la grappe de serveurs gérée par Kubernetes"
msgid "Certificate fingerprint"
-msgstr ""
+msgstr "Empreinte du certificat"
msgid "Change Weight"
-msgstr "Changer le poids"
+msgstr "Modifier le poids"
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
-msgstr ""
+msgstr "Modifiez cette valeur pour influencer la fréquence d’interrogation de l’interface utilisateur GitLab pour les mises à jour."
msgid "ChangeTypeActionLabel|Pick into branch"
msgstr "Picorer dans la branche"
@@ -767,7 +1275,7 @@ msgid "Charts"
msgstr "Statistiques"
msgid "Chat"
-msgstr "Chat"
+msgstr "Discussion"
msgid "Check interval"
msgstr "Intervalle de vérification"
@@ -776,7 +1284,7 @@ msgid "Checking %{text} availability…"
msgstr "Vérification de la disponibilité de %{text}…"
msgid "Checking branch availability..."
-msgstr "Vérification de la disponibilité du nom de branche..."
+msgstr "Vérification de la disponibilité du nom de branche…"
msgid "Cherry-pick this commit"
msgstr "Picorer ce commit"
@@ -784,26 +1292,41 @@ msgstr "Picorer ce commit"
msgid "Cherry-pick this merge request"
msgstr "Picorer cette demande de fusion"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
msgstr "Choisir le fichier…"
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 / tag (par exemple %{master}) ou entrez un commit (par exemple %{sha}) pour voir ce qui a changé ou pour créer une demande de fusion."
+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."
+
+msgid "Choose any color."
+msgstr "Choisissez n’importe quelle couleur."
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr "Choisissez entre <code>clone</code> ou <code>fetch</code> pour obtenir les dernières modifications du code de l’application"
msgid "Choose file..."
msgstr "Choisir le fichier…"
+msgid "Choose the top-level group for your repository imports."
+msgstr "Choisissez le groupe de premier niveau pour vos importations dans le dépôt."
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr "Choisissez les groupes que vous souhaitez synchroniser avec ce nœud secondaire."
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
-msgstr "Choisissez à quels dépôts vous voulez connecter et exécuter des pipelines CI/CD."
+msgstr "Choisissez quels dépôts vous voulez connecter pour exécuter des pipelines d’intégration et de livraison continues (CI/CD)."
msgid "Choose which repositories you want to import."
msgstr "Choisissez les dépôts que vous voulez importer."
msgid "Choose which shards you wish to synchronize to this secondary node."
-msgstr "Choisissez les partitions que vous souhaitez synchroniser avec ce nœud secondaire."
+msgstr ""
msgid "CiStatusLabel|canceled"
msgstr "annulé"
@@ -830,13 +1353,13 @@ msgid "CiStatusLabel|skipped"
msgstr "ignoré"
msgid "CiStatusLabel|waiting for manual action"
-msgstr "en attente d'action manuelle"
+msgstr "en attente d’une action manuelle"
msgid "CiStatusText|blocked"
msgstr "bloqué"
msgid "CiStatusText|canceled"
-msgstr "annulé"
+msgstr "annulé "
msgid "CiStatusText|created"
msgstr "créé"
@@ -869,13 +1392,13 @@ msgid "CiVariables|Remove variable row"
msgstr "Supprimer cette variable"
msgid "CiVariable|* (All environments)"
-msgstr "* (Tous les environnements)"
+msgstr "* (tout environnement)"
msgid "CiVariable|All environments"
msgstr "Tous les environnements"
msgid "CiVariable|Create wildcard"
-msgstr "Créer un caractère générique"
+msgstr ""
msgid "CiVariable|Error occured while saving variables"
msgstr "Une erreur s’est produite pendant la sauvegarde des variables"
@@ -887,7 +1410,7 @@ msgid "CiVariable|Protected"
msgstr "Protégée"
msgid "CiVariable|Search environments"
-msgstr "Chercher dans les environnements"
+msgstr ""
msgid "CiVariable|Toggle protected"
msgstr "Changer l’état de protection"
@@ -898,9 +1421,30 @@ msgstr "La validation a échoué"
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "CircuitBreaker API"
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr "Vider le champ de recherche"
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr "Cliquez sur n’importe quel <strong>nom de projet</strong> dans la liste des projets ciâ€dessous pour naviguer jusqu’au jalon du projet."
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr "Cliquez sur le bouton <strong>Promouvoir</strong> en haut à droite pour le promouvoir en tant que jalon de groupe."
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr "Cliquez sur le bouton <strong>Sélectionner aucun</strong> sur la droite, puisque nous n’avons seulement besoin que de « Google Code Project Hosting »."
+
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr "Cliquez sur le bouton ci-dessous pour lancer le processus d’installation en accédant à la page Kubernetes"
+msgid "Click to expand it."
+msgstr "Cliquez pour l’agrandir."
+
msgid "Click to expand text"
msgstr "Cliquez pour agrandir le texte"
@@ -908,10 +1452,13 @@ msgid "Client authentication certificate"
msgstr "Certificat d’authentification du client"
msgid "Client authentication key"
-msgstr "Clé d’authentification du client"
+msgstr "Clef d’authentification du client"
msgid "Client authentication key password"
-msgstr "Mot de passe de la clé d’authentification client"
+msgstr "Mot de passe de la clef d’authentification client"
+
+msgid "Clients"
+msgstr ""
msgid "Clone repository"
msgstr "Cloner le dépôt"
@@ -920,88 +1467,97 @@ msgid "Close"
msgstr "Fermer"
msgid "Closed"
-msgstr "Fermée"
+msgstr "Fermé(e)"
+
+msgid "Closed issues"
+msgstr "Tickets clos"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
-msgstr "%{appList} a été installé avec succès sur votre cluster Kubernetes"
+msgstr "%{appList} a été installé avec succès sur votre grappe de serveurs Kubernetes"
msgid "ClusterIntegration|API URL"
-msgstr "URL de l'API"
+msgstr "URL de l’API"
msgid "ClusterIntegration|Add Kubernetes cluster"
-msgstr "Ajouter un cluster Kubernetes"
-
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
-msgstr "Ajouter un cluster Kubernetes existant"
+msgstr "Ajouter une grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
-msgstr "Options avancées sur l’intégration de ce cluster Kubernetes"
+msgstr "Options avancées concernant l’intégration de cette grappe de serveurs Kubernetes"
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr "Une erreur est survenue lors de la tentative de récupération des zones du projet : %{error}"
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr "Une erreur est survenue lors de la tentative de récupération de vos projets : %{error}"
msgid "ClusterIntegration|Applications"
msgstr "Applications"
msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
-msgstr "Êtes-vous sûr•e de vouloir supprimer l'intégration de ce cluster Kubernetes? Cela ne supprimera pas votre cluster Kubernetes."
+msgstr "Êtesâ€vous sûr·e de vouloir supprimer l’intégration de cette grappe de serveurs Kubernetes ? Cela ne supprimera pas votre grappe de serveurs Kubernetes."
msgid "ClusterIntegration|CA Certificate"
-msgstr "Certificat d‘autorité de certification"
+msgstr "Certificat d’autorité de certification"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
-msgstr "Paquet de l‘Autorité de certification (format PEM)"
-
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr "Choisissez comment configurer l’intégration de cluster Kubernetes"
+msgstr "Ensemble de certificats des autorités de certification (format PEM)"
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr "Choisissez les environnements de votre projet qui utiliseront ce cluster Kubernetes."
+msgstr "Choisissez les environnements de votre projet qui utiliseront cette grappe de serveurs Kubernetes."
msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr "Contrôlez l’intégration de votre cluster Kubernetes avec GitLab"
+msgstr "Contrôlez l’intégration de votre grappe de serveurs Kubernetes avec GitLab"
msgid "ClusterIntegration|Copy API URL"
msgstr "Copier l’URL de l’API"
msgid "ClusterIntegration|Copy CA Certificate"
-msgstr "Copier le certificat CA"
+msgstr "Copier le certificat de l’AC"
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
-msgstr "Copier l’adresse IP entrante dans le presse-papiers"
+msgstr "Copier l’adresse IP Ingress dans le presseâ€papiers"
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr "Copier le nom d’hôte Jupyter dans le presseâ€papiers"
msgid "ClusterIntegration|Copy Kubernetes cluster name"
-msgstr "Copier le nom du cluster Kubernetes"
+msgstr "Copier le nom de la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Copy Token"
msgstr "Copier le jeton"
msgid "ClusterIntegration|Create Kubernetes cluster"
-msgstr "Créer un cluster Kubernetes"
+msgstr "Créer une grappe de serveurs Kubernetes"
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
-msgstr "Créer un cluster Kubernetes sur Google Kubernetes Engine"
-
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr "Créer un nouveau cluster Kubernetes sur Google Kubernetes Engine directement depuis GitLab"
-
-msgid "ClusterIntegration|Create on GKE"
-msgstr "Créer sur GKE"
-
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr "Entrer les détails pour le cluster Kubernetes existant"
+msgid "ClusterIntegration|Did you know?"
+msgstr "Le saviezâ€vous ?"
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
-msgstr "Entrez les détails de votre cluster Kubernetes"
+msgstr "Entrez les détails de votre grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Environment scope"
msgstr "Portée de l’environnement"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr "Chaque nouveau compte Google Cloud Platform (GCP) reçoit un crédit de 300 US$ sur %{sign_up_link}. En partenariat avec Google, GitLab est en mesure de vous offrir 200 US$ supplémentaires, à la fois pour les nouveaux et les anciens comptes GCP, afin de vous permettre de commencer l’intégration de Google Kubernetes Engine sur GitLab."
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr "Récupération des types de machines"
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr "Récupération des projets"
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr "Récupération des zones"
+
msgid "ClusterIntegration|GitLab Integration"
msgstr "Intégration GitLab"
msgid "ClusterIntegration|GitLab Runner"
-msgstr "Éxécuteur GitLab"
+msgstr "Exécuteur GitLab"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
-msgstr "ID de projet Google Cloud Platform"
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr "Projet Google Cloud Platform"
msgid "ClusterIntegration|Google Kubernetes Engine"
msgstr "Google Kubernetes Engine"
@@ -1012,14 +1568,20 @@ msgstr "Projet Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Hide"
+msgstr "Masquer"
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr "Si vous configurez plusieurs grappes de serveurs et que vous utilisez Auto DevOps, %{help_link_start}veillez d’abord lire ceci%{help_link_end}."
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
-msgstr "Afin d’afficher l’état du cluster, nous devons mettre votre cluster à disposition de Prometheus pour récupérer les données nécessaires."
+msgstr "Afin d’afficher son état de santé, nous devons mettre votre grappe de serveurs à disposition de Prometheus pour récupérer les données nécessaires."
msgid "ClusterIntegration|Ingress"
msgstr "Ingress"
msgid "ClusterIntegration|Ingress IP Address"
-msgstr "Adresse IP entrante"
+msgstr "Adresse IP Ingress"
msgid "ClusterIntegration|Install"
msgstr "Installer"
@@ -1034,49 +1596,61 @@ msgid "ClusterIntegration|Installing"
msgstr "En cours d’installation"
msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
-msgstr "Intégrez l’automatisation du cluster Kubernetes"
+msgstr "Intégrez l’automatisation de la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Integration status"
-msgstr "Statut d’intégration"
+msgstr "Statut de l’intégration"
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr "Nom de l’hôte Jupyter"
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr "JupyterHub"
msgid "ClusterIntegration|Kubernetes cluster"
-msgstr "Cluster Kubernetes"
+msgstr "Grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster details"
-msgstr "Détails du cluster Kubernetes"
+msgstr "Détails de la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "État du cluster Kubernetes"
+msgstr "État de santé de la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster integration"
-msgstr "Intégration d’un cluster Kubernetes"
+msgstr "Intégration d’une grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "L’intégration de cluster Kubernetes est désactivée pour ce projet."
+msgstr "L’intégration de la grappe de serveurs Kubernetes est désactivée pour ce projet."
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "L’intégration de cluster Kubernetes est activée pour ce projet."
+msgstr "L’intégration de la grappe de serveurs Kubernetes est activée pour ce projet."
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "L’intégration de cluster Kubernetes est activée pour ce projet. La désactivation de cette intégration n’affectera pas votre cluster Kubernetes, elle ne désactivera que temporairement la connexion de GitLab."
+msgstr "L’intégration d’une grappe de serveurs Kubernetes est activée pour ce projet. La désactivation de cette intégration n’affectera pas votre grappe de serveurs Kubernetes, elle ne désactivera que temporairement sa connexion à GitLab."
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
-msgstr "Le cluster Kubernetes est en cours de création sur Google Kubernetes Engine…"
+msgstr "La grappe de serveurs Kubernetes est en cours de création sur Google Kubernetes Engine…"
msgid "ClusterIntegration|Kubernetes cluster name"
-msgstr "Nom du cluster Kubernetes"
+msgstr "Nom de la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
-msgstr "Le cluster Kubernetes a été créé avec succès sur Google Kubernetes Engine. Actualisez la page pour voir les détails du cluster Kubernetes"
+msgstr "La grappe de serveurs Kubernetes a été créée avec succès sur Google Kubernetes Engine. Actualisez la page pour voir les détails de la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr "Les clusters Kubernetes vous permettent d’utiliser des applications de révision, de déployer vos applications, d’exécuter vos pipelines et bien plus encore. %{link_to_help_page}"
+msgstr "Les grappes de serveurs Kubernetes vous permettent d’utiliser des applications de révision, de déployer vos applications, d’exécuter vos pipelines et bien plus encore. %{link_to_help_page}"
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
-msgstr "Les clusters Kubernetes peuvent être utilisés pour déployer des applications et fournir des applications de révision pour ce projet"
+msgstr "Les grappes de serveurs Kubernetes peuvent être utilisées pour déployer des applications et fournir des applications de revue Review Apps pour ce projet"
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr "En savoir plus sur les %{help_link_start_machine_type}types de machines%{help_link_end} et la %{help_link_start_pricing}tarification%{help_link_end}."
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
-msgstr "En savoir plus sur %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr "En savoir plus sur %{help_link_start}Kubernetes%{help_link_end}."
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr "En savoir plus sur %{help_link_start}les zones%{help_link_end}."
msgid "ClusterIntegration|Learn more about environments"
msgstr "En savoir plus sur les environnements"
@@ -1088,34 +1662,43 @@ msgid "ClusterIntegration|Machine type"
msgstr "Type de machine"
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
-msgstr "Assurez-vous que votre compte %{link_to_requirements} pour créer des clusters Kubernetes"
+msgstr "Assurezâ€vous que votre compte %{link_to_requirements} pour créer des grappes de serveurs Kubernetes"
msgid "ClusterIntegration|Manage"
msgstr "Gérer"
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
-msgstr "Gérez votre cluster Kubernetes en visitant %{link_gke}"
+msgstr "Gérez votre grappe de serveurs Kubernetes en visitant %{link_gke}"
msgid "ClusterIntegration|More information"
msgstr "Plus d’informations"
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
-msgstr "Plusieurs clusters Kubernetes sont disponibles dans GitLab Enterprise Edition Premium et Ultimate"
+msgstr "Plusieurs grappes de serveurs Kubernetes sont disponibles dans GitLab Enterprise Edition Premium et Ultimate"
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr "Aucun type de machine ne correspond à votre recherche"
+
+msgid "ClusterIntegration|No projects found"
+msgstr "Aucun projet trouvé"
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr "Aucun projet ne correspond à votre recherche"
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr "Aucune zone ne correspond à votre recherche"
msgid "ClusterIntegration|Note:"
-msgstr "Remarque :"
+msgstr "Remarque :"
msgid "ClusterIntegration|Number of nodes"
msgstr "Nombre de nœuds"
msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
-msgstr "Veuillez entrer les informations d’accès de votre cluster Kubernetes. Si vous avez besoin d'aide, vous pouvez lire notre %{link_to_help_page} sur Kubernetes"
+msgstr "Veuillez entrer les informations d’accès de votre grappe de serveurs Kubernetes. Si vous avez besoin d’aide, vous pouvez lire notre %{link_to_help_page} sur Kubernetes"
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
-msgstr "Veuillez vous assurer que votre compte Google répond aux exigences suivantes : "
-
-msgid "ClusterIntegration|Project ID"
-msgstr "ID du projet"
+msgstr "Veuillez vous assurer que votre compte Google répond aux exigences suivantes :"
msgid "ClusterIntegration|Project namespace"
msgstr "Espace de noms du projet"
@@ -1127,37 +1710,55 @@ msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
-msgstr "Lisez notre %{link_to_help_page} sur l’intégration d’un cluster Kubernetes."
+msgstr "Lisez notre %{link_to_help_page} sur l’intégration d’une grappe de serveurs Kubernetes."
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
-msgstr "Supprimer l’intégration du cluster Kubernetes"
+msgstr "Supprimer l’intégration de la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Remove integration"
msgstr "Retirer l’intégration"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
-msgstr "Supprimer la configuration de ce cluster Kubernetes de ce projet. Cela ne supprimera pas votre cluster Kubernetes actuel."
+msgstr "Supprimer la configuration de cette grappe de serveurs Kubernetes de ce projet. Cela ne supprimera pas votre grappe de serveurs Kubernetes actuelle."
msgid "ClusterIntegration|Request to begin installing failed"
-msgstr "La demande de lancement d'installation a échoué"
+msgstr "La demande de lancement de l’installation a échoué"
msgid "ClusterIntegration|Save changes"
msgstr "Enregistrer les modifications"
+msgid "ClusterIntegration|Search machine types"
+msgstr "Rechercher les types de machines"
+
+msgid "ClusterIntegration|Search projects"
+msgstr "Rechercher des projets"
+
+msgid "ClusterIntegration|Search zones"
+msgstr "Rechercher les zones"
+
msgid "ClusterIntegration|Security"
msgstr "Sécurité"
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
-msgstr "Voir et modifier les détails de votre cluster Kubernetes"
+msgstr "Voir et modifier les détails de votre grappe de serveurs Kubernetes"
+
+msgid "ClusterIntegration|Select machine type"
+msgstr "Sélectionnez le type de machine"
+
+msgid "ClusterIntegration|Select project"
+msgstr "Sélectionnez un projet"
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr "Sélectionnez le projet et la zone afin de choisir le type de machine"
-msgid "ClusterIntegration|See machine types"
-msgstr "Voir les types de machine"
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr "Sélectionnez le projet afin de choisir la zone"
-msgid "ClusterIntegration|See your projects"
-msgstr "Voir vos projets"
+msgid "ClusterIntegration|Select zone"
+msgstr "Sélectionnez la zone"
-msgid "ClusterIntegration|See zones"
-msgstr "Voir les zones"
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr "Sélectionnez la zone afin de choisir le type de machine"
msgid "ClusterIntegration|Service token"
msgstr "Jeton de service"
@@ -1169,28 +1770,31 @@ msgid "ClusterIntegration|Something went wrong on our end."
msgstr "Un problème est survenu de notre côté."
msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
-msgstr "Une erreur s’est produite lors de la création de votre cluster Kubernetes sur Google Kubernetes Engine"
+msgstr "Une erreur s’est produite lors de la création de votre grappe de serveurs Kubernetes sur Google Kubernetes Engine"
msgid "ClusterIntegration|Something went wrong while installing %{title}"
-msgstr "Une erreur s’est produite lors de l'installation de %{title}"
+msgstr "Une erreur s’est produite lors de l’installation de %{title}"
msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr "La configuration par défaut du cluster permet d’accéder à un large éventail de fonctionnalités nécessaires pour construire et déployer, avec succès, une application conteneurisée."
+msgstr "La configuration par défaut de la grappe de serveurs permet d’accéder à un large éventail de fonctionnalités nécessaires pour construire et déployer, avec succès, une application conteneurisée."
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
-msgstr "Ce compte doit disposer des autorisations pour créer un cluster Kubernetes dans le %{link_to_container_project} spécifié ci-dessous"
+msgstr "Ce compte doit disposer des autorisations pour créer une grappe de serveurs Kubernetes dans le %{link_to_container_project} spécifié ciâ€dessous"
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
-msgstr "Activer le cluster Kubernetes"
+msgstr "Activer la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Toggle Kubernetes cluster"
-msgstr "Activer/désactiver le cluster Kubernetes"
+msgstr "Activer/désactiver la grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Token"
msgstr "Jeton"
+msgid "ClusterIntegration|Validating project billing status"
+msgstr "Validation de l’état de la facturation du projet"
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr "Avec un cluster Kubernetes associé à ce projet, vous pouvez utiliser des applications de révision, déployer vos applications, exécuter vos pipelines, et bien plus encore."
+msgstr "Avec une grappe de serveurs Kubernetes associée à ce projet, vous pouvez utiliser des applications de revue, déployer vos applications, exécuter vos pipelines, et bien plus encore."
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "Votre compte doit disposer de %{link_to_kubernetes_engine}"
@@ -1211,7 +1815,7 @@ msgid "ClusterIntegration|help page"
msgstr "page d’aide"
msgid "ClusterIntegration|installing applications"
-msgstr "Installation des applications"
+msgstr "installation des applications"
msgid "ClusterIntegration|meets the requirements"
msgstr "répond aux exigences"
@@ -1219,14 +1823,23 @@ msgstr "répond aux exigences"
msgid "ClusterIntegration|properly configured"
msgstr "correctement configuré"
+msgid "ClusterIntegration|sign up"
+msgstr "s’inscrire"
+
+msgid "Cohorts"
+msgstr "Cohortes"
+
msgid "Collapse"
msgstr "Réduire"
-msgid "Comment and resolve discussion"
-msgstr "Commenter et résoudre la discussion"
+msgid "Collapse sidebar"
+msgstr "Réduire la barre latérale"
-msgid "Comment and unresolve discussion"
-msgstr "Commenter et marquer la discussion comme non résolue"
+msgid "Comment & resolve discussion"
+msgstr "Commenter et marquer la discussion comme résolue"
+
+msgid "Comment & unresolve discussion"
+msgstr "Commenter et marquer la discussion comme non résolue"
msgid "Comments"
msgstr "Commentaires"
@@ -1245,13 +1858,13 @@ msgid "Commit Message"
msgstr "Message du commit"
msgid "Commit duration in minutes for last 30 commits"
-msgstr "Durée des 30 derniers pipelines en minutes"
+msgstr "Durée des 30 derniers commits en minutes"
msgid "Commit message"
msgstr "Message de commit"
msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
-msgstr "Statistiques des commits pour %{ref} %{start_time} - %{end_time}"
+msgstr "Statistiques des commits pour %{ref} de %{start_time} à %{end_time}"
msgid "Commit to %{branchName} branch"
msgstr "Valider dans la branche %{branchName}"
@@ -1269,7 +1882,7 @@ msgid "Commits feed"
msgstr "Flux des commits"
msgid "Commits per day hour (UTC)"
-msgstr "Commits par heure du jour (UTC)"
+msgstr "Commits pour chaque heure de la journée (UTC)"
msgid "Commits per day of month"
msgstr "Commits par jour du mois"
@@ -1278,10 +1891,10 @@ msgid "Commits per weekday"
msgstr "Commits par jour de la semaine"
msgid "Commits|An error occurred while fetching merge requests data."
-msgstr "Une erreur s'est produite lors de la récupération des données de demandes de fusion."
+msgstr "Une erreur s’est produite lors de la récupération des données de demandes de fusion."
msgid "Commits|Commit: %{commitText}"
-msgstr "Commit : %{commitText}"
+msgstr "Commit : %{commitText}"
msgid "Commits|History"
msgstr "Historique"
@@ -1290,7 +1903,10 @@ msgid "Commits|No related merge requests found"
msgstr "Aucune demande de fusion associée trouvée"
msgid "Committed by"
-msgstr "Validé par"
+msgstr "Commit de"
+
+msgid "Commit…"
+msgstr "Commit…"
msgid "Compare"
msgstr "Comparer"
@@ -1305,7 +1921,7 @@ msgid "Compare changes with the last commit"
msgstr "Comparer les changements avec le dernier commit"
msgid "Compare changes with the merge request target branch"
-msgstr ""
+msgstr "Comparer les modifications avec la branche cible de la demande de fusion"
msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
msgstr "%{source_branch} et %{target_branch} sont identiques."
@@ -1329,22 +1945,25 @@ msgid "Confidentiality"
msgstr "Confidentialité"
msgid "Configure Gitaly timeouts."
-msgstr ""
+msgstr "Configurer les délais d’expiration de Gitaly."
msgid "Configure Sidekiq job throttling."
-msgstr ""
+msgstr "Configurer la limitation des tâches Sidekiq."
msgid "Configure automatic git checks and housekeeping on repositories."
-msgstr ""
+msgstr "Configurer les vérifications Git automatiques et la maintenance des dépôts."
msgid "Configure limits for web and API requests."
-msgstr ""
+msgstr "Configurer les limites pour les requêtes Web et d’API."
+
+msgid "Configure push and pull mirrors."
+msgstr "Configurez les miroirs où récupérer et pousser le code."
msgid "Configure storage path and circuit breaker settings."
-msgstr ""
+msgstr "Configurez les paramètres du chemin de stockage et du disjoncteur."
msgid "Configure the way a user creates a new account."
-msgstr "Configurez la façon dont un•e utilisateur•rice crée un nouveau compte."
+msgstr "Configurez la façon dont un·e utilisateur·rice crée un nouveau compte."
msgid "Connect"
msgstr "Connecter"
@@ -1356,7 +1975,7 @@ msgid "Connect repositories from GitHub"
msgstr "Se connecter à des dépôts à partir de GitHub"
msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
-msgstr "Connectez vos dépôts externes et les pipelines CI / CD s’exécuteront pour les nouveaux commits. Un projet GitLab sera créé avec uniquement les fonctionnalités CI / CD activées."
+msgstr "Connectez vos dépôts externes afin que les pipelines d’intégration et de livraison continues (CI/CD) s’exécutent lors de nouveaux commits. Un projet GitLab sera créé avec uniquement les fonctionnalités CI/CD activées."
msgid "Connecting..."
msgstr "Connexion en cours…"
@@ -1368,10 +1987,10 @@ msgid "ContainerRegistry|Created"
msgstr "Créé"
msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
-msgstr "D’abord inscrivez-vous au registre de conteneur GitLab à l’aide de votre nom d’utilisateur GitLab et de votre mot de passe. Si vous avez %{link_2fa} vous devrez utiliser un %{link_token} :"
+msgstr "Inscrivezâ€vous d’abord au registre de conteneur GitLab à l’aide de votre nom d’utilisa·teur·trice GitLab et de votre mot de passe. Si vous avez %{link_2fa} vous devrez utiliser un %{link_token} :"
msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
-msgstr "GitLab prend en charge jusqu'à 3 niveaux de noms d’image. Les exemples d’images suivants sont valides pour votre projet :"
+msgstr "GitLab prend en charge jusqu’à trois niveaux de noms d’image. Les exemples d’images suivants sont valides pour votre projet :"
msgid "ContainerRegistry|How to use the Container Registry"
msgstr "Comment utiliser le registre de conteneur"
@@ -1380,7 +1999,7 @@ msgid "ContainerRegistry|Learn more about"
msgstr "En savoir plus sur"
msgid "ContainerRegistry|No tags in Container Registry for this container image."
-msgstr "Aucune étiquettes dans le registre de conteneur pour l’image de ce conteneur."
+msgstr "Aucune étiquette dans le registre de conteneur pour l’image de ce conteneur."
msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
msgstr "Une fois identifié·e, vous êtes libre de créer et télécharger une image de conteneur en utilisant les commandes courantes de %{build} et %{push}"
@@ -1395,64 +2014,100 @@ msgid "ContainerRegistry|Size"
msgstr "Taille"
msgid "ContainerRegistry|Tag"
-msgstr "Tag"
+msgstr "Étiquette"
msgid "ContainerRegistry|Tag ID"
-msgstr "ID du tag"
+msgstr "Identifiant de l’étiquette"
msgid "ContainerRegistry|Use different image names"
-msgstr "Utilisez des noms d’images différents"
+msgstr "Utilisez des noms d’image différents"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr "Avec le registre de conteneur Docker intégré à GitLab, chaque projet peut avoir son propre espace pour stocker ses images Docker."
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr "Vous pouvez également utiliser un %{deploy_token} pour un accès en lecture seule aux images du registre."
+
+msgid "Continue"
+msgstr "Continuer"
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr "Intégration et déploiement continus"
+msgid "Contribute to GitLab"
+msgstr "Contribuer à GitLab"
+
msgid "Contribution"
msgstr "Contribution"
msgid "Contribution guide"
msgstr "Guide de contribution"
+msgid "Contributions per group member"
+msgstr "Contributions par membre du groupe"
+
msgid "Contributors"
msgstr "Contributeurs"
msgid "ContributorsPage|%{startDate} – %{endDate}"
-msgstr "%{startDate} - %{endDate}"
+msgstr "Du %{startDate} au %{endDate}"
msgid "ContributorsPage|Building repository graph."
msgstr "Construction du graphique du dépôt."
msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
-msgstr "Commit sur %{branch_name}, à l'exclusion des commits de fusion. Limité à 6 000 commits."
+msgstr "Commits sur %{branch_name}, à l’exclusion des commits de fusion (limité à 6 000 commits)."
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr "Veuillez patienter, cette page va être automatiquement actualisée."
+msgid "Control the display of third party offers."
+msgstr "Contrôle l’affichage des offres tierces."
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
-msgstr "Contrôler la concurrence maximale des remplacements de fichier-joint LFS pour ce nœud secondaire"
+msgstr "Contrôle la concurrence maximale du remplissage du stockage pour les gros fichiers (LFS) pour ce nœud secondaire"
msgid "Control the maximum concurrency of repository backfill for this secondary node"
-msgstr "Contrôler la concurrence maximale des remplacements de dépôt pour ce nœud secondaire"
+msgstr "Contrôle la concurrence maximale des remplacements de dépôt pour ce nœud secondaire"
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr "Contrôle la concurrence maximale des opérations de vérification pour ce nœud Geo"
+
+msgid "ConvDev Index"
+msgstr "Index ConvDev"
msgid "Copy SSH public key to clipboard"
-msgstr "Copier la clé publique SSH dans le presse-papier"
+msgstr "Copier la clef SSH publique dans le presseâ€papiers"
msgid "Copy URL to clipboard"
-msgstr "Copier l'URL dans le presse-papier"
+msgstr "Copier l’URL dans le presseâ€papiers"
msgid "Copy branch name to clipboard"
-msgstr "Copier le nom de la branche dans le presse-papiers"
+msgstr "Copier le nom de la branche dans le presseâ€papiers"
msgid "Copy command to clipboard"
-msgstr "Copier la commande dans le presse-papiers"
+msgstr "Copier la commande dans le presseâ€papiers"
msgid "Copy commit SHA to clipboard"
-msgstr "Copier le SHA du commit"
+msgstr "Copier le condensat SHA du commit"
+
+msgid "Copy file path to clipboard"
+msgstr "Copier le chemin d’accès du fichier dans le presseâ€papiers"
+
+msgid "Copy incoming email address to clipboard"
+msgstr "Copier l’adresse de courriel de l’expéditeur dans le presse-papiers"
msgid "Copy reference to clipboard"
-msgstr "Copier la référence dans le presse-papiers"
+msgstr "Copier la référence dans le presseâ€papiers"
+
+msgid "Copy to clipboard"
+msgstr "Copier dans le presseâ€papiers"
+
+msgid "Copy token to clipboard"
+msgstr "Copier le jeton dans le presseâ€papiers"
msgid "Create"
msgstr "Créer"
@@ -1464,7 +2119,10 @@ msgid "Create a new branch"
msgstr "Créer une nouvelle branche"
msgid "Create a new branch and merge request"
-msgstr "Créer une nouvelle branche et demande de fusion"
+msgstr "Créer une nouvelle branche et une nouvelle demande de fusion"
+
+msgid "Create a new issue"
+msgstr "Créer un nouveau ticket"
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Créer un jeton d’accès personnel pour votre compte afin de récupérer ou pousser par %{protocol}."
@@ -1472,6 +2130,9 @@ msgstr "Créer un jeton d’accès personnel pour votre compte afin de récupér
msgid "Create branch"
msgstr "Créer une branche"
+msgid "Create commit"
+msgstr "Créer un commit"
+
msgid "Create directory"
msgstr "Créer un dossier"
@@ -1479,16 +2140,22 @@ msgid "Create empty repository"
msgstr "Créer un dépôt vide"
msgid "Create epic"
-msgstr "Créer l'épopée"
+msgstr "Créer l’épopée"
msgid "Create file"
msgstr "Créer un fichier"
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
-msgstr "Créer un label de groupe"
+msgstr "Créer une étiquette de groupe"
+
+msgid "Create issue"
+msgstr "Créer un ticket"
msgid "Create lists from labels. Issues with that label appear in that list."
-msgstr "Créer des listes à partir de labels. Les tickets avec ce label apparaissent dans cette liste."
+msgstr "Créer des listes à partir d’étiquettes. Les tickets avec l’étiquette sélectionnée apparaissent dans cette liste."
msgid "Create merge request"
msgstr "Créer une demande de fusion"
@@ -1505,53 +2172,77 @@ msgstr "Créer un nouveau dossier"
msgid "Create new file"
msgstr "Créer un nouveau fichier"
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
-msgstr "Créer un nouveau label"
+msgstr "Créer une nouvelle étiquette"
msgid "Create new..."
-msgstr "Créer nouveau..."
+msgstr "Créer un nouveau…"
msgid "Create project label"
-msgstr "Créer un label de projet"
+msgstr "Créer une étiquette de projet"
msgid "CreateNewFork|Fork"
-msgstr "Fourcher"
+msgstr "Créer une divergence"
msgid "CreateTag|Tag"
-msgstr "Tag"
+msgstr "Étiquette"
msgid "CreateTokenToCloneLink|create a personal access token"
-msgstr "Créer un jeton d'accès personnel"
+msgstr "Créer un jeton d’accès personnel"
+
+msgid "Created"
+msgstr "Créé"
+
+msgid "Created At"
+msgstr ""
-msgid "Creates a new branch from %{branchName}"
-msgstr "Crée une nouvelle branche à partir de %{branchName}"
+msgid "Created by me"
+msgstr "Créé par moi"
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
-msgstr "Crée une nouvelle branche à partir de %{branchName} et redirige pour créer une nouvelle demande de fusion"
+msgid "Created on:"
+msgstr ""
msgid "Creating epic"
-msgstr "Création de l'épopée en cours"
+msgstr "Création de l’épopée en cours"
msgid "Cron Timezone"
-msgstr "Fuseau horaire de Cron"
+msgstr "Fuseau horaire des tâches planifiées cron"
msgid "Cron syntax"
-msgstr "Syntaxe Cron"
+msgstr "Syntaxe de la planification cron"
msgid "Current node"
msgstr "NÅ“ud actuel"
+msgid "CurrentUser|Profile"
+msgstr "Profil"
+
+msgid "CurrentUser|Settings"
+msgstr "Paramètres"
+
+msgid "Custom CI config path"
+msgstr "Chemin d’accès de la config d’intégration continue personnalisée"
+
msgid "Custom notification events"
msgstr "Événements de notification personnalisés"
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 "Le niveau de notification Personnalisé est similaire au niveau Participation. Cependant, il permet également de recevoir des notifications pour des événements sélectionnés. Pour plus d’information, vous pouvez consulter %{notification_link}."
+msgstr "Les niveaux de notification personnalisés sont similaires aux niveaux de participation. Cependant, ils permettent de recevoir également des notifications pour une sélection d’événements. Pour plus d’information, vous pouvez consulter %{notification_link}."
msgid "Customize colors"
+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 ""
+
+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 "Cycle Analytics"
-msgstr "Analyseur de cycle"
+msgstr "Analyse de cycle"
msgid "CycleAnalyticsStage|Code"
msgstr "Code"
@@ -1569,11 +2260,14 @@ msgid "CycleAnalyticsStage|Review"
msgstr "Examen"
msgid "CycleAnalyticsStage|Staging"
-msgstr "Pré-production"
+msgstr "Préproduction"
msgid "CycleAnalyticsStage|Test"
msgstr "Test"
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr "Tous"
@@ -1581,13 +2275,22 @@ msgid "DashboardProjects|Personal"
msgstr "Personnels"
msgid "Dec"
-msgstr "Déc."
+msgstr "déc."
msgid "December"
-msgstr "Décembre"
+msgstr "décembre"
+
+msgid "Decline and sign out"
+msgstr "Refuser et se déconnecter"
msgid "Default classification label"
-msgstr "Label de classement par défaut"
+msgstr "Étiquette de classement par défaut"
+
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
msgid "Define a custom pattern with cron syntax"
msgstr "Définir un schéma personnalisé avec une syntaxe Cron"
@@ -1595,19 +2298,163 @@ msgstr "Définir un schéma personnalisé avec une syntaxe Cron"
msgid "Delete"
msgstr "Supprimer"
+msgid "Delete Snippet"
+msgstr "Supprimer L’extrait de code"
+
+msgid "Delete list"
+msgstr "Supprimer la liste"
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr "Refuser"
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "Déploiement"
msgstr[1] "Déploiements"
msgid "Deploy Keys"
-msgstr "Clés de déploiement"
+msgstr "Clefs de déploiement"
+
+msgid "DeployKeys|+%{count} others"
+msgstr "+%{count} autres"
+
+msgid "DeployKeys|Current project"
+msgstr "Projet actuel"
+
+msgid "DeployKeys|Deploy key"
+msgstr "Clef de déploiement"
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr "Clefs de déploiement activées"
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr "Erreur lors de l’activation de la clef de déploiement"
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr "Erreur lors de l’obtention des clefs de déploiement"
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr "Erreur lors de la suppression de la clef de déploiement"
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr "Afficher %{count} autres projets"
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr "Chargement des clefs de déploiement"
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr "Aucune clef de déploiement trouvée. Créezâ€en une avec le formulaire ciâ€dessous."
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr "Clefs de déploiement à accès privé"
+
+msgid "DeployKeys|Project usage"
+msgstr "À l’usage du projet"
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr "Clefs de déploiement à accès publique"
+
+msgid "DeployKeys|Read access only"
+msgstr "Accès en lecture seule"
+
+msgid "DeployKeys|Write access allowed"
+msgstr "Accès en écriture autorisé"
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr "Vous êtes sur le point de supprimer cette clef de déploiement. Êtesâ€vous sûr·e ?"
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr "Jetons de déploiement actifs : (%{active_tokens})"
+
+msgid "DeployTokens|Add a deploy token"
+msgstr "Ajouter un jeton de déploiement"
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr "Autorise l’accès en lecture seule aux images du registre"
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr "Autorise l’accès en lecture seule au dépôt"
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr "Copier le jeton de déploiement dans le presseâ€papiers"
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr "Copier le nom d’utilisateur·rice dans le presseâ€papiers"
+
+msgid "DeployTokens|Create deploy token"
+msgstr "Créer un jeton de déploiement"
+
+msgid "DeployTokens|Created"
+msgstr "Créé"
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr "Jetons de déploiement"
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr "Les jetons de déploiement autorisent l’accès en lecture seule à votre dépôt et vos images de registre."
+
+msgid "DeployTokens|Expires"
+msgstr "Expire"
+
+msgid "DeployTokens|Name"
+msgstr "Nom"
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr "Choisissez un nom pour l’application et nous vous donnerons un jeton de déploiement unique."
+
+msgid "DeployTokens|Revoke"
+msgstr "Révoquer"
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr "Révoquer %{name}"
+
+msgid "DeployTokens|Scopes"
+msgstr "Champs d’application"
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr "Cette action ne peut pas être annulée."
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr "Ce projet n’a pas de jetons de déploiement actif."
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr "Utilisez ce jeton en tant que mot de passe. Assurezâ€vous de le sauvegarder, vous n’aurez pas la possibilité d’y accéder à nouveau."
+
+msgid "DeployTokens|Use this username as a login."
+msgstr "Utiliser ce nom d’utilisateur·rice comme identifiant."
+
+msgid "DeployTokens|Username"
+msgstr "Nom d’utilisateur·rice"
+
+msgid "DeployTokens|You are about to revoke"
+msgstr "Vous êtes sur le point de révoquer"
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr "Votre nouveau jeton de déploiement"
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr "Votre nouveau jeton de déploiement pour votre projet a été créé."
+
+msgid "Deprioritize label"
+msgstr "Déprioriser l’étiquette"
+
+msgid "Descending"
+msgstr "Décroissant"
msgid "Description"
msgstr "Description"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
-msgstr "Les modèles de description permettent de définir des modèles spécifiques au contexte pour les champs de description de tickets et demandes de fusion pour votre projet."
+msgstr "Les modèles de description permettent de définir des modèles spécifiques au contexte et propres à votre projet pour les champs de description des tickets et des demandes de fusion."
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr "Détruire"
msgid "Details"
msgstr "Détails"
@@ -1615,56 +2462,80 @@ msgstr "Détails"
msgid "Diffs|No file name available"
msgstr "Aucun nom de fichier disponible"
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr "Quelque chose s’est mal passé lors de la rapatriement des lignes du diff."
+
msgid "Directory name"
msgstr "Nom du dossier"
msgid "Disable"
msgstr "Désactiver"
+msgid "Disable for this project"
+msgstr "Désactiver pour ce projet"
+
+msgid "Disable group Runners"
+msgstr "Désactiver les exécuteurs de groupe"
+
+msgid "Discard changes"
+msgstr "Abandonner les modifications"
+
msgid "Discard draft"
-msgstr "Supprimer le brouillon"
+msgstr "Abandonner le brouillon"
msgid "Discover GitLab Geo."
-msgstr "Découvrez GitLab Geo."
+msgstr "Découvrez GitLab Geo."
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
msgid "Dismiss Cycle Analytics introduction box"
-msgstr "Passer l’introduction Cycle Analytics"
+msgstr "Passer l’introduction à Cycle Analytics"
msgid "Dismiss Merge Request promotion"
msgstr "Rejeter la promotion de la demande de fusion"
-msgid "Documentation for popular identity providers"
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
msgstr ""
+msgid "Documentation for popular identity providers"
+msgstr "Documentation des principaux fournisseurs d’identité"
+
+msgid "Domain"
+msgstr "Domaine"
+
msgid "Don't show again"
-msgstr "Ne plus montrer"
+msgstr "Ne plus afficher"
msgid "Done"
-msgstr "Fait"
+msgstr "Effectué"
msgid "Download"
msgstr "Télécharger"
msgid "Download tar"
-msgstr "Télécharger tar"
+msgstr "Télécharger une archive TAR"
msgid "Download tar.bz2"
-msgstr "Télécharger tar.bz2"
+msgstr "Télécharger une archive tar.bz2"
msgid "Download tar.gz"
-msgstr "Télécharger tar.gz"
+msgstr "Télécharger une archive tar.gz"
msgid "Download zip"
-msgstr "Télécharger zip"
+msgstr "Télécharger une archive ZIP"
msgid "DownloadArtifacts|Download"
msgstr "Télécharger"
msgid "DownloadCommit|Email Patches"
-msgstr "Patch email"
+msgstr "Envoyer les correctifs par courriel"
msgid "DownloadCommit|Plain Diff"
-msgstr "Diff simple"
+msgstr "Pur diff"
msgid "DownloadSource|Download"
msgstr "Télécharger"
@@ -1676,43 +2547,67 @@ msgid "Due date"
msgstr "Date d’échéance"
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
-msgstr ""
+msgstr "Au cours de ce processus, il vous sera demandé les URL de GitLab. Utilisez les URL indiquées ciâ€dessous."
+
+msgid "Each Runner can be in one of the following states:"
+msgstr "Chaque exécuteur peut être dans l’un des états suivants :"
msgid "Edit"
msgstr "Éditer"
+msgid "Edit Label"
+msgstr "Modifier l’étiquette"
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "Éditer le pipeline programmé %{id}"
+msgid "Edit Snippet"
+msgstr "Modifier le fragment de code"
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
-msgstr "Modifier les fichiers dans l'éditeur et valider les modifications ici"
+msgstr "Modifier les fichiers dans l’éditeur et valider les modifications ici"
-msgid "Editing"
-msgstr "En cours de modification"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr "Modifier l’identité de %{user_name}"
msgid "Elasticsearch"
-msgstr ""
+msgstr "Elasticsearch"
msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
-msgstr ""
+msgstr "Intégration d’Elasticsearch. AWS Elasticsearch IAM."
msgid "Email"
-msgstr ""
+msgstr "Courriel"
+
+msgid "Email patch"
+msgstr "Correctif par courriel"
msgid "Emails"
msgstr "Courriels"
+msgid "Embed"
+msgstr "Embarquer"
+
msgid "Enable"
msgstr "Activer"
msgid "Enable Auto DevOps"
msgstr "Activer Auto DevOps"
+msgid "Enable Pseudonymizer data collection"
+msgstr "Activer la collecte de données Pseudonymizer"
+
msgid "Enable SAML authentication for this group"
-msgstr ""
+msgstr "Activer l’authentification SAML pour ce groupe"
msgid "Enable Sentry for error reporting and logging."
-msgstr ""
+msgstr "Activer Sentry pour les rapports d’erreurs et la journalisation."
msgid "Enable and configure InfluxDB metrics."
msgstr "Activer et configurer les métriques InfluxDB."
@@ -1723,27 +2618,54 @@ msgstr "Activer et configurer les métriques Prometheus."
msgid "Enable classification control using an external service"
msgstr "Activer le contrôle de classification à l’aide d’un service externe"
+msgid "Enable for this project"
+msgstr "Activer pour ce projet"
+
+msgid "Enable group Runners"
+msgstr "Activer les exécuteurs de groupe"
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr "Activer ou désactiver certaines fonctions de groupe et choisir les niveaux d’accès."
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr "Activer ou désactiver la collecte de données Pseudonymizer."
+
msgid "Enable or disable version check and usage ping."
-msgstr ""
+msgstr "Activer ou désactiver le contrôle de version et l’envoi des données d’utilisation."
msgid "Enable reCAPTCHA or Akismet and set IP limits."
-msgstr ""
+msgstr "Activer reCAPTCHA ou Akismet et définir des limites d’adresse IP."
msgid "Enable the Performance Bar for a given group."
-msgstr ""
+msgstr "Activer la barre de performance pour un groupe donné."
msgid "Enabled"
-msgstr ""
+msgstr "activé"
+
+msgid "Ends at (UTC)"
+msgstr "Se termine à (UTC)"
+
+msgid "Environments"
+msgstr "Environnements"
msgid "Environments|An error occurred while fetching the environments."
-msgstr "Une erreur s‘est produite lors de la récupération des environnements."
+msgstr "Une erreur s’est produite lors de la récupération des environnements."
msgid "Environments|An error occurred while making the request."
msgstr "Une erreur s’est produite lors de la requête."
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr "Commit"
+msgid "Environments|Deploy to..."
+msgstr "Déployer vers…"
+
msgid "Environments|Deployment"
msgstr "Déploiement"
@@ -1756,35 +2678,56 @@ msgstr "Environnements"
msgid "Environments|Job"
msgstr "Tâche"
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr "Nouvel environnement"
msgid "Environments|No deployments yet"
msgstr "Aucun déploiement pour le moment"
-msgid "Environments|Open"
-msgstr "Ouvert"
+msgid "Environments|No pod name has been specified"
+msgstr "Aucun nom de « pod » n’a été spécifié"
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
-msgid "Environments|Re-deploy"
-msgstr "Re-déployer"
+msgid "Environments|Pod logs from"
+msgstr "Journaux du pod depuis"
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
msgid "Environments|Read more about environments"
msgstr "En savoir plus sur les environnements"
-msgid "Environments|Rollback"
-msgstr "Revenir en arrière"
+msgid "Environments|Rollback environment"
+msgstr ""
msgid "Environments|Show all"
-msgstr "Afficher tous"
+msgstr "Tout afficher"
+
+msgid "Environments|Stop"
+msgstr "Arrêter"
+
+msgid "Environments|Stop environment"
+msgstr "Arrêter l’environnement"
msgid "Environments|Updated"
-msgstr "Mise à jour"
+msgstr "Mis à jour"
msgid "Environments|You don't have any environments right now."
msgstr "Vous n’avez aucun environnement pour le moment."
+msgid "Epic"
+msgstr "Épopée"
+
msgid "Epic will be removed! Are you sure?"
-msgstr "L’épopée sera supprimée ! Êtes-vous sûr•e ?"
+msgstr "L’épopée sera supprimée ! Êtesâ€vous sûr(e) ?"
msgid "Epics"
msgstr "Épopées"
@@ -1793,25 +2736,19 @@ msgid "Epics Roadmap"
msgstr "Feuille de route des épopées"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr "Les épopées vous permettent de gérer votre portefeuille de projets plus efficacement et avec moins d'effort"
+msgstr "Les épopées vous permettent de gérer votre portefeuille de projets plus efficacement et avec moins d’efforts"
msgid "Error Reporting and Logging"
-msgstr ""
-
-msgid "Error checking branch data. Please try again."
-msgstr "Erreur lors de la vérification des données de branche. Veuillez réessayer."
-
-msgid "Error committing changes. Please try again."
-msgstr "Erreur lors de la validation des modifications. Veuillez réessayer."
+msgstr "Rapport d’erreur et journalisation"
msgid "Error creating epic"
msgstr "Erreur lors de la création de l’épopée"
msgid "Error fetching contributors data."
-msgstr "Erreur lors de l’extraction des données des contributeur•rice•s."
+msgstr "Erreur lors de l’extraction des données des contributeur·rice·s."
msgid "Error fetching labels."
-msgstr "Erreur lors de la récupération des labels."
+msgstr "Erreur lors de la récupération des étiquettes."
msgid "Error fetching network graph."
msgstr "Erreur lors de la récupération du graphique du réseau."
@@ -1822,20 +2759,38 @@ msgstr "Erreur lors de la récupération des refs"
msgid "Error fetching usage ping data."
msgstr "Erreur lors de la récupération des données d’utilisation."
+msgid "Error loading branch data. Please try again."
+msgstr "Erreur lors du chargement des données de branche. Veuillez réessayer."
+
+msgid "Error loading last commit."
+msgstr "Erreur lors du chargement du dernier commit."
+
+msgid "Error loading markdown preview"
+msgstr "Erreur lors du chargement de l’aperçu du Markdown"
+
+msgid "Error loading merge requests."
+msgstr "Erreur lors du chargement des demandes de fusion."
+
+msgid "Error loading project data. Please try again."
+msgstr "Erreur lors du chargement des données du projet. Veuillez réessayer."
+
msgid "Error occurred when toggling the notification subscription"
-msgstr "Une erreur s’est produite lors de l’activation/désactivation de l’abonnement aux notifications"
+msgstr "Une erreur s’est produite lors de l’activation ou de la désactivation de l’abonnement aux notifications"
msgid "Error saving label update."
-msgstr "Erreur lors de la mise à jour du label."
+msgstr "Erreur lors de la mise à jour de l’étiquette."
msgid "Error updating status for all todos."
msgstr "Erreur lors de la mise à jour de l’état de la liste de tâches à faire."
msgid "Error updating todo status."
-msgstr "Erreur lors de la mise à jour du statut de la tâche à faire."
+msgstr "Erreur lors de la mise à jour du statut de tâche à faire."
+
+msgid "Estimated"
+msgstr "Estimé"
msgid "EventFilterBy|Filter by all"
-msgstr "Aucun filtre"
+msgstr "Tout filtrer"
msgid "EventFilterBy|Filter by comments"
msgstr "Filtrer par commentaires"
@@ -1847,22 +2802,43 @@ msgid "EventFilterBy|Filter by merge events"
msgstr "Filtrer par événements de fusion"
msgid "EventFilterBy|Filter by push events"
-msgstr "Filtrer par événements de poussée"
+msgstr "Filtrer par événements Git push"
msgid "EventFilterBy|Filter by team"
msgstr "Filtrer par équipe"
msgid "Every day (at 4:00am)"
-msgstr "Chaque jour (à 4h00 du matin)"
+msgstr "Chaque jour (à 4 h du matin)"
msgid "Every month (on the 1st at 4:00am)"
-msgstr "Chaque mois (le 1er à 4:00 du matin)"
+msgstr "Chaque mois (le 1ᵉʳ à 4 h du matin)"
msgid "Every week (Sundays at 4:00am)"
-msgstr "Chaque semaine (dimanche à 4h00 du matin)"
+msgstr "Chaque semaine (dimanche à 4 h du matin)"
+
+msgid "Everyone can contribute"
+msgstr "Tout le monde peut contribuer"
msgid "Expand"
-msgstr "Ouvrir"
+msgstr "Étendre"
+
+msgid "Expand all"
+msgstr "Tout étendre"
+
+msgid "Expand sidebar"
+msgstr "Étendre la barre latérale"
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr "Explorer GitLab"
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr "Explorer les groupes"
msgid "Explore projects"
msgstr "Explorer les projets"
@@ -1874,22 +2850,25 @@ msgid "External Classification Policy Authorization"
msgstr "Autorisation de politique de classification externe"
msgid "External authentication"
-msgstr ""
+msgstr "Authentification externe"
msgid "External authorization denied access to this project"
msgstr "L’autorisation externe a refusé l’accès à ce projet"
msgid "External authorization request timeout"
-msgstr "Expiration de la demande d’autorisation externe"
+msgstr "Expiration du délai d’attente de la demande d’autorisation externe"
msgid "ExternalAuthorizationService|Classification Label"
-msgstr "Label de classification"
+msgstr ""
msgid "ExternalAuthorizationService|Classification label"
-msgstr "Label de classification"
+msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
-msgstr "Quand aucun label de classification n’est défini, le label par défaut `%{default_label}` sera utilisé."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
msgid "Failed"
msgstr "Échec"
@@ -1900,6 +2879,9 @@ msgstr "Tâches ayant échoué"
msgid "Failed to change the owner"
msgstr "Échec du changement de propriétaire"
+msgid "Failed to check related branches."
+msgstr "Échec de la vérification des branches liées."
+
msgid "Failed to remove issue from board, please try again."
msgstr "Impossible de supprimer le ticket du tableau, veuillez réessayer."
@@ -1909,17 +2891,20 @@ msgstr "Échec de la suppression du pipeline programmé"
msgid "Failed to update issues, please try again."
msgstr "Échec de la mise à jour du ticket. Veuillez réessayer."
+msgid "Failure"
+msgstr "Échec"
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr "Plus rapide parce qu’il réutilise l’espace de travail du projet (faire un clone en solution de secours s’il n’existe pas)"
+
msgid "Feb"
-msgstr "Févr."
+msgstr "févr."
msgid "February"
-msgstr "Février"
+msgstr "février"
msgid "Fields on this page are now uneditable, you can configure"
-msgstr "Les champs sur cette page sont désormais non modifiables, vous pouvez configurer"
-
-msgid "File name"
-msgstr "Nom du fichier"
+msgstr "Les champs de cette page sont désormais non modifiables, vous pouvez configurer"
msgid "Files"
msgstr "Fichiers"
@@ -1928,17 +2913,26 @@ msgid "Files (%{human_size})"
msgstr "Fichiers (%{human_size})"
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr "Renseignez les champs ciâ€dessous, activez <strong>%{enable_label}</strong> et appuyez sur <strong>%{save_changes}</strong>"
+
+msgid "Filter"
msgstr ""
msgid "Filter by commit message"
msgstr "Filtrer par message de commit"
msgid "Find by path"
-msgstr "Rechercher par chemin"
+msgstr "Rechercher par chemin d’accès"
msgid "Find file"
msgstr "Rechercher un fichier"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr "Terminé"
@@ -1948,112 +2942,175 @@ msgstr "En premier"
msgid "FirstPushedBy|pushed by"
msgstr "poussé par"
-msgid "Font Color"
+msgid "FogBugz Email"
+msgstr "Courriel de FogBugz"
+
+msgid "FogBugz Import"
+msgstr "Importation de FogBugz"
+
+msgid "FogBugz Password"
+msgstr "Mot de passe FogBugz"
+
+msgid "FogBugz URL"
+msgstr "URL de FogBugz"
+
+msgid "FogBugz import"
+msgstr "Importation de FogBugz"
+
+msgid "Follow the steps below to export your Google Code project data."
msgstr ""
+msgid "Font Color"
+msgstr "Couleur de la police"
+
msgid "Footer message"
-msgstr ""
+msgstr "Message de pied de page"
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr "Pour les projets internes, tout utilisateur connecté peut afficher les pipelines et accéder aux détails des tâches (journaux de sortie et artefacts)"
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr "Pour les projets privés, tout membre (invité ou supérieur) peut afficher les pipelines et accéder aux détails des tâches (journaux de sortie et artefacts)"
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr "Pour les projets publics, tout le monde peut afficher des pipelines et accéder aux détails des tâches (journaux de sortie et artefacts)"
msgid "Fork"
msgid_plural "Forks"
-msgstr[0] "Fourche"
-msgstr[1] "Fourches"
+msgstr[0] "Divergence"
+msgstr[1] "Divergences"
msgid "ForkedFromProjectPath|Forked from"
-msgstr "Fourché depuis"
+msgstr "Divergence issue de"
msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
-msgstr "Fourché depuis %{project_name} (supprimé)"
+msgstr "Divergence issue de %{project_name} (supprimé)"
msgid "Forking in progress"
-msgstr "Fourchage en cours"
+msgstr "Divergence en cours"
msgid "Format"
msgstr "Format"
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr "Erreurs trouvées dans votre fichier .gitlab-ci.yml :"
+
msgid "From %{provider_title}"
msgstr "De %{provider_title}"
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
-msgstr "Depuis la création du ticket jusqu'au déploiement en production"
+msgstr "Depuis la création du ticket jusqu’au déploiement en production"
msgid "From merge request merge until deploy to production"
-msgstr "Depuis la fusion de la demande de fusion jusqu'au déploiement en production"
+msgstr "Depuis la fusion de la demande de fusion jusqu’au déploiement en production"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
-msgstr "À partir de l’affichage des détails du cluster Kubernetes, installez un Exécuteur à partir de la liste des applications"
+msgstr "À partir de l’affichage des détails de la grappe de serveurs Kubernetes, installez un exécuteur à partir de la liste des applications"
msgid "GPG Keys"
-msgstr "Clés GPG"
+msgstr "Clefs GPG"
+
+msgid "General"
+msgstr "Général"
+
+msgid "General pipelines"
+msgstr "Pipelines généraux"
msgid "Generate a default set of labels"
-msgstr "Générer un ensemble de labels par défaut"
+msgstr "Générer un jeu d’étiquettes par défaut"
msgid "Geo Nodes"
msgstr "NÅ“uds Geo"
msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
-msgstr ""
+msgstr "Geo vous permet de répliquer votre instance GitLab vers d’autres emplacements géographiques."
msgid "GeoNodeSyncStatus|Node is failing or broken."
-msgstr "Le nœud est défaillant ou cassé."
+msgstr "Le nœud est défaillant ou en panne."
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
-msgstr "Le nœud est lent, surchargé, ou il vient juste de récupérer après un problème."
+msgstr "Le nœud est lent, surchargé, ou il vient juste de se rétablir après une indisponibilité."
msgid "GeoNodes|Checksummed"
msgstr "Vérifié par somme de contrôle"
-msgid "GeoNodes|Database replication lag:"
-msgstr "Retard de réplication de la base de données :"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr "Données périmées à partir de %{timeago}"
+
+msgid "GeoNodes|Data replication lag"
+msgstr "Latence de la réplication des données"
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
-msgstr "La désactivation d’un nœud arrête le processus de synchronisation. Êtes-vous sûr•e?"
+msgstr ""
msgid "GeoNodes|Does not match the primary storage configuration"
-msgstr "Ne correspond pas à la configuration de stockage principale"
+msgstr ""
msgid "GeoNodes|Failed"
msgstr "Échec"
msgid "GeoNodes|Full"
-msgstr "Complet"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr "Version de GitLab"
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr "La version de GitLab ne correspond pas à la version du nœud principal"
-msgid "GeoNodes|GitLab version:"
-msgstr "Version de GitLab :"
+msgid "GeoNodes|Health status"
+msgstr "État de santé"
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
-msgid "GeoNodes|Health status:"
-msgstr "État :"
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
-msgstr "Dernier ID d’événement traité par le curseur :"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
-msgstr "Dernier événement vu du nœud primaire :"
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
msgid "GeoNodes|Loading nodes"
-msgstr "Chargement des nœuds"
+msgstr ""
-msgid "GeoNodes|Local Attachments:"
-msgstr "Pièces jointes locales :"
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
-msgid "GeoNodes|Local LFS objects:"
-msgstr "Objets LFS locaux :"
+msgid "GeoNodes|Local attachments"
+msgstr ""
-msgid "GeoNodes|Local job artifacts:"
-msgstr "Artefacts de tâches locaux :"
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
msgid "GeoNodes|New node"
msgstr "Nouveau nœud"
msgid "GeoNodes|Node Authentication was successfully repaired."
-msgstr "Le nœud d'Authentification a été réparé avec succès."
+msgstr ""
msgid "GeoNodes|Node was successfully removed."
-msgstr "Le nœud a été supprimé avec succès."
+msgstr ""
msgid "GeoNodes|Not checksummed"
msgstr "Non vérifié par somme de contrôle"
@@ -2062,40 +3119,49 @@ msgid "GeoNodes|Out of sync"
msgstr "Désynchronisé"
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
-msgstr "Supprimer un nœud arrête le processus de synchronisation. Êtes-vous sûr•e ?"
+msgstr "Supprimer un nÅ“ud arrête le processus de synchronisation. Êtesâ€vous sûr(e) ?"
-msgid "GeoNodes|Replication slot WAL:"
-msgstr "Emplacement de réplication WAL :"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr "Emplacements de réplication"
+
+msgid "GeoNodes|Repositories"
+msgstr "Dépôts"
-msgid "GeoNodes|Replication slots:"
-msgstr "Emplacements de réplication :"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
-msgstr "Dépôts avec une somme de contrôle calculée:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
-msgid "GeoNodes|Repositories:"
-msgstr "Dépôts :"
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
-msgstr "Dépôts avec une somme de contrôle vérifiée :"
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
msgid "GeoNodes|Selective"
msgstr "Sélectif"
msgid "GeoNodes|Something went wrong while changing node status"
-msgstr "Une erreur s’est produite lors du changement de statut du nœud"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
msgid "GeoNodes|Something went wrong while removing node"
-msgstr "Une erreur s’est produite lors de la suppression du nœud"
+msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
-msgstr "Une erreur s’est produite lors de la réparation du nœud"
+msgstr ""
-msgid "GeoNodes|Storage config:"
-msgstr "Configuration de stockage :"
+msgid "GeoNodes|Storage config"
+msgstr "Configuration du stockage"
-msgid "GeoNodes|Sync settings:"
-msgstr "Paramètres de synchronisation :"
+msgid "GeoNodes|Sync settings"
+msgstr "Paramètres de synchronisation"
msgid "GeoNodes|Synced"
msgstr "Synchronisé"
@@ -2112,41 +3178,53 @@ msgstr "Emplacements utilisés"
msgid "GeoNodes|Verified"
msgstr "Vérifié"
-msgid "GeoNodes|Wiki checksums verified:"
-msgstr "Wiki avec une somme de contrôle vérifiée :"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
-msgstr "Wiki avec une somme de contrôle calculée :"
+msgid "GeoNodes|Wikis"
+msgstr "Wikis"
-msgid "GeoNodes|Wikis:"
-msgstr "Wikis :"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
-msgstr "Vous avez configuré des nœuds Geo en utilisant une connexion HTTP non sécurisée. Nous recommandons l’utilisation de HTTPS."
+msgstr ""
msgid "Geo|All projects"
msgstr "Tous les projets"
msgid "Geo|File sync capacity"
-msgstr "Capacité de synchronisation de fichier"
+msgstr "Capacité de synchronisation de fichiers"
msgid "Geo|Groups to synchronize"
msgstr "Groupes à synchroniser"
msgid "Geo|Projects in certain groups"
-msgstr "Projets dans certains groupes"
+msgstr ""
msgid "Geo|Projects in certain storage shards"
-msgstr "Projets dans certains fragments de stockage"
+msgstr ""
msgid "Geo|Repository sync capacity"
-msgstr "Capacité de synchronisation du dépôt"
+msgstr ""
msgid "Geo|Select groups to replicate."
-msgstr "Sélectionner les groupes à répliquer."
+msgstr ""
msgid "Geo|Shards to synchronize"
-msgstr "Fragments à synchroniser"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
msgid "Git repository URL"
msgstr "URL du dépôt Git"
@@ -2155,7 +3233,10 @@ msgid "Git revision"
msgstr "Révision de Git"
msgid "Git storage health information has been reset"
-msgstr "Les informations de santé du stockage Git ont été réinitialisées"
+msgstr "Les informations sur l’état de santé du stockage Git ont été réinitialisées"
+
+msgid "Git strategy for pipelines"
+msgstr "Stratégie Git pour les pipelines"
msgid "Git version"
msgstr "Version de Git"
@@ -2167,82 +3248,196 @@ msgid "GitLab CI Linter has been moved"
msgstr "GitLab CI Linter a été déplacé"
msgid "GitLab Geo"
+msgstr "GitLab Geo"
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr "Les exécuteurs de groupe peuvent exécuter du code pour tous les projets de ce groupe."
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "Section de l'Exécuteur GitLab"
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
+msgstr "URL d’authentification unique GitLab"
+
+msgid "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."
msgstr ""
-msgid "Gitaly"
+msgid "GitLab.com import"
msgstr ""
+msgid "Gitaly"
+msgstr "Gitaly"
+
msgid "Gitaly Servers"
msgstr "Serveurs Gitaly"
+msgid "Gitaly|Address"
+msgstr "Adresse"
+
+msgid "Gitea Host URL"
+msgstr "URL de l’hôte Gitea"
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr "Retour"
+
msgid "Go back"
-msgstr "Retour en arrière"
+msgstr "Retour"
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
msgid "Go to your fork"
-msgstr "Aller à votre fourche"
+msgstr "Aller à votre dépôt divergent"
msgid "GoToYourFork|Fork"
-msgstr "Fourche"
+msgstr "Dépôt divergent"
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
-msgstr "L’authentification Google n’est pas %{link_to_documentation}. Demandez à votre administrateur GitLab si vous souhaitez utiliser ce service."
+msgstr "L’authentification Google n’est pas %{link_to_documentation}. Demandez à votre administrat·eur·rice GitLab si vous souhaitez utiliser ce service."
msgid "Got it!"
-msgstr "Compris !"
+msgstr "Compris !"
+
+msgid "Graph"
+msgstr "Graphique"
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr "Paramètres du groupe CI/CD"
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr "Identifiant du groupe"
+
+msgid "Group Runners"
+msgstr "Exécuteurs de groupe"
+
+msgid "Group avatar"
+msgstr "Avatar de groupe"
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr "Les épopées vous permettent de gérer votre portefeuille de projets plus efficacement et avec moins d’effort"
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr "Les responsables de groupe peuvent créer des exécuteurs de groupe via %{link}"
+
+msgid "Group: %{group_name}"
+msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
-msgstr "À partir de %{dateWord}"
+msgstr ""
msgid "GroupRoadmap|Loading roadmap"
-msgstr "Chargement de la feuille de route"
+msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
-msgstr "Une erreur s’est produite lors de la récupération des épopées"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "Pour afficher la feuille de route, ajoutez une date de début ou de fin planifiée à l'une de vos épopées dans ce groupe ou ses sous-groupes. Seules les épopées des 3 derniers mois et des 3 prochains mois sont affichées &ndash; de %{startDate} à %{endDate}."
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
-msgstr "Jusqu’au %{dateWord}"
+msgstr ""
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
-msgstr "Empêcher le partage d'un projet de %{group} avec d'autres groupes"
+msgstr "Empêcher le partage d’un projet du groupe %{group} avec d’autres groupes"
msgid "GroupSettings|Share with group lock"
-msgstr "Verrou de partage avec un groupe"
+msgstr "Partager avec un verrou de groupe"
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
-msgstr "Ce paramètre est appliqué sur %{ancestor_group} et a été modifié dans ce sous-groupe."
+msgstr "Ce paramètre s’applique au groupe %{ancestor_group} et a été forcé pour ce sousâ€groupe."
msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
-msgstr "Ce paramètre est appliqué sur %{ancestor_group}. Pour partager des projets dans ce groupe avec un autre groupe, demander au propriétaire de modifier le paramètre ou %{remove_ancestor_share_with_group_lock}."
+msgstr "Ce paramètre s’applique au groupe %{ancestor_group}. Pour partager des projets de ce groupe avec un autre groupe, demandez au propriétaire d’écraser ce paramètre ou de %{remove_ancestor_share_with_group_lock}."
msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
-msgstr "Ce paramètre s’applique sur %{ancestor_group}. Vous pouvez modifier le paramètre ou le %{remove_ancestor_share_with_group_lock}."
+msgstr "Ce paramètre s’applique au groupe %{ancestor_group}. Vous pouvez écraser le paramètre ou le %{remove_ancestor_share_with_group_lock}."
msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
-msgstr "Ce paramètre s’appliquera à tous les sous-groupes, sauf s’il est modifié par un propriétaire du groupe. Les groupes déjà liés au projet continueront d’y avoir accès à moins d’être retirés manuellement."
+msgstr "Ce paramètre s’appliquera à tous les sousâ€groupes, sauf s’il est modifié par un propriétaire du groupe. Les groupes déjà liés au projet continueront d’y avoir accès à moins d’être retirés manuellement."
msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
-msgstr "ne peut pas être désactivé lorsque le groupe parent « Verrou de partage avec un groupe » est activé, sauf pour le propriétaire du groupe parent"
+msgstr "ne peut pas être désactivé lorsque le groupe parent a activé le « Partage avec verrou de groupe », sauf par le propriétaire du groupe parent."
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
-msgstr "supprimer le verrou de partage avec un groupe pour %{ancestor_group_name}"
+msgstr "supprimer le partage avec verrou de groupe pour %{ancestor_group_name}"
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr "Les groupes peuvent également être imbriqués en créant des %{subgroup_docs_link_start}sousâ€groupes%{subgroup_docs_link_end}."
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr "Un groupe est une collection de plusieurs projets."
msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
-msgstr "Si vous organisez vos projets au sein d’un groupe, il fonctionnera comme un dossier."
+msgstr "Si vous organisez vos projets au sein d’un groupe, cela fonctionne comme un dossier."
msgid "GroupsEmptyState|No groups found"
msgstr "Aucun groupe trouvé"
@@ -2254,7 +3449,7 @@ 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."
+msgstr "Créer un sousâ€groupe de ce groupe."
msgid "GroupsTree|Edit group"
msgstr "Modifier le groupe"
@@ -2278,16 +3473,16 @@ msgid "GroupsTree|Sorry, no groups or projects matched your search"
msgstr "Désolé, aucun groupe ni projet ne correspond à vos critères de recherche"
msgid "Have your users email"
-msgstr "Lister les emails utilisateurs"
+msgstr ""
msgid "Header message"
-msgstr ""
+msgstr "Message d’enâ€tête"
msgid "Health Check"
msgstr "État des services"
msgid "Health information can be retrieved from the following endpoints. More information is available"
-msgstr "L’état des service peut être récupéré depuis les adresses suivantes. Plus d’information disponible."
+msgstr "L’état des services peut être récupéré depuis les emplacements suivants. Plus d’information disponible."
msgid "HealthCheck|Access token is"
msgstr "Le jeton d’accès est"
@@ -2315,36 +3510,114 @@ msgid_plural "Hide values"
msgstr[0] "Masquer la valeur"
msgstr[1] "Masquer les valeurs"
+msgid "Hide whitespace changes"
+msgstr "Masquer les modifications des espaces"
+
msgid "History"
msgstr "Historique"
msgid "Housekeeping successfully started"
msgstr "Maintenance démarrée avec succès"
+msgid "I accept the %{terms_link}"
+msgstr "J’accepte les %{terms_link}"
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr "Conditions générales d’utilisation et politique de confidentialité"
+
+msgid "ID"
+msgstr "Identifiant"
+
+msgid "IDE|Commit"
+msgstr "Valider"
+
+msgid "IDE|Edit"
+msgstr "Modifier"
+
+msgid "IDE|Go back"
+msgstr "Revenir en arrière"
+
+msgid "IDE|Open in file view"
+msgstr "Ouvrir dans le visionneur de fichiers"
+
+msgid "IDE|Review"
+msgstr "Examiner"
+
+msgid "Identifier"
+msgstr "Identifiant"
+
+msgid "Identities"
+msgstr "Identités"
+
msgid "Identity provider single sign on URL"
-msgstr ""
+msgstr "URL d’authentification unique du fournisseur d’identité"
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr "Si désactivé, le niveau d’accès dépendra des autorisations de l’utilisa·teur·trice dans le projet."
+
+msgid "If enabled"
+msgstr "Si activé"
msgid "If enabled, access to projects will be validated on an external service using their classification label."
-msgstr "Si activé, l’accès aux projets sera validé sur un service externe en utilisant leur étiquette de classification."
+msgstr "Si activé, l’accès aux projets sera validé sur un service externe en se basant sur leurs étiquettes de classification respectives."
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 commit et demandes de fusion. %{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 already have files you can push them using the %{link_to_cli} below."
-msgstr "Si vous avez déjà des fichiers, vous pouvez les pousser à l’aide de la %{link_to_cli} ci-dessous."
+msgstr "Si vous avez déjà des fichiers, vous pouvez les pousser à l’aide de la %{link_to_cli} ciâ€dessous."
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
-msgstr "Si votre dépôt HTTP n’est pas accessible publiquement, ajoutez des informations d’authentification à l’URL: <code>https: //utilisateur:mot_de_passe@gitlab.company.com/group/project.git</code>."
+msgstr "Si votre dépôt HTTP n’est pas accessible publiquement, ajoutez des informations d’authentification à l’URL : <code>https://utilisateur:mot_de_passe@gitlab.company.com/group/project.git</code>."
+
+msgid "ImageDiffViewer|2-up"
+msgstr "côte à côte"
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr "en pelure d’oignon"
+
+msgid "ImageDiffViewer|Swipe"
+msgstr "par balayage"
msgid "Import"
msgstr "Importer"
+msgid "Import Projects from Gitea"
+msgstr "Importe des projets depuis Gitea"
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr "Importer tous les dépôts"
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr "Importation en cours"
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr "Importer des dépôts à partir de GitHub"
@@ -2352,22 +3625,34 @@ msgid "Import repository"
msgstr "Importer un dépôt"
msgid "ImportButtons|Connect repositories from"
-msgstr "Connecter des dépôts à partir de"
+msgstr ""
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr "Améliorer les tableaux de tickets avec Gitlab Entreprise Edition."
+msgstr ""
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
-msgstr "Améliorer la gestion des tickets avec les poids de ticket et GitLab Entreprise Edition."
+msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
-msgstr "Améliorer la recherche avec la Recherche Globale Avancée et GitLab Enterprise Edition."
+msgstr ""
-msgid "Install Runner on Kubernetes"
-msgstr "Installez un Exécuteur sur Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr "Inclure un accord sur les conditions générales d’utilisation et la politique de confidentialité que tous les utilisateurs doivent accepter."
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr "En ligne"
+
+msgid "Install GitLab Runner"
+msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "Installez un Exécuteur compatible avec l'intégration continue de GitLab"
+msgid "Install Runner on Kubernetes"
+msgstr "Installez un exécuteur sur Kubernetes"
msgid "Instance"
msgid_plural "Instances"
@@ -2375,28 +3660,34 @@ msgstr[0] "Instance"
msgstr[1] "Instances"
msgid "Instance does not support multiple Kubernetes clusters"
-msgstr "L’instance ne prend pas en charge plusieurs clusters Kubernetes"
+msgstr "L’instance ne prend pas en charge plusieurs grappes de serveurs Kubernetes"
msgid "Integrations"
msgstr "Intégrations"
+msgid "Integrations Settings"
+msgstr "Paramètres des intégrations"
+
msgid "Interested parties can even contribute by pushing commits if they want to."
-msgstr "Les parties intéressées peuvent même contribuer en poussant des commits si elles le souhaitent."
+msgstr "Les personnes intéressées peuvent même contribuer en poussant des commits si elles le souhaitent."
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
-msgstr "Interne - Le groupe ainsi que tous les projets internes sont accessibles pour n’importe quel·le utilisa·teur·trice connecté·e."
+msgstr "Interne — le groupe ainsi que tous les projets internes sont accessibles à n’importe quel·le utilisa·teur·trice connecté·e."
msgid "Internal - The project can be accessed by any logged in user."
-msgstr "Interne - Le projet est accessible pour n’importe quel·le utilisa·teur·trice connecté·e."
+msgstr "Interne — le projet est accessible à n’importe quel·le utilisa·teur·trice connecté·e."
msgid "Interval Pattern"
-msgstr "Schéma d’intervalle"
+msgstr "Modèle d’intervalle"
msgid "Introducing Cycle Analytics"
-msgstr "Introduction à l'analyseur de cycle"
+msgstr "Introduction à l’analyseur de cycle"
+
+msgid "Issue Boards"
+msgstr "Tableaux des tickets"
msgid "Issue board focus mode"
-msgstr "Mode focus du tableau de tickets"
+msgstr ""
msgid "Issue events"
msgstr "Événements du ticket"
@@ -2411,55 +3702,70 @@ msgid "Issues"
msgstr "Tickets"
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
-msgstr "Les tickets peuvent être des bugs, des tâches ou des idées à discuter. De plus, les tickets sont consultables et filtrables."
+msgstr "Les tickets peuvent être des bogues, des tâches ou des sujets de discussion. De plus, les tickets sont consultables et filtrables."
+
+msgid "Issues closed"
+msgstr "Tickets clos"
msgid "Jan"
-msgstr "Janv."
+msgstr "janv."
msgid "January"
-msgstr "Janvier"
+msgstr "janvier"
+
+msgid "Job"
+msgstr "Tâche"
+
+msgid "Job has been erased"
+msgstr "La tâche a été supprimée"
msgid "Jobs"
msgstr "Tâches"
msgid "Jul"
-msgstr "Juill."
+msgstr "juill."
msgid "July"
-msgstr "Juillet"
+msgstr "juillet"
msgid "Jun"
-msgstr "Juin"
+msgstr "juin"
msgid "June"
-msgstr "Juin"
+msgstr "juin"
msgid "Koding"
+msgstr "Koding"
+
+msgid "Koding Dashboard"
msgstr ""
msgid "Kubernetes"
msgstr "Kubernetes"
msgid "Kubernetes Cluster"
-msgstr "Cluster Kubernetes"
+msgstr "Grappe de serveurs Kubernetes"
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
-msgstr "Le temps de création du cluster Kubernetes dépasse le délai d’expiration ; %{timeout}"
+msgstr "Le temps de création de la grappe de serveurs Kubernetes dépasse le délai d’expiration : %{timeout}"
msgid "Kubernetes cluster integration was not removed."
-msgstr "L’intégration du cluster Kubernetes n’a pas été supprimée."
+msgstr "L’intégration de la grappe de serveurs Kubernetes n’a pas été supprimée."
msgid "Kubernetes cluster integration was successfully removed."
-msgstr "L’intégration du cluster Kubernetes a été supprimée avec succès."
+msgstr "L’intégration de la grappe de serveurs Kubernetes a été supprimée avec succès."
msgid "Kubernetes cluster was successfully updated."
-msgstr "Le cluster Kubernetes a été mis à jour avec succès."
+msgstr "La grappe de serveurs Kubernetes a été mise à jour avec succès."
msgid "Kubernetes configured"
msgstr "Kubernetes configuré"
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
-msgstr "L’intégration du service Kubernetes est obsolète. %{deprecated_message_content} vos clusters Kubernetes en utilisant la nouvelle page <a href=\"%{url}\"/>Clusters Kubernetes</a>"
+msgstr "L’intégration du service Kubernetes est obsolète. %{deprecated_message_content} vos grappes de serveurs Kubernetes en utilisant la nouvelle page <a href=\"%{url}\"/>Clusters Kubernetes</a>"
+
+msgid "LFS"
+msgstr ""
msgid "LFSStatus|Disabled"
msgstr "Désactivé"
@@ -2468,7 +3774,13 @@ msgid "LFSStatus|Enabled"
msgstr "Activé"
msgid "Label"
-msgstr "Label"
+msgstr "Étiquette"
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr "%{firstLabelName} +%{remainingLabelCount} de plus"
@@ -2476,25 +3788,31 @@ msgstr "%{firstLabelName} +%{remainingLabelCount} de plus"
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr "%{labelsString} et %{remainingLabelCount} de plus"
+msgid "LabelSelect|Labels"
+msgstr "Étiquettes"
+
msgid "Labels"
-msgstr "Labels"
+msgstr "Étiquettes"
msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr "Les labels peuvent être appliqués à %{features}. Des labels de groupe sont disponibles pour tout type de projet au sein du groupe."
+msgstr "Des étiquettes peuvent être appliquées à %{features}. Les étiquettes de groupe sont disponibles pour tout projet au sein du groupe."
msgid "Labels can be applied to issues and merge requests to categorize them."
-msgstr "Les labels peuvent être appliqués aux tickets et aux demandes de fusion pour les classer."
+msgstr "Les étiquettes peuvent être appliquées aux tickets et aux demandes de fusion afin de les catégoriser."
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr "Les étiquettes peuvent être appliquées aux tickets et aux demandes de fusion."
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
-msgstr "<span>Promouvoir le label</span> %{labelTitle} <span>en label de groupe ?</span>"
+msgstr "<span>Promouvoir l’étiquette</span> %{labelTitle} <span>en étiquette de groupe ?</span>"
msgid "Labels|Promote Label"
-msgstr "Promouvoir le label"
+msgstr "Promouvoir l’étiquette"
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "Le dernier %d jour"
-msgstr[1] "Les derniers %d jours"
+msgstr[1] "Les %d derniers jours"
msgid "Last Pipeline"
msgstr "Dernier pipeline"
@@ -2520,6 +3838,9 @@ msgstr "Vous avez poussé sur"
msgid "LastPushEvent|at"
msgstr "à"
+msgid "Latest changes"
+msgstr "Derniers changements"
+
msgid "Learn more"
msgstr "En savoir plus"
@@ -2530,10 +3851,10 @@ msgid "Learn more about protected branches"
msgstr "En savoir plus sur les branches protégées"
msgid "Learn more in the"
-msgstr "En apprendre plus dans le"
+msgstr "Apprenezâ€en plus dans la"
msgid "Learn more in the|pipeline schedules documentation"
-msgstr "documentation concernant la programmation de pipeline"
+msgstr "documentation sur la programmation de pipelines"
msgid "Leave"
msgstr "Quitter"
@@ -2544,18 +3865,36 @@ 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 ""
+
msgid "License"
msgstr "Licence"
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr "Liste"
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr "Lister vos dépôts GitHub"
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr "Chargement de l’IDE GitLab…"
+msgid "Loading..."
+msgstr "Chargement…"
+
msgid "Lock"
msgstr "Verrouiller"
@@ -2565,50 +3904,92 @@ msgstr "Verrouiller %{issuableDisplayName}"
msgid "Lock not found"
msgstr "Verrou non trouvé"
+msgid "Lock to current projects"
+msgstr "Verrouiller aux projets en cours"
+
msgid "Locked"
msgstr "Verrouillé"
msgid "Locked Files"
msgstr "Fichiers verrouillés"
+msgid "Locked to current projects"
+msgstr "Verrouillé aux projets en cours"
+
msgid "Locks give the ability to lock specific file or folder."
-msgstr "Les verrous donnent la possibilité de verrouiller un fichier ou un dossier spécifique."
+msgstr ""
-msgid "Login"
-msgstr "Se connecter"
+msgid "Logs"
+msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
-msgstr "Rendez votre équipe plus productive quel que soit son emplacement. GitLab Geo crée des miroirs en lecture seule de votre instance GitLab afin que vous puissiez réduire le temps nécessaire pour cloner et récupérer de gros dépôts."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr "Gestion des accès"
msgid "Manage all notifications"
msgstr "Gérer toutes les notifications"
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
-msgstr "Gérer les labels de groupe"
+msgstr "Gérer les étiquettes de groupe"
msgid "Manage labels"
-msgstr "Gérer les labels"
+msgstr "Gérer les étiquettes"
msgid "Manage project labels"
-msgstr "Gérer les labels de projet"
+msgstr "Gérer les étiquettes de projet"
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
-msgstr "Mars"
+msgstr "mars"
msgid "March"
-msgstr "Mars"
+msgstr "mars"
-msgid "Mark done"
+msgid "Mark todo as done"
msgstr "Marquer comme fait"
+msgid "Markdown enabled"
+msgstr "Markdown activé"
+
msgid "Maximum git storage failures"
-msgstr "Nombre maximum d’échecs du stockage git"
+msgstr "Nombre maximum d’échecs du stockage Git"
msgid "May"
-msgstr "Mai"
+msgstr "mai"
msgid "Median"
msgstr "Médian"
@@ -2619,50 +4000,95 @@ msgstr "Membres"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr "Demande de fusion"
+
+msgid "Merge Request:"
+msgstr "Demande de fusion :"
+
msgid "Merge Requests"
msgstr "Demandes de fusion"
+msgid "Merge Requests created"
+msgstr "Demandes de fusion créées"
+
msgid "Merge events"
msgstr "Événements de fusion"
msgid "Merge request"
msgstr "Demande de fusion"
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr "Demandes de fusion"
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "Les demandes de fusion permettent de proposer les modifications que vous avez apportées à un projet et de discuter de ces modifications avec les autres"
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr "Résoudre cette discussion avec un nouveau ticket"
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr "L’enregistrement du commentaire a échoué"
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr "Activer/désactiver les commentaires pour ce fichier"
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr "Échec de la mise à jour des discussions"
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr "Afficher le fichier au commit %{commitId}"
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr "Afficher le fichier remplacé au commit %{commitId}"
+
msgid "Merged"
msgstr "Fusionnée"
msgid "Messages"
msgstr "Messages"
+msgid "Metrics"
+msgstr "Métriques"
+
msgid "Metrics - Influx"
-msgstr "Métriques - Influx"
+msgstr "Métriques — Influx"
msgid "Metrics - Prometheus"
-msgstr "Métriques - Prometheus"
+msgstr "Métriques — Prometheus"
msgid "Metrics|Business"
-msgstr "Affaires"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
msgid "Metrics|Create metric"
-msgstr "Créer une métrique"
+msgstr ""
msgid "Metrics|Edit metric"
-msgstr "Modifier la métrique"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr "Environnement"
msgid "Metrics|For grouping similar metrics"
-msgstr "Pour regrouper des métriques similaires"
+msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
-msgstr "Etiquette de l’axe vertical du graphique. Habituellement, le type de l’unité étant cartographiée. L’axe horizontal (axe X) représente toujours le temps."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
msgid "Metrics|Legend label (optional)"
-msgstr "Légende de l’étiquette (facultatif)"
+msgstr ""
msgid "Metrics|Must be a valid PromQL query."
-msgstr "Doit être une requête PromQL valide."
+msgstr ""
msgid "Metrics|Name"
msgstr "Nom"
@@ -2670,8 +4096,11 @@ msgstr "Nom"
msgid "Metrics|New metric"
msgstr "Nouvelle métrique"
+msgid "Metrics|No deployed environments"
+msgstr "Aucun environnement déployé"
+
msgid "Metrics|Prometheus Query Documentation"
-msgstr "Documentation de requête Prometheus"
+msgstr "Documentation des requêtes Prometheus"
msgid "Metrics|Query"
msgstr "Requête"
@@ -2682,44 +4111,65 @@ msgstr "Réponse"
msgid "Metrics|System"
msgstr "Système"
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr "Une erreur s’est produite lors de la récupération des données d’environnement. Veuillez réessayer."
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr "Une erreur s’est produite lors de l’obtention des informations de déploiement."
+
+msgid "Metrics|There was an error getting environments information."
+msgstr "Une erreur s’est produite lors de l’obtention des informations d’environnement."
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr "Type"
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
-msgstr "Étiquette de l’unité"
+msgstr ""
msgid "Metrics|Used as a title for the chart"
-msgstr "Utilisé comme titre pour le graphique"
+msgstr ""
msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
-msgstr "Utilisé si la requête renvoie une seule série. Si elle renvoie plusieurs séries, leurs étiquettes de légende seront récupérées à partir de la réponse."
+msgstr ""
msgid "Metrics|Y-axis label"
-msgstr "Étiquette de l’axe Y"
+msgstr ""
msgid "Metrics|e.g. HTTP requests"
-msgstr "Par exemple, requêtes HTTP"
+msgstr ""
msgid "Metrics|e.g. Requests/second"
-msgstr "Par exemple, requêtes / seconde"
+msgstr ""
msgid "Metrics|e.g. Throughput"
-msgstr "Par exemple, débit"
+msgstr ""
msgid "Metrics|e.g. rate(http_requests_total[5m])"
-msgstr "Par exemple, rate(http_requests_total[5m])"
+msgstr ""
msgid "Metrics|e.g. req/sec"
-msgstr "Par exemple, req/sec"
+msgstr ""
msgid "Milestone"
msgstr "Jalon"
+msgid "Milestones"
+msgstr "Jalons"
+
msgid "Milestones|Delete milestone"
msgstr "Supprimer le jalon"
msgid "Milestones|Delete milestone %{milestoneTitle}?"
-msgstr "Supprimer le jalon %{milestoneTitle} ?"
+msgstr "Supprimer le jalon %{milestoneTitle} ?"
msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
msgstr "Impossible de supprimer le jalon %{milestoneTitle}"
@@ -2728,16 +4178,16 @@ msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "Le jalon %{milestoneTitle} est introuvable"
msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
-msgstr "Promouvoir %{milestoneTitle} à un jalon de groupe ?"
+msgstr "Promouvoir %{milestoneTitle} en tant que jalon de groupe ?"
msgid "Milestones|Promote Milestone"
msgstr "Promouvoir le jalon"
msgid "Milestones|This action cannot be reversed."
-msgstr "Cette action ne peut pas être annulée."
+msgstr ""
msgid "MissingSSHKeyWarningLink|add an SSH key"
-msgstr "ajouter une clé SSH"
+msgstr "ajouter une clef SSH"
msgid "Modal|Cancel"
msgstr "Annuler"
@@ -2746,7 +4196,16 @@ msgid "Modal|Close"
msgstr "Fermer"
msgid "Monitoring"
-msgstr "Surveillance"
+msgstr "Supervision"
+
+msgid "Months"
+msgstr "Mois"
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr "Plus d’actions"
msgid "More info"
msgstr "En savoir plus"
@@ -2757,6 +4216,9 @@ msgstr "Plus d’informations"
msgid "More information is available|here"
msgstr "ici"
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr "Déplacer"
@@ -2764,24 +4226,63 @@ msgid "Move issue"
msgstr "Déplacer le ticket"
msgid "Multiple issue boards"
-msgstr "Multiple tableaux de tickets"
+msgstr "Tableaux de tickets multiples"
+
+msgid "Name"
+msgstr "Nom"
msgid "Name new label"
-msgstr "Nommez le nouveau label"
+msgstr "Nommez la nouvelle étiquette"
+
+msgid "Name your individual key via a title"
+msgstr "Nommez votre clef personnelle avec un titre"
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr "Aide"
+
+msgid "Nav|Home"
+msgstr "Accueil"
+
+msgid "Nav|Sign In / Register"
+msgstr "Connexion / Inscription"
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr "Se déconnecter et se reconnecter avec un autre compte"
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr "Nouveau"
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr "Nouvelle identité"
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nouveau ticket"
msgstr[1] "Nouveaux tickets"
-msgid "New Kubernetes Cluster"
-msgstr "Nouveau cluster Kubernetes"
-
-msgid "New Kubernetes cluster"
-msgstr "Nouveau cluster Kubernetes"
+msgid "New Label"
+msgstr "Nouvelle étiquette"
msgid "New Pipeline Schedule"
-msgstr "Nouveau pipeline programmé"
+msgstr "Nouvelle planification de pipeline"
+
+msgid "New Snippet"
+msgstr "Nouvel extrait de code"
+
+msgid "New Snippets"
+msgstr "Nouveaux extraits de code"
msgid "New branch"
msgstr "Nouvelle branche"
@@ -2801,32 +4302,44 @@ msgstr "Nouveau fichier"
msgid "New group"
msgstr "Nouveau groupe"
+msgid "New identity"
+msgstr "Nouvelle identité"
+
msgid "New issue"
msgstr "Nouveau ticket"
msgid "New label"
-msgstr "Nouveau label"
+msgstr "Nouvelle étiquette"
msgid "New merge request"
msgstr "Nouvelle demande de fusion"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr "De nouveaux pipelines annuleront les anciens pipelines en attente sur la même branche"
+
msgid "New project"
msgstr "Nouveau projet"
msgid "New schedule"
-msgstr "Nouveau programme"
+msgstr "Nouvelle planification"
msgid "New snippet"
msgstr "Nouvel extrait de code"
msgid "New subgroup"
-msgstr "Nouveau sous-groupe"
+msgstr "Nouveau sousâ€groupe"
msgid "New tag"
-msgstr "Nouveau tag"
+msgstr "Nouvelle étiquette"
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr "Non"
msgid "No Label"
-msgstr "Pas de label"
+msgstr "Aucune étiquette"
msgid "No assignee"
msgstr "Aucune personne assignée"
@@ -2835,7 +4348,7 @@ msgid "No changes"
msgstr "Aucun changement"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
-msgstr "Aucune connexion n’a pu être établie avec un serveur Gitaly, veuillez vérifier vos logs !"
+msgstr "Aucune connexion n’a pu être établie avec un serveur Gitaly, veuillez vérifier votre journal !"
msgid "No due date"
msgstr "Aucune date d’échéance"
@@ -2846,20 +4359,53 @@ msgstr "Aucune estimation ou temps passé"
msgid "No file chosen"
msgstr "Aucun fichier sélectionné"
-msgid "No labels created yet."
-msgstr "Aucun label créé pour le moment."
+msgid "No files found"
+msgstr "Aucun fichier trouvé"
+
+msgid "No files found."
+msgstr "Aucun fichier trouvé."
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr "Aucune demande de fusion trouvée"
+
+msgid "No messages were logged"
+msgstr "Aucun message n’a été enregistré"
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
msgid "No repository"
-msgstr "Pas de dépôt"
+msgstr "Aucun dépôt"
msgid "No schedules"
-msgstr "Aucun programme"
+msgstr "Aucune planification"
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
msgid "None"
msgstr "Aucun·e"
msgid "Not allowed to merge"
-msgstr "Non autorisé•e à fusionner"
+msgstr "Non autorisé·e à fusionner"
msgid "Not available"
msgstr "Indisponible"
@@ -2871,25 +4417,28 @@ msgid "Not available for protected branches"
msgstr "Non disponible pour les branches protégées"
msgid "Not confidential"
-msgstr "Pas confidentiel•le"
+msgstr "Pas confidentiel·le"
msgid "Not enough data"
msgstr "Données insuffisantes"
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
-msgstr "Notez que la branche principale est automatiquement protégée. %{link_to_protected_branches}"
+msgstr "Notez que la branche principale « master » est automatiquement protégée. %{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr "Remarque : En tant qu’administrateur•rice, vous pouvez configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et de permettre la connexion de dépôts sans générer de jeton d’accès personnel."
+msgstr ""
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
-msgstr "Remarque : En tant qu’administrateur•rice, vous pouvez configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et de permettre l’importation de dépôts sans générer de jeton d’accès personnel."
+msgstr "Remarque : En tant qu’administra·teur·trice, vous pouvez configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et de permettre l’importation de dépôts sans générer de jeton d’accès personnel."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr "Remarque : Envisagez de demander à votre administrateur•rice GitLab de configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et de permettre la connexion des dépôts sans générer de jeton d’accès personnel."
+msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
-msgstr "Remarque : Envisagez de demander à votre administrateur•rice GitLab de configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et d’importer des dépôts sans générer de jeton d’accès personnel."
+msgstr "Remarque : Envisagez de demander à votre administra·teur·trice GitLab de configurer %{github_integration_link}, ce qui vous permettra de vous connecter via GitHub et d’importer des dépôts sans générer de jeton d’accès personnel."
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
msgid "Notification events"
msgstr "Événement de notifications"
@@ -2901,10 +4450,10 @@ msgid "NotificationEvent|Close merge request"
msgstr "Clore la demande de fusion"
msgid "NotificationEvent|Failed pipeline"
-msgstr "Pipeline échoué"
+msgstr "Pipeline en échec"
msgid "NotificationEvent|Merge merge request"
-msgstr "Fusionner le demande de fusion"
+msgstr "Fusionner la demande de fusion"
msgid "NotificationEvent|New issue"
msgstr "Nouveau ticket"
@@ -2922,7 +4471,7 @@ msgid "NotificationEvent|Reassign merge request"
msgstr "Réassigner la demande de fusion"
msgid "NotificationEvent|Reopen issue"
-msgstr "Ré-ouvrir le ticket"
+msgstr "Rouvrir le ticket"
msgid "NotificationEvent|Successful pipeline"
msgstr "Pipeline réussi"
@@ -2937,13 +4486,13 @@ msgid "NotificationLevel|Global"
msgstr "Global"
msgid "NotificationLevel|On mention"
-msgstr "En cas de mention"
+msgstr "En cas de citation"
msgid "NotificationLevel|Participate"
-msgstr "Participation"
+msgstr "Participer"
msgid "NotificationLevel|Watch"
-msgstr "Surveillé"
+msgstr "Surveiller"
msgid "Notifications"
msgstr "Notifications"
@@ -2955,40 +4504,67 @@ msgid "Notifications on"
msgstr "Notifications activées"
msgid "Nov"
-msgstr "Nov."
+msgstr "nov."
msgid "November"
-msgstr "Novembre"
+msgstr "novembre"
msgid "Number of access attempts"
-msgstr "Nombre de tentatives d'accès"
+msgstr "Nombre de tentatives d’accès"
msgid "OK"
msgstr "OK"
msgid "Oct"
-msgstr "Oct."
+msgstr "oct."
msgid "October"
-msgstr "Octobre"
+msgstr "octobre"
msgid "OfSearchInADropdown|Filter"
msgstr "Filtre"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
-msgstr "Une fois importés, les dépôts peuvent être mis en miroir via SSH. Lire plus %{ssh_link}"
+msgstr "Une fois importés, les dépôts peuvent être mis en miroir via SSH. Voir %{ssh_link}"
-msgid "Online IDE integration settings."
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
+msgstr "Paramètres d’intégration de l’EDI en ligne."
+
+msgid "Only comments from the following commit are shown below"
+msgstr "Seuls les commentaires du commit suivant sont affichés ciâ€dessous"
+
msgid "Only project members can comment."
msgstr "Seuls les membres du projet peuvent commenter."
+msgid "Oops, are you sure?"
+msgstr "Ouh là, êtesâ€vous sûr(e) ?"
+
msgid "Open"
msgstr "Ouvrir"
+msgid "Open in Xcode"
+msgstr "Ouvrir dans Xcode"
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
-msgstr "Ouvert"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr "Tickets ouverts"
msgid "OpenedNDaysAgo|Opened"
msgstr "Ouvert"
@@ -2996,17 +4572,35 @@ msgstr "Ouvert"
msgid "Opens in a new window"
msgstr "Ouvrir dans une nouvelle fenêtre"
+msgid "Operations"
+msgstr "Opérations"
+
+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 "Paramètres"
+msgstr "Options"
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr "Ou vous pouvez choisir l’une des couleurs suggérées ciâ€dessous"
+
+msgid "Other Labels"
+msgstr "Autres étiquettes"
+
+msgid "Other information"
+msgstr "Autres informations"
msgid "Otherwise it is recommended you start with one of the options below."
-msgstr "Sinon, il est recommandé de commencer avec l’une des options ci-dessous."
+msgstr "Sinon, il est recommandé de commencer avec l’une des options ciâ€dessous."
msgid "Outbound requests"
-msgstr ""
+msgstr "Requêtes sortantes"
msgid "Overview"
-msgstr "Vue d'ensemble"
+msgstr "Vue d’ensemble"
msgid "Owner"
msgstr "Propriétaire"
@@ -3027,16 +4621,34 @@ msgid "Pagination|« First"
msgstr "« Première"
msgid "Part of merge request changes"
-msgstr ""
+msgstr "Partie des modifications de la demande de fusion"
msgid "Password"
msgstr "Mot de Passe"
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr "Collez votre clef SSH publique, qui est habituellement située dans le fichier « ~/.ssh/id_rsa.pub » et commence par « ssh-rsa ». N’utilisez pas votre clef SSH privée !"
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr "Pause"
+
msgid "Pending"
msgstr "En attente"
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr "Par tâche. Si une tâche dépasse ce seuil, elle sera marquée comme ayant échoué"
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr "Effectuer des actions avancées telles que la modification du chemin d’accès, le transfert ou la suppression du groupe."
+
msgid "Performance optimization"
-msgstr ""
+msgstr "Optimisation des performances"
+
+msgid "Permissions"
+msgstr "Droits d’accès"
msgid "Personal Access Token"
msgstr "Jeton d’accès personnel"
@@ -3045,28 +4657,31 @@ msgid "Pipeline"
msgstr "Pipeline"
msgid "Pipeline Health"
-msgstr "Santé du Pipeline"
+msgstr "État de santé du pipeline"
msgid "Pipeline Schedule"
-msgstr "Programmation de pipeline"
+msgstr "Planification de pipeline"
msgid "Pipeline Schedules"
-msgstr "Programmations de pipeline"
+msgstr "Planifications de pipelines"
msgid "Pipeline quota"
-msgstr "Quota de pipeline"
+msgstr "Quota du pipeline"
+
+msgid "Pipeline triggers"
+msgstr "Déclencheurs de pipeline"
msgid "PipelineCharts|Failed:"
-msgstr "Échecs : "
+msgstr "Échecs :"
msgid "PipelineCharts|Overall statistics"
msgstr "Statistiques générales"
msgid "PipelineCharts|Success ratio:"
-msgstr "Ratio de succès : "
+msgstr "Taux de réussite :"
msgid "PipelineCharts|Successful:"
-msgstr "Succès :"
+msgstr "Réussites :"
msgid "PipelineCharts|Total:"
msgstr "Total :"
@@ -3087,7 +4702,7 @@ msgid "PipelineSchedules|Next Run"
msgstr "Prochaine exécution"
msgid "PipelineSchedules|None"
-msgstr "Aucune"
+msgstr "Aucun"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Indiquez une courte description"
@@ -3108,10 +4723,10 @@ msgid "Pipelines"
msgstr "Pipelines"
msgid "Pipelines charts"
-msgstr "Graphique des pipelines"
+msgstr "Graphiques des pipelines"
msgid "Pipelines for last month"
-msgstr "Pipelines du dernier mois"
+msgstr "Pipelines du mois dernier"
msgid "Pipelines for last week"
msgstr "Pipelines de la semaine dernière"
@@ -3126,19 +4741,19 @@ msgid "Pipelines|CI Lint"
msgstr "CI Lint"
msgid "Pipelines|Clear Runner Caches"
-msgstr "Vider les caches des Exécuteurs"
+msgstr "Vider les caches des exécuteurs"
msgid "Pipelines|Get started with Pipelines"
-msgstr "Premiers pas avec les Pipelines"
+msgstr "Premiers pas avec les pipelines"
msgid "Pipelines|Loading Pipelines"
msgstr "Chargement des pipelines"
msgid "Pipelines|Project cache successfully reset."
-msgstr "Réinitialisation réussie du cache de projet."
+msgstr "Réinitialisation du cache de projet réussie."
msgid "Pipelines|Run Pipeline"
-msgstr "Éxécuter un pipeline"
+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."
@@ -3147,88 +4762,151 @@ msgid "Pipelines|There are currently no %{scope} pipelines."
msgstr "Il n’y a actuellement pas de pipelines %{scope}."
msgid "Pipelines|There are currently no pipelines."
-msgstr "Il n’y a actuellement pas de pipelines."
+msgstr "Il n’y a actuellement aucun pipeline."
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "Ce projet n’est actuellement pas configuré pour exécuter des pipelines."
-msgid "Pipeline|Retry pipeline"
-msgstr "Relancer le pipeline"
+msgid "Pipeline|Create for"
+msgstr "Créer pour"
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
-msgstr "Relancer le pipeline #%{pipelineId} ?"
+msgid "Pipeline|Create pipeline"
+msgstr "Créer un pipeline"
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr "Nom de branche ou d’étiquette existant"
+
+msgid "Pipeline|Run Pipeline"
+msgstr "Exécuter le pipeline"
+
+msgid "Pipeline|Search branches"
+msgstr "Chercher des branches"
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr "Spécifier la valeur de la variable à utiliser lors de l’exécution. Les valeurs spécifiées dans %{settings_link} seront utilisées par défaut."
msgid "Pipeline|Stop pipeline"
msgstr "Arrêter le pipeline"
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
-msgstr "Arrêter le pipeline #%{pipelineId} ?"
+msgstr "Arrêter le pipeline numéro %{pipelineId} ?"
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
-msgstr "Vous êtes sur le point de relancer le pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
+msgstr "Variables"
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
msgstr "Vous êtes sur le point d’arrêter le pipeline %{pipelineId}."
msgid "Pipeline|all"
-msgstr "Tous"
+msgstr "tous"
msgid "Pipeline|success"
-msgstr "Succès"
+msgstr "réussi"
msgid "Pipeline|with stage"
-msgstr "avec l'étape"
+msgstr "avec l’étape"
msgid "Pipeline|with stages"
msgstr "avec les étapes"
+msgid "Plain diff"
+msgstr "Diff brut"
+
+msgid "Planned finish date"
+msgstr "Date de fin prévisionnelle"
+
+msgid "Planned start date"
+msgstr "Date de début prévisionnelle"
+
msgid "PlantUML"
-msgstr ""
+msgstr "PlantUML"
msgid "Play"
msgstr "Lancer"
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
-msgstr "Merci d’<a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">activer la facturation pour un de vos projets afin d’être en mesure de créer un cluster Kubernetes</a>, puis essayez à nouveau."
+msgid "Please accept the Terms of Service before continuing."
+msgstr "Veuillez accepter les conditions générales d’utilisation avant de continuer."
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr "Veuillez sélectionner au moins un filtre pour voir les résultats"
msgid "Please solve the reCAPTCHA"
msgstr "Veuillez résoudre le reCAPTCHA"
+msgid "Please try again"
+msgstr "Veuillez réessayer"
+
msgid "Please wait while we connect to your repository. Refresh at will."
-msgstr "Veuillez patienter pendant que nous nous connectons à votre dépôt. Actualisez à volonté."
+msgstr "Veuillez patienter pendant la connexion à votre dépôt. Actualisez à votre guise."
msgid "Please wait while we import the repository for you. Refresh at will."
-msgstr "Veuillez patienter pendant que nous importons le dépôt pour vous. Actualisez à volonté."
+msgstr "Veuillez patienter pendant l’importation de votre dépôt. Actualisez à votre guise."
msgid "Preferences"
msgstr "Préférences"
+msgid "Preferences|Navigation theme"
+msgstr "Thème de navigation"
+
msgid "Primary"
msgstr "Principal"
+msgid "Prioritize"
+msgstr "Prioriser"
+
+msgid "Prioritize label"
+msgstr "Prioriser l’étiquette"
+
+msgid "Prioritized Labels"
+msgstr "Étiquettes prioritaires"
+
+msgid "Prioritized label"
+msgstr "Étiquette prioritaire"
+
msgid "Private - Project access must be granted explicitly to each user."
-msgstr "Privé - L’accès au projet doit être autorisé explicitement pour chaque utilisa·teur·trice."
+msgstr "Privé — l’accès au projet doit être autorisé explicitement pour chaque utilisa·teur·trice."
msgid "Private - The group and its projects can only be viewed by members."
-msgstr "Privé - Le groupe ainsi que ses projets ne sont accessibles qu’à ses membres."
+msgstr "Privé — le groupe ainsi que ses projets ne sont accessibles qu’à ses membres."
msgid "Private projects can be created in your personal namespace with:"
-msgstr "Des projets privés peuvent être créés dans votre espace de noms personnel avec :"
+msgstr "Des projets privés peuvent être créés dans votre espace de noms personnel avec :"
msgid "Profile"
msgstr "Profil"
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
-msgstr "Compte programmé pour sa suppression."
+msgstr "Compte programmé pour suppression."
+
+msgid "Profiles|Add key"
+msgstr "Ajouter une clef"
+
+msgid "Profiles|Change username"
+msgstr "Changer le nom d’utilisateur·rice"
+
+msgid "Profiles|Current path: %{path}"
+msgstr "Chemin d’accès actuel : %{path}"
msgid "Profiles|Delete Account"
-msgstr "Supprimer le compte"
+msgstr "Supprimer un compte"
msgid "Profiles|Delete account"
msgstr "Supprimer le compte"
msgid "Profiles|Delete your account?"
-msgstr "Supprimer votre compte ?"
+msgstr "Supprimer votre compte ?"
msgid "Profiles|Deleting an account has the following effects:"
msgstr "Supprimer un compte aura les conséquences suivantes :"
@@ -3239,9 +4917,27 @@ msgstr "Mot de passe incorrect"
msgid "Profiles|Invalid username"
msgstr "Nom d’utilisateur incorrect"
+msgid "Profiles|Path"
+msgstr "Chemin d’accès"
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Saisissez votre %{confirmationValue} pour confirmer :"
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr "Mettre à jour le nom d’utilisateur·rice"
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr "Le changement de nom d’utilisateur·rice a échoué : %{message}"
+
+msgid "Profiles|Username successfully changed"
+msgstr "Changement de nom d’utilisa·teur·trice effectué"
+
msgid "Profiles|You don't have access to delete this user."
msgstr "Vous n’avez pas les autorisations suffisantes pour supprimer cet·te utilisa·teur·trice."
@@ -3251,26 +4947,38 @@ msgstr "Vous devez transférer la propriété ou supprimer ces groupes avant de
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Votre compte est actuellement propriétaire des groupes suivants :"
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr "votre compte"
msgid "Profiling - Performance bar"
-msgstr ""
+msgstr "Profilage — barre de performance"
msgid "Programming languages used in this repository"
msgstr "Langages de programmation utilisés dans ce dépôt"
+msgid "Progress"
+msgstr "Progression"
+
+msgid "Project"
+msgstr "Projet"
+
msgid "Project '%{project_name}' is in the process of being deleted."
-msgstr "Le projet “%{project_name}†est en train d’être supprimé."
+msgstr "Le projet « %{project_name} » est en cours de suppression."
msgid "Project '%{project_name}' queued for deletion."
-msgstr "Projet '%{project_name}' en attente de suppression."
+msgstr "Projet « %{project_name} » en attente de suppression."
msgid "Project '%{project_name}' was successfully created."
-msgstr "Projet '%{project_name}' créé avec succès."
+msgstr "Création du projet « %{project_name} » effectuée."
msgid "Project '%{project_name}' was successfully updated."
-msgstr "Projet '%{project_name}' mis à jour avec succès."
+msgstr "Mise à jour du projet « %{project_name} » effectuée."
+
+msgid "Project Badges"
+msgstr "Badges de projet"
msgid "Project access must be granted explicitly to each user."
msgstr "L’accès au projet doit être explicitement accordé à chaque utilisateur."
@@ -3279,50 +4987,44 @@ msgid "Project avatar"
msgstr "Avatar du projet"
msgid "Project avatar in repository: %{link}"
-msgstr "Avatar du project dans le dépôt : %{link}"
+msgstr "Avatar du projet dans le dépôt : %{link}"
msgid "Project details"
msgstr "Détails du projet"
msgid "Project export could not be deleted."
-msgstr "L'export du projet n'a pas pu être supprimé."
+msgstr "L’exportation du projet n’a pas pu être supprimée."
msgid "Project export has been deleted."
-msgstr "L'export du projet a été supprimé."
+msgstr "L’exportation du projet a été supprimée."
msgid "Project export link has expired. Please generate a new export from your project settings."
-msgstr "Le lien de l’export du projet a expiré. Merci de générer un nouvel export depuis les paramètres du projet."
+msgstr "Le lien de l’exportation du projet a expiré. Merci de générer une nouvelle exportation depuis les paramètres du projet."
msgid "Project export started. A download link will be sent by email."
-msgstr "L'export du projet a débuté. Un lien de téléchargement sera envoyé par courriel."
+msgstr "L’exportation du projet a débuté. Un lien de téléchargement sera envoyé par courriel."
+
+msgid "Project name"
+msgstr ""
msgid "ProjectActivityRSS|Subscribe"
msgstr "S’abonner"
msgid "ProjectCreationLevel|Allowed to create projects"
-msgstr "Autorisé•e à créer des projets"
+msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
-msgstr "Protection de création de projet par défaut"
+msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
-msgstr "Développeur•euse•s + Expert•e•s"
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
-msgid "ProjectCreationLevel|Masters"
-msgstr "Expert•e•s"
+msgid "ProjectCreationLevel|Maintainers"
+msgstr "Responsables"
msgid "ProjectCreationLevel|No one"
msgstr "Personne"
-msgid "ProjectFeature|Disabled"
-msgstr "Désactivé"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "Toute personne ayant accès"
-
-msgid "ProjectFeature|Only team members"
-msgstr "Seulement les membres de l'équipe"
-
msgid "ProjectFileTree|Name"
msgstr "Nom"
@@ -3332,32 +5034,41 @@ msgstr "Jamais"
msgid "ProjectLifecycle|Stage"
msgstr "Étape"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "Graphes"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
-msgstr "Contactez un administrateur pour modifier ce paramètre."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
-msgstr "Seules les commits signés peuvent être poussés sur ce dépôt."
+msgstr ""
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
-msgstr "Ce paramètre est appliqué au niveau du serveur et peut être modifié par un administrateur."
+msgstr ""
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
-msgstr "Ce paramètre est appliqué au niveau du serveur mais il a été modifié pour ce projet."
+msgstr ""
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
-msgstr "Ce paramètre s’appliquera à tous les projets à moins qu’un•e administrat•eur•rice ne le modifie."
+msgstr ""
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
-msgstr "Les utilisateurs peuvent uniquement pousser sur ce dépôt des commits qui ont été validés avec une de leurs adresses courriels vérifiées."
+msgstr ""
msgid "Projects"
msgstr "Projets"
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
-msgstr "Fréquemment visité"
+msgstr "Les plus consultés"
msgid "ProjectsDropdown|Loading projects"
msgstr "Chargement des projets"
@@ -3374,8 +5085,38 @@ msgstr "Un problème est survenu de notre côté."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Désolé, aucun projet ne correspond à votre recherche"
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr "Cette fonctionnalité requiert le support du localStorage par votre navigateur"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr "Heure"
msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr "%{exporters} avec %{metrics} ont été trouvés"
@@ -3390,31 +5131,31 @@ msgid "PrometheusService|Auto configuration"
msgstr "Configuration automatique"
msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
-msgstr "Déployer et configurer automatiquement Prometheus sur vos clusters pour surveiller les environnements de vos projets"
+msgstr "Déployer et configurer automatiquement Prometheus sur vos grappes de serveurs pour surveiller les environnements de vos projets"
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
-msgstr "Par défaut, Prometheus écoute sur 'http://localhost:9090'. Il n’est pas recommandé de changer l’adresse et le port par défaut car cela pourrait affecter ou entrer en conflit avec d'autres services fonctionnant sur le serveur GitLab."
+msgstr "Par défaut, Prometheus écoute sur « http://localhost:9090 ». Il n’est pas recommandé de changer l’adresse et le port par défaut car cela pourrait affecter ou entrer en conflit avec d’autres services fonctionnant sur le serveur GitLab."
msgid "PrometheusService|Common metrics"
msgstr "Métriques communes"
msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
-msgstr "Les métriques communes sont automatiquement surveillées en fonction d’une bibliothèque de métriques provenant d’exportateurs populaires."
+msgstr ""
msgid "PrometheusService|Custom metrics"
-msgstr "Métriques personnalisés"
+msgstr ""
msgid "PrometheusService|Finding and configuring metrics..."
msgstr "Recherche et configuration des métriques en cours…"
msgid "PrometheusService|Finding custom metrics..."
-msgstr "Recherche de métriques personnalisées…"
+msgstr ""
msgid "PrometheusService|Install Prometheus on clusters"
-msgstr "Installer Prometheus sur les clusters"
+msgstr "Installer Prometheus sur les grappes de serveurs"
msgid "PrometheusService|Manage clusters"
-msgstr "Gérer les clusters"
+msgstr "Gérer les grappes de serveurs"
msgid "PrometheusService|Manual configuration"
msgstr "Configuration manuelle"
@@ -3429,25 +5170,25 @@ msgid "PrometheusService|More information"
msgstr "Plus d’informations"
msgid "PrometheusService|New metric"
-msgstr "Nouvelle métrique"
+msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
-msgstr "URL de base de l’API Prometheus, comme http://prometheus.example.com/"
+msgstr "URL de base de l’API Prometheus, telle que http://prometheus.example.com/"
msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
-msgstr "Prometheus est géré automatiquement sur vos clusters"
+msgstr "Prometheus est géré automatiquement sur vos grappes de serveurs"
msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
-msgstr "Ces métriques ne seront surveillés qu’après votre premier déploiement dans un environnement"
+msgstr ""
msgid "PrometheusService|Time-series monitoring service"
-msgstr "Service de surveillance de séries temporelles"
+msgstr "Service de supervision de séries temporelles"
msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
-msgstr "Pour activer la configuration manuelle, désinstallez Prometheus de vos clusters"
+msgstr "Pour activer la configuration manuelle, désinstallez Prometheus de vos grappes de serveurs"
msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
-msgstr "Pour activer l’installation de Prometheus sur vos clusters, désactivez la configuration manuelle ci-dessous"
+msgstr "Pour activer l’installation de Prometheus sur vos grappes de serveurs, désactivez la configuration manuelle ciâ€dessous"
msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
msgstr "En attente de votre premier déploiement dans un environnement pour trouver des métriques communes"
@@ -3455,26 +5196,50 @@ msgstr "En attente de votre premier déploiement dans un environnement pour trou
msgid "Promote"
msgstr "Promouvoir"
-msgid "Promote to Group Label"
-msgstr "Promouvoir en label de groupe"
+msgid "Promote these project milestones into a group milestone."
+msgstr "Promouvoir ces jalons de projets en jalon de groupe."
msgid "Promote to Group Milestone"
msgstr "Promouvoir en jalon de groupe"
+msgid "Promote to group label"
+msgstr "Promouvoir en tant qu’étiquette de groupe"
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
-msgstr "Astuce :"
+msgstr "Astuce :"
+
+msgid "Provider"
+msgstr "Fournisseur"
+
+msgid "Pseudonymizer data collection"
+msgstr "Collecte de données Pseudonymizer"
msgid "Public - The group and any public projects can be viewed without any authentication."
-msgstr "Public - Le groupe ainsi que n’importe quel projet public est accessible sans authentification."
+msgstr "Public — le groupe ainsi que n’importe quel projet public est accessible sans authentification."
msgid "Public - The project can be accessed without any authentication."
-msgstr "Public - Le projet est accessible sans aucune authentification."
+msgstr "Public - le projet est accessible sans aucune authentification."
+
+msgid "Public pipelines"
+msgstr "Pipelines publics"
msgid "Push Rules"
-msgstr "Règles de poussée"
+msgstr "Règles de poussage Git"
msgid "Push events"
-msgstr "Évènements de poussée"
+msgstr "Événements de poussée"
msgid "Push project from command line"
msgstr "Pousser le projet en ligne de commande"
@@ -3483,7 +5248,16 @@ msgid "Push to create a project"
msgstr "Pousser pour créer un projet"
msgid "PushRule|Committer restriction"
-msgstr "Restriction du validateur"
+msgstr ""
+
+msgid "Pushed"
+msgstr "Poussé"
+
+msgid "Pushes"
+msgstr "Poussées Git"
+
+msgid "Quarters"
+msgstr "Trimestres"
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "Les actions rapides peuvent être utilisées dans la description des tickets et dans les zones de commentaire."
@@ -3491,24 +5265,30 @@ msgstr "Les actions rapides peuvent être utilisées dans la description des tic
msgid "Read more"
msgstr "Lire plus"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "LisezMoi"
msgid "Real-time features"
-msgstr ""
-
-msgid "RefSwitcher|Branches"
-msgstr "Branches"
-
-msgid "RefSwitcher|Tags"
-msgstr "Tags"
+msgstr "Fonctionnalités en temps réel"
msgid "Reference:"
-msgstr "Référence :"
+msgstr "Référence :"
+
+msgid "Refresh"
+msgstr "Actualiser"
msgid "Register / Sign In"
msgstr "Inscription / Connexion"
+msgid "Register and see your runners for this group."
+msgstr "Identifiezâ€vous et accédez à vos exécuteurs pour ce groupe."
+
+msgid "Register and see your runners for this project."
+msgstr "Enregistrer et afficher vos exécuteurs pour ce projet."
+
msgid "Registry"
msgstr "Registre"
@@ -3516,7 +5296,7 @@ msgid "Related Commits"
msgstr "Commits liés"
msgid "Related Deployed Jobs"
-msgstr "Tâches de déploiement liées"
+msgstr "Tâches déployées liées"
msgid "Related Issues"
msgstr "Tickets liés"
@@ -3539,35 +5319,59 @@ msgstr "Me le rappeler ultérieurement"
msgid "Remove"
msgstr "Supprimer"
+msgid "Remove Runner"
+msgstr "Supprimer l’exécuteur"
+
msgid "Remove avatar"
msgstr "Supprimer l’avatar"
+msgid "Remove priority"
+msgstr "Supprimer la priorité"
+
msgid "Remove project"
msgstr "Supprimer le projet"
msgid "Repair authentication"
msgstr "Réparer l’authentification"
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr "Dépôt par URL"
msgid "Repository"
msgstr "Dépôt"
-msgid "Repository has no locks."
-msgstr "Le dépôt n’a pas de verrous."
+msgid "Repository Settings"
+msgstr "Paramètres du dépôt"
-msgid "Repository maintenance"
+msgid "Repository URL"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository has no locks."
msgstr ""
+msgid "Repository maintenance"
+msgstr "Maintenance du dépôt"
+
+msgid "Repository mirror"
+msgstr "Miroir du dépôt"
+
msgid "Repository storage"
+msgstr "Stockage du dépôt"
+
+msgid "RepositorySettingsAccessLevel|Select"
msgstr ""
msgid "Request Access"
-msgstr "Demander l'accès"
+msgstr "Demander l’accès"
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr "Exiger que tous les utilisateurs acceptent les conditions générales d’utilisation et la politique de confidentialité quand ils accèdent à GitLab."
msgid "Reset git storage health information"
msgstr "Réinitialiser les informations de santé du stockage Git"
@@ -3578,11 +5382,29 @@ msgstr "Réinitialiser le jeton d’accès au bilan de santé"
msgid "Reset runners registration token"
msgstr "Réinitialiser le jeton d’inscription des exécuteurs"
+msgid "Resolve all discussions in new issue"
+msgstr "Résoudre toutes les discussions dans un nouveau ticket"
+
+msgid "Resolve conflicts on source branch"
+msgstr "Résoudre les conflits sur la branche source"
+
msgid "Resolve discussion"
msgstr "Résoudre la discussion"
-msgid "Response"
-msgstr "Réponse"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr "Reprendre"
+
+msgid "Retry"
+msgstr "Réessayer"
+
+msgid "Retry this job"
+msgstr "Relancer cette tâche"
+
+msgid "Retry verification"
+msgstr "Relancer la vérification"
msgid "Reveal value"
msgid_plural "Reveal values"
@@ -3595,26 +5417,47 @@ msgstr "Défaire ce commit"
msgid "Revert this merge request"
msgstr "Défaire cette demande de fusion"
+msgid "Review"
+msgstr "Examiner"
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
msgid "Reviewing"
-msgstr "Examiner"
+msgstr "Examen"
msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr "Examen (demande de fusion !%{mergeRequestId})"
+
+msgid "Revoke"
msgstr ""
msgid "Roadmap"
msgstr "Feuille de route"
msgid "Run CI/CD pipelines for external repositories"
-msgstr "Exécuter des pipelines CI / CD pour les dépôts externes"
+msgstr ""
+
+msgid "Runner token"
+msgstr "Jeton de l’exécuteur"
msgid "Runners"
msgstr "Exécuteurs"
+msgid "Runners API"
+msgstr "API des exécuteurs"
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr "Les exécuteurs peuvent être placés sur différents utilisateurs et serveurs, voire sur votre machine locale."
+
msgid "Running"
-msgstr "En cours"
+msgstr "En cours d’exécution"
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
msgid "SAML Single Sign On"
msgstr ""
@@ -3626,121 +5469,190 @@ msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from you
msgstr ""
msgid "SSH Keys"
-msgstr "Clés SSH"
+msgstr "Clefs SSH"
+
+msgid "SSL Verification"
+msgstr "Vérification SSL"
+
+msgid "Save"
+msgstr "Enregistrer"
+
+msgid "Save application"
+msgstr ""
msgid "Save changes"
msgstr "Enregistrer les modifications"
msgid "Save pipeline schedule"
-msgstr "Sauvegarder le pipeline programmé"
+msgstr "Sauvegarder la planification du pipeline"
msgid "Save variables"
msgstr "Enregistrer les variables"
msgid "Schedule a new pipeline"
-msgstr "Programmer un nouveau pipeline"
+msgstr "Planifier un nouveau pipeline"
msgid "Scheduled"
msgstr "Planifié"
msgid "Schedules"
-msgstr "Programmes"
+msgstr "Planifications"
msgid "Scheduling Pipelines"
-msgstr "Programmer des pipelines"
+msgstr "Planification des pipelines"
+
+msgid "Scope"
+msgstr ""
msgid "Scoped issue boards"
-msgstr "Tableaux de tickets avec portée limitée"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr "Faire défiler vers le bas"
+
+msgid "Scroll to top"
+msgstr "Faire défiler vers le haut"
msgid "Search"
msgstr "Rechercher"
+msgid "Search branches"
+msgstr "Rechercher des branches"
+
msgid "Search branches and tags"
msgstr "Rechercher dans les branches et les étiquettes"
+msgid "Search files"
+msgstr "Rechercher des fichiers"
+
+msgid "Search for projects, issues, etc."
+msgstr "Rechercher des projets, des tickets, etc."
+
+msgid "Search merge requests"
+msgstr "Rechercher des demandes de fusion"
+
msgid "Search milestones"
-msgstr "Recherche de jalons"
+msgstr "Rechercher des jalons"
msgid "Search project"
-msgstr "Recherche de projets"
+msgstr "Rechercher des projets"
msgid "Search users"
-msgstr "Recherche d’utilisateur•rice•s"
+msgstr "Rechercher des utilisa·teur·trice·s"
msgid "Seconds before reseting failure information"
-msgstr "Nombre de secondes avant de réinitialiser les informations d’échec"
+msgstr "Nombre de secondes avant réinitialisation des informations d’échec"
msgid "Seconds to wait for a storage access attempt"
-msgstr "Nombre de secondes d’attente pour un essai d'accès au stockage"
+msgstr "Nombre de secondes d’attente avant une tentative d’accès au stockage"
-msgid "Secret variables"
-msgstr "Variables secrètes"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
msgid "Security report"
-msgstr "Rapport de sécurité"
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr "Sélectionner"
msgid "Select Archive Format"
-msgstr "Sélectionnez le format de l'archive"
+msgstr "Sélectionnez le format de l’archive"
+
+msgid "Select a namespace to fork the project"
+msgstr "Sélectionnez un espace de noms afin de créer une divergence du projet"
msgid "Select a timezone"
msgstr "Sélectionnez un fuseau horaire"
msgid "Select an existing Kubernetes cluster or create a new one"
-msgstr "Sélectionnez un cluster Kubernetes existant ou créez-en un nouveau"
+msgstr "Sélectionnez une grappe de serveurs Kubernetes existante ou créezâ€en une nouvelle"
msgid "Select assignee"
msgstr "Sélectionner une personne assignée"
msgid "Select branch/tag"
-msgstr "Sélectionnez une branche / un tag"
+msgstr "Sélectionner une branche ou une étiquette"
+
+msgid "Select project"
+msgstr "Sélectionner un projet"
+
+msgid "Select project and zone to choose machine type"
+msgstr "Sélectionnez le projet et la zone afin de choisir le type de machine"
+
+msgid "Select project to choose zone"
+msgstr "Sélectionnez le projet afin de choisir la zone"
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr "Sélectionner une branche source"
msgid "Select target branch"
-msgstr "Sélectionnez une branche cible"
+msgstr "Sélectionner une branche cible"
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
msgid "Selective synchronization"
-msgstr "Synchronisation sélective"
+msgstr ""
msgid "Send email"
msgstr "Envoyer un courriel"
msgid "Sep"
-msgstr "Sept."
+msgstr "sept."
msgid "September"
-msgstr "Septembre"
+msgstr "septembre"
msgid "Server version"
msgstr "Version du serveur"
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr "Modèles de service"
msgid "Service URL"
-msgstr "URL du service"
+msgstr ""
msgid "Session expiration, projects limit and attachment size."
-msgstr "Expiration de la session, limite des projets et taille des pièces jointes."
+msgstr "Expiration de la session, restrictions des projets et taille des pièces jointes."
msgid "Set a password on your account to pull or push via %{protocol}."
-msgstr "Définissez un mot de passe pour votre compte pour pouvoir tirer ou pousser par %{protocol}."
+msgstr "Définissez un mot de passe pour votre compte afin de pouvoir récupérer ou pousser vos modification via %{protocol}."
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
-msgstr "Définir les valeurs par défaut et limiter les niveaux de visibilité. Configurer les sources d’importation et le protocole d’accès git."
+msgstr "Définissez les valeurs par défaut et restreignez les niveaux de visibilité. Configurez les sources d’importation et le protocole d’accès pour Git."
msgid "Set max session time for web terminal."
-msgstr ""
+msgstr "Définissez le temps maximal de la session pour le terminal Web."
msgid "Set notification email for abuse reports."
-msgstr ""
+msgstr "Définissez un courriel de notification pour les rapports d’abus."
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
-msgstr "Définir les exigences pour la connexion d’un utilisateur. Activer l’authentification obligatoire à deux facteurs."
+msgstr "Définissez les exigences pour la connexion d’un utilisateur. Activez l’authentification obligatoire à deux facteurs."
msgid "Set up CI/CD"
-msgstr "Configurer CI/CD"
+msgstr "Configuration CI/CD"
msgid "Set up Koding"
-msgstr "Mettre en place Koding"
+msgstr "Configurer Koding"
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
msgstr ""
@@ -3752,45 +5664,84 @@ msgid "Settings"
msgstr "Paramètres"
msgid "Setup a specific Runner automatically"
-msgstr "Configurer un Exécuteur spécifique automatiquement"
+msgstr "Configurer automatiquement un exécuteur spécifique"
+
+msgid "Share"
+msgstr "Partager"
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr "Exécuteurs partagés"
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
-msgstr "En réinitialisant les minutes du pipeline pour cet espace de noms, les minutes actuellement utilisées seront remises à zéro."
+msgstr ""
msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
-msgstr "Réinitialiser les minutes du pipeline"
+msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
-msgstr "Réinitialiser les minutes de pipeline utilisées"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
msgid "Show command"
msgstr "Afficher la commande"
+msgid "Show complete raw log"
+msgstr "Afficher le journal brut complet"
+
+msgid "Show latest version"
+msgstr "Afficher la dernière version"
+
+msgid "Show latest version of the diff"
+msgstr "Afficher la dernière version du diff"
+
msgid "Show parent pages"
msgstr "Afficher les pages parentes"
msgid "Show parent subgroups"
-msgstr "Afficher les sous-groupes parents"
+msgstr "Afficher les sousâ€groupes parents"
+
+msgid "Show whitespace changes"
+msgstr "Afficher les modifications des espaces"
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Affichage de %d évènement"
-msgstr[1] "Affichage de %d évènements"
+msgstr[1] "Affichage de %d événements"
-msgid "Sidebar|Change weight"
-msgstr "Changer le poids"
+msgid "Side-by-side"
+msgstr "côte à côte"
-msgid "Sidebar|No"
-msgstr "Non"
+msgid "Sidebar|Change weight"
+msgstr ""
msgid "Sidebar|None"
-msgstr "Aucun"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
msgid "Sidebar|Weight"
-msgstr "Poids"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr "Se déconnecter"
msgid "Sign-in restrictions"
msgstr "Restrictions de connexion"
@@ -3799,11 +5750,14 @@ msgid "Sign-up restrictions"
msgstr "Restrictions d’inscription"
msgid "Size and domain settings for static websites"
-msgstr "Paramètres de taille et de domaine pour les sites web statiques"
+msgstr "Paramètres de taille et de domaine pour les sites Web statiques"
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr "Plus lent, mais permet de s’assurer que l’espace de travail du projet est vierge, comme il clone le dépôt à partir de zéro pour chaque tâche"
+
msgid "Snippets"
msgstr "Extraits de code"
@@ -3813,24 +5767,39 @@ msgstr "Une erreur est survenue de notre côté"
msgid "Something went wrong on our end."
msgstr "Une erreur est survenue de notre côté."
+msgid "Something went wrong on our end. Please try again!"
+msgstr "Quelque chose s’est mal passé de notre côté. Veuillez réessayer."
+
msgid "Something went wrong when toggling the button"
msgstr "Une erreur s’est produite lors du basculement du bouton"
-msgid "Something went wrong while fetching Dependency Scanning."
-msgstr "Une erreur s’est produite lors de la recherche des dépendances."
+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 fetching assignees list"
+msgstr ""
-msgid "Something went wrong while fetching SAST."
-msgstr "Une erreur s’est produite lors de la récupération de la SAST."
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
msgid "Something went wrong while fetching the projects."
-msgstr "Une erreur s'est produite lors de la récupération des projets."
+msgstr "Une erreur s’est produite lors de la récupération des projets."
msgid "Something went wrong while fetching the registry list."
-msgstr "Une erreur s'est produite lors de la récupération de la liste du registre."
+msgstr "Une erreur s’est produite lors de la récupération de la liste du registre."
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr "Une erreur s’est produite lors de la « résolution » de la discussion. Veuillez réessayer."
msgid "Something went wrong. Please try again."
msgstr "Quelque chose s’est mal passé. Veuillez réessayer."
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr "Trier par"
@@ -3838,13 +5807,13 @@ msgid "SortOptions|Access level, ascending"
msgstr "Niveau d’accès, croissant"
msgid "SortOptions|Access level, descending"
-msgstr "Niveau d’accès, decroissant"
+msgstr "Niveau d’accès décroissant"
msgid "SortOptions|Created date"
msgstr "Date de création"
msgid "SortOptions|Due date"
-msgstr "Date d'échéance"
+msgstr "Date d’échéance"
msgid "SortOptions|Due later"
msgstr "Échéance lointaine"
@@ -3853,7 +5822,7 @@ msgid "SortOptions|Due soon"
msgstr "Échéance proche"
msgid "SortOptions|Label priority"
-msgstr "Priorité du label"
+msgstr "Priorité de l’étiquette"
msgid "SortOptions|Largest group"
msgstr "Taille de groupe"
@@ -3865,16 +5834,16 @@ msgid "SortOptions|Last created"
msgstr "Créé récemment"
msgid "SortOptions|Last joined"
-msgstr "Rejoint récemment"
+msgstr "Date de participation décroissante"
msgid "SortOptions|Last updated"
-msgstr "Mise à jour récemment"
+msgstr "Mis à jour récemment"
msgid "SortOptions|Least popular"
-msgstr "Moins populaire"
+msgstr "Popularité croissante"
msgid "SortOptions|Less weight"
-msgstr "Poids croissant"
+msgstr ""
msgid "SortOptions|Milestone"
msgstr "Jalon"
@@ -3886,10 +5855,10 @@ msgid "SortOptions|Milestone due soon"
msgstr "Jalon avec une échéance proche"
msgid "SortOptions|More weight"
-msgstr "Poids décroissant"
+msgstr ""
msgid "SortOptions|Most popular"
-msgstr "Populaire"
+msgstr "Popularité décroissante"
msgid "SortOptions|Name"
msgstr "Nom"
@@ -3901,16 +5870,16 @@ msgid "SortOptions|Name, descending"
msgstr "Nom, par ordre décroissant"
msgid "SortOptions|Oldest created"
-msgstr "Créé depuis longtemps"
+msgstr "Date de création croissante"
msgid "SortOptions|Oldest joined"
-msgstr "Rejoint depuis longtemps"
+msgstr "Ancienneté des participants croissante"
msgid "SortOptions|Oldest sign in"
-msgstr "Authentifié·e depuis longtemps"
+msgstr "Date d’inscription croissante"
msgid "SortOptions|Oldest updated"
-msgstr "Mise à jour depuis longtemps"
+msgstr "Date de mise à jour croissante"
msgid "SortOptions|Popularity"
msgstr "Popularité"
@@ -3919,7 +5888,7 @@ msgid "SortOptions|Priority"
msgstr "Priorité"
msgid "SortOptions|Recent sign in"
-msgstr "Authentifié·e récemment"
+msgstr "Date d’inscription décroissante"
msgid "SortOptions|Start later"
msgstr "Commence plus tard"
@@ -3928,13 +5897,13 @@ msgid "SortOptions|Start soon"
msgstr "Commence bientôt"
msgid "SortOptions|Weight"
-msgstr "Poids"
+msgstr ""
msgid "Source"
msgstr "Source"
msgid "Source (branch or tag)"
-msgstr "Source (branche ou tag)"
+msgstr "Source (branche ou étiquette)"
msgid "Source code"
msgstr "Code source"
@@ -3946,10 +5915,37 @@ msgid "Spam Logs"
msgstr "Journaux des messages indésirables"
msgid "Spam and Anti-bot Protection"
-msgstr ""
+msgstr "Protection antiâ€pourriel et antiâ€robot"
+
+msgid "Specific Runners"
+msgstr "Exécuteurs spécifiques"
msgid "Specify the following URL during the Runner setup:"
-msgstr "Spécifiez l’URL suivante lors de la configuration de l'Exécuteur :"
+msgstr "Spécifiez l’URL suivante lors de la configuration de l’exécuteur :"
+
+msgid "Squash commits"
+msgstr "Combiner (squash) les commits"
+
+msgid "Stage"
+msgstr "Étape"
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr "Marquer toutes les modifications comme une étape"
+
+msgid "Stage changes"
+msgstr "Changements d’étape"
+
+msgid "Staged"
+msgstr "Prêt à valider"
+
+msgid "Staged %{type}"
+msgstr "%{type} en étape"
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr "Mettez une étoile sur une étiquette pour en faire une étiquette prioritaire. Ordonnez les étiquettes prioritaires pour changer leurs priorités relatives en les glissant."
msgid "StarProject|Star"
msgstr "Mettre en favori"
@@ -3967,16 +5963,25 @@ msgid "Start a %{new_merge_request} with these changes"
msgstr "Créer une %{new_merge_request} avec ces changements"
msgid "Start the Runner!"
-msgstr "Démarrer l'Exécuteur !"
+msgstr "Démarrer l’exécuteur !"
msgid "Started"
msgstr "Démarré"
+msgid "Starts at (UTC)"
+msgstr "Démarre à (UTC)"
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
-msgstr "État"
+msgstr "État "
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr "Arrêter cet environnement"
msgid "Stopped"
msgstr "Arrêté"
@@ -3984,28 +5989,55 @@ msgstr "Arrêté"
msgid "Storage"
msgstr "Stockage"
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
-msgstr "Sous-groupes"
+msgstr "Sousâ€groupes"
+
+msgid "Submit as spam"
+msgstr "Soumettre comme indésirable"
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr "S’abonner"
+
+msgid "Subscribe at group level"
+msgstr "S’abonner au niveau du groupe"
+
+msgid "Subscribe at project level"
+msgstr "S’abonner au niveau du projet"
msgid "Switch branch/tag"
-msgstr "Changer de branche / tag"
+msgstr "Changer de branche ou d’étiquette"
-msgid "System"
-msgstr "Système"
+msgid "Sync information"
+msgstr "Synchroniser les informations"
msgid "System Hooks"
-msgstr "Crochets système"
+msgstr "« Hooks » système"
+
+msgid "System Info"
+msgstr ""
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
-msgstr[0] "Tag (%{tag_count})"
-msgstr[1] "Tags (%{tag_count})"
+msgstr[0] "Étiquette (%{tag_count})"
+msgstr[1] "Étiquettes (%{tag_count})"
msgid "Tags"
-msgstr "Tags"
+msgstr "Étiquettes"
+
+msgid "Tags:"
+msgstr "Étiquettes :"
msgid "TagsPage|Browse commits"
msgstr "Parcourir les commits"
@@ -4014,64 +6046,64 @@ msgid "TagsPage|Browse files"
msgstr "Parcourir les fichiers"
msgid "TagsPage|Can't find HEAD commit for this tag"
-msgstr "Impossible de trouver le commit HEAD pour ce tag"
+msgstr "Impossible de trouver le commit HEAD pour cette étiquette"
msgid "TagsPage|Cancel"
msgstr "Annuler"
msgid "TagsPage|Create tag"
-msgstr "Créer le tag"
+msgstr "Créer l’étiquette"
msgid "TagsPage|Delete tag"
-msgstr "Supprimer le tag"
+msgstr "Supprimer l’étiquette"
msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
-msgstr "La suppression du tag %{tag_name} ne peut être annulée. Êtes-vous sûr•e ?"
+msgstr "La suppression de l’étiquette %{tag_name} ne peut être annulée. Êtesâ€vous sûr·e ?"
msgid "TagsPage|Edit release notes"
msgstr "Modifier les notes de version"
msgid "TagsPage|Existing branch name, tag, or commit SHA"
-msgstr "Nom de branche, tag, ou SHA de commit existant"
+msgstr "Branche, étiquette ou condensat SHA d’un commit"
msgid "TagsPage|Filter by tag name"
-msgstr "Filtrer par nom de tag"
+msgstr "Filtrer par nom d’étiquette"
msgid "TagsPage|New Tag"
-msgstr "Nouveau tag"
+msgstr "Nouvelle étiquette"
msgid "TagsPage|New tag"
-msgstr "Nouveau tag"
+msgstr "Nouvelle étiquette"
msgid "TagsPage|Optionally, add a message to the tag."
-msgstr "Éventuellement, ajoutez un message au tag."
+msgstr "Vous pouvez éventuellement ajouter un message de commentaire à l’étiquette."
msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
-msgstr "Éventuellement, ajouter des notes de version pour le tag. Elles seront stockées dans la base de données de GitLab et affichées sur la page des tags."
+msgstr "Vous pouvez éventuellement ajouter des notes de version à l’étiquette. Elles seront stockées dans la base de données de GitLab et affichées sur la page des étiquettes."
msgid "TagsPage|Release notes"
msgstr "Notes de version"
msgid "TagsPage|Repository has no tags yet."
-msgstr "Le dépôt n‘a pas de tags pour le moment."
+msgstr "Le dépôt n’a pour le moment aucune étiquette."
msgid "TagsPage|Sort by"
msgstr "Trier par"
msgid "TagsPage|Tags"
-msgstr "Tags"
+msgstr "Étiquettes"
msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
-msgstr "Les tags permettent de marquer des validations spécifiques commes importantes dans l‘historique du project"
+msgstr "Les étiquettes permettent de marquer des moments spécifiques de l’historique du projet comme étant importants"
msgid "TagsPage|This tag has no release notes."
-msgstr "Ce tag n‘a pas de notes de version."
+msgstr "Cette étiquette n’a pas de notes de version."
msgid "TagsPage|Use git tag command to add a new one:"
-msgstr "Utilisez la commande git tag pour en ajouter un nouveau :"
+msgstr "Utilisez la commande « git tag » pour en rajouter :"
-msgid "TagsPage|Write your release notes or drag files here..."
-msgstr "Écrivez les notes de version ou faîtes glisser des fichiers ici…"
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr "Rédigez vos notes de version ou faites glisser des fichiers ici…"
msgid "TagsPage|protected"
msgstr "protégé"
@@ -4085,71 +6117,86 @@ msgstr "Branche cible"
msgid "Team"
msgstr "Équipe"
+msgid "Terms of Service Agreement and Privacy Policy"
+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 "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
-msgstr "Merci ! Ne plus afficher ce message"
+msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
-msgstr "La Recherche Globale Avancée de Gitlab est un outils puissant qui vous fait gagner du temps. Au lieu de créer du code similaire et perdre du temps, vous pouvez maintenant chercher dans le code d'autres équipes pour vous aider sur votre projet."
+msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
-msgstr "Le suivi des tickets est l’endroit où ajouter des éléments à améliorer ou à résoudre dans un projet"
+msgstr "Le système de suivi est un endroit où l’on peut ouvrir un ticket pour signaler des choses à améliorer ou des dysfonctionnements à résoudre dans un projet"
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
-msgstr "Le suivi des tickets est l’endroit où ajouter des éléments à améliorer ou à résoudre dans un projet. Vous pouvez vous inscrire ou vous connecter pour créer des tickets pour ce projet."
+msgstr "Le système de suivi est un endroit où l’on peut ouvrir un ticket pour signaler des choses à améliorer ou des dysfonctionnements à résoudre dans un projet. Vous pouvez vous inscrire ou vous connecter pour créer des tickets de suivi pour ce projet."
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 X509 à utiliser lorsque le protocole TLS est requis pour communiquer avec le service d’autorisation externe. Si ce champ est vide, le certificat du serveur est utilisé lors de l’accès via HTTPS."
+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 "L’étape de développement montre le temps entre le premier commit et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion."
+msgstr "Le présentoir de code affiche le temps entre le premier commit et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion."
msgid "The collection of events added to the data gathered for that stage."
-msgstr "L’ensemble d’évènements ajoutés aux données récupérées pour cette étape."
+msgstr "L’ensemble d’événements ajoutés aux données recueillies pour cette étape."
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
-msgstr "La connexion expirera après %{timeout}. Pour les dépôts qui prennent plus de temps, utilisez une combinaison cloner / pousser."
+msgstr ""
msgid "The fork relationship has been removed."
-msgstr "La relation de fourche a été supprimée."
+msgstr "La relation de divergence a été supprimée."
msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
-msgstr "L’importation expirera après %{timeout}. Pour les dépôts qui prennent plus de temps, utilisez une combinaison clone / push."
+msgstr "L’importation expirera après %{timeout}. Pour les dépôts qui prennent plus de temps, utilisez une combinaison de clone et push."
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 "L'étape des tickets montre le temps nécessaire entre la création d'un ticket et son assignation à un jalon, ou son ajout à une liste d'un tableau de tickets. Commencez par créer des tickets pour voir des données pour cette étape."
+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 maximum file size allowed is 200KB."
-msgstr "La taille de fichier maximale autorisée est de 200 Ko."
+msgstr "La taille maximale autorisée pour un fichier est de 200 Kio."
msgid "The number of attempts GitLab will make to access a storage."
-msgstr "Le nombre de tentatives que GitLab va effectuer pour accéder au stockage."
+msgstr "Le nombre de tentatives que GitLab va effectuer pour accéder à un stockage."
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr "Nombre d’échecs avant que GitLab n’empêche tout accès au stockage. Ce nombre d’échecs peut être réinitialisé dans l’interface d’administration : %{link_to_health_page} ou en suivant le %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
-msgstr "La phrase secrète est requise pour déchiffrer la clé privée. Ceci est facultatif et la valeur est cryptée à l’arrêt."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr "Le chemin d’accès au fichier de configuration de l’intégration continue. Par défaut, <code>.gitlab-ci.yml</code>"
msgid "The phase of the development lifecycle."
-msgstr "Les étapes du cycle de développement."
+msgstr "La phase du cycle de développement."
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 "L’étape de planification montre le temps entre l’étape précédente et l’envoi de votre premier commit. Ce temps sera automatiquement ajouté quand vous pousserez votre premier commit."
msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
-msgstr "La clé privée à utiliser lorsqu’un certificat client est fourni. Cette valeur est cryptée à l’arrêt."
+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 "L’étape de mise en production montre le temps nécessaire entre la création d’un ticket et le déploiement du code en production. Les données seront automatiquement ajoutées une fois que vous aurez complété le cycle complet, depuis l’idée jusqu’à la mise en production."
msgid "The project can be accessed by any logged in user."
-msgstr "Votre projet peut être accédé par n’importe quel utilisa·teur·trice authentifié·e."
+msgstr "Votre projet est accessible à n’importe quel·le utilisa·teur·trice authentifié·e."
msgid "The project can be accessed without any authentication."
-msgstr "Votre projet peut être accédé sans aucune authentification."
+msgstr "Votre projet est accessible sans aucune authentification."
+
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
msgid "The repository for this project does not exist."
-msgstr "Le dépôt pour ce projet n'existe pas."
+msgstr "Le dépôt de ce projet n’existe pas."
msgid "The repository for this project is empty"
msgstr "Le dépôt de ce projet est vide"
@@ -4158,44 +6205,53 @@ msgid "The repository must be accessible over <code>http://</code>, <code>https:
msgstr "Le dépôt doit être accessible via <code>http://</code>, <code>https://</code> ou <code>git://</code>."
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 "L’étape d’évaluation montre le temps entre la création de la demande de fusion et la fusion effective de celle-ci. Ces données seront automatiquement ajoutées après que vous ayez fusionné votre première demande de fusion."
+msgstr "L’étape d’évaluation montre le temps entre la création de la demande de fusion et la fusion effective de celleâ€ci. Ces données seront automatiquement ajoutées après que vous aurez fusionné votre première demande de fusion."
msgid "The roadmap shows the progress of your epics along a timeline"
-msgstr "La feuille de route montre la progression de vos épopées dans le temps"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+msgstr "Le jeton sécurisé utilisé par l’exécuteur pour vérifier (checkout) le projet"
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 "L’étape de pré-production indique le temps entre la fusion de la DF et le déploiement du code dans l’environnent de production. Les données seront automatiquement ajoutées une fois que vous déploierez en production pour la première fois."
+msgstr "L’étape de pré-production indique le temps entre l’acceptation d’une demande fusion et le déploiement du code dans l’environnent de production. Les données seront automatiquement ajoutées lorsque vous aurez fait votre première mise en production."
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 le CI de GitLab met pour exécuter chaque pipeline liés à la demande de fusion. Les données seront automatiquement ajoutées après que votre premier pipeline s’achèvera."
+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."
msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
msgstr "Délai en secondes pendant lequel GitLab gardera les informations d’échec. Si aucun échec ne survient pendant ce délai, les informations de ce montage seront réinitialisées."
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
-msgstr "Temps en secondes pendant lequel GitLab essaiera d’accéder au stockage. Après ce délai, une erreur d’expiration d’attente sera déclenchée."
+msgstr "Temps en secondes pendant lequel GitLab essaiera d’accéder au stockage. Après ce délai, une erreur d’expiration du délai d’attente sera déclenchée."
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
-msgstr "Le temps en secondes entre les vérifications de stockage. GitLab ne débute pas de nouvelle vérification si une vérification est déjà en cours."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "Le temps pris par chaque entrée récoltée durant cette étape."
+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>:</code>. 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 ""
+
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 "La valeur située au point médian d’une série de valeur observée. C.à.d., entre 3, 5, 9, le médian est 5. Entre 3, 5, 7, 8, le médian est (5+7)/2 = 6."
+msgstr "La valeur située au point médian d’une série de valeur observée. Par exemple., entre 3, 5 et 9, le médian est 5. Entre 3, 5, 7 et 8, le médian est (5+7)/2 = 6."
msgid "There are no issues to show"
msgstr "Il n’y a aucun ticket à afficher"
+msgid "There are no labels yet"
+msgstr "Il n’y a pas encore d’étiquette"
+
msgid "There are no merge requests to show"
msgstr "Il n’y a aucune demande de fusion à afficher"
msgid "There are problems accessing Git storage: "
msgstr "Il y a des difficultés à accéder aux données Git : "
-msgid "There was an error loading results"
-msgstr "Une erreur s’est produite lors du chargement des résultats"
-
msgid "There was an error loading users activity calendar."
msgstr "Une erreur s’est produite lors du chargement du calendrier d’activité des utilisateurs."
@@ -4203,23 +6259,50 @@ msgid "There was an error saving your notification settings."
msgstr "Une erreur s’est produite lors de l’enregistrement de vos paramètres de notification."
msgid "There was an error subscribing to this label."
-msgstr "Une erreur s’est produite lors de l’abonnement à ce label."
+msgstr "Une erreur s’est produite lors de l’abonnement à cette étiquette."
msgid "There was an error when reseting email token."
msgstr "Une erreur s’est produite lors de la réinitialisation du jeton de courriel."
msgid "There was an error when subscribing to this label."
-msgstr "Une erreur s’est produite lors de l’abonnement à ce label."
+msgstr "Une erreur s’est produite lors de l’abonnement à cette étiquette."
msgid "There was an error when unsubscribing from this label."
-msgstr "Une erreur s’est produite lors de la désinscription à ce label."
+msgstr "Une erreur s’est produite lors de la désinscription à cette étiquette."
+
+msgid "They can be managed using the %{link}."
+msgstr "Ils peuvent être gérés en utilisant %{link}."
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr "Cette instance de GitLab ne fournit aucun exécuteur partagé pour le moment. Les administra·teur·trice·s de l’instance peuvent enregistrer des exécuteurs partagés dans la zone d’administration."
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
-msgid "This board\\'s scope is reduced"
-msgstr "La portée de ce tableau est limitée"
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr "Ce diff est replié."
msgid "This directory"
msgstr "Ce répertoire"
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr "Ce groupe ne fournit pas encore d’exécuteurs de groupe."
+
msgid "This is a confidential issue."
msgstr "Ce ticket est confidentiel."
@@ -4236,43 +6319,67 @@ msgid "This issue is locked."
msgstr "Ce ticket est verrouillé."
msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
-msgstr "Cette tâche dépend de l’utilisateur•rice pour déclencher son processus. Elles sont souvent utilisées pour déployer du code dans des environnements de production"
+msgstr "Cette tâche dépend de l’utilisa·teur·trice pour déclencher son processus. Elles sont souvent utilisées pour déployer du code dans des environnements de production"
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
-msgstr "Cette tâche dépend des tâches en amont qui doivent réussir pour que celle-ci soit déclenchée"
+msgstr "Cette tâche dépend des tâches en amont qui doivent réussir pour que celleâ€ci soit déclenchée"
+
+msgid "This job does not have a trace."
+msgstr "Cette tâche n’a pas de trace."
+
+msgid "This job has been canceled"
+msgstr "Cette tâche a été annulée"
+
+msgid "This job has been skipped"
+msgstr "Cette tâche a été sautée"
msgid "This job has not been triggered yet"
msgstr "Cette tâche n’a pas encore été déclenchée"
msgid "This job has not started yet"
-msgstr "Cete tâche n’a pas encore commencée"
+msgstr "Cette tâche n’a pas encore commencée"
msgid "This job is in pending state and is waiting to be picked by a runner"
-msgstr "Cette tâche est en attente et attend d’être choisie par un exécuteur"
+msgstr "Cette tâche est en attente d’être choisie par un exécuteur"
msgid "This job requires a manual action"
msgstr "Cette tâche nécessite une action manuelle"
msgid "This means you can not push code until you create an empty repository or import existing one."
-msgstr "Cela signifie que vous ne pouvez pas pousser du code tant que vous n’avez pas créé un dépôt vide, ou que vous n’avez pas importé un dépôt existant."
+msgstr "Cela signifie que vous ne pouvez pas pousser du code tant que vous n’avez pas créé un dépôt vide ou que vous n’avez pas importé un dépôt existant."
msgid "This merge request is locked."
msgstr "Cette demande de fusion est verrouillée."
+msgid "This option is disabled while you still have unstaged changes"
+msgstr "Cette option est désactivée tant que vous avez des changements non indexés"
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
-msgstr "Cette page n’est pas disponible car vous n’êtes pas autorisé à lire des informations dans plusieurs projets."
+msgstr "Cette page n’est pas disponible car vous n’êtes pas autorisé à lire des informations à travers de multiples projets."
+
+msgid "This page will be removed in a future release."
+msgstr "Cette page sera supprimée dans une version ultérieure."
msgid "This project"
msgstr "Ce projet"
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr "Ce projet n’appartient pas à un groupe et ne peut donc pas faire appel à un exécuteur de groupe."
+
msgid "This repository"
msgstr "Ce dépôt"
+msgid "This source diff could not be displayed because it is too large."
+msgstr "Ce diff n’a pas pu être affiché car il est trop grand."
+
+msgid "This user has no identities"
+msgstr "Cet utilisa·teur·trice n’a aucune identité"
+
msgid "This will delete the custom metric, Are you sure?"
-msgstr "Cela supprimera la métrique personnalisée, êtes-vous sûr•e ?"
+msgstr ""
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr "Ces emails deviennent automatiquement des tickets (les commentaires étant extrait de la conversation par email) répertoriés ici."
+msgstr ""
msgid "Time before an issue gets scheduled"
msgstr "Temps avant qu’un ticket ne soit planifié"
@@ -4281,13 +6388,16 @@ msgid "Time before an issue starts implementation"
msgstr "Temps avant que la résolution du ticket ne débute"
msgid "Time between merge request creation and merge/close"
-msgstr "Temps entre la création d'une demande de fusion et sa fusion/clôture"
+msgstr "Temps entre la création d’une demande de fusion et sa fusion/clôture"
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
-msgstr "Temps en secondes où GitLab attendra une réponse du service externe. Si le service ne répond pas à temps, l’accès sera refusé."
+msgid "Time remaining"
+msgstr "Temps restant"
+
+msgid "Time spent"
+msgstr "Temps passé"
msgid "Time tracking"
msgstr "Suivi du temps"
@@ -4299,7 +6409,7 @@ msgid "TimeTrackingEstimated|Est"
msgstr "Est"
msgid "TimeTracking|Estimated:"
-msgstr "Estimé :"
+msgstr "Estimé :"
msgid "TimeTracking|Spent"
msgstr "Passé"
@@ -4310,26 +6420,32 @@ msgstr "il y a %s jours"
msgid "Timeago|%s days remaining"
msgstr "Il reste %s jours"
+msgid "Timeago|%s hours ago"
+msgstr "il y a %s heures"
+
msgid "Timeago|%s hours remaining"
msgstr "Il reste %s heures"
msgid "Timeago|%s minutes ago"
-msgstr "Il y a %s minutes"
+msgstr "il y a %s minutes"
msgid "Timeago|%s minutes remaining"
msgstr "Il reste %s minutes"
msgid "Timeago|%s months ago"
-msgstr "Il y a %s mois"
+msgstr "il y a %s mois"
msgid "Timeago|%s months remaining"
msgstr "Il reste %s mois"
+msgid "Timeago|%s seconds ago"
+msgstr "il y a %s secondes"
+
msgid "Timeago|%s seconds remaining"
msgstr "Il reste %s secondes"
msgid "Timeago|%s weeks ago"
-msgstr "Il y a %s semaines"
+msgstr "il y a %s semaines"
msgid "Timeago|%s weeks remaining"
msgstr "Il reste %s semaines"
@@ -4340,92 +6456,92 @@ msgstr "il y a %s ans"
msgid "Timeago|%s years remaining"
msgstr "Il reste %s ans"
+msgid "Timeago|1 day ago"
+msgstr "il y a une journée"
+
msgid "Timeago|1 day remaining"
msgstr "Il reste un jour"
+msgid "Timeago|1 hour ago"
+msgstr "il y a une heure"
+
msgid "Timeago|1 hour remaining"
msgstr "Il reste une heure"
+msgid "Timeago|1 minute ago"
+msgstr "il y a une minute"
+
msgid "Timeago|1 minute remaining"
msgstr "Il reste une minute"
+msgid "Timeago|1 month ago"
+msgstr "il y a un mois"
+
msgid "Timeago|1 month remaining"
msgstr "Il reste un mois"
+msgid "Timeago|1 week ago"
+msgstr "il y a une semaine"
+
msgid "Timeago|1 week remaining"
msgstr "Il reste une semaine"
+msgid "Timeago|1 year ago"
+msgstr "il y a un an"
+
msgid "Timeago|1 year remaining"
msgstr "Il reste un an"
msgid "Timeago|Past due"
msgstr "En retard"
-msgid "Timeago|a day ago"
-msgstr "Il y a un jour"
-
-msgid "Timeago|a month ago"
-msgstr "Il y a un mois"
-
-msgid "Timeago|a week ago"
-msgstr "Il y a une semaine"
-
-msgid "Timeago|a year ago"
-msgstr "Il y a un an"
-
-msgid "Timeago|about %s hours ago"
-msgstr "Il y a environ %s heures"
-
-msgid "Timeago|about a minute ago"
-msgstr "Il y a environ une minute"
-
-msgid "Timeago|about an hour ago"
-msgstr "Il y a environ une heure"
-
msgid "Timeago|in %s days"
-msgstr "Dans %s jours"
+msgstr "dans %s jours"
msgid "Timeago|in %s hours"
-msgstr "Dans %s heures"
+msgstr "dans %s heures"
msgid "Timeago|in %s minutes"
-msgstr "Dans %s minutes"
+msgstr "dans %s minutes"
msgid "Timeago|in %s months"
-msgstr "Dans %s mois"
+msgstr "dans %s mois"
msgid "Timeago|in %s seconds"
-msgstr "Dans %s secondes"
+msgstr "dans %s secondes"
msgid "Timeago|in %s weeks"
-msgstr "Dans %s semaines"
+msgstr "dans %s semaines"
msgid "Timeago|in %s years"
-msgstr "Dans %s années"
+msgstr "dans %s années"
msgid "Timeago|in 1 day"
-msgstr "Dans 1 jour"
+msgstr "dans 1 jour"
msgid "Timeago|in 1 hour"
-msgstr "Dans 1 heure"
+msgstr "dans 1 heure"
msgid "Timeago|in 1 minute"
-msgstr "Dans 1 minute"
+msgstr "dans 1 minute"
msgid "Timeago|in 1 month"
-msgstr "Dans 1 mois"
+msgstr "dans 1 mois"
msgid "Timeago|in 1 week"
-msgstr "Dans 1 semaine"
+msgstr "dans 1 semaine"
msgid "Timeago|in 1 year"
-msgstr "Dans 1 an"
+msgstr "dans 1 an"
+
+msgid "Timeago|just now"
+msgstr "à l’instant"
-msgid "Timeago|in a while"
-msgstr "il y a longtemps"
+msgid "Timeago|right now"
+msgstr "immédiatement"
-msgid "Timeago|less than a minute ago"
-msgstr "il y a moins d'une minute"
+msgid "Timeout"
+msgstr "Délai d’attente"
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4449,14 +6565,23 @@ msgstr "Titre"
msgid "To GitLab"
msgstr "Vers GitLab"
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr "Afin d’ajouter une clef SSH, vous devez soit %{generate_link_start}en génèrer une%{link_end}, soit utiliser une %{existing_link_start}clef existante%{link_end}."
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr "Pour connecter les dépôts GitHub, vous pouvez utiliser un %{personal_access_token_link}. Lorsque vous créez votre jeton d’accès, vous devrez sélectionner le champ <code>repo</code>, afin que nous puissions afficher une liste de vos dépôts publics et privés qui sont disponibles pour se connecter."
+msgstr ""
msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
-msgstr "Pour connecter les dépôts GitHub, vous devez d’abord autoriser GitLab à accéder à la liste de vos dépôts GitHub :"
+msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
-msgstr "Pour connecter un dépôt SVN, consultez %{svn_link}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "Pour importer les dépôts GitHub, vous pouvez utiliser un %{personal_access_token_link}. Lorsque vous créez votre jeton d’accès, vous devrez sélectionner le champ <code>repo</code>, afin que nous puissions afficher une liste de vos dépôts publics et privés qui sont disponibles pour être importés."
@@ -4467,21 +6592,45 @@ msgstr "Pour importer des dépôts GitHub, vous devez d’abord autoriser GitLab
msgid "To import an SVN repository, check out %{svn_link}."
msgstr "Pour importer un dépôt SVN, consultez %{svn_link}."
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
-msgstr "Pour utiliser uniquement les fonctionnalités CI / CD pour un dépôt externe, choisissez <strong>CI / CD pour le dépôt externe</strong>."
+msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr "Pour commencer à exécuter vos tâches, vous pouvez ajouter des exécuteurs à votre groupe"
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr "Pour valider vos configurations GitLab CI, allez dans 'CI / CD → Pipelines' dans votre projet, et cliquez sur le bouton 'CI Lint'."
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
-msgstr "Pour afficher la feuille de route, ajoutez une date de début ou de fin planifiée à l’une de vos épopées dans ce groupe ou ses sous-groupes. Seules les épopées des 3 derniers mois et des 3 prochains mois sont affichées."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
msgid "Todo"
msgstr "Tâche"
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr "Afficher/masquer la barre latérale"
+
+msgid "Toggle discussion"
+msgstr "Basculer la discussion"
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr "Afficher/masquer la barre latérale"
@@ -4491,6 +6640,12 @@ msgstr "État du commutateur : Inactif"
msgid "ToggleButton|Toggle Status: ON"
msgstr "État du commutateur : Actif"
+msgid "Too many changes to show."
+msgstr "Trop de changements à afficher."
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr "Temps total"
@@ -4501,19 +6656,37 @@ msgid "Total: %{total}"
msgstr "Total : %{total}"
msgid "Track activity with Contribution Analytics."
-msgstr "Suivre l’activité avec Contribution Analytics."
+msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
-msgstr "Suivez les groupes de tickets qui partagent un thème, entre projets et jalons"
+msgstr ""
msgid "Track time with quick actions"
msgstr "Suivre le temps estimé/passé avec les actions rapides"
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr "Déclencher cette action manuelle"
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr "Les déclencheurs peuvent forcer la reconstruction pour une branche ou une étiquette spécifique via l’appel à une API. Ces jetons emprunteront l’identité de l’utilisateur auquel ils sont associés, ainsi que ses accès et permissions sur les projets."
+
+msgid "Try again"
+msgstr "Veuillez réessayer"
+
msgid "Turn on Service Desk"
-msgstr "Activer le Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr "Impossible de charger le diff. %{button_try_again}"
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
msgid "Unknown"
msgstr "Inconnu"
@@ -4527,26 +6700,62 @@ msgstr "Déverrouillé"
msgid "Unresolve discussion"
msgstr "Marquer la discussion comme non résolue"
+msgid "Unstage all changes"
+msgstr "Désindexer les changements en étape"
+
+msgid "Unstage changes"
+msgstr "Désindexer les changements"
+
+msgid "Unstaged"
+msgstr "Désindexé"
+
+msgid "Unstaged %{type}"
+msgstr "Désindexation de %{type}"
+
+msgid "Unstaged and staged %{type}"
+msgstr "%{type} non-indéxés et indexés"
+
msgid "Unstar"
msgstr "Supprimer des favoris"
+msgid "Unsubscribe"
+msgstr "Se désabonner"
+
+msgid "Unsubscribe at group level"
+msgstr "Se désabonner au niveau du groupe"
+
+msgid "Unsubscribe at project level"
+msgstr "Se désabonner au niveau du projet"
+
+msgid "Unverified"
+msgstr "Non vérifié"
+
msgid "Up to date"
msgstr "À jour"
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr "Modifiez le nom du groupe, sa description, son logo et d’autres paramètres généraux."
+
msgid "Upgrade your plan to activate Advanced Global Search."
-msgstr "Mettez à jour votre abonnement pour activer la Recherche Globale Avancée."
+msgstr ""
msgid "Upgrade your plan to activate Contribution Analytics."
-msgstr "Mettez à jour votre abonnement pour activer Contribution Analytics."
+msgstr ""
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr "Mettez à jour votre abonnement pour activer les Webhooks de groupe."
+msgstr ""
msgid "Upgrade your plan to activate Issue weight."
-msgstr "Mettez à niveau votre abonnement pour activer les poids de ticket."
+msgstr ""
msgid "Upgrade your plan to improve Issue boards."
-msgstr "Mettez à niveau votre forfait pour améliorer les tableaux de tickets."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
msgid "Upload New File"
msgstr "Téléverser un nouveau fichier"
@@ -4564,10 +6773,19 @@ msgid "Upvotes"
msgstr "Votes positifs"
msgid "Usage statistics"
+msgstr "Statistiques d’utilisation"
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
-msgstr "Utilisez Service Desk pour intéragir avec vos utilisateurs (par exemple pour offrir un support client) par email directement dans GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr "Utilisez les jalons de groupe pour gérer les tickets de plusieurs projets dans le même jalon."
+
+msgid "Use one line per URI"
+msgstr ""
msgid "Use the following registration token during setup:"
msgstr "Utiliser le jeton d’inscription suivant pendant l’installation :"
@@ -4578,26 +6796,41 @@ msgstr "Utiliser vos paramètres de notification globaux"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
+msgstr "Limites de l’utilisateur et du débit IP"
+
+msgid "User map"
msgstr ""
+msgid "Users"
+msgstr "Utilisa·teur·trice·s"
+
+msgid "Variables"
+msgstr "Variables"
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr "Les variables sont appliquées aux environnements via l’exécuteur. Elles peuvent être protégées en les exposant uniquement à des branches ou des tags protégées. Vous pouvez utiliser des variables pour les mots de passe, les clés secrètes ou tout ce que vous voulez."
msgid "Various container registry settings."
-msgstr ""
+msgstr "Divers paramètres de registre de conteneur."
msgid "Various email settings."
-msgstr ""
+msgstr "Divers paramètres de courriel."
msgid "Various settings that affect GitLab performance."
+msgstr "Divers paramètres qui affectent les performances de GitLab."
+
+msgid "Verification information"
msgstr ""
-msgid "View and edit lines"
-msgstr "Afficher et modifier les lignes"
+msgid "Verified"
+msgstr "Vérifié"
msgid "View epics list"
-msgstr "Afficher la liste des épopées"
+msgstr ""
msgid "View file @ "
msgstr "Voir le fichier @ "
@@ -4605,9 +6838,21 @@ msgstr "Voir le fichier @ "
msgid "View group labels"
msgstr "Afficher les labels de groupe"
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr "Afficher les tâches"
+
msgid "View labels"
msgstr "Afficher les labels"
+msgid "View log"
+msgstr "Afficher le journal"
+
msgid "View open merge request"
msgstr "Afficher la demande de fusion"
@@ -4620,6 +6865,12 @@ msgstr "Voir le fichier remplacé @ "
msgid "Visibility and access controls"
msgstr "Contrôles de visibilité et d’accès"
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "Interne"
@@ -4635,8 +6886,8 @@ msgstr "Inconnu"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Vous voulez voir les données ? Merci de contacter un administrateur pour en obtenir l’accès."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
-msgstr "Nous n’avons pas pu vérifier que l’un de vos projets sur GCP est activé pour la facturation. Veuillez réessayer."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "Nous n'avons pas suffisamment de données pour afficher cette étape."
@@ -4648,16 +6899,28 @@ msgid "Web IDE"
msgstr "Web IDE"
msgid "Web terminal"
-msgstr ""
+msgstr "Terminal Web"
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr "Les webhooks vous permettent d’appeler une URL si, par exemple, du nouveau code est poussé ou un nouveau ticket est créé. Vous pouvez configurer les webhooks pour écouter les événements spécifiques comme des poussées de code, des tickets ou des demandes de fusion. Les webhooks de groupes s’appliqueront à tous les projets dans un groupe, ce qui vous permet de normaliser la fonctionnalité du webhook dans votre groupe entier."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
msgid "Weight"
msgstr "Poids"
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
-msgstr "Lorsque vous laissez l’URL vide, les étiquettes de classification peuvent toujours être spécifiées sans désactiver les fonctionnalités inter-projets ou effectuer des vérifications d’autorisation externes."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr "Lorsqu’un exécuteur est verrouillé, il ne peut pas être affecté à d’autres projets"
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr "Lorsque cette option est activée, les utilisateur•rice•s ne pourront pas utiliser GitLab tant que les conditions générales d’utilisation ne seront pas acceptés."
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
msgid "Wiki"
msgstr "Wiki"
@@ -4672,7 +6935,7 @@ msgid "WikiClone|Install Gollum"
msgstr "Installer Gollum"
msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
-msgstr "Il est recommandé d’installer %{markdown} pour que les spécificités de GFM s’affichent en local :"
+msgstr "Il est recommandé d’installer %{markdown} pour que les spécificités de GFM s’affichent en local :"
msgid "WikiClone|Start Gollum and edit locally"
msgstr "Démarrer Gollum et modifier localement"
@@ -4683,8 +6946,32 @@ msgstr "Astuce : Vous pouvez déplacer cette page en ajoutant le chemin au débu
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr "Il y a déjà une page avec le même titre pour ce chemin."
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
-msgstr "Vous n’êtes pas autorisé·e à créer des pages wiki"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr "Suggérer une amélioration du wiki"
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr "Vous devez être un membre du projet pour ajouter des pages wiki. Si vous avez des suggestions sur la manière d’améliorer le wiki pour ce projet, veuillez envisager l’ouverture d’un ticket sur %{issues_link}."
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr "Suivi d’incidents"
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr "Un wiki est un endroit où vous pouvez stocker tous les détails concernant votre projet. Ceci peut inclure sa raison d’être, ses principes, comment l’utiliser, etc."
+
+msgid "WikiEmpty|Create your first page"
+msgstr "Créer votre première page"
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr "Suggérer une amélioration du wiki"
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr "Le wiki vous permet d’écrire la documentation de votre projet"
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr "Ce projet n’a pas de pages wiki"
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr "Vous devez être un membre du projet pour ajouter des pages wiki."
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "Il s’agit d’une ancienne version de la page."
@@ -4719,6 +7006,12 @@ msgstr "Nouvelle Page Wiki"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr "Êtes-vous sûr·e de vouloir supprimer cette page ?"
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr "Supprimer la page"
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr "Supprimer la page %{pageTitle} ?"
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr "Quelqu’un a modifié la page en même temps que vous. Veuillez consulter %{page_link} et vérifiez que vos modifications ne suppriment pas involontairement les siennes."
@@ -4734,8 +7027,8 @@ msgstr "Mettre à jour %{page_title}"
msgid "WikiPage|Page slug"
msgstr "Slug de la page"
-msgid "WikiPage|Write your content or drag files here..."
-msgstr "Écrivez du contenu ou faîtes glisser des fichiers ici..."
+msgid "WikiPage|Write your content or drag files here…"
+msgstr "Écrivez du contenu ou faites glisser des fichiers ici…"
msgid "Wiki|Create Page"
msgstr "Créer une Page"
@@ -4744,10 +7037,7 @@ msgid "Wiki|Create page"
msgstr "Créer la page"
msgid "Wiki|Edit Page"
-msgstr "Modifier cette Page"
-
-msgid "Wiki|Empty page"
-msgstr "Page vide"
+msgstr "Modifier cette page"
msgid "Wiki|More Pages"
msgstr "Plus de Pages"
@@ -4768,22 +7058,31 @@ msgid "Wiki|Wiki Pages"
msgstr "Pages du Wiki"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr "Avec Contribution Analytics vous pouvez avoir un aperçu de l’activité des tickets, demandes de fusion et des événements de poussée de votre organisation et de ses membres."
+msgstr ""
msgid "Withdraw Access Request"
msgstr "Retirer la demande d'accès"
-msgid "Write a commit message..."
-msgstr "Écrire un message de commit…"
+msgid "Yes"
+msgstr "Oui"
+
+msgid "Yes, add it"
+msgstr "Oui, l’ajouter"
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
-msgstr "Vous êtes sur le point de supprimer %{group_name}. Les groupes supprimés NE PEUVENT PAS être restaurés ! Êtes vous ABSOLUMENT sûr·e ?"
+msgstr "Vous êtes sur le point de supprimer %{group_name}. Les groupes supprimés NE PEUVENT PAS être restaurés ! Êtes vous ABSOLUMENT sûr·e ?"
msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
-msgstr "Vous êtes sur le point de supprimer %{project_full_name}. Les projets supprimés NE PEUVENT PAS être restaurés ! Êtes-vous ABSOLUMENT sûr•e ?"
+msgstr "Vous êtes sur le point de supprimer %{project_full_name}. Les projets supprimés NE PEUVENT PAS être restaurés ! Êtesâ€vous ABSOLUMENT sûr·e ?"
msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
-msgstr "Vous allez supprimer la relation de fourche avec le projet source %{forked_from_project}. Êtes-vous ABSOLUMENT sûr·e ?"
+msgstr "Vous êtes sur le point de supprimer la relation de divergence avec le projet source %{forked_from_project}. En êtesâ€vous ABSOLUMENT sûr·e ?"
msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
msgstr "Vous allez transférer %{project_full_name} à un•e nouveau•elle propriétaire. Êtes-vous VRAIMENT sûr•e ?"
@@ -4791,8 +7090,11 @@ msgstr "Vous allez transférer %{project_full_name} à un•e nouveau•elle pro
msgid "You are on a read-only GitLab instance."
msgstr "Vous êtes sur une instance GitLab en lecture seule."
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
-msgstr "Vous êtes sur un nœud Geo secondaire (en lecture seule). Si vous voulez apporter des modifications, vous devez visiter le %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr "Vous pouvez %{linkStart}afficher les données brutes%{linkEnd} à la place."
msgid "You can also create a project from the command line."
msgstr "Vous pouvez également créer un projet en ligne de commande."
@@ -4800,6 +7102,12 @@ msgstr "Vous pouvez également créer un projet en ligne de commande."
msgid "You can also star a label to make it a priority label."
msgstr "Vous pouvez marquer un label comme important pour en faire un label prioritaire."
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr "Vous pouvez également tester votre .gitlab-ci.yml avec %{linkStart}Lint%{linkEnd}"
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr "Vous pouvez facilement installer un Exécuteur sur un cluster Kubernetes. %{link_to_help_page}"
@@ -4810,31 +7118,52 @@ msgid "You can only add files when you are on a branch"
msgstr "Vous ne pouvez ajouter de fichier que dans une branche"
msgid "You can only edit files when you are on a branch"
-msgstr "Vous ne pouvez éditer de fichier que dans une branche"
+msgstr "Vous ne pouvez modifier des fichiers que dans une branche"
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr "Vous pouvez résoudre le conflit de fusion Git soit en mode interactif, en cliquant sur les boutons « %{use_ours} » ou « %{use_theirs} », soit en modifiant directement les fichiers. Valider ces modifications dans la branche « %{branch_name} »"
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
-msgstr "Vous ne pouvez pas écrire sur une instance GitLab Geo secondaire en lecture-seule. Veuillez utiliser le %{link_to_primary_node} à la place."
+msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr "Vous ne pouvez pas écrire sur cette instance GitLab en lecture-seule."
+msgid "You do not have any assigned merge requests"
+msgstr "Aucune demande de fusion ne vous a été affectée"
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
-msgstr "Vous ne disposez pas des autorisations appropriées pour remplacer les paramètres de synchronisation du groupe LDAP."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
msgid "You have no permissions"
msgstr "Vous n’avez pas les autorisations"
+msgid "You have not created any merge requests"
+msgstr "Vous n’avez créé aucune demande de fusion"
+
msgid "You have reached your project limit"
msgstr "Vous avez atteint votre limite de projet"
-msgid "You must have master access to force delete a lock"
-msgstr "Vous devez avoir un accès Expert pour forcer la suppression d’un verrou"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr "Vous devez accepter les conditions générales d’utilisation et la politique de confidentialité afin de pouvoir vous créer un compte"
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr "Seul un responsable peut forcer la suppression d’un verrou"
msgid "You must sign in to star a project"
msgstr "Vous devez vous connecter pour mettre un projet en favori"
msgid "You need a different license to enable FileLocks feature"
-msgstr "Vous avez besoin d’une licence différente pour activer la fonctionnalité FileLocks"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
msgid "You need permission."
msgstr "Vous avez besoin d’une autorisation."
@@ -4866,9 +7195,18 @@ msgstr "Vous ne pourrez pas récupérer ou pousser de code par SSH tant que vous
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "Vous devrez utiliser différents noms de branches pour obtenir une comparaison valide."
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr "Vous recevez ce courriel en raison de votre compte sur %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr "Vos groupes"
@@ -4884,6 +7222,12 @@ msgstr "Activité de vos projets favoris"
msgid "Your Todos"
msgstr "Vos tâches à faire"
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr "Vos modifications peuvent être validées sur %{branch_name} car une demande de fusion est ouverte."
@@ -4902,13 +7246,16 @@ msgstr "Votre nom"
msgid "Your projects"
msgstr "Vos projets"
+msgid "ago"
+msgstr "auparavant "
+
msgid "among other things"
msgstr "entre autres choses"
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] "et %d vulnérabilité corrigée"
-msgstr[1] "et %d vulnérabilités corrigées"
+msgstr[0] ""
+msgstr[1] ""
msgid "assign yourself"
msgstr "assignez vous"
@@ -4919,83 +7266,212 @@ msgstr "nom de la branche"
msgid "by"
msgstr "par"
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
-msgstr "%{type} n’a détecté aucune nouvelle vulnérabilité de sécurité"
+msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
-msgstr "%{type} n’a détecté aucune vulnérabilité de sécurité"
+msgstr ""
+
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
msgid "ciReport|Code quality"
-msgstr "Qualité du code"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
-msgstr "DAST n’a détecté aucune alerte en analysant l’application de revue"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
-msgid "ciReport|Dependency scanning"
-msgstr "Analyse de dépendance"
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgstr ""
msgid "ciReport|Dependency scanning detected"
-msgstr "Analyse de dépendance détectée"
+msgstr ""
+
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
-msgstr "L’analyse des dépendances n’a détecté aucune nouvelle vulnérabilité de sécurité"
+msgid "ciReport|Dismissed by"
+msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
-msgstr "L’analyse des dépendances n’a détecté aucune vulnérabilité de sécurité"
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
-msgstr "Impossible de charger le rapport %{reportName}"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
msgid "ciReport|Fixed:"
-msgstr "Réparé :"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
msgid "ciReport|Instances"
-msgstr "Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
msgid "ciReport|Learn more about whitelisting"
-msgstr "En savoir plus sur la liste blanche"
+msgstr ""
+
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
msgid "ciReport|Loading %{reportName} report"
-msgstr "Chargement du rapport %{reportName}"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
msgid "ciReport|No changes to code quality"
-msgstr "Aucun changement dans la qualité du code"
+msgstr ""
msgid "ciReport|No changes to performance metrics"
-msgstr "Aucun changement dans les indicateurs de performance"
+msgstr ""
msgid "ciReport|Performance metrics"
-msgstr "Indicateurs de performance"
+msgstr ""
-msgid "ciReport|SAST"
-msgstr "SAST"
+msgid "ciReport|Revert dismissal"
+msgstr ""
msgid "ciReport|SAST detected"
-msgstr "SAST détectée"
-
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr "SAST n’a détecté aucune nouvelle vulnérabilité de sécurité"
+msgstr ""
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr "SAST n’a détecté aucune vulnérabilité de sécurité"
+msgid "ciReport|SAST is loading"
+msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
-msgstr "SAST:conteneur aucune vulnérabilité n’a été trouvée"
+msgid "ciReport|SAST resulted in error while loading results"
+msgstr ""
msgid "ciReport|Security scanning"
-msgstr "Analyse de sécurité"
+msgstr ""
msgid "ciReport|Security scanning failed loading any results"
-msgstr "L’analyse de sécurité n’a pas réussi à charger de résultats"
+msgstr ""
+
+msgid "ciReport|Security scanning is loading"
+msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
-msgstr "Afficher le rapport complet sur les vulnérabilités du code"
+msgid "ciReport|Severity"
+msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
-msgstr "Les vulnérabilités non approuvées (en rouge) peuvent être marquées comme approuvées. %{helpLink}"
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
+msgstr ""
msgid "ciReport|no vulnerabilities"
-msgstr "aucune vulnérabilité"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
msgid "command line instructions"
msgstr "instructions en ligne de commande"
@@ -5004,49 +7480,70 @@ msgid "connecting"
msgstr "connexion en cours"
msgid "could not read private key, is the passphrase correct?"
-msgstr "impossible de lire la clé privée, la phrase secrète est-elle correcte ?"
+msgstr "impossible de lire la clef privée, la phrase secrète estâ€elle correcte ?"
+
+msgid "customize"
+msgstr ""
msgid "day"
msgid_plural "days"
msgstr[0] "jour"
msgstr[1] "jours"
+msgid "deploy token"
+msgstr "jeton de déploiement"
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] "a détecté %d vulnérabilité corrigée"
-msgstr[1] "a détecté %d vulnérabilités corrigées"
+msgstr[0] ""
+msgstr[1] ""
msgid "detected %d new vulnerability"
msgid_plural "detected %d new vulnerabilities"
-msgstr[0] "a détecté %d nouvelle vulnérabilité"
-msgstr[1] "a détecté %d nouvelle vulnérabilité"
+msgstr[0] ""
+msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr "n’a détecté aucune vulnérabilité"
+msgid "disabled"
+msgstr "désactivé"
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr "activé"
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr "%{slash_command} mettra à jour la durée estimée avec la dernière commande."
+msgid "for this project"
+msgstr "pour ce projet"
+
msgid "here"
msgstr "ici"
+msgid "import flow"
+msgstr ""
+
msgid "importing"
msgstr "importation en cours"
-msgid "in progress"
-msgstr "en cours"
-
msgid "is invalid because there is downstream lock"
-msgstr "est invalide car il y a un verrou en aval"
+msgstr ""
msgid "is invalid because there is upstream lock"
-msgstr "est invalide car il y a un verrou en amont"
+msgstr ""
msgid "is not a valid X509 certificate."
-msgstr "n’est pas un certificat X509 valide."
+msgstr ""
+
+msgid "latest version"
+msgstr "dernière version"
msgid "locked by %{path_lock_user_name} %{created_at}"
-msgstr "verrouillé par %{path_lock_user_name} %{created_at}"
+msgstr ""
msgid "merge request"
msgid_plural "merge requests"
@@ -5066,28 +7563,22 @@ msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasis
msgstr "%{metricsLinkStart}L’usage mémoire%{metricsLinkEnd} %{emphasisStart}est resté stable%{emphasisEnd} à %{memoryFrom}MO"
msgid "mrWidget|Add approval"
-msgstr "Ajouter une approbation"
+msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
-msgstr "Autoriser les modifications par les mainteneurs"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr "Autoriser les commits des membres qui peuvent fusionner dans la branche cible"
msgid "mrWidget|An error occured while removing your approval."
-msgstr "Une erreur est survenue lors de la suppression de votre approbation."
-
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr "Une erreur s’est produite lors de la récupération des données d’approbation pour cette demande de fusion."
+msgstr ""
-msgid "mrWidget|An error occured while submitting your approval."
-msgstr "Une erreur est survenue pendant l’envoi de votre approbation."
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
msgid "mrWidget|Approve"
-msgstr "Approuver"
-
-msgid "mrWidget|Approved"
msgstr ""
msgid "mrWidget|Approved by"
-msgstr "Approuvée par"
+msgstr ""
msgid "mrWidget|Cancel automatic merge"
msgstr "Annuler la fusion automatique"
@@ -5113,6 +7604,9 @@ msgstr "Fermée par"
msgid "mrWidget|Closes"
msgstr "Résout"
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr "Créer un ticket pour les résoudre plus tard"
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr "Les statistiques de déploiement ne sont pas disponibles pour le moment"
@@ -5146,9 +7640,24 @@ msgstr "La fusion a échoué."
msgid "mrWidget|Merge locally"
msgstr "Fusionner localement"
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr "Fusionnée par"
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr "Diff simple"
@@ -5168,7 +7677,7 @@ msgid "mrWidget|Remove source branch"
msgstr "Supprimer la branche source"
msgid "mrWidget|Remove your approval"
-msgstr "Supprimer votre approbation"
+msgstr ""
msgid "mrWidget|Request to merge"
msgstr "Demande de fusion de"
@@ -5209,6 +7718,9 @@ msgstr "La branche source ne sera pas supprimée"
msgid "mrWidget|There are merge conflicts"
msgstr "Il y a des conflits de fusion"
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr "Il y a des discussions non résolues. Veuillez résoudre ces discussions"
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr "Cette demande de fusion n’a pas pu être fusionnée automatiquement"
@@ -5218,9 +7730,6 @@ msgstr "Cette demande de fusion est en cours de fusion"
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "Ce projet est archivé, l’accès en écriture a été désactivé"
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "Vous pouvez fusionner cette demande de fusion manuellement à l’aide de la"
@@ -5260,29 +7769,46 @@ msgid "personal access token"
msgstr "jeton d’accès personnel"
msgid "private key does not match certificate."
-msgstr "clé privée ne correspond pas au certificat."
+msgstr ""
+
+msgid "remaining"
+msgstr "restant"
msgid "remove due date"
msgstr "supprimer la date d’échéance"
+msgid "remove weight"
+msgstr "supprimer le poids"
+
msgid "source"
msgstr "source"
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "%{slash_command} mettra à jour la somme du temps passé."
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr "ce document"
msgid "to help your contributors communicate effectively!"
-msgstr "pour aider vos contributeurs à communiquer efficacement !"
+msgstr "pour aider vos contributeurs à communiquer efficacement !"
msgid "username"
-msgstr "nom d’utilisateur"
+msgstr "nom d’utilisa·teur·trice"
msgid "uses Kubernetes clusters to deploy your code!"
msgstr "utilise les clusters Kubernetes pour déployer votre code !"
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr "avec %{additions} ajouts, %{deletions} suppressions."
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] "dans %d minute "
+msgstr[1] "dans les %d minutes"
+
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 80c1cb09964..bec60cf592a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-06-29 16:17+1000\n"
-"PO-Revision-Date: 2018-06-29 16:17+1000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -18,6 +16,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+msgid " Status"
+msgstr ""
+
msgid "%d changed file"
msgid_plural "%d changed files"
msgstr[0] ""
@@ -38,6 +39,16 @@ msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
+msgid "%d failed test result"
+msgid_plural "%d failed test results"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d fixed test result"
+msgid_plural "%d fixed test results"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d issue"
msgid_plural "%d issues"
msgstr[0] ""
@@ -79,6 +90,9 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -119,6 +133,11 @@ msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts
msgstr[0] ""
msgstr[1] ""
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr ""
@@ -128,9 +147,6 @@ msgstr ""
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr ""
-
msgid "+ %{moreCount} more"
msgstr ""
@@ -204,6 +220,21 @@ msgstr ""
msgid "404|Please contact your GitLab administrator if you think this is a mistake."
msgstr ""
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{group_name}</strong> group members"
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
@@ -219,9 +250,18 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr ""
@@ -237,9 +277,15 @@ msgstr ""
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr ""
@@ -270,6 +316,9 @@ msgstr ""
msgid "Add Readme"
msgstr ""
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr ""
@@ -279,6 +328,21 @@ msgstr ""
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -345,9 +409,15 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
+msgid "All users"
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
@@ -360,9 +430,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
msgid "An error occured creating the new branch."
msgstr ""
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -390,7 +499,7 @@ msgstr ""
msgid "An error occurred while importing project: ${details}"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -426,9 +535,27 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr ""
@@ -444,6 +571,12 @@ msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
msgid "Are you sure you want to remove this identity?"
msgstr ""
@@ -504,12 +637,36 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -525,6 +682,9 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
msgid "AutoDevOps|Auto DevOps"
msgstr ""
@@ -564,6 +724,9 @@ msgstr ""
msgid "Average per day: %{average}"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
msgid "Background color"
msgstr ""
@@ -642,6 +805,21 @@ msgstr ""
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Bitbucket Server Import"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
msgid "Boards"
msgstr ""
@@ -803,13 +981,13 @@ msgstr ""
msgid "CI / CD"
msgstr ""
-msgid "CI/CD configuration"
+msgid "CI / CD Settings"
msgstr ""
-msgid "CI/CD settings"
+msgid "CI/CD configuration"
msgstr ""
-msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgid "CI/CD settings"
msgstr ""
msgid "CICD|Auto DevOps"
@@ -824,34 +1002,34 @@ msgstr ""
msgid "CICD|Continuous deployment to production"
msgstr ""
-msgid "CICD|Deployment strategy"
+msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
-msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgid "CICD|Deployment strategy"
msgstr ""
-msgid "CICD|Disable Auto DevOps"
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
msgstr ""
-msgid "CICD|Enable Auto DevOps"
+msgid "CICD|Jobs"
msgstr ""
-msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgid "CICD|Learn more about Auto DevOps"
msgstr ""
-msgid "CICD|Instance default (%{state})"
+msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
-msgid "CICD|Jobs"
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
-msgid "CICD|Learn more about Auto DevOps"
+msgid "CICD|instance enabled"
msgstr ""
-msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgid "Callback URL"
msgstr ""
-msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgid "Callback url"
msgstr ""
msgid "Can't find HEAD commit for this branch"
@@ -869,6 +1047,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Change template"
+msgstr ""
+
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
msgstr ""
@@ -914,6 +1095,12 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
msgstr ""
@@ -923,9 +1110,15 @@ msgstr ""
msgid "Choose any color."
msgstr ""
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which repositories you want to import."
msgstr ""
@@ -1019,9 +1212,15 @@ msgstr ""
msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
msgstr ""
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
msgstr ""
+msgid "Click the <strong>Select none</strong> 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 ""
@@ -1031,6 +1230,9 @@ msgstr ""
msgid "Click to expand text"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -1046,9 +1248,6 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
-msgstr ""
-
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
@@ -1070,9 +1269,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -1100,16 +1296,7 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
-msgstr ""
-
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr ""
-
-msgid "ClusterIntegration|Create on Google Kubernetes Engine"
-msgstr ""
-
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
@@ -1148,6 +1335,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
msgid "ClusterIntegration|Ingress"
msgstr ""
@@ -1274,9 +1464,6 @@ msgstr ""
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
-msgid "ClusterIntegration|Redeem up to $500 in free credit for Google Cloud Platform"
-msgstr ""
-
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
@@ -1391,6 +1578,9 @@ msgstr ""
msgid "ClusterIntegration|sign up"
msgstr ""
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
@@ -1584,6 +1774,9 @@ msgstr ""
msgid "Continue"
msgstr ""
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
@@ -1611,6 +1804,12 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy URL to clipboard"
msgstr ""
@@ -1623,9 +1822,6 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr ""
-msgid "Copy file name to clipboard"
-msgstr ""
-
msgid "Copy file path to clipboard"
msgstr ""
@@ -1635,6 +1831,9 @@ msgstr ""
msgid "Copy to clipboard"
msgstr ""
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1647,6 +1846,9 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr ""
@@ -1665,6 +1867,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
@@ -1686,6 +1891,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1707,27 +1915,45 @@ msgstr ""
msgid "Created"
msgstr ""
+msgid "Created At"
+msgstr ""
+
msgid "Created by me"
msgstr ""
+msgid "Created on:"
+msgstr ""
+
msgid "Cron Timezone"
msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current Branch"
+msgstr ""
+
msgid "CurrentUser|Profile"
msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "Custom CI config path"
+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 "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 "Cycle Analytics"
msgstr ""
@@ -1752,6 +1978,9 @@ msgstr ""
msgid "CycleAnalyticsStage|Test"
msgstr ""
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1767,15 +1996,30 @@ msgstr ""
msgid "Decline and sign out"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Snippet"
+msgstr ""
+
msgid "Delete list"
msgstr ""
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] ""
@@ -1910,6 +2154,12 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
@@ -1937,9 +2187,18 @@ msgstr ""
msgid "Discard draft"
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Domain"
msgstr ""
@@ -1994,9 +2253,18 @@ msgstr ""
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
+msgid "Edit group: %{group_name}"
+msgstr ""
+
msgid "Edit identity for %{user_name}"
msgstr ""
@@ -2048,6 +2316,9 @@ msgstr ""
msgid "Ends at (UTC)"
msgstr ""
+msgid "Enter in your Bitbucket Server URL and personal access token below"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -2057,9 +2328,18 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -2072,43 +2352,49 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
-msgid "Environments|Updated"
+msgid "Environments|Stop"
msgstr ""
-msgid "Environments|You don't have any environments right now."
+msgid "Environments|Stop environment"
msgstr ""
-msgid "Error Reporting and Logging"
+msgid "Environments|Updated"
msgstr ""
-msgid "Error committing changes. Please try again."
+msgid "Environments|You don't have any environments right now."
msgstr ""
-msgid "Error fetching contributors data."
+msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error fetching job trace"
+msgid "Error fetching contributors data."
msgstr ""
msgid "Error fetching labels."
@@ -2126,16 +2412,19 @@ msgstr ""
msgid "Error loading branch data. Please try again."
msgstr ""
+msgid "Error loading branches."
+msgstr ""
+
msgid "Error loading last commit."
msgstr ""
-msgid "Error loading merge requests."
+msgid "Error loading markdown preview"
msgstr ""
-msgid "Error loading project data. Please try again."
+msgid "Error loading merge requests."
msgstr ""
-msgid "Error loading tree data. Please try again."
+msgid "Error loading project data. Please try again."
msgstr ""
msgid "Error occurred when toggling the notification subscription"
@@ -2180,6 +2469,9 @@ msgstr ""
msgid "Every week (Sundays at 4:00am)"
msgstr ""
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
@@ -2189,12 +2481,27 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
msgid "Explore public groups"
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -2219,13 +2526,13 @@ msgstr ""
msgid "Failure"
msgstr ""
-msgid "Feb"
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
msgstr ""
-msgid "February"
+msgid "Feb"
msgstr ""
-msgid "Fetching diff files failed. Please reload the page to try again!"
+msgid "February"
msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
@@ -2237,6 +2544,9 @@ msgstr ""
msgid "Files (%{human_size})"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
@@ -2246,6 +2556,12 @@ msgstr ""
msgid "Find file"
msgstr ""
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2255,6 +2571,33 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] ""
@@ -2278,6 +2621,21 @@ msgstr ""
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From Bitbucket Server"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
@@ -2293,9 +2651,15 @@ msgstr ""
msgid "General"
msgstr ""
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2305,6 +2669,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2317,7 +2684,16 @@ msgstr ""
msgid "GitLab Group Runners can execute code for all the projects in this group."
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab.com import"
msgstr ""
msgid "Gitaly"
@@ -2329,18 +2705,33 @@ msgstr ""
msgid "Gitaly|Address"
msgstr ""
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
msgid "Go Back"
msgstr ""
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
msgid "GoToYourFork|Fork"
msgstr ""
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -2350,18 +2741,36 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "Group"
+msgstr ""
+
msgid "Group CI/CD settings"
msgstr ""
+msgid "Group Git LFS status:"
+msgstr ""
+
msgid "Group ID"
msgstr ""
msgid "Group Runners"
msgstr ""
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group: %{group_name}"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -2386,9 +2795,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
msgstr ""
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2478,18 +2911,39 @@ msgstr ""
msgid "ID"
msgstr ""
+msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation."
+msgstr ""
+
+msgid "IDE|Back"
+msgstr ""
+
+msgid "IDE|Client side evaluation"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
msgid "IDE|Edit"
msgstr ""
-msgid "IDE|Go back"
+msgid "IDE|Get started with Live Preview"
+msgstr ""
+
+msgid "IDE|Go to project"
+msgstr ""
+
+msgid "IDE|Live Preview"
msgstr ""
msgid "IDE|Open in file view"
msgstr ""
+msgid "IDE|Preview your web application using Web IDE client-side evaluation."
+msgstr ""
+
+msgid "IDE|Refresh preview"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
@@ -2499,6 +2953,12 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If you already have files you can push them using the %{link_to_cli} below."
msgstr ""
@@ -2517,28 +2977,76 @@ msgstr ""
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from Bitbucket Server"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from Bitbucket Server"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
msgid "Import repository"
msgstr ""
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Incompatible Project"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Install GitLab Runner"
+msgstr ""
+
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Instance Statistics"
+msgstr ""
+
+msgid "Instance Statistics visibility"
msgstr ""
msgid "Instance does not support multiple Kubernetes clusters"
@@ -2547,6 +3055,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2562,7 +3073,7 @@ msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
-msgid "Issue Board"
+msgid "Issue Boards"
msgstr ""
msgid "Issue events"
@@ -2607,6 +3118,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2726,12 +3240,30 @@ msgstr ""
msgid "Leave project"
msgstr ""
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your Bitbucket Server repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Live preview"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
@@ -2756,12 +3288,30 @@ msgstr ""
msgid "Locked to current projects"
msgstr ""
-msgid "Login"
+msgid "Logs"
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage Web IDE features"
+msgstr ""
+
+msgid "Manage access"
msgstr ""
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2771,6 +3321,24 @@ msgstr ""
msgid "Manage project labels"
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
@@ -2795,6 +3363,9 @@ msgstr ""
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Request:"
msgstr ""
@@ -2837,12 +3408,42 @@ msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Milestone"
msgstr ""
@@ -2879,6 +3480,9 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More actions"
msgstr ""
@@ -2888,6 +3492,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2903,6 +3510,9 @@ msgstr ""
msgid "Name your individual key via a title"
msgstr ""
+msgid "Name:"
+msgstr ""
+
msgid "Nav|Help"
msgstr ""
@@ -2915,6 +3525,18 @@ msgstr ""
msgid "Nav|Sign out and sign in with a different account"
msgstr ""
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
msgid "New Identity"
msgstr ""
@@ -2923,16 +3545,16 @@ msgid_plural "New Issues"
msgstr[0] ""
msgstr[1] ""
-msgid "New Kubernetes Cluster"
+msgid "New Label"
msgstr ""
-msgid "New Kubernetes cluster"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Label"
+msgid "New Snippet"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Snippets"
msgstr ""
msgid "New branch"
@@ -2962,6 +3584,9 @@ msgstr ""
msgid "New merge request"
msgstr ""
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2977,12 +3602,18 @@ msgstr ""
msgid "New tag"
msgstr ""
+msgid "New..."
+msgstr ""
+
msgid "No"
msgstr ""
msgid "No assignee"
msgstr ""
+msgid "No branches found"
+msgstr ""
+
msgid "No changes"
msgstr ""
@@ -3004,18 +3635,33 @@ msgstr ""
msgid "No files found."
msgstr ""
+msgid "No labels with such name or description"
+msgstr ""
+
msgid "No merge requests found"
msgstr ""
msgid "No messages were logged"
msgstr ""
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
msgid "No repository"
msgstr ""
msgid "No schedules"
msgstr ""
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -3046,6 +3692,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -3127,18 +3776,36 @@ msgstr ""
msgid "OfSearchInADropdown|Filter"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only admins"
+msgstr ""
+
msgid "Only comments from the following commit are shown below"
msgstr ""
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open in Xcode"
msgstr ""
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr ""
@@ -3148,6 +3815,12 @@ msgstr ""
msgid "Operations"
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 ""
@@ -3193,12 +3866,18 @@ msgstr ""
msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
msgstr ""
+msgid "Path:"
+msgstr ""
+
msgid "Pause"
msgstr ""
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
msgid "Perform advanced options such as changing path, transferring, or removing the group."
msgstr ""
@@ -3223,6 +3902,9 @@ msgstr ""
msgid "Pipeline Schedules"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr ""
@@ -3373,6 +4055,15 @@ msgstr ""
msgid "Please accept the Terms of Service before continuing."
msgstr ""
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
@@ -3391,6 +4082,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preview"
+msgstr ""
+
msgid "Prioritize"
msgstr ""
@@ -3415,12 +4109,24 @@ msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Add status emoji"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
+msgid "Profiles|Clear status"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
@@ -3445,9 +4151,18 @@ msgstr ""
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
@@ -3457,6 +4172,9 @@ msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|What's your status?"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3466,6 +4184,12 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|Your status"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3520,6 +4244,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -3532,9 +4259,15 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3553,9 +4286,6 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr ""
-
msgid "PrometheusDashboard|Time"
msgstr ""
@@ -3580,6 +4310,9 @@ msgstr ""
msgid "PrometheusService|Common metrics"
msgstr ""
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
msgid "PrometheusService|Finding and configuring metrics..."
msgstr ""
@@ -3595,9 +4328,6 @@ msgstr ""
msgid "PrometheusService|Metrics"
msgstr ""
-msgid "PrometheusService|Metrics are automatically configured and monitored based on a library of metrics from popular exporters."
-msgstr ""
-
msgid "PrometheusService|Missing environment variable"
msgstr ""
@@ -3646,6 +4376,9 @@ msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push events"
msgstr ""
@@ -3658,10 +4391,10 @@ msgstr ""
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
-msgid "Re-deploy"
+msgid "Read more"
msgstr ""
-msgid "Read more"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
msgstr ""
msgid "Readme"
@@ -3679,6 +4412,9 @@ msgstr ""
msgid "Register and see your runners for this group."
msgstr ""
+msgid "Register and see your runners for this project."
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -3721,9 +4457,54 @@ msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Rename"
+msgstr ""
+
+msgid "Rename file"
+msgstr ""
+
+msgid "Rename folder"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Reports|%{failedString} and %{resolvedString}"
+msgstr ""
+
+msgid "Reports|Class"
+msgstr ""
+
+msgid "Reports|Execution time"
+msgstr ""
+
+msgid "Reports|Failure"
+msgstr ""
+
+msgid "Reports|System output"
+msgstr ""
+
+msgid "Reports|Test summary"
+msgstr ""
+
+msgid "Reports|Test summary failed loading results"
+msgstr ""
+
+msgid "Reports|Test summary results are being parsed"
+msgstr ""
+
+msgid "Reports|no changed test results"
+msgstr ""
+
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository maintenance"
msgstr ""
@@ -3736,6 +4517,9 @@ msgstr ""
msgid "Request Access"
msgstr ""
+msgid "Requests Profiles"
+msgstr ""
+
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
@@ -3789,7 +4573,10 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
-msgid "Rollback"
+msgid "Revoke"
+msgstr ""
+
+msgid "Runner token"
msgstr ""
msgid "Runners"
@@ -3807,9 +4594,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSL Verification"
+msgstr ""
+
msgid "Save"
msgstr ""
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3831,6 +4624,12 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr ""
+msgid "Scope"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
msgid "Scroll to bottom"
msgstr ""
@@ -3858,18 +4657,48 @@ msgstr ""
msgid "Search milestones"
msgstr ""
+msgid "Search or jump to…"
+msgstr ""
+
msgid "Search project"
msgstr ""
msgid "Search users"
msgstr ""
+msgid "SearchAutocomplete|All GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Issues assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests I've created"
+msgstr ""
+
+msgid "SearchAutocomplete|Merge requests assigned to me"
+msgstr ""
+
+msgid "SearchAutocomplete|in all GitLab"
+msgstr ""
+
+msgid "SearchAutocomplete|in this group"
+msgstr ""
+
+msgid "SearchAutocomplete|in this project"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret:"
+msgstr ""
+
msgid "Select"
msgstr ""
@@ -3900,12 +4729,18 @@ msgstr ""
msgid "Select project to choose zone"
msgstr ""
+msgid "Select projects you want to import."
+msgstr ""
+
msgid "Select source branch"
msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Send email"
msgstr ""
@@ -3960,6 +4795,9 @@ msgstr ""
msgid "Shared Runners"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
@@ -3989,6 +4827,12 @@ msgstr[1] ""
msgid "Side-by-side"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
msgid "Sign out"
msgstr ""
@@ -4001,6 +4845,9 @@ msgstr ""
msgid "Size and domain settings for static websites"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -4199,6 +5046,9 @@ msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
msgid "Stop this environment"
msgstr ""
@@ -4208,9 +5058,18 @@ msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
msgid "Subscribe"
msgstr ""
@@ -4226,6 +5085,9 @@ msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4315,12 +5177,18 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Template"
+msgstr ""
+
msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -4351,6 +5219,9 @@ msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr ""
@@ -4378,6 +5249,9 @@ 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 secure token used by the Runner to checkout the project"
+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 ""
@@ -4396,6 +5270,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 ""
@@ -4411,12 +5291,6 @@ msgstr ""
msgid "There are problems accessing Git storage: "
msgstr ""
-msgid "There was an error loading jobs"
-msgstr ""
-
-msgid "There was an error loading latest pipeline"
-msgstr ""
-
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -4438,15 +5312,27 @@ msgstr ""
msgid "They can be managed using the %{link}."
msgstr ""
+msgid "Third party offers"
+msgstr ""
+
msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
msgstr ""
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
msgid "This group does not provide any group Runners yet."
msgstr ""
@@ -4678,6 +5564,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeout"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -4694,12 +5583,21 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Title"
+msgstr ""
+
msgid "To GitLab"
msgstr ""
msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4709,21 +5607,33 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To start serving your jobs you can add Runners to your group"
msgstr ""
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
msgid "Toggle Sidebar"
msgstr ""
msgid "Toggle discussion"
msgstr ""
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4748,12 +5658,21 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
msgid "Try again"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
@@ -4799,14 +5718,15 @@ msgstr ""
msgid "Up to date"
msgstr ""
-msgid "Update %{files}"
-msgid_plural "Update %{files} files"
-msgstr[0] ""
-msgstr[1] ""
+msgid "Update"
+msgstr ""
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr ""
@@ -4825,21 +5745,39 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use group milestones to manage issues from multiple projects in the same milestone."
msgstr ""
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use template"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
msgid "Use your global notification setting"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
msgid "Users"
msgstr ""
+msgid "User|Current status"
+msgstr ""
+
msgid "Variables"
msgstr ""
@@ -4864,6 +5802,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View it on GitLab"
+msgstr ""
+
msgid "View jobs"
msgstr ""
@@ -4885,6 +5826,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr ""
@@ -4900,6 +5847,9 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr ""
@@ -5059,6 +6009,15 @@ msgstr ""
msgid "Yes"
msgstr ""
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr ""
@@ -5086,6 +6045,9 @@ msgstr ""
msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
msgstr ""
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -5104,13 +6066,13 @@ msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
-msgid "You do not have any assigned merge requests"
+msgid "You don't have any applications"
msgstr ""
-msgid "You have no permissions"
+msgid "You don't have any authorized applications"
msgstr ""
-msgid "You have not created any merge requests"
+msgid "You have no permissions"
msgstr ""
msgid "You have reached your project limit"
@@ -5155,9 +6117,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -5173,6 +6144,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -5209,6 +6186,9 @@ msgstr ""
msgid "connecting"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
@@ -5220,6 +6200,9 @@ msgstr ""
msgid "disabled"
msgstr ""
+msgid "done"
+msgstr ""
+
msgid "enabled"
msgstr ""
@@ -5229,6 +6212,15 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "here"
+msgstr ""
+
+msgid "https://your-bitbucket-server"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
msgid "importing"
msgstr ""
@@ -5318,6 +6310,9 @@ msgstr ""
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5387,9 +6382,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5417,6 +6409,11 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "out of %d total test"
+msgid_plural "out of %d total tests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -5440,6 +6437,9 @@ msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5449,6 +6449,9 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
diff --git a/locale/gl_ES/gitlab.po b/locale/gl_ES/gitlab.po
new file mode 100644
index 00000000000..401895f0a16
--- /dev/null
+++ b/locale/gl_ES/gitlab.po
@@ -0,0 +1,7814 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab-ee\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
+"Language-Team: Galician\n"
+"Language: gl_ES\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"
+"X-Generator: crowdin.com\n"
+"X-Crowdin-Project: gitlab-ee\n"
+"X-Crowdin-Language: gl\n"
+"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
+
+msgid " and"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] "%d commit"
+msgstr[1] "%d commits"
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d exporter"
+msgid_plural "%d exporters"
+msgstr[0] "%d exportador"
+msgstr[1] "%d exportadores"
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] "%d incidencia"
+msgstr[1] "%d incidencias"
+
+msgid "%d layer"
+msgid_plural "%d layers"
+msgstr[0] "%d capa"
+msgstr[1] "%d capas"
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] "%d merge request"
+msgstr[1] "%d merge requests"
+
+msgid "%d metric"
+msgid_plural "%d metrics"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+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 "%{actionText} & %{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
+msgid "%{count} participant"
+msgid_plural "%{count} participants"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr ""
+
+msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr ""
+
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+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 not retry automatically. Reset storage information when the problem is resolved."
+msgstr ""
+
+msgid "%{openOrClose} %{noteable}"
+msgstr ""
+
+msgid "%{percent}%% complete"
+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 "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{text} is available"
+msgstr ""
+
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
+
+msgid "+ %{moreCount} more"
+msgstr ""
+
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
+msgid "- show less"
+msgstr ""
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 pipeline"
+msgid_plural "%d pipelines"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1st contribution!"
+msgstr ""
+
+msgid "2FA enabled"
+msgstr ""
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr ""
+
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr ""
+
+msgid "A new branch will be created in your fork and a new merge request will be started."
+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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
+msgid "A user with write access to the source branch selected this option"
+msgstr ""
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "About this feature"
+msgstr ""
+
+msgid "Abuse Reports"
+msgstr ""
+
+msgid "Abuse reports"
+msgstr ""
+
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
+msgid "Access Tokens"
+msgstr ""
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
+msgid "Account"
+msgstr ""
+
+msgid "Account and limit"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Active Sessions"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add Group Webhooks and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Add Kubernetes cluster"
+msgstr ""
+
+msgid "Add License"
+msgstr ""
+
+msgid "Add Readme"
+msgstr ""
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Add reaction"
+msgstr ""
+
+msgid "Add todo"
+msgstr ""
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
+msgstr ""
+
+msgid "AdminHealthPageLink|health page"
+msgstr ""
+
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
+msgid "Advanced"
+msgstr ""
+
+msgid "Advanced settings"
+msgstr ""
+
+msgid "All"
+msgstr ""
+
+msgid "All changes are committed"
+msgstr ""
+
+msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr ""
+
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr ""
+
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr ""
+
+msgid "Allow requests to the local network from hooks and services."
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
+msgstr ""
+
+msgid "Also called \"Relying party service URL\" or \"Reply URL\""
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "An error occurred when updating the issue weight"
+msgstr ""
+
+msgid "An error occurred while adding approver"
+msgstr ""
+
+msgid "An error occurred while detecting host keys"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
+msgid "An error occurred while fetching sidebar data"
+msgstr ""
+
+msgid "An error occurred while fetching the pipeline."
+msgstr ""
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while importing project: ${details}"
+msgstr ""
+
+msgid "An error occurred while initializing path locks"
+msgstr ""
+
+msgid "An error occurred while loading commit signatures"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while loading the file"
+msgstr ""
+
+msgid "An error occurred while making the request."
+msgstr ""
+
+msgid "An error occurred while removing approver"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while saving LDAP override status. Please try again."
+msgstr ""
+
+msgid "An error occurred while saving assignees"
+msgstr ""
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr ""
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr ""
+
+msgid "Appearance"
+msgstr ""
+
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
+msgid "Applications"
+msgstr ""
+
+msgid "Apr"
+msgstr ""
+
+msgid "April"
+msgstr ""
+
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+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 you want to unlock %{path_lock_path}?"
+msgstr ""
+
+msgid "Are you sure?"
+msgstr ""
+
+msgid "Artifacts"
+msgstr ""
+
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
+msgid "Assertion consumer service URL"
+msgstr ""
+
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assigned Issues"
+msgstr ""
+
+msgid "Assigned Merge Requests"
+msgstr ""
+
+msgid "Assigned to :name"
+msgstr ""
+
+msgid "Assigned to me"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Audit Events"
+msgstr ""
+
+msgid "Aug"
+msgstr ""
+
+msgid "August"
+msgstr ""
+
+msgid "Authentication Log"
+msgstr ""
+
+msgid "Authentication log"
+msgstr ""
+
+msgid "Author"
+msgstr ""
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto DevOps"
+msgstr ""
+
+msgid "Auto DevOps enabled"
+msgstr ""
+
+msgid "Auto DevOps, runners and job artifacts"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgstr ""
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps documentation"
+msgstr ""
+
+msgid "AutoDevOps|Enable in settings"
+msgstr ""
+
+msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
+msgstr ""
+
+msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
+msgstr ""
+
+msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
+msgstr ""
+
+msgid "AutoDevOps|add a Kubernetes cluster"
+msgstr ""
+
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr ""
+
+msgid "Available"
+msgstr ""
+
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
+msgid "Background Color"
+msgstr ""
+
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
+msgid "Background jobs"
+msgstr ""
+
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
+msgid "Begin with the selected commit"
+msgstr ""
+
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Billing"
+msgstr ""
+
+msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
+msgstr ""
+
+msgid "BillingPlans|Current plan"
+msgstr ""
+
+msgid "BillingPlans|Customer Support"
+msgstr ""
+
+msgid "BillingPlans|Downgrade"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
+msgstr ""
+
+msgid "BillingPlans|Manage plan"
+msgstr ""
+
+msgid "BillingPlans|Please contact %{customer_support_link} in that case."
+msgstr ""
+
+msgid "BillingPlans|See all %{plan_name} features"
+msgstr ""
+
+msgid "BillingPlans|This group uses the plan associated with its parent group."
+msgstr ""
+
+msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
+msgstr ""
+
+msgid "BillingPlans|Upgrade"
+msgstr ""
+
+msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
+msgid "BillingPlans|frequently asked questions"
+msgstr ""
+
+msgid "BillingPlans|monthly"
+msgstr ""
+
+msgid "BillingPlans|paid annually at %{price_per_year}"
+msgstr ""
+
+msgid "BillingPlans|per user"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
+msgid "Branch (%{branch_count})"
+msgid_plural "Branches (%{branch_count})"
+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 "Branch has changed"
+msgstr ""
+
+msgid "Branch is already taken"
+msgstr ""
+
+msgid "Branch name"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Branches|Active"
+msgstr ""
+
+msgid "Branches|Active branches"
+msgstr ""
+
+msgid "Branches|All"
+msgstr ""
+
+msgid "Branches|Cant find HEAD commit for this branch"
+msgstr ""
+
+msgid "Branches|Compare"
+msgstr ""
+
+msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
+msgstr ""
+
+msgid "Branches|Delete branch"
+msgstr ""
+
+msgid "Branches|Delete merged branches"
+msgstr ""
+
+msgid "Branches|Delete protected branch"
+msgstr ""
+
+msgid "Branches|Delete protected branch '%{branch_name}'?"
+msgstr ""
+
+msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "Branches|Filter by branch name"
+msgstr ""
+
+msgid "Branches|Merged into %{default_branch}"
+msgstr ""
+
+msgid "Branches|New branch"
+msgstr ""
+
+msgid "Branches|No branches to show"
+msgstr ""
+
+msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
+
+msgid "Branches|Overview"
+msgstr ""
+
+msgid "Branches|Protected branches can be managed in %{project_settings_link}."
+msgstr ""
+
+msgid "Branches|Show active branches"
+msgstr ""
+
+msgid "Branches|Show all branches"
+msgstr ""
+
+msgid "Branches|Show more active branches"
+msgstr ""
+
+msgid "Branches|Show more stale branches"
+msgstr ""
+
+msgid "Branches|Show overview of the branches"
+msgstr ""
+
+msgid "Branches|Show stale branches"
+msgstr ""
+
+msgid "Branches|Sort by"
+msgstr ""
+
+msgid "Branches|Stale"
+msgstr ""
+
+msgid "Branches|Stale branches"
+msgstr ""
+
+msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
+msgstr ""
+
+msgid "Branches|The default branch cannot be deleted"
+msgstr ""
+
+msgid "Branches|This branch hasn’t been merged into %{default_branch}."
+msgstr ""
+
+msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
+msgstr ""
+
+msgid "Branches|To confirm, type %{branch_name_confirmation}:"
+msgstr ""
+
+msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
+msgstr ""
+
+msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
+msgstr ""
+
+msgid "Branches|diverged from upstream"
+msgstr ""
+
+msgid "Branches|merged"
+msgstr ""
+
+msgid "Branches|project settings"
+msgstr ""
+
+msgid "Branches|protected"
+msgstr ""
+
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
+msgid "Business metrics (Custom)"
+msgstr ""
+
+msgid "ByAuthor|by"
+msgstr ""
+
+msgid "CI / CD"
+msgstr ""
+
+msgid "CI / CD Settings"
+msgstr ""
+
+msgid "CI/CD"
+msgstr ""
+
+msgid "CI/CD configuration"
+msgstr ""
+
+msgid "CI/CD for external repo"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
+msgid "CICD|Jobs"
+msgstr ""
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Cancel this job"
+msgstr ""
+
+msgid "Cannot be merged automatically"
+msgstr ""
+
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
+msgid "Certificate fingerprint"
+msgstr ""
+
+msgid "Change Weight"
+msgstr ""
+
+msgid "Change this value to influence how frequently the GitLab UI polls for updates."
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Chat"
+msgstr ""
+
+msgid "Check interval"
+msgstr ""
+
+msgid "Checking %{text} availability…"
+msgstr ""
+
+msgid "Checking branch availability..."
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
+msgid "Choose File ..."
+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 ""
+
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which repositories you want to connect and run CI/CD pipelines."
+msgstr ""
+
+msgid "Choose which repositories you want to import."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+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 "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr ""
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+
+msgid "Click to expand it."
+msgstr ""
+
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Client authentication certificate"
+msgstr ""
+
+msgid "Client authentication key"
+msgstr ""
+
+msgid "Client authentication key password"
+msgstr ""
+
+msgid "Clients"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
+msgstr ""
+
+msgid "Closed"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
+
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgstr ""
+
+msgid "ClusterIntegration|Copy API URL"
+msgstr ""
+
+msgid "ClusterIntegration|Copy CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
+
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
+
+msgid "ClusterIntegration|GitLab Runner"
+msgstr ""
+
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Google Kubernetes Engine project"
+msgstr ""
+
+msgid "ClusterIntegration|Helm Tiller"
+msgstr ""
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
+msgstr ""
+
+msgid "ClusterIntegration|Ingress"
+msgstr ""
+
+msgid "ClusterIntegration|Ingress IP Address"
+msgstr ""
+
+msgid "ClusterIntegration|Install"
+msgstr ""
+
+msgid "ClusterIntegration|Install Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Installed"
+msgstr ""
+
+msgid "ClusterIntegration|Installing"
+msgstr ""
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster health"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about environments"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about security configuration"
+msgstr ""
+
+msgid "ClusterIntegration|Machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
+
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|Note:"
+msgstr ""
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr ""
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace"
+msgstr ""
+
+msgid "ClusterIntegration|Project namespace (optional, unique)"
+msgstr ""
+
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove integration"
+msgstr ""
+
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Request to begin installing failed"
+msgstr ""
+
+msgid "ClusterIntegration|Save changes"
+msgstr ""
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
+msgid "ClusterIntegration|Security"
+msgstr ""
+
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Service token"
+msgstr ""
+
+msgid "ClusterIntegration|Show"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong on our end."
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|Something went wrong while installing %{title}"
+msgstr ""
+
+msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
+msgstr ""
+
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Token"
+msgstr ""
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
+
+msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
+msgstr ""
+
+msgid "ClusterIntegration|Zone"
+msgstr ""
+
+msgid "ClusterIntegration|access to Google Kubernetes Engine"
+msgstr ""
+
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
+msgid "ClusterIntegration|documentation"
+msgstr ""
+
+msgid "ClusterIntegration|help page"
+msgstr ""
+
+msgid "ClusterIntegration|installing applications"
+msgstr ""
+
+msgid "ClusterIntegration|meets the requirements"
+msgstr ""
+
+msgid "ClusterIntegration|properly configured"
+msgstr ""
+
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
+msgid "Collapse"
+msgstr ""
+
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
+
+msgid "Comments"
+msgstr ""
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit (%{commit_count})"
+msgid_plural "Commits (%{commit_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Commit Message"
+msgstr ""
+
+msgid "Commit duration in minutes for last 30 commits"
+msgstr ""
+
+msgid "Commit message"
+msgstr ""
+
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
+msgid "Commit to %{branchName} branch"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits feed"
+msgstr ""
+
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Commits|No related merge requests found"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Commit…"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "Compare changes with the last commit"
+msgstr ""
+
+msgid "Compare changes with the merge request target branch"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidential"
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
+msgid "Configure Gitaly timeouts."
+msgstr ""
+
+msgid "Configure Sidekiq job throttling."
+msgstr ""
+
+msgid "Configure automatic git checks and housekeeping on repositories."
+msgstr ""
+
+msgid "Configure limits for web and API requests."
+msgstr ""
+
+msgid "Configure push and pull mirrors."
+msgstr ""
+
+msgid "Configure storage path and circuit breaker settings."
+msgstr ""
+
+msgid "Configure the way a user creates a new account."
+msgstr ""
+
+msgid "Connect"
+msgstr ""
+
+msgid "Connect all repositories"
+msgstr ""
+
+msgid "Connect repositories from GitHub"
+msgstr ""
+
+msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
+msgstr ""
+
+msgid "Connecting..."
+msgstr ""
+
+msgid "Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Created"
+msgstr ""
+
+msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
+msgstr ""
+
+msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
+msgstr ""
+
+msgid "ContainerRegistry|How to use the Container Registry"
+msgstr ""
+
+msgid "ContainerRegistry|Learn more about"
+msgstr ""
+
+msgid "ContainerRegistry|No tags in Container Registry for this container image."
+msgstr ""
+
+msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
+msgstr ""
+
+msgid "ContainerRegistry|Remove repository"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tag"
+msgstr ""
+
+msgid "ContainerRegistry|Size"
+msgstr ""
+
+msgid "ContainerRegistry|Tag"
+msgstr ""
+
+msgid "ContainerRegistry|Tag ID"
+msgstr ""
+
+msgid "ContainerRegistry|Use different image names"
+msgstr ""
+
+msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr ""
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
+msgid "Continuous Integration and Deployment"
+msgstr ""
+
+msgid "Contribute to GitLab"
+msgstr ""
+
+msgid "Contribution"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributions per group member"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
+msgid "ContributorsPage|Building repository graph."
+msgstr ""
+
+msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
+msgstr ""
+
+msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr ""
+
+msgid "Control the display of third party offers."
+msgstr ""
+
+msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of repository backfill for this secondary node"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy branch name to clipboard"
+msgstr ""
+
+msgid "Copy command to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create a new branch"
+msgstr ""
+
+msgid "Create a new branch and merge request"
+msgstr ""
+
+msgid "Create a new issue"
+msgstr ""
+
+msgid "Create a personal access token on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Create branch"
+msgstr ""
+
+msgid "Create commit"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty repository"
+msgstr ""
+
+msgid "Create epic"
+msgstr ""
+
+msgid "Create file"
+msgstr ""
+
+msgid "Create group"
+msgstr ""
+
+msgid "Create group label"
+msgstr ""
+
+msgid "Create issue"
+msgstr ""
+
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create merge request and branch"
+msgstr ""
+
+msgid "Create new branch"
+msgstr ""
+
+msgid "Create new directory"
+msgstr ""
+
+msgid "Create new file"
+msgstr ""
+
+msgid "Create new file or directory"
+msgstr ""
+
+msgid "Create new label"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "Create project label"
+msgstr ""
+
+msgid "CreateNewFork|Fork"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
+msgid "CreateTokenToCloneLink|create a personal access token"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
+msgstr ""
+
+msgid "Creating epic"
+msgstr ""
+
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Current node"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+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 "Customize colors"
+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 "Cycle Analytics"
+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 "Dashboard"
+msgstr ""
+
+msgid "DashboardProjects|All"
+msgstr ""
+
+msgid "DashboardProjects|Personal"
+msgstr ""
+
+msgid "Dec"
+msgstr ""
+
+msgid "December"
+msgstr ""
+
+msgid "Decline and sign out"
+msgstr ""
+
+msgid "Default classification label"
+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 ""
+
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Deploy Keys"
+msgstr ""
+
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Diffs|No file name available"
+msgstr ""
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
+msgid "Directory name"
+msgstr ""
+
+msgid "Disable"
+msgstr ""
+
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
+msgid "Discard draft"
+msgstr ""
+
+msgid "Discover GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr ""
+
+msgid "Dismiss Merge Request promotion"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
+msgid "Documentation for popular identity providers"
+msgstr ""
+
+msgid "Domain"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Done"
+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 "Downvotes"
+msgstr ""
+
+msgid "Due date"
+msgstr ""
+
+msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
+msgstr ""
+
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Label"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr ""
+
+msgid "Elasticsearch"
+msgstr ""
+
+msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
+msgstr ""
+
+msgid "Email"
+msgstr ""
+
+msgid "Email patch"
+msgstr ""
+
+msgid "Emails"
+msgstr ""
+
+msgid "Embed"
+msgstr ""
+
+msgid "Enable"
+msgstr ""
+
+msgid "Enable Auto DevOps"
+msgstr ""
+
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
+msgid "Enable SAML authentication for this group"
+msgstr ""
+
+msgid "Enable Sentry for error reporting and logging."
+msgstr ""
+
+msgid "Enable and configure InfluxDB metrics."
+msgstr ""
+
+msgid "Enable and configure Prometheus metrics."
+msgstr ""
+
+msgid "Enable classification control using an external service"
+msgstr ""
+
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
+msgid "Enable or disable version check and usage ping."
+msgstr ""
+
+msgid "Enable reCAPTCHA or Akismet and set IP limits."
+msgstr ""
+
+msgid "Enable the Performance Bar for a given group."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
+msgid "Environments|An error occurred while fetching the environments."
+msgstr ""
+
+msgid "Environments|An error occurred while making the request."
+msgstr ""
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
+msgid "Environments|Commit"
+msgstr ""
+
+msgid "Environments|Deploy to..."
+msgstr ""
+
+msgid "Environments|Deployment"
+msgstr ""
+
+msgid "Environments|Environment"
+msgstr ""
+
+msgid "Environments|Environments"
+msgstr ""
+
+msgid "Environments|Job"
+msgstr ""
+
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
+msgid "Environments|New environment"
+msgstr ""
+
+msgid "Environments|No deployments yet"
+msgstr ""
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
+
+msgid "Environments|Read more about environments"
+msgstr ""
+
+msgid "Environments|Rollback environment"
+msgstr ""
+
+msgid "Environments|Show all"
+msgstr ""
+
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr ""
+
+msgid "Environments|You don't have any environments right now."
+msgstr ""
+
+msgid "Epic"
+msgstr ""
+
+msgid "Epic will be removed! Are you sure?"
+msgstr ""
+
+msgid "Epics"
+msgstr ""
+
+msgid "Epics Roadmap"
+msgstr ""
+
+msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgstr ""
+
+msgid "Error Reporting and Logging"
+msgstr ""
+
+msgid "Error creating epic"
+msgstr ""
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr ""
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
+msgid "Estimated"
+msgstr ""
+
+msgid "EventFilterBy|Filter by all"
+msgstr ""
+
+msgid "EventFilterBy|Filter by comments"
+msgstr ""
+
+msgid "EventFilterBy|Filter by issue events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by merge events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by push events"
+msgstr ""
+
+msgid "EventFilterBy|Filter by team"
+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 "Everyone can contribute"
+msgstr ""
+
+msgid "Expand"
+msgstr ""
+
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
+msgid "Explore projects"
+msgstr ""
+
+msgid "Explore public groups"
+msgstr ""
+
+msgid "External Classification Policy Authorization"
+msgstr ""
+
+msgid "External authentication"
+msgstr ""
+
+msgid "External authorization denied access to this project"
+msgstr ""
+
+msgid "External authorization request timeout"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification Label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|Classification label"
+msgstr ""
+
+msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
+msgstr ""
+
+msgid "Facebook"
+msgstr ""
+
+msgid "Failed"
+msgstr ""
+
+msgid "Failed Jobs"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to check related branches."
+msgstr ""
+
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Failed to update issues, please try again."
+msgstr ""
+
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
+msgid "Feb"
+msgstr ""
+
+msgid "February"
+msgstr ""
+
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
+msgid "Files"
+msgstr ""
+
+msgid "Files (%{human_size})"
+msgstr ""
+
+msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
+msgid "Filter by commit message"
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
+msgstr ""
+
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Finished"
+msgstr ""
+
+msgid "FirstPushedBy|First"
+msgstr ""
+
+msgid "FirstPushedBy|pushed by"
+msgstr ""
+
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr ""
+
+msgid "Footer message"
+msgstr ""
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
+msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
+msgstr ""
+
+msgid "Forking in progress"
+msgstr ""
+
+msgid "Format"
+msgstr ""
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
+msgid "From %{provider_title}"
+msgstr ""
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
+msgid "From issue creation until deploy to production"
+msgstr ""
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+
+msgid "From the Kubernetes cluster details view, install Runner from the applications list"
+msgstr ""
+
+msgid "GPG Keys"
+msgstr ""
+
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
+msgid "Generate a default set of labels"
+msgstr ""
+
+msgid "Geo Nodes"
+msgstr ""
+
+msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr ""
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr ""
+
+msgid "GeoNodes|Checksummed"
+msgstr ""
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects"
+msgstr ""
+
+msgid "GeoNodes|Local attachments"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Node Authentication was successfully repaired."
+msgstr ""
+
+msgid "GeoNodes|Node was successfully removed."
+msgstr ""
+
+msgid "GeoNodes|Not checksummed"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
+msgstr ""
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while changing node status"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while removing node"
+msgstr ""
+
+msgid "GeoNodes|Something went wrong while repairing node"
+msgstr ""
+
+msgid "GeoNodes|Storage config"
+msgstr ""
+
+msgid "GeoNodes|Sync settings"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Unverified"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Verified"
+msgstr ""
+
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
+
+msgid "Geo|File sync capacity"
+msgstr ""
+
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
+
+msgid "Geo|Repository sync capacity"
+msgstr ""
+
+msgid "Geo|Select groups to replicate."
+msgstr ""
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
+msgid "Git repository URL"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
+msgid "Git storage health information has been reset"
+msgstr ""
+
+msgid "Git strategy for pipelines"
+msgstr ""
+
+msgid "Git version"
+msgstr ""
+
+msgid "GitHub import"
+msgstr ""
+
+msgid "GitLab CI Linter has been moved"
+msgstr ""
+
+msgid "GitLab Geo"
+msgstr ""
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab single sign on URL"
+msgstr ""
+
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "Gitaly"
+msgstr ""
+
+msgid "Gitaly Servers"
+msgstr ""
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
+msgid "Go back"
+msgstr ""
+
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
+msgid "Go to your fork"
+msgstr ""
+
+msgid "GoToYourFork|Fork"
+msgstr ""
+
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr ""
+
+msgid "Got it!"
+msgstr ""
+
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
+msgstr ""
+
+msgid "GroupRoadmap|From %{dateWord}"
+msgstr ""
+
+msgid "GroupRoadmap|Loading roadmap"
+msgstr ""
+
+msgid "GroupRoadmap|Something went wrong while fetching epics"
+msgstr ""
+
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|Until %{dateWord}"
+msgstr ""
+
+msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
+msgstr ""
+
+msgid "GroupSettings|Share with group lock"
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
+msgstr ""
+
+msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
+msgstr ""
+
+msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
+msgstr ""
+
+msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr ""
+
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr ""
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr ""
+
+msgid "GroupsEmptyState|No groups found"
+msgstr ""
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the 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 ""
+
+msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
+msgstr ""
+
+msgid "GroupsTree|Filter by name..."
+msgstr ""
+
+msgid "GroupsTree|Leave this group"
+msgstr ""
+
+msgid "GroupsTree|Loading groups"
+msgstr ""
+
+msgid "GroupsTree|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsTree|Sorry, no groups or projects matched your search"
+msgstr ""
+
+msgid "Have your users email"
+msgstr ""
+
+msgid "Header message"
+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 "Help"
+msgstr ""
+
+msgid "Help page"
+msgstr ""
+
+msgid "Help page text and support page url."
+msgstr ""
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Hide whitespace changes"
+msgstr ""
+
+msgid "History"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
+msgid "Identity provider single sign on URL"
+msgstr ""
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+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 already have files you can push them using the %{link_to_cli} below."
+msgstr ""
+
+msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
+msgid "Import"
+msgstr ""
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
+msgid "Import all repositories"
+msgstr ""
+
+msgid "Import an exported GitLab project"
+msgstr ""
+
+msgid "Import in progress"
+msgstr ""
+
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
+msgid "ImportButtons|Connect repositories from"
+msgstr ""
+
+msgid "Improve Issue boards with GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
+
+msgid "Instance"
+msgid_plural "Instances"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Integrations"
+msgstr ""
+
+msgid "Integrations Settings"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "Internal - The group and any internal projects can be viewed by any logged in user."
+msgstr ""
+
+msgid "Internal - The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "Interval Pattern"
+msgstr ""
+
+msgid "Introducing Cycle Analytics"
+msgstr ""
+
+msgid "Issue Boards"
+msgstr ""
+
+msgid "Issue board focus mode"
+msgstr ""
+
+msgid "Issue events"
+msgstr ""
+
+msgid "IssueBoards|Board"
+msgstr ""
+
+msgid "IssueBoards|Boards"
+msgstr ""
+
+msgid "Issues"
+msgstr ""
+
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
+msgid "Issues closed"
+msgstr ""
+
+msgid "Jan"
+msgstr ""
+
+msgid "January"
+msgstr ""
+
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
+msgid "Jobs"
+msgstr ""
+
+msgid "Jul"
+msgstr ""
+
+msgid "July"
+msgstr ""
+
+msgid "Jun"
+msgstr ""
+
+msgid "June"
+msgstr ""
+
+msgid "Koding"
+msgstr ""
+
+msgid "Koding Dashboard"
+msgstr ""
+
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes configured"
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
+msgid "LFS"
+msgstr ""
+
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
+msgid "Label"
+msgstr ""
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr ""
+
+msgid "LabelSelect|Labels"
+msgstr ""
+
+msgid "Labels"
+msgstr ""
+
+msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
+msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
+msgstr ""
+
+msgid "Labels|Promote Label"
+msgstr ""
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Last Pipeline"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Last edited %{date}"
+msgstr ""
+
+msgid "Last edited by %{name}"
+msgstr ""
+
+msgid "Last update"
+msgstr ""
+
+msgid "Last updated"
+msgstr ""
+
+msgid "LastPushEvent|You pushed to"
+msgstr ""
+
+msgid "LastPushEvent|at"
+msgstr ""
+
+msgid "Latest changes"
+msgstr ""
+
+msgid "Learn more"
+msgstr ""
+
+msgid "Learn more about Kubernetes"
+msgstr ""
+
+msgid "Learn more about protected branches"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
+msgid "License"
+msgstr ""
+
+msgid "LinkedIn"
+msgstr ""
+
+msgid "List"
+msgstr ""
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
+msgid "List your GitHub repositories"
+msgstr ""
+
+msgid "Loading contribution stats for group members"
+msgstr ""
+
+msgid "Loading the GitLab IDE..."
+msgstr ""
+
+msgid "Loading..."
+msgstr ""
+
+msgid "Lock"
+msgstr ""
+
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock not found"
+msgstr ""
+
+msgid "Lock to current projects"
+msgstr ""
+
+msgid "Locked"
+msgstr ""
+
+msgid "Locked Files"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
+
+msgid "Locks give the ability to lock specific file or folder."
+msgstr ""
+
+msgid "Logs"
+msgstr ""
+
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
+msgid "Manage all notifications"
+msgstr ""
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage group labels"
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
+msgid "Manage project labels"
+msgstr ""
+
+msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr ""
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
+msgid "Mar"
+msgstr ""
+
+msgid "March"
+msgstr ""
+
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
+msgstr ""
+
+msgid "Maximum git storage failures"
+msgstr ""
+
+msgid "May"
+msgstr ""
+
+msgid "Median"
+msgstr ""
+
+msgid "Members"
+msgstr ""
+
+msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
+msgstr ""
+
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
+msgid "Merge Requests"
+msgstr ""
+
+msgid "Merge Requests created"
+msgstr ""
+
+msgid "Merge events"
+msgstr ""
+
+msgid "Merge request"
+msgstr ""
+
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
+msgid "Messages"
+msgstr ""
+
+msgid "Metrics"
+msgstr ""
+
+msgid "Metrics - Influx"
+msgstr ""
+
+msgid "Metrics - Prometheus"
+msgstr ""
+
+msgid "Metrics|Business"
+msgstr ""
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Create metric"
+msgstr ""
+
+msgid "Metrics|Edit metric"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|For grouping similar metrics"
+msgstr ""
+
+msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|Legend label (optional)"
+msgstr ""
+
+msgid "Metrics|Must be a valid PromQL query."
+msgstr ""
+
+msgid "Metrics|Name"
+msgstr ""
+
+msgid "Metrics|New metric"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|Prometheus Query Documentation"
+msgstr ""
+
+msgid "Metrics|Query"
+msgstr ""
+
+msgid "Metrics|Response"
+msgstr ""
+
+msgid "Metrics|System"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
+msgid "Metrics|Type"
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unit label"
+msgstr ""
+
+msgid "Metrics|Used as a title for the chart"
+msgstr ""
+
+msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
+msgstr ""
+
+msgid "Metrics|Y-axis label"
+msgstr ""
+
+msgid "Metrics|e.g. HTTP requests"
+msgstr ""
+
+msgid "Metrics|e.g. Requests/second"
+msgstr ""
+
+msgid "Metrics|e.g. Throughput"
+msgstr ""
+
+msgid "Metrics|e.g. rate(http_requests_total[5m])"
+msgstr ""
+
+msgid "Metrics|e.g. req/sec"
+msgstr ""
+
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
+msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
+msgstr ""
+
+msgid "Milestones|Promote Milestone"
+msgstr ""
+
+msgid "Milestones|This action cannot be reversed."
+msgstr ""
+
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
+msgid "Modal|Cancel"
+msgstr ""
+
+msgid "Modal|Close"
+msgstr ""
+
+msgid "Monitoring"
+msgstr ""
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
+msgid "More info"
+msgstr ""
+
+msgid "More information"
+msgstr ""
+
+msgid "More information is available|here"
+msgstr ""
+
+msgid "Most stars"
+msgstr ""
+
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr ""
+
+msgid "Name new label"
+msgstr ""
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "New Label"
+msgstr ""
+
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
+msgid "New branch"
+msgstr ""
+
+msgid "New branch unavailable"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New epic"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New group"
+msgstr ""
+
+msgid "New identity"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New label"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
+msgid "New project"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New subgroup"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
+msgid "No Label"
+msgstr ""
+
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
+msgid "None"
+msgstr ""
+
+msgid "Not allowed to merge"
+msgstr ""
+
+msgid "Not available"
+msgstr ""
+
+msgid "Not available for private projects"
+msgstr ""
+
+msgid "Not available for protected branches"
+msgstr ""
+
+msgid "Not confidential"
+msgstr ""
+
+msgid "Not enough data"
+msgstr ""
+
+msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr ""
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+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 "Notifications"
+msgstr ""
+
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
+msgid "Nov"
+msgstr ""
+
+msgid "November"
+msgstr ""
+
+msgid "Number of access attempts"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Oct"
+msgstr ""
+
+msgid "October"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
+msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr ""
+
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
+msgstr ""
+
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
+msgid "Only project members can comment."
+msgstr ""
+
+msgid "Oops, are you sure?"
+msgstr ""
+
+msgid "Open"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
+msgid "Opened"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr ""
+
+msgid "Opens in a new window"
+msgstr ""
+
+msgid "Operations"
+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 ""
+
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr ""
+
+msgid "Outbound requests"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Owner"
+msgstr ""
+
+msgid "Pages"
+msgstr ""
+
+msgid "Pagination|Last »"
+msgstr ""
+
+msgid "Pagination|Next"
+msgstr ""
+
+msgid "Pagination|Prev"
+msgstr ""
+
+msgid "Pagination|« First"
+msgstr ""
+
+msgid "Part of merge request changes"
+msgstr ""
+
+msgid "Password"
+msgstr ""
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
+msgid "Pending"
+msgstr ""
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
+msgid "Performance optimization"
+msgstr ""
+
+msgid "Permissions"
+msgstr ""
+
+msgid "Personal Access Token"
+msgstr ""
+
+msgid "Pipeline"
+msgstr ""
+
+msgid "Pipeline Health"
+msgstr ""
+
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "Pipeline quota"
+msgstr ""
+
+msgid "Pipeline triggers"
+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|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+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 "Pipelines for last month"
+msgstr ""
+
+msgid "Pipelines for last week"
+msgstr ""
+
+msgid "Pipelines for last year"
+msgstr ""
+
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|CI Lint"
+msgstr ""
+
+msgid "Pipelines|Clear Runner Caches"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
+msgid "Pipelines|Loading Pipelines"
+msgstr ""
+
+msgid "Pipelines|Project cache successfully reset."
+msgstr ""
+
+msgid "Pipelines|Run Pipeline"
+msgstr ""
+
+msgid "Pipelines|Something went wrong while cleaning runners cache."
+msgstr ""
+
+msgid "Pipelines|There are currently no %{scope} pipelines."
+msgstr ""
+
+msgid "Pipelines|There are currently no pipelines."
+msgstr ""
+
+msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr ""
+
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr ""
+
+msgid "Pipeline|Variables"
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
+msgstr ""
+
+msgid "Pipeline|all"
+msgstr ""
+
+msgid "Pipeline|success"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
+msgid "PlantUML"
+msgstr ""
+
+msgid "Play"
+msgstr ""
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
+msgid "Please try again"
+msgstr ""
+
+msgid "Please wait while we connect to your repository. Refresh at will."
+msgstr ""
+
+msgid "Please wait while we import the repository for you. Refresh at will."
+msgstr ""
+
+msgid "Preferences"
+msgstr ""
+
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Primary"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr ""
+
+msgid "Private projects can be created in your personal namespace with:"
+msgstr ""
+
+msgid "Profile"
+msgstr ""
+
+msgid "Profile Settings"
+msgstr ""
+
+msgid "Profiles|Account scheduled for removal."
+msgstr ""
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
+msgid "Profiles|Delete Account"
+msgstr ""
+
+msgid "Profiles|Delete account"
+msgstr ""
+
+msgid "Profiles|Delete your account?"
+msgstr ""
+
+msgid "Profiles|Deleting an account has the following effects:"
+msgstr ""
+
+msgid "Profiles|Invalid password"
+msgstr ""
+
+msgid "Profiles|Invalid username"
+msgstr ""
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
+msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr ""
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
+msgid "Profiles|You don't have access to delete this user."
+msgstr ""
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr ""
+
+msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr ""
+
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
+msgid "Profiles|your account"
+msgstr ""
+
+msgid "Profiling - Performance bar"
+msgstr ""
+
+msgid "Programming languages used in this repository"
+msgstr ""
+
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
+msgid "Project '%{project_name}' is in the process of being deleted."
+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 Badges"
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+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 name"
+msgstr ""
+
+msgid "ProjectActivityRSS|Subscribe"
+msgstr ""
+
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
+msgid "ProjectLifecycle|Stage"
+msgstr ""
+
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
+msgid "ProjectSettings|Contact an admin to change this setting."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
+msgid "ProjectSettings|Only signed commits can be pushed to this repository."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
+msgstr ""
+
+msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
+msgstr ""
+
+msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
+msgstr ""
+
+msgid "Projects"
+msgstr ""
+
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
+msgid "ProjectsDropdown|Frequently visited"
+msgstr ""
+
+msgid "ProjectsDropdown|Loading projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Projects you visit often will appear here"
+msgstr ""
+
+msgid "ProjectsDropdown|Search your projects"
+msgstr ""
+
+msgid "ProjectsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr ""
+
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr ""
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr ""
+
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
+msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
+msgstr ""
+
+msgid "PrometheusService|Common metrics"
+msgstr ""
+
+msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
+msgstr ""
+
+msgid "PrometheusService|Custom metrics"
+msgstr ""
+
+msgid "PrometheusService|Finding and configuring metrics..."
+msgstr ""
+
+msgid "PrometheusService|Finding custom metrics..."
+msgstr ""
+
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
+msgid "PrometheusService|Metrics"
+msgstr ""
+
+msgid "PrometheusService|Missing environment variable"
+msgstr ""
+
+msgid "PrometheusService|More information"
+msgstr ""
+
+msgid "PrometheusService|New metric"
+msgstr ""
+
+msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
+msgstr ""
+
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
+msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
+msgstr ""
+
+msgid "PrometheusService|Time-series monitoring service"
+msgstr ""
+
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
+msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
+msgstr ""
+
+msgid "Promote"
+msgstr ""
+
+msgid "Promote these project milestones into a group milestone."
+msgstr ""
+
+msgid "Promote to Group Milestone"
+msgstr ""
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
+msgid "Protip:"
+msgstr ""
+
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr ""
+
+msgid "Public - The project can be accessed without any authentication."
+msgstr ""
+
+msgid "Public pipelines"
+msgstr ""
+
+msgid "Push Rules"
+msgstr ""
+
+msgid "Push events"
+msgstr ""
+
+msgid "Push project from command line"
+msgstr ""
+
+msgid "Push to create a project"
+msgstr ""
+
+msgid "PushRule|Committer restriction"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
+msgid "Read more"
+msgstr ""
+
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
+msgid "Readme"
+msgstr ""
+
+msgid "Real-time features"
+msgstr ""
+
+msgid "Reference:"
+msgstr ""
+
+msgid "Refresh"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
+msgid "Registry"
+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 "Related merge requests"
+msgstr ""
+
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove"
+msgstr ""
+
+msgid "Remove Runner"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
+msgid "Remove priority"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Repair authentication"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
+msgid "Repo by URL"
+msgstr ""
+
+msgid "Repository"
+msgstr ""
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
+msgid "Repository has no locks."
+msgstr ""
+
+msgid "Repository maintenance"
+msgstr ""
+
+msgid "Repository mirror"
+msgstr ""
+
+msgid "Repository storage"
+msgstr ""
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
+msgid "Reset git storage health information"
+msgstr ""
+
+msgid "Reset health check access token"
+msgstr ""
+
+msgid "Reset runners registration token"
+msgstr ""
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
+msgid "Resolve discussion"
+msgstr ""
+
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
+msgstr ""
+
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
+msgid "Review"
+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 ""
+
+msgid "Reviewing"
+msgstr ""
+
+msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr ""
+
+msgid "Revoke"
+msgstr ""
+
+msgid "Roadmap"
+msgstr ""
+
+msgid "Run CI/CD pipelines for external repositories"
+msgstr ""
+
+msgid "Runner token"
+msgstr ""
+
+msgid "Runners"
+msgstr ""
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
+msgid "Running"
+msgstr ""
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
+msgid "SAML Single Sign On"
+msgstr ""
+
+msgid "SAML Single Sign On Settings"
+msgstr ""
+
+msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
+msgstr ""
+
+msgid "SSH Keys"
+msgstr ""
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr ""
+
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Save variables"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Scheduled"
+msgstr ""
+
+msgid "Schedules"
+msgstr ""
+
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Scope"
+msgstr ""
+
+msgid "Scoped issue boards"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Search branches"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
+msgstr ""
+
+msgid "Seconds to wait for a storage access attempt"
+msgstr ""
+
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
+
+msgid "Security report"
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
+msgid "Select a namespace to fork the project"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select an existing Kubernetes cluster or create a new one"
+msgstr ""
+
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
+msgid "Selective synchronization"
+msgstr ""
+
+msgid "Send email"
+msgstr ""
+
+msgid "Sep"
+msgstr ""
+
+msgid "September"
+msgstr ""
+
+msgid "Server version"
+msgstr ""
+
+msgid "Service Desk"
+msgstr ""
+
+msgid "Service Templates"
+msgstr ""
+
+msgid "Service URL"
+msgstr ""
+
+msgid "Session expiration, projects limit and attachment size."
+msgstr ""
+
+msgid "Set a password on your account to pull or push via %{protocol}."
+msgstr ""
+
+msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
+msgstr ""
+
+msgid "Set max session time for web terminal."
+msgstr ""
+
+msgid "Set notification email for abuse reports."
+msgstr ""
+
+msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
+msgstr ""
+
+msgid "Set up CI/CD"
+msgstr ""
+
+msgid "Set up Koding"
+msgstr ""
+
+msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
+msgid "Settings"
+msgstr ""
+
+msgid "Setup a specific Runner automatically"
+msgstr ""
+
+msgid "Share"
+msgstr ""
+
+msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
+msgstr ""
+
+msgid "Shared Runners"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
+msgid "Sherlock Transactions"
+msgstr ""
+
+msgid "Show command"
+msgstr ""
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr ""
+
+msgid "Show parent subgroups"
+msgstr ""
+
+msgid "Show whitespace changes"
+msgstr ""
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Side-by-side"
+msgstr ""
+
+msgid "Sidebar|Change weight"
+msgstr ""
+
+msgid "Sidebar|None"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
+msgid "Sidebar|Weight"
+msgstr ""
+
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
+msgid "Sign-in restrictions"
+msgstr ""
+
+msgid "Sign-up restrictions"
+msgstr ""
+
+msgid "Size and domain settings for static websites"
+msgstr ""
+
+msgid "Slack application"
+msgstr ""
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
+msgid "Snippets"
+msgstr ""
+
+msgid "Something went wrong on our end"
+msgstr ""
+
+msgid "Something went wrong on our end."
+msgstr ""
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr ""
+
+msgid "Something went wrong while fetching the projects."
+msgstr ""
+
+msgid "Something went wrong while fetching the registry list."
+msgstr ""
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
+msgid "Something went wrong. Please try again."
+msgstr ""
+
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
+msgid "Sort by"
+msgstr ""
+
+msgid "SortOptions|Access level, ascending"
+msgstr ""
+
+msgid "SortOptions|Access level, descending"
+msgstr ""
+
+msgid "SortOptions|Created date"
+msgstr ""
+
+msgid "SortOptions|Due date"
+msgstr ""
+
+msgid "SortOptions|Due later"
+msgstr ""
+
+msgid "SortOptions|Due soon"
+msgstr ""
+
+msgid "SortOptions|Label priority"
+msgstr ""
+
+msgid "SortOptions|Largest group"
+msgstr ""
+
+msgid "SortOptions|Largest repository"
+msgstr ""
+
+msgid "SortOptions|Last created"
+msgstr ""
+
+msgid "SortOptions|Last joined"
+msgstr ""
+
+msgid "SortOptions|Last updated"
+msgstr ""
+
+msgid "SortOptions|Least popular"
+msgstr ""
+
+msgid "SortOptions|Less weight"
+msgstr ""
+
+msgid "SortOptions|Milestone"
+msgstr ""
+
+msgid "SortOptions|Milestone due later"
+msgstr ""
+
+msgid "SortOptions|Milestone due soon"
+msgstr ""
+
+msgid "SortOptions|More weight"
+msgstr ""
+
+msgid "SortOptions|Most popular"
+msgstr ""
+
+msgid "SortOptions|Name"
+msgstr ""
+
+msgid "SortOptions|Name, ascending"
+msgstr ""
+
+msgid "SortOptions|Name, descending"
+msgstr ""
+
+msgid "SortOptions|Oldest created"
+msgstr ""
+
+msgid "SortOptions|Oldest joined"
+msgstr ""
+
+msgid "SortOptions|Oldest sign in"
+msgstr ""
+
+msgid "SortOptions|Oldest updated"
+msgstr ""
+
+msgid "SortOptions|Popularity"
+msgstr ""
+
+msgid "SortOptions|Priority"
+msgstr ""
+
+msgid "SortOptions|Recent sign in"
+msgstr ""
+
+msgid "SortOptions|Start later"
+msgstr ""
+
+msgid "SortOptions|Start soon"
+msgstr ""
+
+msgid "SortOptions|Weight"
+msgstr ""
+
+msgid "Source"
+msgstr ""
+
+msgid "Source (branch or tag)"
+msgstr ""
+
+msgid "Source code"
+msgstr ""
+
+msgid "Source is not available"
+msgstr ""
+
+msgid "Spam Logs"
+msgstr ""
+
+msgid "Spam and Anti-bot Protection"
+msgstr ""
+
+msgid "Specific Runners"
+msgstr ""
+
+msgid "Specify the following URL during the Runner setup:"
+msgstr ""
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Starred Projects"
+msgstr ""
+
+msgid "Starred Projects' Activity"
+msgstr ""
+
+msgid "Starred projects"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start the Runner!"
+msgstr ""
+
+msgid "Started"
+msgstr ""
+
+msgid "Starts at (UTC)"
+msgstr ""
+
+msgid "State your message to activate"
+msgstr ""
+
+msgid "Status"
+msgstr ""
+
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr ""
+
+msgid "Storage"
+msgstr ""
+
+msgid "Storage:"
+msgstr ""
+
+msgid "Subgroups"
+msgstr ""
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Sync information"
+msgstr ""
+
+msgid "System Hooks"
+msgstr ""
+
+msgid "System Info"
+msgstr ""
+
+msgid "System header and footer:"
+msgstr ""
+
+msgid "System metrics (Custom)"
+msgstr ""
+
+msgid "Tag (%{tag_count})"
+msgid_plural "Tags (%{tag_count})"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
+msgid "Tags:"
+msgstr ""
+
+msgid "TagsPage|Browse commits"
+msgstr ""
+
+msgid "TagsPage|Browse files"
+msgstr ""
+
+msgid "TagsPage|Can't find HEAD commit for this tag"
+msgstr ""
+
+msgid "TagsPage|Cancel"
+msgstr ""
+
+msgid "TagsPage|Create tag"
+msgstr ""
+
+msgid "TagsPage|Delete tag"
+msgstr ""
+
+msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
+msgstr ""
+
+msgid "TagsPage|Edit release notes"
+msgstr ""
+
+msgid "TagsPage|Existing branch name, tag, or commit SHA"
+msgstr ""
+
+msgid "TagsPage|Filter by tag name"
+msgstr ""
+
+msgid "TagsPage|New Tag"
+msgstr ""
+
+msgid "TagsPage|New tag"
+msgstr ""
+
+msgid "TagsPage|Optionally, add a message to the tag."
+msgstr ""
+
+msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
+msgstr ""
+
+msgid "TagsPage|Release notes"
+msgstr ""
+
+msgid "TagsPage|Repository has no tags yet."
+msgstr ""
+
+msgid "TagsPage|Sort by"
+msgstr ""
+
+msgid "TagsPage|Tags"
+msgstr ""
+
+msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
+msgstr ""
+
+msgid "TagsPage|This tag has no release notes."
+msgstr ""
+
+msgid "TagsPage|Use git tag command to add a new one:"
+msgstr ""
+
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
+
+msgid "TagsPage|protected"
+msgstr ""
+
+msgid "Target Branch"
+msgstr ""
+
+msgid "Target branch"
+msgstr ""
+
+msgid "Team"
+msgstr ""
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
+msgid "Thanks! Don't show me this again"
+msgstr ""
+
+msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+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 ""
+
+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 connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+msgstr ""
+
+msgid "The fork relationship has been removed."
+msgstr ""
+
+msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
+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 maximum file size allowed is 200KB."
+msgstr ""
+
+msgid "The number of attempts GitLab will make to access a storage."
+msgstr ""
+
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
+
+msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
+msgstr ""
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
+msgid "The phase of the development lifecycle."
+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 private key to use when a client certificate is provided. This value is encrypted at rest."
+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 pseudonymizer data collection is disabled. 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."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
+msgid "The repository for this project is empty"
+msgstr ""
+
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
+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 roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "The secure token used by the Runner to checkout the project"
+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 in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
+msgstr ""
+
+msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
+msgstr ""
+
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
+
+msgid "The time taken by each data entry gathered by that stage."
+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>:</code>. 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 ""
+
+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 no issues to show"
+msgstr ""
+
+msgid "There are no labels yet"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
+msgid "There are problems accessing Git storage: "
+msgstr ""
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
+
+msgid "This directory"
+msgstr ""
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr ""
+
+msgid "This is the author's first Merge Request to this project."
+msgstr ""
+
+msgid "This issue is confidential"
+msgstr ""
+
+msgid "This issue is confidential and locked."
+msgstr ""
+
+msgid "This issue is locked."
+msgstr ""
+
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
+msgid "This merge request is locked."
+msgstr ""
+
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
+msgid "This page will be removed in a future release."
+msgstr ""
+
+msgid "This project"
+msgstr ""
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
+msgid "This will delete the custom metric, Are you sure?"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+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 in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
+msgstr ""
+
+msgid "Time tracking"
+msgstr ""
+
+msgid "Time until first merge request"
+msgstr ""
+
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours ago"
+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 ago"
+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 ago"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour ago"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute ago"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month ago"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week ago"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year ago"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+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|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
+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 "Tip:"
+msgstr ""
+
+msgid "Title"
+msgstr ""
+
+msgid "To GitLab"
+msgstr ""
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr ""
+
+msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr ""
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr ""
+
+msgid "To import an SVN repository, check out %{svn_link}."
+msgstr ""
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
+msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
+msgstr ""
+
+msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr ""
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr ""
+
+msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
+msgstr ""
+
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
+msgid "Todo"
+msgstr ""
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
+msgid "Total Time"
+msgstr ""
+
+msgid "Total test time for all commits/merges"
+msgstr ""
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track activity with Contribution Analytics."
+msgstr ""
+
+msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trending"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
+msgid "Turn on Service Desk"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
+msgid "Unlock"
+msgstr ""
+
+msgid "Unlocked"
+msgstr ""
+
+msgid "Unresolve discussion"
+msgstr ""
+
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
+msgid "Unstar"
+msgstr ""
+
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
+msgid "Up to date"
+msgstr ""
+
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
+msgid "Upgrade your plan to activate Advanced Global Search."
+msgstr ""
+
+msgid "Upgrade your plan to activate Contribution Analytics."
+msgstr ""
+
+msgid "Upgrade your plan to activate Group Webhooks."
+msgstr ""
+
+msgid "Upgrade your plan to activate Issue weight."
+msgstr ""
+
+msgid "Upgrade your plan to improve Issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "Upload new avatar"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Upvotes"
+msgstr ""
+
+msgid "Usage statistics"
+msgstr ""
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
+msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
+msgid "Use the following registration token during setup:"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "Used by members to sign in to your group in GitLab"
+msgstr ""
+
+msgid "User Settings"
+msgstr ""
+
+msgid "User and IP Rate Limits"
+msgstr ""
+
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
+msgid "Various container registry settings."
+msgstr ""
+
+msgid "Various email settings."
+msgstr ""
+
+msgid "Various settings that affect GitLab performance."
+msgstr ""
+
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
+msgstr ""
+
+msgid "View epics list"
+msgstr ""
+
+msgid "View file @ "
+msgstr ""
+
+msgid "View group labels"
+msgstr ""
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
+msgid "View labels"
+msgstr ""
+
+msgid "View log"
+msgstr ""
+
+msgid "View open merge request"
+msgstr ""
+
+msgid "View project labels"
+msgstr ""
+
+msgid "View replaced file @ "
+msgstr ""
+
+msgid "Visibility and access controls"
+msgstr ""
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+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 detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
+msgid "We don't have enough data to show this stage."
+msgstr ""
+
+msgid "We want to be sure it is you, please confirm you are not a robot."
+msgstr ""
+
+msgid "Web IDE"
+msgstr ""
+
+msgid "Web terminal"
+msgstr ""
+
+msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
+
+msgid "Weight"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr ""
+
+msgid "Wiki"
+msgstr ""
+
+msgid "WikiClone|Clone your wiki"
+msgstr ""
+
+msgid "WikiClone|Git Access"
+msgstr ""
+
+msgid "WikiClone|Install Gollum"
+msgstr ""
+
+msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
+msgstr ""
+
+msgid "WikiClone|Start Gollum and edit locally"
+msgstr ""
+
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr ""
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr ""
+
+msgid "WikiHistoricalPage|history"
+msgstr ""
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr ""
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr ""
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr ""
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr ""
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr ""
+
+msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
+msgstr ""
+
+msgid "WikiNewPageTitle|New Wiki Page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr ""
+
+msgid "WikiPageConflictMessage|the page"
+msgstr ""
+
+msgid "WikiPageCreate|Create %{page_title}"
+msgstr ""
+
+msgid "WikiPageEdit|Update %{page_title}"
+msgstr ""
+
+msgid "WikiPage|Page slug"
+msgstr ""
+
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
+
+msgid "Wiki|Create Page"
+msgstr ""
+
+msgid "Wiki|Create page"
+msgstr ""
+
+msgid "Wiki|Edit Page"
+msgstr ""
+
+msgid "Wiki|More Pages"
+msgstr ""
+
+msgid "Wiki|New page"
+msgstr ""
+
+msgid "Wiki|Page history"
+msgstr ""
+
+msgid "Wiki|Page version"
+msgstr ""
+
+msgid "Wiki|Pages"
+msgstr ""
+
+msgid "Wiki|Wiki Pages"
+msgstr ""
+
+msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
+msgstr ""
+
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+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_full_name}. 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_full_name} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are on a read-only GitLab instance."
+msgstr ""
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr ""
+
+msgid "You can also create a project from the command line."
+msgstr ""
+
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr ""
+
+msgid "You cannot write to this read-only GitLab instance."
+msgstr ""
+
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
+msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
+msgid "You have no permissions"
+msgstr ""
+
+msgid "You have not created any merge requests"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr ""
+
+msgid "You must sign in to star a project"
+msgstr ""
+
+msgid "You need a different license to enable FileLocks feature"
+msgstr ""
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+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 "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 need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr ""
+
+msgid "YouTube"
+msgstr ""
+
+msgid "Your Groups"
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
+msgid "Your Projects (default)"
+msgstr ""
+
+msgid "Your Projects' Activity"
+msgstr ""
+
+msgid "Your Todos"
+msgstr ""
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr ""
+
+msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
+msgstr ""
+
+msgid "Your comment will not be visible to the public."
+msgstr ""
+
+msgid "Your groups"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
+msgid "Your projects"
+msgstr ""
+
+msgid "ago"
+msgstr ""
+
+msgid "among other things"
+msgstr ""
+
+msgid "and 1 fixed vulnerability"
+msgid_plural "and %d fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "assign yourself"
+msgstr ""
+
+msgid "branch name"
+msgstr ""
+
+msgid "by"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|%{type} detected no new security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{type} detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgstr ""
+
+msgid "ciReport|Dependency scanning detected"
+msgstr ""
+
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr ""
+
+msgid "ciReport|Failed to load %{reportName} report"
+msgstr ""
+
+msgid "ciReport|File"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Identifiers"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
+msgid "ciReport|Loading %{reportName} report"
+msgstr ""
+
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|Revert dismissal"
+msgstr ""
+
+msgid "ciReport|SAST detected"
+msgstr ""
+
+msgid "ciReport|SAST is loading"
+msgstr ""
+
+msgid "ciReport|SAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Security scanning"
+msgstr ""
+
+msgid "ciReport|Security scanning failed loading any results"
+msgstr ""
+
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
+msgstr ""
+
+msgid "ciReport|no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|on pipeline"
+msgstr ""
+
+msgid "command line instructions"
+msgstr ""
+
+msgid "connecting"
+msgstr ""
+
+msgid "could not read private key, is the passphrase correct?"
+msgstr ""
+
+msgid "customize"
+msgstr ""
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "deploy token"
+msgstr ""
+
+msgid "detected %d fixed vulnerability"
+msgid_plural "detected %d fixed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "detected %d new vulnerability"
+msgid_plural "detected %d new vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "detected no vulnerabilities"
+msgstr ""
+
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "for this project"
+msgstr ""
+
+msgid "here"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
+msgid "importing"
+msgstr ""
+
+msgid "is invalid because there is downstream lock"
+msgstr ""
+
+msgid "is invalid because there is upstream lock"
+msgstr ""
+
+msgid "is not a valid X509 certificate."
+msgstr ""
+
+msgid "latest version"
+msgstr ""
+
+msgid "locked by %{path_lock_user_name} %{created_at}"
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
+msgstr ""
+
+msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
+msgstr ""
+
+msgid "mrWidget|Add approval"
+msgstr ""
+
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr ""
+
+msgid "mrWidget|An error occured while removing your approval."
+msgstr ""
+
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
+
+msgid "mrWidget|Approve"
+msgstr ""
+
+msgid "mrWidget|Approved by"
+msgstr ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
+msgid "mrWidget|Deployment statistics are not available currently"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|Failed to load deployment statistics"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
+msgid "mrWidget|Loading deployment statistics"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Remove your approval"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "or"
+msgstr ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "password"
+msgstr ""
+
+msgid "personal access token"
+msgstr ""
+
+msgid "private key does not match certificate."
+msgstr ""
+
+msgid "remaining"
+msgstr ""
+
+msgid "remove due date"
+msgstr ""
+
+msgid "remove weight"
+msgstr ""
+
+msgid "source"
+msgstr ""
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
+msgid "started"
+msgstr ""
+
+msgid "this document"
+msgstr ""
+
+msgid "to help your contributors communicate effectively!"
+msgstr ""
+
+msgid "username"
+msgstr ""
+
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
+msgid "view it on GitLab"
+msgstr ""
+
+msgid "with %{additions} additions, %{deletions} deletions."
+msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/id_ID/gitlab.po b/locale/id_ID/gitlab.po
index adb9746e854..27b6e13db7e 100644
--- a/locale/id_ID/gitlab.po
+++ b/locale/id_ID/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:39-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Indonesian\n"
"Language: id_ID\n"
@@ -15,10 +13,23 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: id\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:39\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -47,6 +58,22 @@ msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+
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] ""
@@ -57,16 +84,31 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
@@ -79,22 +121,70 @@ msgstr ""
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+msgstr ""
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] ""
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+
msgid "%{text} is available"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr ""
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] ""
@@ -105,9 +195,51 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -117,33 +249,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr ""
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr ""
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr ""
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr ""
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr ""
@@ -168,12 +330,39 @@ msgstr ""
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr ""
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -240,7 +429,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -264,6 +456,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -279,6 +513,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -294,13 +531,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -336,18 +573,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr ""
@@ -357,12 +618,21 @@ msgstr ""
msgid "April"
msgstr ""
-msgid "Archived project! Repository is read-only"
+msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -378,6 +648,12 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -402,12 +678,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr ""
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -417,12 +708,36 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -438,7 +753,10 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
@@ -459,12 +777,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr ""
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -474,12 +798,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -498,6 +903,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -522,6 +930,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -534,6 +951,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -610,7 +1039,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -691,7 +1120,7 @@ msgstr ""
msgid "Browse files"
msgstr ""
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -700,6 +1129,9 @@ msgstr ""
msgid "CI / CD"
msgstr ""
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -709,12 +1141,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr ""
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -772,15 +1264,30 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -886,9 +1393,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -901,6 +1429,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -910,6 +1441,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -919,10 +1453,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -937,9 +1474,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -955,6 +1489,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -964,22 +1501,25 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -988,7 +1528,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1000,6 +1540,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1027,6 +1573,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1063,7 +1615,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1090,6 +1648,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -1102,9 +1672,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1132,19 +1699,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select project to choose zone"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1177,6 +1762,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1207,13 +1795,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1278,6 +1875,9 @@ msgstr ""
msgid "Committed by"
msgstr ""
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr ""
@@ -1326,6 +1926,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1392,15 +1995,30 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr ""
@@ -1416,12 +2034,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1437,9 +2064,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr ""
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1452,12 +2091,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr ""
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr ""
@@ -1470,9 +2115,15 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1491,6 +2142,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1509,10 +2163,16 @@ msgstr ""
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr ""
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1527,6 +2187,15 @@ msgstr ""
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr ""
@@ -1536,6 +2205,12 @@ msgstr ""
msgid "Customize colors"
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 "Cycle Analytics"
msgstr ""
@@ -1560,6 +2235,9 @@ msgstr ""
msgid "CycleAnalyticsStage|Test"
msgstr ""
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1572,15 +2250,36 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] ""
@@ -1588,39 +2287,195 @@ msgstr[0] ""
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr ""
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr ""
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr ""
@@ -1663,16 +2518,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr ""
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1684,15 +2554,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1708,6 +2587,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1720,15 +2611,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1741,33 +2647,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1783,12 +2710,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1807,6 +2728,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1819,6 +2755,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1846,9 +2785,30 @@ msgstr ""
msgid "Every week (Sundays at 4:00am)"
msgstr ""
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1876,6 +2836,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1885,6 +2848,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr ""
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1894,6 +2860,12 @@ msgstr ""
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1903,9 +2875,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr ""
@@ -1915,6 +2884,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
@@ -1924,6 +2896,12 @@ msgstr ""
msgid "Find file"
msgstr ""
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1933,12 +2911,39 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] ""
@@ -1955,9 +2960,24 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
@@ -1970,6 +2990,12 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -1988,7 +3014,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2003,31 +3032,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2048,19 +3089,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2069,16 +3116,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2096,13 +3146,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2132,6 +3188,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2141,6 +3203,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2153,34 +3218,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
msgid "GoToYourFork|Fork"
msgstr ""
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2192,7 +3323,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2222,6 +3374,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2298,15 +3477,54 @@ msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr ""
msgid "Housekeeping successfully started"
msgstr ""
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2319,15 +3537,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2346,10 +3603,22 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance"
@@ -2362,6 +3631,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2377,6 +3649,9 @@ msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2395,12 +3670,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2419,6 +3703,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2443,6 +3730,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr ""
@@ -2452,12 +3742,21 @@ msgstr ""
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2467,6 +3766,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2501,6 +3803,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2525,18 +3830,36 @@ msgstr ""
msgid "Leave project"
msgstr ""
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2546,24 +3869,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2576,13 +3920,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2600,24 +3965,60 @@ msgstr ""
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2627,18 +4028,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2651,6 +4061,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2663,9 +4076,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2696,6 +4127,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2729,6 +4163,15 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2738,6 +4181,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2747,20 +4193,59 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] ""
-msgid "New Kubernetes Cluster"
+msgid "New Label"
msgstr ""
-msgid "New Kubernetes cluster"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
msgstr ""
msgid "New branch"
@@ -2781,6 +4266,9 @@ msgstr ""
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -2790,6 +4278,9 @@ msgstr ""
msgid "New merge request"
msgstr ""
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2805,6 +4296,12 @@ msgstr ""
msgid "New tag"
msgstr ""
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2826,7 +4323,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2835,6 +4362,9 @@ msgstr ""
msgid "No schedules"
msgstr ""
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -2871,6 +4401,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -2958,27 +4491,72 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr ""
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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 ""
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3012,12 +4590,30 @@ msgstr ""
msgid "Password"
msgstr ""
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3036,6 +4632,9 @@ msgstr ""
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr ""
@@ -3132,10 +4731,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3144,7 +4755,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3162,18 +4773,42 @@ msgstr ""
msgid "Pipeline|with stages"
msgstr ""
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3183,9 +4818,24 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3198,9 +4848,21 @@ msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3219,9 +4881,27 @@ msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3231,6 +4911,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3240,6 +4923,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -3252,6 +4941,9 @@ msgstr ""
msgid "Project '%{project_name}' was successfully updated."
msgstr ""
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -3276,6 +4968,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -3285,24 +4980,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr ""
-
-msgid "ProjectFeature|Everyone with access"
-msgstr ""
-
-msgid "ProjectFeature|Only team members"
-msgstr ""
-
msgid "ProjectFileTree|Name"
msgstr ""
@@ -3312,12 +4998,18 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
-msgid "ProjectNetworkGraph|Graph"
+msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3336,6 +5028,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3354,7 +5049,37 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3435,21 +5160,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3465,28 +5214,43 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr ""
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr ""
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
+msgid "Reference:"
msgstr ""
-msgid "RefSwitcher|Tags"
+msgid "Refresh"
msgstr ""
-msgid "Reference:"
+msgid "Register / Sign In"
msgstr ""
-msgid "Register / Sign In"
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
msgstr ""
msgid "Registry"
@@ -3519,36 +5283,60 @@ msgstr ""
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr ""
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr ""
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr ""
@@ -3558,10 +5346,28 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3574,6 +5380,9 @@ msgstr ""
msgid "Revert this merge request"
msgstr ""
+msgid "Review"
+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 ""
@@ -3583,18 +5392,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3607,6 +5434,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3628,15 +5464,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr ""
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr ""
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3652,15 +5512,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr ""
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr ""
@@ -3673,9 +5548,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3691,6 +5584,9 @@ msgstr ""
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3733,9 +5629,15 @@ msgstr ""
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3745,31 +5647,64 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3782,6 +5717,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3791,13 +5729,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3806,9 +5750,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -3926,9 +5879,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr ""
@@ -3950,33 +5930,66 @@ msgstr ""
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr ""
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -3984,6 +5997,9 @@ msgstr[0] ""
msgid "Tags"
msgstr ""
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4047,7 +6063,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4062,6 +6078,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4101,12 +6126,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr ""
@@ -4125,6 +6153,9 @@ msgstr ""
msgid "The project can be accessed without any authentication."
msgstr ""
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr ""
@@ -4140,6 +6171,9 @@ msgstr ""
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 ""
@@ -4152,25 +6186,31 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 no issues to show"
msgstr ""
-msgid "There are no merge requests to show"
+msgid "There are no labels yet"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no merge requests to show"
msgstr ""
-msgid "There was an error loading results"
+msgid "There are problems accessing Git storage: "
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -4191,12 +6231,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4218,6 +6285,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4236,15 +6312,30 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4260,10 +6351,13 @@ msgstr ""
msgid "Time between merge request creation and merge/close"
msgstr ""
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4287,6 +6381,9 @@ msgstr ""
msgid "Timeago|%s days remaining"
msgstr ""
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr ""
@@ -4302,6 +6399,9 @@ msgstr ""
msgid "Timeago|%s months remaining"
msgstr ""
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr ""
@@ -4317,46 +6417,43 @@ msgstr ""
msgid "Timeago|%s years remaining"
msgstr ""
-msgid "Timeago|1 day remaining"
-msgstr ""
-
-msgid "Timeago|1 hour remaining"
+msgid "Timeago|1 day ago"
msgstr ""
-msgid "Timeago|1 minute remaining"
+msgid "Timeago|1 day remaining"
msgstr ""
-msgid "Timeago|1 month remaining"
+msgid "Timeago|1 hour ago"
msgstr ""
-msgid "Timeago|1 week remaining"
+msgid "Timeago|1 hour remaining"
msgstr ""
-msgid "Timeago|1 year remaining"
+msgid "Timeago|1 minute ago"
msgstr ""
-msgid "Timeago|Past due"
+msgid "Timeago|1 minute remaining"
msgstr ""
-msgid "Timeago|a day ago"
+msgid "Timeago|1 month ago"
msgstr ""
-msgid "Timeago|a month ago"
+msgid "Timeago|1 month remaining"
msgstr ""
-msgid "Timeago|a week ago"
+msgid "Timeago|1 week ago"
msgstr ""
-msgid "Timeago|a year ago"
+msgid "Timeago|1 week remaining"
msgstr ""
-msgid "Timeago|about %s hours ago"
+msgid "Timeago|1 year ago"
msgstr ""
-msgid "Timeago|about a minute ago"
+msgid "Timeago|1 year remaining"
msgstr ""
-msgid "Timeago|about an hour ago"
+msgid "Timeago|Past due"
msgstr ""
msgid "Timeago|in %s days"
@@ -4398,10 +6495,13 @@ msgstr ""
msgid "Timeago|in 1 year"
msgstr ""
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
msgstr ""
-msgid "Timeago|less than a minute ago"
+msgid "Timeout"
msgstr ""
msgid "Time|hr"
@@ -4424,6 +6524,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4433,6 +6536,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4442,21 +6551,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4466,6 +6599,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr ""
@@ -4484,12 +6623,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4502,12 +6659,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr ""
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4523,6 +6713,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr ""
@@ -4541,9 +6734,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4553,9 +6755,21 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4568,7 +6782,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4580,9 +6797,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr ""
@@ -4595,6 +6824,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr ""
@@ -4610,7 +6845,7 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4628,10 +6863,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4658,7 +6905,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4694,6 +6965,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4709,7 +6986,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4721,9 +6998,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4748,7 +7022,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4766,7 +7049,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4775,6 +7061,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4787,22 +7079,40 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4811,6 +7121,9 @@ msgstr ""
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr ""
@@ -4841,9 +7154,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4859,6 +7181,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4877,10 +7205,13 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
@@ -4893,45 +7224,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4941,19 +7365,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4962,15 +7383,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -4980,10 +7440,16 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -4995,16 +7461,28 @@ msgstr[0] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5016,6 +7494,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5038,24 +7519,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5083,6 +7558,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5116,9 +7594,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5179,6 +7672,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5188,9 +7684,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5231,15 +7724,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5252,6 +7754,13 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+
diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po
index 41d6a76be66..14335f97bd6 100644
--- a/locale/it/gitlab.po
+++ b/locale/it/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:37-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Italian\n"
"Language: it_IT\n"
@@ -15,10 +13,26 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: it\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
@@ -51,6 +65,26 @@ msgstr[1] ""
msgid "%d metric"
msgid_plural "%d metrics"
+msgstr[0] "%d metrica"
+msgstr[1] "%d metriche"
+
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -63,6 +97,9 @@ msgid "%{actionText} & %{openOrClose} %{noteable}"
msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr "%{commit_author_link} fatto %{commit_timeago}"
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
msgstr ""
msgid "%{count} participant"
@@ -70,12 +107,24 @@ msgid_plural "%{count} participants"
msgstr[0] "%{count} partecipante"
msgstr[1] "%{count} partecipanti"
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr "%{number_commits_behind} commits precedenti %{default_branch}, %{number_commits_ahead} commits avanti"
@@ -88,23 +137,80 @@ msgstr "%{number_of_failures} di %{maximum_failures} fallimenti. Gitlab non rite
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+msgstr ""
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] "%{storage_name}: tentativo d'accesso all'archiviazione fallito da parte dell'host:"
msgstr[1] "%{storage_name}: %{failed_attempts} tentativi d'accesso all'archiviazione falliti:"
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr "%{text} è disponibile"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(vedi il %{link} su come installarlo)."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} più"
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr "- riduci"
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "1 pipeline"
@@ -116,9 +222,51 @@ msgstr "Primo contributo!"
msgid "2FA enabled"
msgstr "2FA abilitata"
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Un insieme di grafici riguardo la Continuous Integration"
@@ -128,33 +276,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr "Riguardo il rilascio automatico"
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr "Segnalazioni di abuso"
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr "Token di accesso"
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "L'accesso agli storages è stato temporaneamente disabilitato per consentire il mount di ripristino. Resetta le info d'archiviazione dopo che l'issue è stato risolto per consentire nuovamente l'accesso."
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr "Account"
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "Attivo"
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr "Attività"
@@ -179,12 +357,39 @@ msgstr "Aggiungi Licenza"
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "Aggiungi una directory (cartella)"
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -251,7 +456,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -275,6 +483,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -290,6 +540,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -305,13 +558,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -347,18 +600,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr "Si è verificato un errore. Riprova."
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr "Aspetto"
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr "Applicazioni"
@@ -368,12 +645,21 @@ msgstr "Apr"
msgid "April"
msgstr "Aprile"
-msgid "Archived project! Repository is read-only"
-msgstr "Progetto archiviato! La Repository è sola-lettura"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Sei sicuro di voler cancellare questa pipeline programmata?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Sei sicuro di voler ripristinare il token di registrazione?"
@@ -389,6 +675,12 @@ msgstr "Sei sicuro?"
msgid "Artifacts"
msgstr "Artefatti"
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -413,12 +705,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Aggiungi un file tramite trascina &amp; rilascia ( drag &amp; drop) o %{upload_link}"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr "Ago"
@@ -428,12 +735,36 @@ msgstr "Agosto"
msgid "Authentication Log"
msgstr "Log di autenticazione"
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr "Autore"
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -449,8 +780,11 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Le app d'auto-review e l'Auto Deploy (rilascio automatico) necessita di un nome dominio per funzionare correttamente."
-msgid "AutoDevOps|Auto DevOps (Beta)"
-msgstr "Auto DevOps (Béta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
msgstr "Documentazione Auto DevOps"
@@ -470,12 +804,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr "Disponibile"
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -485,12 +825,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -509,6 +930,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -533,6 +957,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -545,6 +978,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -622,8 +1067,8 @@ msgstr "Nessuna branch da mostrare"
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr "Una volta confermato e premuto %{delete_protected_branch} non sarà possibile ripristinare allo stato precedente."
-msgid "Branches|Only a project master or owner can delete a protected branch"
-msgstr "Solo gli Owner e i Master possono eliminare una branch protetta"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
msgid "Branches|Overview"
msgstr ""
@@ -703,7 +1148,7 @@ msgstr "Esplora Files"
msgid "Browse files"
msgstr "Guarda i files"
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -712,6 +1157,9 @@ msgstr "per"
msgid "CI / CD"
msgstr "CI / CD"
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -721,12 +1169,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr "Jobs"
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "Cancella"
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -784,15 +1292,30 @@ msgstr "Cherry-pick di questo commit"
msgid "Cherry-pick this merge request"
msgstr "Cherry-pick questa richiesta di merge"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -898,9 +1421,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "api circuitbreaker"
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -913,6 +1457,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr "Clona repository"
@@ -922,6 +1469,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -931,10 +1481,13 @@ msgstr "API URL"
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -949,9 +1502,6 @@ msgstr "Certificato CA"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Certificate Authority bundle (formato PEM)"
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -967,6 +1517,9 @@ msgstr "Copia Certificato CA"
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -976,22 +1529,25 @@ msgstr "Copia Token"
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
-msgstr "Crea su GKE"
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr "Inserisci i dettagli per un cluster Kubernetes esistente"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -1000,8 +1556,8 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr "Gitlab Runner"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
-msgstr "ID Progetto di Google Cloud Platform"
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
msgstr ""
@@ -1012,6 +1568,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1039,6 +1601,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1075,7 +1643,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1102,6 +1676,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr "Nota:"
@@ -1114,9 +1700,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1144,19 +1727,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select zone"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1189,6 +1790,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1219,13 +1823,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1292,6 +1905,9 @@ msgstr ""
msgid "Committed by"
msgstr "Committato da "
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr "Confronta"
@@ -1340,6 +1956,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1406,15 +2025,30 @@ msgstr "Utilizza nomi d'immagine differenti"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr "Con il Docker Container Registry integrato in Gitlab, ogni progetto può avere il suo spazio d'archiviazione sulle immagini Docker."
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr "Guida per contribuire"
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr "Collaboratori"
@@ -1430,12 +2064,21 @@ msgstr "Esegui i commit su %{branch_name}, escludendo i commit di merge. Limitat
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr "Attendere prego, questa pagina si ricaricherà automaticamente appena pronta."
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1451,9 +2094,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr "Copia l'SHA del commit negli appunti"
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1466,12 +2121,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Creare un token di accesso sul tuo account per eseguire pull o push tramite %{protocol}"
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr "Crea cartella"
@@ -1484,9 +2145,15 @@ msgstr ""
msgid "Create file"
msgstr "Crea file"
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1505,6 +2172,9 @@ msgstr "Crea una nuova cartella"
msgid "Create new file"
msgstr "Crea un nuovo File"
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1523,10 +2193,16 @@ msgstr "Tag"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "Crea token d'accesso personale"
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1541,6 +2217,15 @@ msgstr "Sintassi Cron"
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Eventi-Notifica personalizzati"
@@ -1550,6 +2235,12 @@ msgstr "I livelli di notifica personalizzati sono uguali a quelli di partecipazi
msgid "Customize colors"
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 "Cycle Analytics"
msgstr "Statistiche Cicliche"
@@ -1574,6 +2265,9 @@ msgstr "Pre-rilascio"
msgid "CycleAnalyticsStage|Test"
msgstr "Test"
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr "Tutti"
@@ -1586,15 +2280,36 @@ msgstr "Dic"
msgid "December"
msgstr "Dicembre"
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr "Definisci un patter personalizzato mediante la sintassi cron"
msgid "Delete"
msgstr "Elimina"
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "Rilascio"
@@ -1603,39 +2318,195 @@ msgstr[1] "Rilasci"
msgid "Deploy Keys"
msgstr "Chiavi di Deploy (rilascio)"
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr "Descrizione"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr "Dettagli"
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr "Nome cartella"
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Chiudi l'introduzione alle Analisi Cicliche"
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr "Non mostrare più"
@@ -1678,16 +2549,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr "Modifica"
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "Cambia programmazione della pipeline %{id}"
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1699,15 +2585,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr "E-mail"
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1723,6 +2618,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1735,15 +2642,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr "Errore durante il fetch degli ambienti."
msgid "Environments|An error occurred while making the request."
msgstr "Errore durante l'esecuzione della richiesta."
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr "Commit"
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr "Rilascio"
@@ -1756,33 +2678,54 @@ msgstr "Ambienti"
msgid "Environments|Job"
msgstr "Job"
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr "Nuovo ambiente"
msgid "Environments|No deployments yet"
msgstr "Ancora nessuna chiave di rilascio"
-msgid "Environments|Open"
-msgstr "Apri"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
-msgid "Environments|Re-deploy"
-msgstr "Rilascia di nuovo"
+msgid "Environments|Re-deploy to environment"
+msgstr ""
msgid "Environments|Read more about environments"
msgstr "Leggi di più sugli ambienti"
-msgid "Environments|Rollback"
-msgstr "Rollback (ripristina)"
+msgid "Environments|Rollback environment"
+msgstr ""
msgid "Environments|Show all"
msgstr "Mostra tutti"
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Aggiornato"
msgid "Environments|You don't have any environments right now."
msgstr "Attualmente non hai alcun ambiente."
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1798,12 +2741,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1822,6 +2759,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Errore durante l'attivazione/disattivazione della sottoscrizione per l'iscrizione"
@@ -1834,6 +2786,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "Filtra per tutti"
@@ -1861,9 +2816,30 @@ msgstr "Ogni primo giorno del mese (alle 4 del mattino)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Ogni settimana (Di domenica alle 4 del mattino)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr "Esplora progetti"
@@ -1891,6 +2867,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1900,6 +2879,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr "Impossibile cambiare owner"
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1909,6 +2891,12 @@ msgstr "Impossibile rimuovere la pipeline pianificata"
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr "Feb"
@@ -1918,9 +2906,6 @@ msgstr "Febbraio"
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr "Nome file"
-
msgid "Files"
msgstr "Files"
@@ -1930,6 +2915,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Filtra per messaggio di commit"
@@ -1939,6 +2927,12 @@ msgstr "Trova in percorso"
msgid "Find file"
msgstr "Trova file"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1948,12 +2942,39 @@ msgstr "Primo"
msgid "FirstPushedBy|pushed by"
msgstr "Push di"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "Fork"
@@ -1971,9 +2992,24 @@ msgstr ""
msgid "Format"
msgstr "Formato"
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+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"
@@ -1986,6 +3022,12 @@ msgstr ""
msgid "GPG Keys"
msgstr "Chiavi GPG"
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -2004,7 +3046,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2019,31 +3064,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2064,19 +3121,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2085,16 +3148,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2112,13 +3178,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2148,6 +3220,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2157,6 +3235,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr "Le informazioni sullo stato dell'archiviazione Git è stata ripristinata"
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2169,34 +3250,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "Sezione Gitlab Runner"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "Vai il tuo fork"
msgid "GoToYourFork|Fork"
msgstr "Fork"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "L'autenticazione Google non è %{link_to_documentation}. Richiedi al tuo amministratore Gitlab se desideri utilizzare il servizio."
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2208,7 +3355,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2238,6 +3406,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2315,15 +3510,54 @@ msgid_plural "Hide values"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr "Cronologia"
msgid "Housekeeping successfully started"
msgstr "Housekeeping iniziato con successo"
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2336,15 +3570,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2363,10 +3636,22 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance"
@@ -2380,6 +3665,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2395,6 +3683,9 @@ msgstr "Intervallo di Pattern"
msgid "Introducing Cycle Analytics"
msgstr "Introduzione delle Analisi Cicliche"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2413,12 +3704,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr "Gen"
msgid "January"
msgstr "Gennaio"
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2437,6 +3737,9 @@ msgstr "Giugno"
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2461,6 +3764,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Disabilitato"
@@ -2470,12 +3776,21 @@ msgstr "Abilitato"
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2485,6 +3800,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2520,6 +3838,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2544,18 +3865,36 @@ 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 "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2565,24 +3904,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr "Bloccato"
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
-msgstr "Login"
+msgid "Logs"
+msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2595,13 +3955,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr "Mar"
msgid "March"
msgstr "Marzo"
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2619,24 +4000,60 @@ msgstr "Membri"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr "Richieste di merge"
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr ""
msgid "Merge request"
msgstr "Richiesta di merge"
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr "Messaggi"
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2646,18 +4063,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2670,6 +4096,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2682,9 +4111,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2715,6 +4162,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2748,6 +4198,15 @@ msgstr ""
msgid "Monitoring"
msgstr "Monitoraggio"
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2757,6 +4216,9 @@ msgstr ""
msgid "More information is available|here"
msgstr "Ulteriori informazioni sono disponibili | qui"
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2766,23 +4228,62 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nuovo Issue"
msgstr[1] "Nuovi Issues"
-msgid "New Kubernetes Cluster"
-msgstr ""
-
-msgid "New Kubernetes cluster"
+msgid "New Label"
msgstr ""
msgid "New Pipeline Schedule"
msgstr "Nuova pianificazione Pipeline"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "Nuova Branch"
@@ -2801,6 +4302,9 @@ msgstr "Nuovo file"
msgid "New group"
msgstr "Nuovo gruppo"
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr "Nuovo Issue"
@@ -2810,6 +4314,9 @@ msgstr ""
msgid "New merge request"
msgstr "Nuova richiesta di merge"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr "Nuovo progetto"
@@ -2825,6 +4332,12 @@ msgstr "Nuovo sottogruppo"
msgid "New tag"
msgstr "Nuovo tag"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2846,7 +4359,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2855,6 +4398,9 @@ msgstr "Nessuna Repository"
msgid "No schedules"
msgstr "Nessuna pianificazione"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr "Nessuno"
@@ -2891,6 +4437,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "Notifica eventi"
@@ -2978,27 +4527,72 @@ msgstr "Filtra"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr "Solo i membri del progetto possono commentare."
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Aperto"
msgid "Opens in a new window"
msgstr "Si apre in una nuova finestra"
+msgid "Operations"
+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"
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3032,12 +4626,30 @@ msgstr ""
msgid "Password"
msgstr "Password"
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3056,6 +4668,9 @@ msgstr "Pianificazione multipla Pipeline"
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr "Fallita:"
@@ -3152,10 +4767,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3164,7 +4791,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3182,18 +4809,42 @@ msgstr "con stadio"
msgid "Pipeline|with stages"
msgstr "con più stadi"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3203,9 +4854,24 @@ msgstr ""
msgid "Preferences"
msgstr "Preferenze"
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Privato - L'accesso al progetto deve essere fornito esplicitamente ad ogni utente."
@@ -3218,9 +4884,21 @@ msgstr ""
msgid "Profile"
msgstr "Profilo"
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr "Account pianificato per la rimozione."
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr "Elimina account"
@@ -3239,9 +4917,27 @@ msgstr "Password non valida"
msgid "Profiles|Invalid username"
msgstr "Username non valido"
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Inserisci il tuo %{confirmationValue} per confermare:"
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr "Non hai i permessi per eliminare questo utente."
@@ -3251,6 +4947,9 @@ msgstr "Devi trasferire la proprietà o eliminare questi gruppi prima che tu pos
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Il tuo account è attualmente proprietario in questi gruppi:"
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr "il tuo account"
@@ -3260,6 +4959,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Il progetto '%{project_name}' è in fase di eliminazione."
@@ -3272,6 +4977,9 @@ msgstr "Il Progetto '%{project_name}' è stato creato con successo."
msgid "Project '%{project_name}' was successfully updated."
msgstr "Il Progetto '%{project_name}' è stato aggiornato con successo."
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "L'accesso al progetto dev'esser fornito esplicitamente ad ogni utente"
@@ -3296,6 +5004,9 @@ msgstr "Il link d'esportazione del progetto è scaduto. Genera una nuova esporta
msgid "Project export started. A download link will be sent by email."
msgstr "Esportazione del progetto iniziata. Un link di download sarà inviato via email."
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "Iscriviti"
@@ -3305,24 +5016,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr "Disabilitato"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "Chiunque con accesso"
-
-msgid "ProjectFeature|Only team members"
-msgstr "Solo i membri del team"
-
msgid "ProjectFileTree|Name"
msgstr "Nome"
@@ -3332,12 +5034,18 @@ msgstr "Mai"
msgid "ProjectLifecycle|Stage"
msgstr "Stadio"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "Grafico"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3356,6 +5064,9 @@ msgstr ""
msgid "Projects"
msgstr "Progetti"
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr "Visitati di frequente"
@@ -3374,8 +5085,38 @@ msgstr "Qualcosa è andato storto dalla nostra parte."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Siamo spiacenti, non ci sono progetti che corrispondono alla tua ricerca"
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr "Questa feature richiede il supporto del localStorage del browser"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr ""
@@ -3455,21 +5196,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Pubblico - il gruppo e tutti i progetti pubblici possono essere visualizzati senza alcuna autenticazione."
msgid "Public - The project can be accessed without any authentication."
msgstr "Public - Chiunque può accedere a questo progetto senza alcuna autenticazione."
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3485,30 +5250,45 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr "Vedi altro"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "Leggimi"
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
-msgstr "Branches"
-
-msgid "RefSwitcher|Tags"
-msgstr "Tags"
-
msgid "Reference:"
msgstr ""
+msgid "Refresh"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -3539,36 +5319,60 @@ msgstr "Ricordamelo più tardi"
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr "Rimuovi progetto"
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr "Richiedi accesso"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr ""
@@ -3578,10 +5382,28 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3595,6 +5417,9 @@ msgstr "Ripristina questo commit"
msgid "Revert this merge request"
msgstr "Ripristina questa richiesta di merge"
+msgid "Review"
+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 ""
@@ -3604,18 +5429,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3628,6 +5471,15 @@ msgstr ""
msgid "SSH Keys"
msgstr "Chiavi SSH"
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr "Salva modifiche"
@@ -3649,15 +5501,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr "Pianificazione pipelines"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr "Ricerca branches e tags"
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3673,15 +5549,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Seleziona formato d'archivio"
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr "Seleziona una timezone"
@@ -3694,9 +5585,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr "Seleziona una branch di destinazione"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3712,6 +5621,9 @@ msgstr "Settembre"
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3754,9 +5666,15 @@ msgstr "Impostazioni"
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3766,32 +5684,65 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Visualizza %d evento"
msgstr[1] "Visualizza %d eventi"
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3804,6 +5755,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr "Snippet"
@@ -3813,13 +5767,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching assignees list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3828,9 +5788,18 @@ msgstr "Qualcosa è andato storto durante il fetch dei progetti."
msgid "Something went wrong while fetching the registry list."
msgstr "Qualcosa è andato storto durante il recupero dell'elenco dei registri."
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr "Ordina per"
@@ -3948,9 +5917,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr "Star"
@@ -3972,33 +5968,66 @@ msgstr ""
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr "Cambia branch/tag"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4007,6 +6036,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4070,7 +6102,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4085,6 +6117,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4124,12 +6165,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "Il ciclo vitale della fase di sviluppo."
@@ -4148,6 +6192,9 @@ msgstr "Qualunque utente autenticato può accedere a questo progetto."
msgid "The project can be accessed without any authentication."
msgstr "Chiunque può accedere a questo progetto (senza alcuna autenticazione)."
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "La repository di questo progetto non esiste."
@@ -4163,6 +6210,9 @@ msgstr "Lo stadio di revisione mostra il tempo tra una richiesta di merge al suo
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "Lo stadio di pre-rilascio mostra il tempo che trascorre da una MR (Richiesta di Merge) completata al suo rilascio in ambiente di produzione. Questa informazione sarà disponibile dal tuo primo rilascio in produzione"
@@ -4175,25 +6225,31 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "Il tempo aggregato relativo eventi/data entry raccolto in quello stadio."
+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>:</code>. 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 ""
+
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 "Il valore falsato nel mezzo di una serie di dati osservati. ES: tra 3,5,9 il mediano è 5. Tra 3,5,7,8 il mediano è (5+7)/2 quindi 6."
msgid "There are no issues to show"
msgstr ""
-msgid "There are no merge requests to show"
+msgid "There are no labels yet"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no merge requests to show"
msgstr ""
-msgid "There was an error loading results"
+msgid "There are problems accessing Git storage: "
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -4214,12 +6270,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4241,6 +6324,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4259,15 +6351,30 @@ msgstr "Questo significa che non è possibile effettuare push di codice fino a c
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4283,10 +6390,13 @@ msgstr "Il tempo che impiega un issue per esser implementato"
msgid "Time between merge request creation and merge/close"
msgstr "Il tempo tra la creazione di una richiesta di merge ed il merge/close"
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4310,6 +6420,9 @@ msgstr "%s giorni fa"
msgid "Timeago|%s days remaining"
msgstr "%s giorni rimanenti"
+msgid "Timeago|%s hours ago"
+msgstr "%s ore fa"
+
msgid "Timeago|%s hours remaining"
msgstr "%s ore rimanenti"
@@ -4325,6 +6438,9 @@ msgstr "%s mesi fa"
msgid "Timeago|%s months remaining"
msgstr "%s mesi rimanenti"
+msgid "Timeago|%s seconds ago"
+msgstr "%s secondi fa"
+
msgid "Timeago|%s seconds remaining"
msgstr "%s secondi rimanenti"
@@ -4340,48 +6456,45 @@ msgstr "%s anni fa"
msgid "Timeago|%s years remaining"
msgstr "%s anni rimanenti"
+msgid "Timeago|1 day ago"
+msgstr "1 giorno fa"
+
msgid "Timeago|1 day remaining"
msgstr "1 giorno rimanente"
+msgid "Timeago|1 hour ago"
+msgstr "1 ora fa"
+
msgid "Timeago|1 hour remaining"
msgstr "1 ora rimanente"
+msgid "Timeago|1 minute ago"
+msgstr "1 minuto fa"
+
msgid "Timeago|1 minute remaining"
msgstr "1 minuto rimanente"
+msgid "Timeago|1 month ago"
+msgstr "1 mese fa"
+
msgid "Timeago|1 month remaining"
msgstr "1 mese rimanente"
+msgid "Timeago|1 week ago"
+msgstr "1 settimana fa"
+
msgid "Timeago|1 week remaining"
msgstr "1 settimana rimanente"
+msgid "Timeago|1 year ago"
+msgstr "1 anno fa"
+
msgid "Timeago|1 year remaining"
msgstr "1 anno rimanente"
msgid "Timeago|Past due"
msgstr "Entro"
-msgid "Timeago|a day ago"
-msgstr "un giorno fa"
-
-msgid "Timeago|a month ago"
-msgstr "un mese fa"
-
-msgid "Timeago|a week ago"
-msgstr "una settimana fa"
-
-msgid "Timeago|a year ago"
-msgstr "un anno fa"
-
-msgid "Timeago|about %s hours ago"
-msgstr "circa %s ore fa"
-
-msgid "Timeago|about a minute ago"
-msgstr "circa un minuto fa"
-
-msgid "Timeago|about an hour ago"
-msgstr "circa un ora fa"
-
msgid "Timeago|in %s days"
msgstr "in %s giorni"
@@ -4421,11 +6534,14 @@ msgstr "in 1 settimana"
msgid "Timeago|in 1 year"
msgstr "in 1 anno"
-msgid "Timeago|in a while"
-msgstr ""
+msgid "Timeago|just now"
+msgstr "poco fa"
-msgid "Timeago|less than a minute ago"
-msgstr "meno di un minuto fa"
+msgid "Timeago|right now"
+msgstr "adesso"
+
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4444,11 +6560,14 @@ msgid "Tip:"
msgstr ""
msgid "Title"
-msgstr "Titolo"
+msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4458,6 +6577,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4467,21 +6592,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4491,6 +6640,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr "Tempo Totale"
@@ -4509,12 +6664,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4527,12 +6700,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr ""
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4548,6 +6754,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr "Carica un nuovo file"
@@ -4566,9 +6775,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4578,9 +6796,21 @@ msgstr "Usa le tue impostazioni globali "
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4593,7 +6823,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4605,9 +6838,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr "Mostra la richieste di merge aperte"
@@ -4620,6 +6865,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "Interno"
@@ -4635,7 +6886,7 @@ msgstr "Sconosciuto"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Vuoi visualizzare i dati? Richiedi l'accesso ad un amministratore, grazie."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4653,10 +6904,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4683,7 +6946,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4719,6 +7006,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4734,7 +7027,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4746,9 +7039,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4773,7 +7063,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "Ritira richiesta d'accesso"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4791,7 +7090,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4800,6 +7102,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4812,22 +7120,40 @@ msgstr "Puoi aggiungere files solo quando sei in una branch"
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Hai raggiunto il tuo limite di progetto"
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4836,6 +7162,9 @@ msgstr "Devi accedere per porre una star al progetto"
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "Necessiti del permesso."
@@ -4866,9 +7195,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4884,6 +7222,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4902,10 +7246,13 @@ msgstr "Il tuo nome"
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr "fa"
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4919,45 +7266,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4967,19 +7407,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4988,15 +7425,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5006,11 +7482,17 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "giorno"
msgstr[1] "giorni"
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5024,16 +7506,28 @@ msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5045,6 +7539,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5068,24 +7565,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5113,6 +7604,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5146,9 +7640,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5209,6 +7718,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5218,9 +7730,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5262,15 +7771,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5283,6 +7801,14 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] "in %d minuto "
+msgstr[1] "entro %d minuti "
+
diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po
index b526b0ba202..acd78058d50 100644
--- a/locale/ja/gitlab.po
+++ b/locale/ja/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:36-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Japanese\n"
"Language: ja_JP\n"
@@ -15,9 +13,22 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ja\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:39\n"
msgid " and"
-msgstr ""
+msgstr " ã¨"
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] " %dãƒã‚¤ãƒ³ãƒˆæ‚ªåŒ–"
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] " %dãƒã‚¤ãƒ³ãƒˆã§æ”¹å–„"
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] "%d 個ã®å¤‰æ›´ã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«"
msgid "%d commit"
msgid_plural "%d commits"
@@ -25,26 +36,42 @@ msgstr[0] "%d個ã®ã‚³ãƒŸãƒƒãƒˆ"
msgid "%d commit behind"
msgid_plural "%d commits behind"
-msgstr[0] ""
+msgstr[0] "%d個ã®ã‚³ãƒŸãƒƒãƒˆå¾…ã¡"
msgid "%d exporter"
msgid_plural "%d exporters"
-msgstr[0] ""
+msgstr[0] "%d exporter"
msgid "%d issue"
msgid_plural "%d issues"
-msgstr[0] ""
+msgstr[0] "%d個ã®èª²é¡Œ"
msgid "%d layer"
msgid_plural "%d layers"
-msgstr[0] ""
+msgstr[0] "%d 個ã®ãƒ¬ã‚¤ãƒ¤ãƒ¼"
msgid "%d merge request"
msgid_plural "%d merge requests"
-msgstr[0] ""
+msgstr[0] "%d 個ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
msgid "%d metric"
msgid_plural "%d metrics"
+msgstr[0] "%d メトリクス"
+
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] "%d件ã®ã‚¹ãƒ†ãƒ¼ã‚¸æ¸ˆã¿å¤‰æ›´"
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] "%d件ã®æœªã‚¹ãƒ†ãƒ¼ã‚¸ã®å¤‰æ›´"
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
msgstr[0] ""
msgid "%s additional commit has been omitted to prevent performance issues."
@@ -52,103 +79,238 @@ msgid_plural "%s additional commits have been omitted to prevent performance iss
msgstr[0] "パフォーマンス低下をé¿ã‘ã‚‹ãŸã‚ %s 個ã®ã‚³ãƒŸãƒƒãƒˆã‚’çœç•¥ã—ã¾ã—ãŸã€‚"
msgid "%{actionText} & %{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{actionText} 㨠%{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr "%{commit_author_link} ㌠%{commit_timeago} ã«ã‚³ãƒŸãƒƒãƒˆã—ã¾ã—ãŸ"
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
-msgstr[0] ""
+msgstr[0] "%{count} 人ã®å‚加者"
+
+msgid "%{filePath} deleted"
+msgstr "%{filePath} ãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr "%{group_docs_link_start}グループ%{group_docs_link_end}を使用ã™ã‚‹ã¨ã€è¤‡æ•°ã®ãƒ—ロジェクトを管ç†ã—ã¦å…±åŒä½œæ¥­ã‚’è¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚グループã®ãƒ¡ãƒ³ãƒãƒ¼ã¯ã€æ‰€å±žã™ã‚‹ãƒ—ロジェクトã®ã™ã¹ã¦ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚"
msgid "%{loadingIcon} Started"
-msgstr ""
+msgstr "%{loadingIcon} 開始"
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
+msgstr "%{lock_path} ã¯GitLab ユーザー %{lock_user_id} ã«ã‚ˆã£ã¦ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™"
+
+msgid "%{name}'s avatar"
msgstr ""
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr "%{nip_domain} ã¯ã€ã‚«ã‚¹ã‚¿ãƒ ãƒ‰ãƒ¡ã‚¤ãƒ³ã®ä»£æ›¿ã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã™ã€‚"
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
-msgstr ""
+msgstr "%{default_branch} ã‹ã‚‰ %{number_commits_behind} コミットé…ã‚Œã¦ã„ã¦ã€ %{number_commits_ahead} コミット進んã§ã„ã¾ã™ã€‚"
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
-msgstr ""
+msgstr "%{maximum_failures}回中 %{number_of_failures}回失敗。次回アクセスå¯èƒ½ã§ã™ã€‚"
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
-msgstr ""
+msgstr "%{maximum_failures}回中 %{number_of_failures}回失敗。GitLab ã¯å†è©¦è¡Œã—ã¾ã›ã‚“。å•é¡Œã‚’解決ã—ã¦ã‹ã‚‰ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸æƒ…報をリセットã—ã¦ãã ã•ã„。"
msgid "%{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{openOrClose} %{noteable}"
+
+msgid "%{percent}%% complete"
+msgstr "%{percent}%% 完了"
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] "%{storage_name}: 失敗ã—ãŸã‚¢ã‚¯ã‚»ã‚¹ã®è©¦è¡Œå›žæ•° %{failed_attempts}:"
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
msgstr[0] ""
msgid "%{text} is available"
-msgstr ""
+msgstr "%{text} ãŒåˆ©ç”¨ã§ãã¾ã™ã€‚"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr ""
+msgid "%{title} changes"
+msgstr "%{title} ã®å¤‰æ›´"
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr "%{unstaged} 件ã®æœªã‚¹ãƒ†ãƒ¼ã‚¸ã®å¤‰æ›´ã¨ã€ %{staged} 件ã®ã‚¹ãƒ†ãƒ¼ã‚¸æ¸ˆã¿å¤‰æ›´"
msgid "+ %{moreCount} more"
-msgstr ""
+msgstr "+ 他 %{moreCount} 件"
+
+msgid "- Runner is active and can process any new jobs"
+msgstr "- RunnerãŒã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã§æ–°ã—ã„ジョブを処ç†ã§ãã¾ã™"
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr "- RunnerãŒåœæ­¢ä¸­ã®ãŸã‚æ–°ã—ã„ジョブã¯å‡¦ç†ã•ã‚Œã¾ã›ã‚“"
msgid "- show less"
-msgstr ""
+msgstr "- 折りãŸãŸã‚€"
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] "%{count} %{type} 件ã®è¿½åŠ æƒ…å ±"
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] "%{count} 件 %{type} ã®ä¿®æ­£"
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] "%d件ã®ã‚¯ãƒ­ãƒ¼ã‚ºã•ã‚ŒãŸèª²é¡Œ"
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] "%d件ã®ã‚¯ãƒ­ãƒ¼ã‚ºã•ã‚ŒãŸãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] "%d件ã®ãƒžãƒ¼ã‚¸ã•ã‚ŒãŸãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] "%d件ã®èª²é¡Œ"
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] "%d件ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "%d 個ã®ãƒ‘イプライン"
msgid "1st contribution!"
-msgstr ""
+msgstr "最åˆã®è²¢çŒ®!"
msgid "2FA enabled"
+msgstr "2段階èªè¨¼ãŒæœ‰åŠ¹"
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã®é–²è¦§ã«ã¯æ¨©é™ãŒå¿…è¦ã§ã™ã€‚GitLab管ç†è€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。"
+
+msgid "403|You don't have the permission to access this page."
+msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹æ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“。"
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr "URLãŒæ­£ã—ããªã„ã‹ã€ãƒšãƒ¼ã‚¸ãŒç§»å‹•ã•ã‚ŒãŸå¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚"
+
+msgid "404|Page Not Found"
+msgstr "ページãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr "ã“ã®ãƒšãƒ¼ã‚¸ãŒæ­£ã—ããªã„å ´åˆã€GitLab 管ç†è€…ã«é€£çµ¡ã—ã¦ãã ã•ã„。"
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
msgstr ""
-msgid "<strong>Removes</strong> source branch"
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
msgstr ""
+msgid "<strong>Removes</strong> source branch"
+msgstr "ソースブランãƒã‚’<strong>削除</strong>"
+
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr "「Runnerã€ã¯ã‚¸ãƒ§ãƒ–を実行ã™ã‚‹ãƒ—ロセスã§ã™ã€‚å¿…è¦ãªæ•°ã® Runner ã‚’ä»»æ„ã«ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—ã§ãã¾ã™ã€‚"
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "CIã«ã¤ã„ã¦ã®ã‚°ãƒ©ãƒ•"
msgid "A new branch will be created in your fork and a new merge request will be started."
-msgstr ""
+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 "プロジェクトã¨ã¯ãƒ•ã‚¡ã‚¤ãƒ«ã‚’æ ¼ç´(リãƒã‚¸ãƒˆãƒª) ã—ã€è¨ˆç”»ã‚’ç«‹ã¦(課題)ã€ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’公開(wiki) ã™ã‚‹å ´æ‰€ã§ã™ã€‚ %{among_other_things_link}"
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
msgstr ""
msgid "A user with write access to the source branch selected this option"
+msgstr "ã“ã®ã‚ªãƒ—ションをé¸æŠžã—ãŸã‚½ãƒ¼ã‚¹ãƒ–ランãƒã¸ã®æ›¸ãè¾¼ã¿ã‚’許å¯ã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼"
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
msgstr ""
msgid "About auto deploy"
msgstr "自動デプロイã«ã¤ã„ã¦"
+msgid "About this feature"
+msgstr "ã“ã®æ©Ÿèƒ½ã«ã¤ã„ã¦"
+
msgid "Abuse Reports"
-msgstr ""
+msgstr "ä¸æ­£è¡Œç‚ºãƒ¬ãƒãƒ¼ãƒˆ"
msgid "Abuse reports"
+msgstr "迷惑行為レãƒãƒ¼ãƒˆ"
+
+msgid "Accept terms"
+msgstr "利用è¦ç´„ã«åŒæ„ã™ã‚‹"
+
+msgid "Accepted MR"
msgstr ""
msgid "Access Tokens"
+msgstr "アクセス トークン"
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
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 "mount ã«ã‚ˆã£ã¦å¾©æ—§ã§ãるよã†ã«ã€å¤±æ•—ãŒç™ºç”Ÿã—ã¦ã„るストレージã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’一時的ã«æŠ‘æ­¢ã—ã¾ã—ãŸã€‚å†åº¦ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ãŸã‚ã«ã¯ã€å•é¡Œã‚’解決ã—ã¦ã‹ã‚‰ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸æƒ…報をリセットã—ã¦ãã ã•ã„。"
+
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
msgstr ""
msgid "Account"
-msgstr ""
+msgstr "アカウント"
-msgid "Account and limit settings"
-msgstr ""
+msgid "Account and limit"
+msgstr "アカウントã¨åˆ¶é™"
msgid "Active"
msgstr "有効"
+msgid "Active Sessions"
+msgstr "アクティブ セッション"
+
msgid "Activity"
msgstr "アクティビティー"
msgid "Add"
-msgstr ""
+msgstr "追加"
msgid "Add Changelog"
msgstr "変更履歴を追加"
@@ -160,97 +322,127 @@ msgid "Add Group Webhooks and GitLab Enterprise Edition."
msgstr ""
msgid "Add Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes クラスターを追加"
msgid "Add License"
msgstr "ライセンスを追加"
msgid "Add Readme"
+msgstr "Readmeを追加"
+
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
msgstr ""
msgid "Add new directory"
msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’追加"
+msgid "Add reaction"
+msgstr "リアクションã®è¿½åŠ "
+
msgid "Add todo"
+msgstr "Todoを追加"
+
+msgid "Add user(s) to the group:"
msgstr ""
-msgid "AdminArea|Stop all jobs"
+msgid "Add users to group"
msgstr ""
-msgid "AdminArea|Stop all jobs?"
+msgid "Additional text"
+msgstr "追加テキスト"
+
+msgid "Admin Area"
msgstr ""
-msgid "AdminArea|Stop jobs"
+msgid "Admin Overview"
msgstr ""
-msgid "AdminArea|Stopping jobs failed"
+msgid "Admin area"
msgstr ""
+msgid "AdminArea|Stop all jobs"
+msgstr "å…¨ã¦ã®ã‚¸ãƒ§ãƒ–ã‚’åœæ­¢"
+
+msgid "AdminArea|Stop all jobs?"
+msgstr "å…¨ã¦ã®ã‚¸ãƒ§ãƒ–ã‚’åœæ­¢ã—ã¾ã™ã‹?"
+
+msgid "AdminArea|Stop jobs"
+msgstr "ジョブをåœæ­¢"
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr "ジョブã®åœæ­¢ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
-msgstr ""
+msgstr "å…¨ã¦ã®ã‚¸ãƒ§ãƒ–ã‚’åœæ­¢ã—ã¾ã™ã€‚ã“ã‚Œã«ã‚ˆã‚Šç¾åœ¨å®Ÿè¡Œä¸­ã®ã‚¸ãƒ§ãƒ–ã¯åœæ­¢ã•ã‚Œã¾ã™ã€‚"
msgid "AdminHealthPageLink|health page"
-msgstr ""
+msgstr "ステータスページ"
msgid "AdminProjects|Delete"
-msgstr ""
+msgstr "削除"
msgid "AdminProjects|Delete Project %{projectName}?"
-msgstr ""
+msgstr "プロジェクト %{projectName} を削除ã—ã¾ã™ã‹?"
msgid "AdminProjects|Delete project"
-msgstr ""
+msgstr "プロジェクトã®å‰Šé™¤"
msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ãƒ—ロジェクトã®Auto Review AppsãŠã‚ˆã³Auto Deployステージã§ä½¿ç”¨ã™ã‚‹ã€ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®ãƒ‰ãƒ¡ã‚¤ãƒ³ã‚’指定ã—ã¾ã™ã€‚"
msgid "AdminUsers|Block user"
-msgstr ""
+msgstr "ブロックユーザー"
msgid "AdminUsers|Delete User %{username} and contributions?"
-msgstr ""
+msgstr "ユーザー %{username} ã¨è²¢çŒ®åº¦ã‚’削除ã—ã¾ã™ã‹?"
msgid "AdminUsers|Delete User %{username}?"
-msgstr ""
+msgstr "ユーザー %{username} を削除ã—ã¾ã™ã‹?"
msgid "AdminUsers|Delete user"
-msgstr ""
+msgstr "ユーザーã®å‰Šé™¤"
msgid "AdminUsers|Delete user and contributions"
-msgstr ""
+msgstr "ユーザーã¨è²¢çŒ®åº¦ã®å‰Šé™¤"
msgid "AdminUsers|To confirm, type %{projectName}"
-msgstr ""
+msgstr "確èªã®ãŸã‚ã€%{projectName} を入力ã—ã¦ãã ã•ã„"
msgid "AdminUsers|To confirm, type %{username}"
-msgstr ""
+msgstr "確èªã®ãŸã‚ã€%{username} を入力ã—ã¦ãã ã•ã„"
msgid "Advanced"
-msgstr ""
+msgstr "高度ãªè¨­å®š"
msgid "Advanced settings"
-msgstr ""
+msgstr "高度ãªè¨­å®š"
msgid "All"
-msgstr ""
+msgstr "ã™ã¹ã¦"
msgid "All changes are committed"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®å¤‰æ›´ãŒã‚³ãƒŸãƒƒãƒˆã•ã‚Œã¦ã„ã¾ã™"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
-msgstr ""
+msgstr "空ã®ãƒ—ロジェクトã€ãƒ†ãƒ³ãƒ—レートã‹ã‚‰ã€ã¾ãŸã¯ã‚¤ãƒ³ãƒãƒ¼ãƒˆæ™‚ã«ã™ã¹ã¦ã®æ©Ÿèƒ½ãŒæœ‰åŠ¹ã«ãªã£ã¦ã„ã¾ã™ãŒã€å¾Œã§ãƒ—ロジェクト設定ã§ç„¡åŠ¹ã«ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr "ターゲットブランãƒã«ãƒžãƒ¼ã‚¸ã§ãるメンãƒãƒ¼ã‹ã‚‰ã®ã‚³ãƒŸãƒƒãƒˆã‚’許å¯ã—ã¾ã™ã€‚"
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
-msgstr ""
+msgstr "Asciidocドキュメントã§ã®PlantUML図ã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ã‚’許å¯ã—ã¾ã™ã€‚"
msgid "Allow requests to the local network from hooks and services."
-msgstr ""
+msgstr "フックãŠã‚ˆã³ã‚µãƒ¼ãƒ“スã‹ã‚‰ã®ãƒ­ãƒ¼ã‚«ãƒ«ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã¸ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’許å¯ã™ã‚‹ã€‚"
msgid "Allows you to add and manage Kubernetes clusters."
-msgstr ""
+msgstr "Kubernetes クラスターを追加ãŠã‚ˆã³ç®¡ç†ã§ãã¾ã™ã€‚"
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
msgstr ""
@@ -262,14 +454,56 @@ msgid "Alternatively, you can use a %{personal_access_token_link}. When you crea
msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr "ã‚ã‚‹ã„ã¯ã€ %{personal_access_token_link} を使用ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚パーソナルアクセストークンを作æˆã™ã‚‹éš›ã«ã€<code>repo</code>スコープをé¸æŠžã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ã“ã‚Œã«ã‚ˆã‚Šã€ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹ã“ã¨ãŒã§ãるパブリックリãƒã‚¸ãƒˆãƒªã¨ãƒ—ライベートリãƒã‚¸ãƒˆãƒªã®ä¸€è¦§ã‚’表示ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
-msgid "An error occurred previewing the blob"
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
-msgid "An error occurred when toggling the notification subscription"
+msgid "An error accured whilst committing your changes."
msgstr ""
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr "æ–°ã—ã„ブランãƒã®ä½œæˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr "Blobã®ãƒ—レビュー中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr "通知購読ã®åˆ‡ã‚Šæ›¿ãˆæ™‚ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+
msgid "An error occurred when updating the issue weight"
msgstr ""
@@ -279,210 +513,381 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr "アラートã®æ¶ˆåŽ»ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ページを更新ã—ã¦ã‚„ã‚Šç›´ã—ã¦ä¸‹ã•ã„。"
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
-msgstr ""
+msgstr "ãƒã‚¤ãƒ©ã‚¤ãƒˆã‚’消去ã—ã¦ã„ã‚‹ã¨ãã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ページを更新ã—ã¦ã‚‚ã†ä¸€åº¦æ¶ˆåŽ»ã—ã¦ãã ã•ã„。"
msgid "An error occurred while fetching markdown preview"
msgstr ""
msgid "An error occurred while fetching sidebar data"
-msgstr ""
+msgstr "サイドãƒãƒ¼ã®ãƒ‡ãƒ¼ã‚¿å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while fetching the pipeline."
-msgstr ""
+msgstr "パイプラインã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
-msgstr ""
+msgstr "差分ã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while loading filenames"
-msgstr ""
+msgstr "ファイルåã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while loading the file"
-msgstr ""
+msgstr "ファイルã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while making the request."
-msgstr ""
+msgstr "リクエスト作æˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while removing approver"
msgstr ""
msgid "An error occurred while rendering KaTeX"
-msgstr ""
+msgstr "KaTeXã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while rendering preview broadcast message"
msgstr ""
msgid "An error occurred while retrieving calendar activity"
-msgstr ""
+msgstr "カレンダーアクティビティーå–å¾—ã®éš›ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while retrieving diff"
-msgstr ""
+msgstr "差分をå–å¾—ã®éš›ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while saving LDAP override status. Please try again."
msgstr ""
msgid "An error occurred while saving assignees"
+msgstr "担当者ã®ç™»éŒ²ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+
+msgid "An error occurred while subscribing to notifications."
msgstr ""
-msgid "An error occurred while validating username"
+msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while validating username"
+msgstr "ユーザåã®æ¤œè¨¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+
msgid "An error occurred. Please try again."
+msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚å†åº¦ãŠè©¦ã—ãã ã•ã„。"
+
+msgid "Anonymous"
msgstr ""
-msgid "Any Label"
+msgid "Anti-spam verification"
msgstr ""
+msgid "Any"
+msgstr ""
+
+msgid "Any Label"
+msgstr "ä»»æ„ã®ãƒ©ãƒ™ãƒ«"
+
msgid "Appearance"
+msgstr "外観"
+
+msgid "Application"
+msgstr "アプリケーション"
+
+msgid "Application Id"
msgstr ""
-msgid "Applications"
+msgid "Application: %{name}"
msgstr ""
+msgid "Applications"
+msgstr "アプリケーション"
+
msgid "Apr"
-msgstr ""
+msgstr "4月"
msgid "April"
-msgstr ""
+msgstr "4月"
-msgid "Archived project! Repository is read-only"
-msgstr "アーカイブ済ã¿ãƒ—ロジェクトï¼ï¼ˆãƒ¬ãƒã‚¸ãƒˆãƒªãƒ¼ã¯èª­ã¿å–り専用ã§ã™ï¼‰"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr "ã“ã®ãƒ—ロジェクトã¯ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã•ã‚Œã¦ã„ã¾ã™ã€‚リãƒã‚¸ãƒˆãƒªãŠã‚ˆã³ãã®ä»–ã®ãƒ—ロジェクトリソースã¯èª­ã¿å–り専用ã§ã™"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ã“ã®ãƒ‘イプラインスケジュールを削除ã—ã¾ã™ã‹ï¼Ÿ"
-msgid "Are you sure you want to reset registration token?"
+msgid "Are you sure you want to lose unsaved changes?"
msgstr ""
-msgid "Are you sure you want to reset the health check token?"
+msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove this identity?"
+msgstr "ã“ã® ID を削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+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 you want to unlock %{path_lock_path}?"
msgstr ""
msgid "Are you sure?"
-msgstr ""
+msgstr "本当ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "Artifacts"
-msgstr ""
+msgstr "アーティファクト"
+
+msgid "Ascending"
+msgstr "昇順"
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr "グループ Runner ã®è¨­å®šã¯ã€ã‚°ãƒ«ãƒ¼ãƒ—ã® Maintainer ã«ä¾é ¼ã—ã¦ãã ã•ã„。"
msgid "Assertion consumer service URL"
msgstr ""
msgid "Assign custom color like #FF0000"
-msgstr ""
+msgstr "#FF0000ã®ã‚ˆã†ãªã‚«ã‚¹ã‚¿ãƒ ã‚«ãƒ©ãƒ¼ã‚’割り当ã¦ã‚‹"
msgid "Assign labels"
-msgstr ""
+msgstr "ラベルを割り当ã¦ã‚‹"
msgid "Assign milestone"
-msgstr ""
+msgstr "マイルストーンを割り当ã¦ã‚‹"
msgid "Assign to"
-msgstr ""
+msgstr "割り当ã¦å…ˆ"
msgid "Assigned Issues"
-msgstr ""
+msgstr "割り当ã¦ã‚‰ã‚ŒãŸèª²é¡Œ"
msgid "Assigned Merge Requests"
-msgstr ""
+msgstr "割り当ã¦ã‚‰ã‚ŒãŸãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
msgid "Assigned to :name"
-msgstr ""
+msgstr ":name ãŒæ‹…当"
+
+msgid "Assigned to me"
+msgstr "自分ã«å‰²ã‚Šå½“ã¦ã‚‹"
msgid "Assignee"
+msgstr "担当者"
+
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
msgstr ""
+msgid "Assignee(s)"
+msgstr "担当者"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "ドラッグ&ドロップã¾ãŸã¯ %{upload_link} ã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚’添付"
+msgid "Audit Events"
+msgstr "監査イベント"
+
msgid "Aug"
-msgstr ""
+msgstr "8月"
msgid "August"
-msgstr ""
+msgstr "8月"
msgid "Authentication Log"
+msgstr "èªè¨¼ãƒ­ã‚°"
+
+msgid "Authentication log"
msgstr ""
msgid "Author"
+msgstr "作æˆè€…"
+
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
msgstr ""
msgid "Authors: %{authors}"
+msgstr "作æˆè€…: %{authors}"
+
+msgid "Auto DevOps"
msgstr ""
msgid "Auto DevOps enabled"
-msgstr ""
+msgstr "Auto DevOps ãŒæœ‰åŠ¹ã§ã™"
msgid "Auto DevOps, runners and job artifacts"
-msgstr ""
+msgstr "Auto DevOps, Runner,ãŠã‚ˆã³æˆæžœç‰©"
msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
-msgstr ""
+msgstr "自動 Review Apps ã¨è‡ªå‹•ãƒ‡ãƒ—ロイã§ã¯ã€ %{kubernetes} ãŒæ­£ã—ã動作ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
-msgstr ""
+msgstr "自動 Review Apps ã¨è‡ªå‹•ãƒ‡ãƒ—ロイã§ã¯ã€ãƒ‰ãƒ¡ã‚¤ãƒ³åã¨æ­£å¸¸ã«å‹•ä½œã™ã‚‹ %{kubernetes} ãŒå¿…è¦ã§ã™ã€‚"
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
-msgstr ""
+msgstr "自動 Review Apps ã¨è‡ªå‹•ãƒ‡ãƒ—ロイを正ã—ãå‹•ã‹ã™ãŸã‚ã«ã¯ãƒ‰ãƒ¡ã‚¤ãƒ³åãŒå¿…è¦ã§ã™ã€‚"
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
msgstr ""
+msgid "AutoDevOps|Auto DevOps"
+msgstr "Auto DevOps"
+
msgid "AutoDevOps|Auto DevOps documentation"
-msgstr ""
+msgstr "Auto DevOps ドキュメント"
msgid "AutoDevOps|Enable in settings"
-msgstr ""
+msgstr "設定を有効ã«ã™ã‚‹"
msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
-msgstr ""
+msgstr "ã‚らã‹ã˜ã‚定義ã•ã‚ŒãŸ CI/CD ã®æ§‹æˆã‚’基ã«è‡ªå‹•çš„ã«ã‚¢ãƒ—リケーションをビルドã€ãƒ†ã‚¹ãƒˆã€ãƒ‡ãƒ—ロイã—ã¾ã™ã€‚"
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
-msgstr ""
+msgstr "詳ã—ãã¯ã€ %{link_to_documentation} を見ã¦ãã ã•ã„。"
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
-msgstr ""
+msgstr "ã“ã®ãƒ—ロジェクト㮠%{link_to_auto_devops_settings} ã‚’ã™ã‚‹å ´åˆã€è‡ªå‹•ãƒ“ルドãŠã‚ˆã³ãƒ†ã‚¹ãƒˆã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚%{link_to_add_kubernetes_cluster} ã—ãŸå ´åˆã€è‡ªå‹•ãƒ‡ãƒ—ロイã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
msgid "AutoDevOps|add a Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes クラスターを追加"
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
-msgstr ""
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr "Auto DevOps を有効ã«ã™ã‚‹"
msgid "Available"
-msgstr ""
+msgstr "利用å¯èƒ½"
+
+msgid "Available group Runners : %{runners}"
+msgstr "利用å¯èƒ½ãªã‚°ãƒ«ãƒ¼ãƒ— Runner : %{runners}"
+
+msgid "Available group Runners : %{runners}."
+msgstr "利用å¯èƒ½ãªã‚°ãƒ«ãƒ¼ãƒ— Runner : %{runners}。"
msgid "Avatar will be removed. Are you sure?"
-msgstr ""
+msgstr "ã‚¢ãƒã‚¿ãƒ¼ã‚’削除ã—ã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "Average per day: %{average}"
-msgstr ""
+msgstr "1æ—¥ã‚ãŸã‚Šã®å¹³å‡: %{average}"
msgid "Background Color"
+msgstr "背景色"
+
+msgid "Background Jobs"
msgstr ""
+msgid "Background color"
+msgstr "背景色"
+
msgid "Background jobs"
-msgstr ""
+msgstr "ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã‚¸ãƒ§ãƒ–"
+
+msgid "Badges"
+msgstr "ãƒãƒƒã‚¸"
+
+msgid "Badges|A new badge was added."
+msgstr "æ–°ã—ã„ãƒãƒƒã‚¸ãŒè¿½åŠ ã•ã‚Œã¾ã—ãŸã€‚"
+
+msgid "Badges|Add badge"
+msgstr "ãƒãƒƒã‚¸ã‚’追加"
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr "ãƒãƒƒã‚¸ã‚’追加ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚入力ã—ãŸURLを確èªã—ã¦ã€ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。"
+
+msgid "Badges|Badge image URL"
+msgstr "ãƒãƒƒã‚¸ç”»åƒã®URL"
+
+msgid "Badges|Badge image preview"
+msgstr "ãƒãƒƒã‚¸ç”»åƒãƒ—レビュー"
+
+msgid "Badges|Delete badge"
+msgstr "ãƒãƒƒã‚¸ã‚’削除"
+
+msgid "Badges|Delete badge?"
+msgstr "ãƒãƒƒã‚¸ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr "ãƒãƒƒã‚¸ã‚’削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。"
+
+msgid "Badges|Group Badge"
+msgstr "グループãƒãƒƒã‚¸"
+
+msgid "Badges|Link"
+msgstr "リンク"
+
+msgid "Badges|No badge image"
+msgstr "ãƒãƒƒã‚¸ç”»åƒãªã—"
+
+msgid "Badges|No image to preview"
+msgstr "プレビューã™ã‚‹ç”»åƒã¯ã‚ã‚Šã¾ã›ã‚“"
+
+msgid "Badges|Project Badge"
+msgstr "プロジェクト ãƒãƒƒã‚¸"
+
+msgid "Badges|Reload badge image"
+msgstr "ãƒãƒƒã‚¸ç”»åƒã®å†èª­ã¿è¾¼ã¿"
+
+msgid "Badges|Save changes"
+msgstr "変更ã®ä¿å­˜"
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr "ãƒãƒƒã‚¸ã‚’ä¿å­˜ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚入力ã—ãŸURLを確èªã—ã¦ã€ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。"
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr "GitLabã¯æ¬¡ã® %{docsLinkStart}変数%{docsLinkEnd} をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™: %{placeholders}"
+
+msgid "Badges|The badge was deleted."
+msgstr "ãƒãƒƒã‚¸ãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚"
+
+msgid "Badges|The badge was saved."
+msgstr "ãƒãƒƒã‚¸ãŒä¿å­˜ã•ã‚Œã¾ã—ãŸã€‚"
+
+msgid "Badges|This group has no badges"
+msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ãƒãƒƒã‚¸ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+msgid "Badges|This project has no badges"
+msgstr "ã“ã®ãƒ—ロジェクトã«ãƒãƒƒã‚¸ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+msgid "Badges|Your badges"
+msgstr "ãƒãƒƒã‚¸"
msgid "Begin with the selected commit"
+msgstr "é¸æŠžã—ãŸã‚³ãƒŸãƒƒãƒˆã§ã¯ã˜ã‚ã‚‹"
+
+msgid "Below are examples of regex for existing tools:"
msgstr ""
-msgid "Billing"
+msgid "Below you will find all the groups that are public."
msgstr ""
+msgid "Billing"
+msgstr "請求"
+
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
msgstr ""
@@ -490,19 +895,22 @@ msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently n
msgstr ""
msgid "BillingPlans|Current plan"
-msgstr ""
+msgstr "ç¾åœ¨ã®ãƒ—ラン"
msgid "BillingPlans|Customer Support"
-msgstr ""
+msgstr "カスタマー サãƒãƒ¼ãƒˆ"
msgid "BillingPlans|Downgrade"
+msgstr "ダウングレード"
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
msgstr ""
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
msgid "BillingPlans|Manage plan"
-msgstr ""
+msgstr "プランã®ç®¡ç†"
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
msgstr ""
@@ -517,11 +925,20 @@ msgid "BillingPlans|To manage the plan for this group, visit the billing section
msgstr ""
msgid "BillingPlans|Upgrade"
-msgstr ""
+msgstr "アップグレード"
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -534,21 +951,33 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr "ボード"
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr "%{branchName} ブランãƒã¯ã“ã®ãƒ—ロジェクトã®ãƒªãƒã‚¸ãƒˆãƒªã«è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚"
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
-msgstr[0] ""
+msgstr[0] "ブランム(%{branch_count})"
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 "<strong>%{branch_name}</strong> ブランãƒãŒä½œæˆã•ã‚Œã¾ã—ãŸã€‚自動デプロイを設定ã™ã‚‹ã«ã¯ã€GitLab CI Yaml テンプレートをé¸æŠžã—ã¦ã€å¤‰æ›´ã‚’コミットã—ã¦ãã ã•ã„。 %{link_to_autodeploy_doc}"
msgid "Branch has changed"
-msgstr ""
+msgstr "ブランãƒãŒå¤‰æ›´ã•ã‚Œã¾ã—ãŸ"
msgid "Branch is already taken"
-msgstr ""
+msgstr "ブランãƒã¯æ—¢ã«ä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™"
msgid "Branch name"
-msgstr ""
+msgstr "ブランãƒå"
msgid "BranchSwitcherPlaceholder|Search branches"
msgstr "ブランãƒã‚’検索"
@@ -560,124 +989,124 @@ msgid "Branches"
msgstr "ブランãƒ"
msgid "Branches|Active"
-msgstr ""
+msgstr "アクティブ"
msgid "Branches|Active branches"
-msgstr ""
+msgstr "アクティブブランãƒ"
msgid "Branches|All"
-msgstr ""
+msgstr "ã™ã¹ã¦"
msgid "Branches|Cant find HEAD commit for this branch"
-msgstr ""
+msgstr "ã“ã®ãƒ–ランãƒã«ã¯HEADコミットãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
msgid "Branches|Compare"
-msgstr ""
+msgstr "比較"
msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
-msgstr ""
+msgstr "'%{default_branch}'ã«ãƒžãƒ¼ã‚¸ã•ã‚ŒãŸã™ã¹ã¦ã®ãƒ–ランãƒã‚’削除"
msgid "Branches|Delete branch"
-msgstr ""
+msgstr "ブランãƒã‚’削除"
msgid "Branches|Delete merged branches"
-msgstr ""
+msgstr "マージã•ã‚ŒãŸãƒ–ランãƒã‚’削除"
msgid "Branches|Delete protected branch"
-msgstr ""
+msgstr "ä¿è­·ã•ã‚ŒãŸãƒ–ランãƒã‚’削除"
msgid "Branches|Delete protected branch '%{branch_name}'?"
-msgstr ""
+msgstr "ä¿è­·ã•ã‚Œã¦ã„るブランム'%{branch_name}' を削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?"
-msgstr ""
+msgstr "'%{branch_name}' ブランãƒã‚’削除ã—ãŸã‚‰å…ƒã«ã¯æˆ»ã›ã¾ã›ã‚“。よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
-msgstr ""
+msgstr "マージã•ã‚ŒãŸãƒ–ランãƒã‚’削除ã™ã‚‹ã¨å…ƒã«ã¯æˆ»ã›ã¾ã›ã‚“。よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "Branches|Filter by branch name"
-msgstr ""
+msgstr "ブランãƒåã§ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼"
msgid "Branches|Merged into %{default_branch}"
-msgstr ""
+msgstr "%{default_branch} ã«ãƒžãƒ¼ã‚¸"
msgid "Branches|New branch"
-msgstr ""
+msgstr "æ–°è¦ãƒ–ランãƒ"
msgid "Branches|No branches to show"
-msgstr ""
+msgstr "表示ã™ã‚‹ãƒ–ランãƒã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
-msgstr ""
+msgstr "ã‚‚ã†ä¸€åº¦ç¢ºèªã— %{delete_protected_branch} を押ã—ã¦ãã ã•ã„。æ“作後ã€å…ƒã«æˆ»ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。"
-msgid "Branches|Only a project master or owner can delete a protected branch"
-msgstr ""
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr "プロジェクト㮠Maintainer 㨠Owner ã ã‘ãŒä¿è­·ã•ã‚Œã¦ã„るブランãƒã‚’削除ã§ãã¾ã™"
msgid "Branches|Overview"
-msgstr ""
+msgstr "概è¦"
msgid "Branches|Protected branches can be managed in %{project_settings_link}."
-msgstr ""
+msgstr "ä¿è­·ã•ã‚ŒãŸãƒ–ランãƒã¯ %{project_settings_link} ã‹ã‚‰è¨­å®šå¤‰æ›´ã§ãã¾ã™ã€‚"
msgid "Branches|Show active branches"
-msgstr ""
+msgstr "アクティブブランãƒã‚’表示"
msgid "Branches|Show all branches"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ãƒ–ランãƒã‚’表示"
msgid "Branches|Show more active branches"
-msgstr ""
+msgstr "アクティブブランãƒã‚’ã•ã‚‰ã«è¡¨ç¤º"
msgid "Branches|Show more stale branches"
-msgstr ""
+msgstr "å¤ã„ブランãƒã‚’ã™ã¹ã¦è¡¨ç¤º"
msgid "Branches|Show overview of the branches"
-msgstr ""
+msgstr "ブランãƒã®æ¦‚è¦ã‚’表示"
msgid "Branches|Show stale branches"
-msgstr ""
+msgstr "éŽåŽ»ã®ãƒ–ランãƒã‚’表示"
msgid "Branches|Sort by"
-msgstr ""
+msgstr "並ã¹æ›¿ãˆ"
msgid "Branches|Stale"
-msgstr ""
+msgstr "å¤ã„"
msgid "Branches|Stale branches"
-msgstr ""
+msgstr "å¤ã„ブランãƒ"
msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
msgstr ""
msgid "Branches|The default branch cannot be deleted"
-msgstr ""
+msgstr "デフォルトブランãƒã¯å‰Šé™¤ã§ãã¾ã›ã‚“"
msgid "Branches|This branch hasn’t been merged into %{default_branch}."
-msgstr ""
+msgstr "ã“ã®ãƒ–ランãƒã¯%{default_branch} ã«ãƒžãƒ¼ã‚¸ã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
-msgstr ""
+msgstr "データã®æ¶ˆå¤±ã‚’é¿ã‘ã‚‹ãŸã‚ã€å‰Šé™¤ã™ã‚‹å‰ã«ã“ã®ãƒ–ランãƒã‚’マージã™ã‚‹ã“ã¨ã‚’オススメã—ã¾ã™ã€‚"
msgid "Branches|To confirm, type %{branch_name_confirmation}:"
-msgstr ""
+msgstr "確èªã®ãŸã‚ã€%{branch_name_confirmation} を入力ã—ã¦ãã ã•ã„"
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
-msgstr ""
+msgstr "ä¿è­·ã•ã‚ŒãŸ %{branch_name} ブランãƒã‚’削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚"
msgid "Branches|diverged from upstream"
msgstr ""
msgid "Branches|merged"
-msgstr ""
+msgstr "マージ済ã¿"
msgid "Branches|project settings"
-msgstr ""
+msgstr "プロジェクト設定"
msgid "Branches|protected"
-msgstr ""
+msgstr "ä¿è­·"
msgid "Browse Directory"
msgstr "ディレクトリを表示"
@@ -691,35 +1120,98 @@ msgstr "ファイルを表示"
msgid "Browse files"
msgstr "ファイルを表示"
-msgid "Business"
-msgstr ""
+msgid "Business metrics (Custom)"
+msgstr "ビジãƒã‚¹ãƒ¡ãƒˆãƒªã‚¯ã‚¹ï¼ˆã‚«ã‚¹ã‚¿ãƒ ï¼‰"
msgid "ByAuthor|by"
msgstr "作者"
msgid "CI / CD"
-msgstr ""
+msgstr "CI / CD"
+
+msgid "CI / CD Settings"
+msgstr "CI / CD 設定"
msgid "CI/CD"
-msgstr ""
+msgstr "CI/CD"
msgid "CI/CD configuration"
-msgstr ""
+msgstr "CI/CD 設定"
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr "CI/CD 設定"
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr "継続的インテグレーションã¨ãƒ‡ãƒªãƒãƒªãƒ¼ã‚’使用ã™ã‚‹ã«ã¯ã€æ˜Žç¤ºçš„ã« %{ci_file} を指定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
+
+msgid "CICD|Auto DevOps"
+msgstr "Auto DevOps"
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr "Auto DevOps ã¯ã‚らã‹ã˜ã‚定義ã•ã‚ŒãŸç¶™ç¶šçš„インテグレーションã¨ãƒ‡ãƒªãƒãƒªãƒ¼ã®è¨­å®šã«åŸºã¥ã„ã¦ã€ã‚¢ãƒ—リケーションを自動的ã«ãƒ“ルドã€ãƒ†ã‚¹ãƒˆã€ãƒ‡ãƒ—ロイã—ã¾ã™ã€‚"
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr "ステージングã¸ã®è‡ªå‹•ãƒ‡ãƒ—ロイã€æœ¬ç•ªç’°å¢ƒã¸ã®æ‰‹å‹•ãƒ‡ãƒ—ロイ"
+
+msgid "CICD|Continuous deployment to production"
+msgstr "本番環境ã¸ã®ç¶™ç¶šçš„デプロイ"
+
+msgid "CICD|Deployment strategy"
+msgstr "デプロイ戦略"
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr "デプロイ戦略を正ã—ã動作ã•ã›ã‚‹ãŸã‚ã«ã¯ãƒ‰ãƒ¡ã‚¤ãƒ³åãŒå¿…è¦ã§ã™ã€‚"
+
+msgid "CICD|Disable Auto DevOps"
+msgstr "Auto DevOps を無効ã«ã™ã‚‹"
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr "Auto DevOps を有効ã«ã™ã‚‹"
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr "プロジェクト固有㮠%{ci_file} ãŒç„¡ã„å ´åˆã€ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã«å¾“ã„ Auto DevOps ã®æœ‰åŠ¹ã¨ç„¡åŠ¹ã‚’切り替ãˆã¾ã™ã€‚"
+
+msgid "CICD|Instance default (%{state})"
+msgstr "インスタンスã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆï¼ˆ%{state})"
+
msgid "CICD|Jobs"
+msgstr "ジョブ"
+
+msgid "CICD|Learn more about Auto DevOps"
+msgstr "Auto DevOps ã®è©³ç´°"
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr "プロジェクト㫠%{ci_file} ãŒãªã„å ´åˆã€Auto DevOps ã®ãƒ‘イプライン設定ãŒä½¿ç”¨ã•ã‚Œã¾ã™ã€‚"
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr "自動 Review Apps ã¨è‡ªå‹•ãƒ‡ãƒ—ロイステージを使用ã—ãŸã„å ´åˆã¯ã€ãƒ‰ãƒ¡ã‚¤ãƒ³ã‚’指定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
msgstr ""
+msgid "Can't find HEAD commit for this branch"
+msgstr "ã“ã®ãƒ–ランãƒã«ã¯ HEAD コミットãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
msgid "Cancel"
msgstr "キャンセル"
+msgid "Cancel this job"
+msgstr "ã“ã®ã‚¸ãƒ§ãƒ–をキャンセルã™ã‚‹"
+
msgid "Cannot be merged automatically"
-msgstr ""
+msgstr "自動的ã«ãƒžãƒ¼ã‚¸ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“"
msgid "Cannot modify managed Kubernetes cluster"
-msgstr ""
+msgstr "管ç†ä¸‹ã® Kubernetes クラスターを変更ã§ãã¾ã›ã‚“"
msgid "Certificate fingerprint"
msgstr ""
@@ -728,7 +1220,7 @@ msgid "Change Weight"
msgstr ""
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
-msgstr ""
+msgstr "GitLab UI ã®æ›´æ–°é »åº¦ã‚’変更ã™ã‚‹ã«ã¯ã“ã®å€¤ã‚’変更ã—ã¦ãã ã•ã„。"
msgid "ChangeTypeActionLabel|Pick into branch"
msgstr "ピック先ブランãƒ:"
@@ -743,28 +1235,28 @@ msgid "ChangeTypeAction|Revert"
msgstr "リãƒãƒ¼ãƒˆ"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
-msgstr ""
+msgstr "コミット済ã®å¤‰æ›´ã‚’ revert ã™ã‚‹ãŸã‚ã«æ–°ã—ã„コミットを作æˆã—ã¾ã™"
msgid "Changelog"
msgstr "変更履歴"
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
-msgstr ""
+msgstr "<b>source</b>リビジョンãŒ<b>target</b>リビジョン内ã«å–ã‚Šè¾¼ã¾ã‚Œã¦ã„るよã†ãªå¤‰æ›´ã¨ã—ã¦è¡¨ç¤ºã•ã‚Œã¾ã™"
msgid "Charts"
msgstr "ãƒãƒ£ãƒ¼ãƒˆ"
msgid "Chat"
-msgstr ""
+msgstr "ãƒãƒ£ãƒƒãƒˆ"
msgid "Check interval"
-msgstr ""
+msgstr "ãƒã‚§ãƒƒã‚¯ã®é–“éš”"
msgid "Checking %{text} availability…"
-msgstr ""
+msgstr "%{text} ãŒåˆ©ç”¨å¯èƒ½ã‹ç¢ºèªã—ã¦ã„ã¾ã™â€¦"
msgid "Checking branch availability..."
-msgstr ""
+msgstr "ブランãƒãŒåˆ©ç”¨å¯èƒ½ã‹ç¢ºèªã—ã¦ã„ã¾ã™â€¦"
msgid "Cherry-pick this commit"
msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã‚’ãƒã‚§ãƒªãƒ¼ãƒ”ック"
@@ -772,13 +1264,28 @@ msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã‚’ãƒã‚§ãƒªãƒ¼ãƒ”ック"
msgid "Cherry-pick this merge request"
msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ãƒã‚§ãƒªãƒ¼ãƒ”ック"
-msgid "Choose File ..."
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
msgstr ""
+msgid "Choose File ..."
+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})を入力ã—ã¦ãã ã•ã„。"
+
+msgid "Choose any color."
+msgstr "カラーをé¸æŠžã—ã¦ãã ã•ã„。"
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
msgstr ""
msgid "Choose file..."
+msgstr "ファイルをé¸æŠž..."
+
+msgid "Choose the top-level group for your repository imports."
msgstr ""
msgid "Choose which groups you wish to synchronize to this secondary node."
@@ -788,7 +1295,7 @@ msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr ""
msgid "Choose which repositories you want to import."
-msgstr ""
+msgstr "インãƒãƒ¼ãƒˆã—ãŸã„リãƒã‚¸ãƒˆãƒªã‚’é¸æŠžã—ã¦ãã ã•ã„。"
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
@@ -848,376 +1355,466 @@ msgid "CiStatus|running"
msgstr "実行中"
msgid "CiVariables|Input variable key"
-msgstr ""
+msgstr "キーを入力"
msgid "CiVariables|Input variable value"
-msgstr ""
+msgstr "値を入力"
msgid "CiVariables|Remove variable row"
-msgstr ""
+msgstr "環境変数を削除"
msgid "CiVariable|* (All environments)"
-msgstr ""
+msgstr "* (ã™ã¹ã¦ã®ç’°å¢ƒ)"
msgid "CiVariable|All environments"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ç’°å¢ƒ"
msgid "CiVariable|Create wildcard"
-msgstr ""
+msgstr "ワイルドカードã®ä½œæˆ"
msgid "CiVariable|Error occured while saving variables"
-msgstr ""
+msgstr "変数ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "CiVariable|New environment"
-msgstr ""
+msgstr "æ–°ã—ã„環境"
msgid "CiVariable|Protected"
-msgstr ""
+msgstr "ä¿è­·"
msgid "CiVariable|Search environments"
msgstr ""
msgid "CiVariable|Toggle protected"
-msgstr ""
+msgstr "ä¿è­·ã®åˆ‡ã‚Šæ›¿ãˆ"
msgid "CiVariable|Validation failed"
-msgstr ""
+msgstr "検証ã«å¤±æ•—ã—ã¾ã—ãŸ"
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr "検索欄ã®æ¶ˆåŽ»"
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr "プロジェクトリストã§<strong>プロジェクトå</strong>をクリックã™ã‚‹ã¨ã€ãƒ—ロジェクトã®ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³ã«ç§»å‹•ã—ã¾ã™ã€‚"
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr "å³ä¸Šã®<strong>昇格</strong>ボタンをクリックã—ã¦ã‚°ãƒ«ãƒ¼ãƒ—マイルストーンã¸æ˜‡æ ¼"
+
+msgid "Click the <strong>Select none</strong> 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 "下ã®ãƒœã‚¿ãƒ³ã‚’クリックã™ã‚‹ã¨ã€Kubernetesã®ãƒšãƒ¼ã‚¸ã«é·ç§»ã—ã€ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãƒ—ロセスを開始ã—ã¾ã™"
+
+msgid "Click to expand it."
msgstr ""
msgid "Click to expand text"
-msgstr ""
+msgstr "クリックã—ã¦ãƒ†ã‚­ã‚¹ãƒˆã‚’展開ã™ã‚‹"
msgid "Client authentication certificate"
msgstr ""
msgid "Client authentication key"
-msgstr ""
+msgstr "クライアントèªè¨¼ã‚­ãƒ¼"
msgid "Client authentication key password"
msgstr ""
-msgid "Clone repository"
+msgid "Clients"
msgstr ""
+msgid "Clone repository"
+msgstr "リãƒã‚¸ãƒˆãƒªã‚’クローン"
+
msgid "Close"
msgstr ""
msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgid "Closed issues"
msgstr ""
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr "%{appList} ã¯æ­£å¸¸ã« Kubernetes クラスターã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¾ã—ãŸ"
+
msgid "ClusterIntegration|API URL"
-msgstr ""
+msgstr "API URL"
msgid "ClusterIntegration|Add Kubernetes cluster"
-msgstr ""
-
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes クラスターを追加"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
-msgstr ""
+msgstr "ã“ã® Kubernetes クラスター統åˆã«é–¢ã™ã‚‹è©³ç´°ã‚ªãƒ—ション"
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr "プロジェクトゾーンã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ: %{error}"
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr "プロジェクトã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ: %{error}"
msgid "ClusterIntegration|Applications"
-msgstr ""
+msgstr "アプリケーション"
msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
-msgstr ""
+msgstr "ã“ã® Kubernetes クラスター統åˆã‚’削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿã“ã®æ“作ã§ã¯å®Ÿéš›ã® Kubernetes クラスターã¯å‰Šé™¤ã•ã‚Œã¾ã›ã‚“。"
msgid "ClusterIntegration|CA Certificate"
-msgstr ""
+msgstr "CA 証明書"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
-msgstr ""
-
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
+msgstr "èªè¨¼å±€ãƒãƒ³ãƒ‰ãƒ« (PEMå½¢å¼)"
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr ""
+msgstr "ã©ã®ãƒ—ロジェクト環境ã§ã“ã® Kubernetes クラスターを使ã†ã‹é¸æŠžã—ã¦ãã ã•ã„。"
msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr ""
+msgstr "Kubernetes クラスター㨠GitLab ã®çµ±åˆæ–¹æ³•ã®åˆ¶å¾¡"
msgid "ClusterIntegration|Copy API URL"
-msgstr ""
+msgstr "API URLをコピー"
msgid "ClusterIntegration|Copy CA Certificate"
-msgstr ""
+msgstr "CA 証明書をコピー"
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
-msgstr ""
+msgstr "Ingressã®IPアドレスをクリップボードã«ã‚³ãƒ”ーã™ã‚‹"
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr "Jupyter ã®ãƒ›ã‚¹ãƒˆåをクリップボードã¸ã‚³ãƒ”ー"
msgid "ClusterIntegration|Copy Kubernetes cluster name"
-msgstr ""
+msgstr "Kubernetes クラスターåをコピー"
msgid "ClusterIntegration|Copy Token"
-msgstr ""
+msgstr "トークンをコピー"
msgid "ClusterIntegration|Create Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes クラスターを作æˆ"
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr ""
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr "Kubernetes クラスターã®è©³ç´°ã‚’入力ã—ã¦ãã ã•ã„"
-msgid "ClusterIntegration|Create on GKE"
-msgstr ""
+msgid "ClusterIntegration|Environment scope"
+msgstr "環境スコープ"
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr ""
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr "Google Cloud Platform (GCP) ã®ã™ã¹ã¦ã®æ–°è¦ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã¯ã€300ドル分ã®ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆãŒä»˜ä¸Žã•ã‚Œã¾ã™ %{sign_up_link} 。ã¾ãŸã€Google ã¨ã®ãƒ‘ートナーシップã«ã‚ˆã‚Šã€Google Kubernetes Engine 㨠GitLab ã®ã‚¤ãƒ³ãƒ†ã‚°ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’開始ã™ã‚‹ãŸã‚ã«ã€æ–°è¦ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãƒ»æ—¢å­˜ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’å•ã‚ãšã€GCP アカウントã«è¿½åŠ ã®200ドル分ã®ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆã‚’æä¾›ã—ã¾ã™ã€‚"
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
-msgstr ""
+msgid "ClusterIntegration|Fetching machine types"
+msgstr "マシンタイプをå–得中"
-msgid "ClusterIntegration|Environment scope"
-msgstr ""
+msgid "ClusterIntegration|Fetching projects"
+msgstr "プロジェクトをå–得中"
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr "ゾーンをå–得中"
msgid "ClusterIntegration|GitLab Integration"
-msgstr ""
+msgstr "GitLabã‚’çµ±åˆ"
msgid "ClusterIntegration|GitLab Runner"
-msgstr ""
+msgstr "GitLab Runner"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
-msgstr ""
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr "Google Cloud Platform プロジェクト"
msgid "ClusterIntegration|Google Kubernetes Engine"
-msgstr ""
+msgstr "Google Kubernetes Engine"
msgid "ClusterIntegration|Google Kubernetes Engine project"
-msgstr ""
+msgstr "Google Kubernetes Engine プロジェクト"
msgid "ClusterIntegration|Helm Tiller"
+msgstr "Helm Tiller"
+
+msgid "ClusterIntegration|Hide"
+msgstr "éžè¡¨ç¤º"
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
msgid "ClusterIntegration|Ingress"
-msgstr ""
+msgstr "Ingress"
msgid "ClusterIntegration|Ingress IP Address"
-msgstr ""
+msgstr "Ingress 㮠IP アドレス"
msgid "ClusterIntegration|Install"
-msgstr ""
+msgstr "インストール"
msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
+msgstr "Prometheus をインストール"
msgid "ClusterIntegration|Installed"
-msgstr ""
+msgstr "インストール済ã¿"
msgid "ClusterIntegration|Installing"
-msgstr ""
+msgstr "インストール中"
msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
-msgstr ""
+msgstr "Kubernetes クラスターを自動統åˆ"
msgid "ClusterIntegration|Integration status"
-msgstr ""
+msgstr "çµ±åˆã®çŠ¶æ…‹"
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr "Jupyter ã®ãƒ›ã‚¹ãƒˆå"
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr "JupyterHub"
msgid "ClusterIntegration|Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes クラスター"
msgid "ClusterIntegration|Kubernetes cluster details"
-msgstr ""
+msgstr "Kubernetes クラスターã®è©³ç´°"
msgid "ClusterIntegration|Kubernetes cluster health"
msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
-msgstr ""
+msgstr "Kubernetes クラスターã®çµ±åˆ"
msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
+msgstr "Kubernetes クラスターã®çµ±åˆãŒã“ã®ãƒ—ロジェクトã§ç„¡åŠ¹ã§ã™ã€‚"
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
+msgstr "Kubernetes クラスターã®çµ±åˆãŒã“ã®ãƒ—ロジェクトã§æœ‰åŠ¹ã§ã™ã€‚"
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
+msgstr "Kubernetes クラスターã®çµ±åˆãŒã“ã®ãƒ—ロジェクトã§æœ‰åŠ¹ã§ã™ã€‚ã“ã®çµ±åˆã‚’無効ã«ã—ã¦ã‚‚ Kubernetes クラスターã«ã¯å½±éŸ¿ã›ãšã€GitLab ã¨ã®æŽ¥ç¶šãŒä¸€æ™‚çš„ã«åˆ‡æ–­ã•ã‚Œã‚‹ã ã‘ã§ã™ã€‚"
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
-msgstr ""
+msgstr "Kubernetes クラスターを Google Kubernetes Engine 上ã«ä½œæˆã—ã¦ã„ã¾ã™..."
msgid "ClusterIntegration|Kubernetes cluster name"
-msgstr ""
+msgstr "Kubernetes クラスターå"
msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
-msgstr ""
+msgstr "Kubernetes クラスター ã¯æ­£å¸¸ã« Google Kubernetes Engine ã«ä½œæˆã•ã‚Œã¾ã—ãŸã€‚クラスター ã®è©³ç´°ã‚’表示ã™ã‚‹ã«ã¯ãƒšãƒ¼ã‚¸ã‚’æ›´æ–°ã—ã¦ãã ã•ã„。"
msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr ""
+msgstr "Kubernetes クラスターを利用ã™ã‚‹ã¨ã€ Review Apps ã®ä½¿ç”¨ã€ã‚¢ãƒ—リケーションã®ãƒ‡ãƒ—ロイã€ãƒ‘イプラインã®å®Ÿè¡Œãªã©ã‚’より簡å˜ã«å®Ÿç¾ã§ãã¾ã™ã€‚%{link_to_help_page}"
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
-msgstr ""
+msgstr "Kubernetes クラスターã¯ã€ã“ã®ãƒ—ロジェクトã«ã‚¢ãƒ—リケーションã®ãƒ‡ãƒ—ロイや Review Apps ã®ç’°å¢ƒã‚’æä¾›ã™ã‚‹ã®ã«ä½¿ç”¨ã§ãã¾ã™ã€‚"
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
-msgstr ""
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr "%{help_link_start_machine_type}マシンタイプ%{help_link_end}ã¨%{help_link_start_pricing}価格%{help_link_end}ã®è©³ç´°ã€‚"
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr "%{help_link_start}Kubernetes%{help_link_end}ã®è©³ç´°ã€‚"
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr "%{help_link_start}ゾーン%{help_link_end}ã®è©³ç´°ã€‚"
msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
+msgstr "環境ã®è©³ç´°"
msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
+msgstr "セキュリティ設定ã®è©³ç´°"
msgid "ClusterIntegration|Machine type"
-msgstr ""
+msgstr "マシンタイプ"
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
-msgstr ""
+msgstr "Kubernetes クラスター を作æˆã™ã‚‹ã«ã¯ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã® %{link_to_requirements} を確èªã—ã¦ãã ã•ã„"
msgid "ClusterIntegration|Manage"
-msgstr ""
+msgstr "管ç†"
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
-msgstr ""
+msgstr "%{link_gke} ã«ã‚¢ã‚¯ã‚»ã‚¹ã—㦠Kubernetes クラスターを管ç†"
msgid "ClusterIntegration|More information"
-msgstr ""
+msgstr "詳細情報"
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr "検索æ¡ä»¶ã«ä¸€è‡´ã™ã‚‹ãƒžã‚·ãƒ³ã‚¿ã‚¤ãƒ—ã¯ã‚ã‚Šã¾ã›ã‚“"
+
+msgid "ClusterIntegration|No projects found"
+msgstr "プロジェクトãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr "検索æ¡ä»¶ã«ä¸€è‡´ã™ã‚‹ãƒ—ロジェクトã¯ã‚ã‚Šã¾ã›ã‚“"
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr "検索æ¡ä»¶ã«ä¸€è‡´ã™ã‚‹ã‚¾ãƒ¼ãƒ³ã¯ã‚ã‚Šã¾ã›ã‚“"
+
msgid "ClusterIntegration|Note:"
-msgstr ""
+msgstr "注æ„:"
msgid "ClusterIntegration|Number of nodes"
-msgstr ""
+msgstr "ノード数"
msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
-msgstr ""
-
-msgid "ClusterIntegration|Project ID"
-msgstr ""
+msgstr "Google アカウントãŒæ¬¡ã®è¦ä»¶ã‚’満ãŸã—ã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
msgid "ClusterIntegration|Project namespace"
-msgstr ""
+msgstr "プロジェクトã®åå‰ç©ºé–“"
msgid "ClusterIntegration|Project namespace (optional, unique)"
-msgstr ""
+msgstr "プロジェクトã®åå‰ç©ºé–“ (çœç•¥å¯èƒ½ã€ä¸€æ„)"
msgid "ClusterIntegration|Prometheus"
-msgstr ""
+msgstr "Prometheus"
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
-msgstr ""
+msgstr "Kubernetes クラスターã®çµ±åˆã«ã¤ã„ã¦ã¯ã€%{link_to_help_page} ã‚’ãŠèª­ã¿ãã ã•ã„。"
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
-msgstr ""
+msgstr "Kubernetes クラスターã®çµ±åˆã‚’削除"
msgid "ClusterIntegration|Remove integration"
-msgstr ""
+msgstr "çµ±åˆã‚’削除"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
-msgstr ""
+msgstr "プロジェクトã‹ã‚‰ Kubernetes クラスターã®è¨­å®šã‚’削除ã—ã¾ã™ã€‚ãªãŠã€å®Ÿéš›ã® Kubernetes クラスターã¯å‰Šé™¤ã•ã‚Œã¾ã›ã‚“。"
msgid "ClusterIntegration|Request to begin installing failed"
-msgstr ""
+msgstr "インストール開始ã«å¤±æ•—ã—ã¾ã—ãŸ"
msgid "ClusterIntegration|Save changes"
-msgstr ""
+msgstr "変更をä¿å­˜"
+
+msgid "ClusterIntegration|Search machine types"
+msgstr "マシンタイプã®æ¤œç´¢"
+
+msgid "ClusterIntegration|Search projects"
+msgstr "プロジェクトã®æ¤œç´¢"
+
+msgid "ClusterIntegration|Search zones"
+msgstr "ゾーンを検索"
msgid "ClusterIntegration|Security"
-msgstr ""
+msgstr "セキュリティ"
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes クラスターã®è©³ç´°ã‚’閲覧ã€ç·¨é›†ã™ã‚‹"
-msgid "ClusterIntegration|See machine types"
-msgstr ""
+msgid "ClusterIntegration|Select machine type"
+msgstr "マシンタイプをé¸æŠž"
-msgid "ClusterIntegration|See your projects"
-msgstr ""
+msgid "ClusterIntegration|Select project"
+msgstr "プロジェクトをé¸æŠž"
-msgid "ClusterIntegration|See zones"
-msgstr ""
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr "プロジェクトã¨ã‚¾ãƒ¼ãƒ³ã‚’é¸æŠžã—ã¦ãƒžã‚·ãƒ³ã‚¿ã‚¤ãƒ—ã‚’é¸æŠž"
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr "プロジェクトをé¸æŠžã—ã¦ã‚¾ãƒ¼ãƒ³ã‚’é¸æŠž"
+
+msgid "ClusterIntegration|Select zone"
+msgstr "ゾーンをé¸æŠž"
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr "ゾーンをé¸æŠžã—ã¦ãƒžã‚·ãƒ³ã‚¿ã‚¤ãƒ—ã‚’é¸æŠž"
msgid "ClusterIntegration|Service token"
-msgstr ""
+msgstr "サービストークン"
msgid "ClusterIntegration|Show"
-msgstr ""
+msgstr "表示"
msgid "ClusterIntegration|Something went wrong on our end."
-msgstr ""
+msgstr "å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
-msgstr ""
+msgstr "Google Kubernetes Engine 上㮠Kubernetes クラスターを作æˆä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "ClusterIntegration|Something went wrong while installing %{title}"
-msgstr ""
+msgstr "%{title} ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr ""
+msgstr "既定ã®ã‚¯ãƒ©ã‚¹ã‚¿ãƒ¼è¨­å®šã§ã¯ã€ã‚³ãƒ³ãƒ†ãƒŠåŒ–ã•ã‚ŒãŸã‚¢ãƒ—リケーションを正常ã«ãƒ“ルドやデプロイã™ã‚‹ãŸã‚ã«å¿…è¦ãªæ§˜ã€…ãªæ©Ÿèƒ½ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚"
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
-msgstr ""
+msgstr "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ %{link_to_container_project} 㧠Kubernetes クラスターを作æˆã™ã‚‹ã®ã«ä»¥ä¸‹ã®æ¨©é™ãŒå¿…è¦ã§ã™"
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
-msgstr ""
+msgstr "Kubernetes クラスターを切り替ãˆ"
msgid "ClusterIntegration|Toggle Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes クラスターを切り替ãˆ"
msgid "ClusterIntegration|Token"
-msgstr ""
+msgstr "トークン"
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr "プロジェクトã®è«‹æ±‚ステータスを検証ã—ã¦ã„ã¾ã™"
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr ""
+msgstr "ã“ã®ãƒ—ロジェクト㫠Kubernetes クラスターを関連付ã‘ã‚‹ã“ã¨ã§ã€Review Apps ã®ä½¿ç”¨ã€ã‚¢ãƒ—リケーションã®ãƒ‡ãƒ—ロイã€ãƒ‘イプラインã®å®Ÿè¡Œãªã©ã‚’ç°¡å˜ã«è¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
-msgstr ""
+msgstr "アカウント㫠%{link_to_kubernetes_engine} ãŒå¿…è¦ã§ã™ã€‚"
msgid "ClusterIntegration|Zone"
-msgstr ""
+msgstr "ゾーン"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
-msgstr ""
+msgstr "Google Kubernetes Engineã«ã‚¢ã‚¯ã‚»ã‚¹"
msgid "ClusterIntegration|check the pricing here"
-msgstr ""
+msgstr "価格を確èªã™ã‚‹"
msgid "ClusterIntegration|documentation"
-msgstr ""
+msgstr "ドキュメント"
msgid "ClusterIntegration|help page"
-msgstr ""
+msgstr "ヘルプ ページ"
msgid "ClusterIntegration|installing applications"
-msgstr ""
+msgstr "アプリケーションをインストールã—ã¦ã„ã¾ã™"
msgid "ClusterIntegration|meets the requirements"
-msgstr ""
+msgstr "å¿…è¦æ¡ä»¶"
msgid "ClusterIntegration|properly configured"
+msgstr "æ­£ã—ã設定ã•ã‚Œã¦ã„ã‚‹"
+
+msgid "ClusterIntegration|sign up"
+msgstr "æ–°è¦ç™»éŒ²"
+
+msgid "Cohorts"
msgstr ""
msgid "Collapse"
-msgstr ""
+msgstr "折りãŸãŸã‚€"
-msgid "Comment and resolve discussion"
-msgstr ""
+msgid "Collapse sidebar"
+msgstr "サイドãƒãƒ¼ã‚’éš ã™"
-msgid "Comment and unresolve discussion"
-msgstr ""
+msgid "Comment & resolve discussion"
+msgstr "ã“ã®æ¤œè¨Žã«ã‚³ãƒ¡ãƒ³ãƒˆã—解決ã™ã‚‹"
+
+msgid "Comment & unresolve discussion"
+msgstr "ã“ã®æ¤œè¨Žã«ã‚³ãƒ¡ãƒ³ãƒˆã—未解決ã«ã™ã‚‹"
msgid "Comments"
-msgstr ""
+msgstr "コメント"
msgid "Commit"
msgid_plural "Commits"
@@ -1225,10 +1822,10 @@ msgstr[0] "コミット"
msgid "Commit (%{commit_count})"
msgid_plural "Commits (%{commit_count})"
-msgstr[0] ""
+msgstr[0] "コミット (%{commit_count})"
msgid "Commit Message"
-msgstr ""
+msgstr "コミットメッセージ"
msgid "Commit duration in minutes for last 30 commits"
msgstr "ç›´è¿‘30コミットã®æ‰€è¦æ™‚é–“(分)"
@@ -1240,7 +1837,7 @@ msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
msgstr ""
msgid "Commit to %{branchName} branch"
-msgstr ""
+msgstr "%{branchName} ブランãƒã«ã‚³ãƒŸãƒƒãƒˆ"
msgid "CommitBoxTitle|Commit"
msgstr "コミット"
@@ -1264,49 +1861,52 @@ msgid "Commits per weekday"
msgstr ""
msgid "Commits|An error occurred while fetching merge requests data."
-msgstr ""
+msgstr "マージリクエストデータã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Commits|Commit: %{commitText}"
-msgstr ""
+msgstr "コミット: %{commitText}"
msgid "Commits|History"
msgstr "履歴"
msgid "Commits|No related merge requests found"
-msgstr ""
+msgstr "関連ã™ã‚‹ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
msgid "Committed by"
msgstr "コミット担当者: "
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr "比較"
msgid "Compare Git revisions"
-msgstr ""
+msgstr "Git リビジョンを比較"
msgid "Compare Revisions"
-msgstr ""
+msgstr "リビジョンを比較"
msgid "Compare changes with the last commit"
-msgstr ""
+msgstr "最後ã®ã‚³ãƒŸãƒƒãƒˆã¨å¤‰æ›´ã‚’比較"
msgid "Compare changes with the merge request target branch"
-msgstr ""
+msgstr "マージリクエストã®ã‚¿ãƒ¼ã‚²ãƒƒãƒˆãƒ–ランãƒã¨ã®å¤‰æ›´ã‚’比較ã™ã‚‹"
msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
-msgstr ""
+msgstr "%{source_branch} 㨠%{target_branch} ã¯ä¸€è‡´ã—ã¾ã™ã€‚"
msgid "CompareBranches|Compare"
-msgstr ""
+msgstr "比較"
msgid "CompareBranches|Source"
-msgstr ""
+msgstr "比較元"
msgid "CompareBranches|Target"
-msgstr ""
+msgstr "比較先"
msgid "CompareBranches|There isn't anything to compare."
-msgstr ""
+msgstr "比較ã™ã‚‹ã‚‚ã®ã¯ã‚ã‚Šã¾ã›ã‚“。"
msgid "Confidential"
msgstr ""
@@ -1315,105 +1915,126 @@ msgid "Confidentiality"
msgstr ""
msgid "Configure Gitaly timeouts."
-msgstr ""
+msgstr "Gitaly ã®ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã‚’設定ã—ã¾ã™ã€‚"
msgid "Configure Sidekiq job throttling."
-msgstr ""
+msgstr "Sidekiq job throttling ã®è¨­å®š"
msgid "Configure automatic git checks and housekeeping on repositories."
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªã«å¯¾ã—ã¦è‡ªå‹•å®Ÿè¡Œã™ã‚‹ Git ãƒã‚§ãƒƒã‚¯ã¨ãƒã‚¦ã‚¹ã‚­ãƒ¼ãƒ”ングを設定ã—ã¾ã™ã€‚"
msgid "Configure limits for web and API requests."
+msgstr "ウェブãŠã‚ˆã³APIリクエストã®åˆ¶é™ã‚’設定ã™ã‚‹ã€‚"
+
+msgid "Configure push and pull mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
-msgstr ""
+msgstr "ストレージã®ãƒ‘スã¨ã‚µãƒ¼ã‚­ãƒƒãƒˆãƒ–レーカーを設定ã™ã‚‹ã€‚"
msgid "Configure the way a user creates a new account."
-msgstr ""
+msgstr "ユーザーãŒæ–°ã—ã„アカウントを作æˆã™ã‚‹æ–¹æ³•ã‚’設定ã—ã¾ã™ã€‚"
msgid "Connect"
-msgstr ""
+msgstr "接続"
msgid "Connect all repositories"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ãƒªãƒã‚¸ãƒˆãƒªã¸æŽ¥ç¶š"
msgid "Connect repositories from GitHub"
-msgstr ""
+msgstr "GitHubã‹ã‚‰ãƒªãƒã‚¸ãƒˆãƒªã«æŽ¥ç¶š"
msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
msgstr ""
msgid "Connecting..."
-msgstr ""
+msgstr "接続ã—ã¦ã„ã¾ã™..."
msgid "Container Registry"
-msgstr ""
+msgstr "コンテナレジストリ"
msgid "ContainerRegistry|Created"
-msgstr ""
+msgstr "作æˆæ¸ˆã¿"
msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
-msgstr ""
+msgstr "ã¾ãšã€GitLab ã®ã‚³ãƒ³ãƒ†ãƒŠãƒ¬ã‚¸ã‚¹ãƒˆãƒªã« GitLab ユーザーåã¨ãƒ‘スワードを使ã£ã¦ãƒ­ã‚°ã‚¤ãƒ³ã—ã¾ã™ã€‚%{link_2fa} を利用ã—ã¦ã„ã‚Œã°ã€%{link_token} を使用ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™:"
msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
-msgstr ""
+msgstr "GitLab ã§ã¯ã€æœ€å¤§ 3 ã¤ã®ãƒ¬ãƒ™ãƒ«ã®ã‚¤ãƒ¡ãƒ¼ã‚¸åをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™ã€‚例ã¨ã—ã¦æ¬¡ã®ã‚ˆã†ãªã‚¤ãƒ¡ãƒ¼ã‚¸åãŒãƒ—ロジェクトã§æœ‰åŠ¹ã§ã™:"
msgid "ContainerRegistry|How to use the Container Registry"
-msgstr ""
+msgstr "コンテナレジストリ使用方法"
msgid "ContainerRegistry|Learn more about"
-msgstr ""
+msgstr "詳細ã«ã¤ã„ã¦"
msgid "ContainerRegistry|No tags in Container Registry for this container image."
-msgstr ""
+msgstr "ã“ã®ã‚³ãƒ³ãƒ†ãƒŠã‚¤ãƒ¡ãƒ¼ã‚¸ã¯ã‚³ãƒ³ãƒ†ãƒŠãƒ¬ã‚¸ã‚¹ãƒˆãƒªã«ã‚¿ã‚°ãŒã‚ã‚Šã¾ã›ã‚“。"
msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
-msgstr ""
+msgstr "ログインã™ã‚‹ã¨è‡ªç”±ã« %{build} 㨠%{push} コマンドã§ã‚³ãƒ³ãƒ†ãƒŠã‚¤ãƒ¡ãƒ¼ã‚¸ã‚’作æˆãƒ»ã‚¢ãƒƒãƒ—ロードã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "ContainerRegistry|Remove repository"
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªã®å‰Šé™¤"
msgid "ContainerRegistry|Remove tag"
-msgstr ""
+msgstr "ã‚¿ã‚°ã®å‰Šé™¤"
msgid "ContainerRegistry|Size"
-msgstr ""
+msgstr "サイズ"
msgid "ContainerRegistry|Tag"
-msgstr ""
+msgstr "ã‚¿ã‚°"
msgid "ContainerRegistry|Tag ID"
-msgstr ""
+msgstr "ã‚¿ã‚°ID"
msgid "ContainerRegistry|Use different image names"
-msgstr ""
+msgstr "ä»–ã®ã‚¤ãƒ¡ãƒ¼ã‚¸åを付ã‘ã¦ãã ã•ã„"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr "GitLabã¨çµ±åˆã•ã‚ŒãŸã‚³ãƒ³ãƒ†ãƒŠãƒ¬ã‚¸ã‚¹ãƒˆãƒªã§ã€å…¨ã¦ã®ãƒ—ロジェクトã«Dockerイメージを格ç´ã™ã‚‹å ´æ‰€ãŒã‚ã‚Šã¾ã™ã€‚"
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr "レジストリイメージã¸ã®èª­ã¿å–り専用アクセスã®ãŸã‚ã« %{deploy_token} を使用ã§ãã¾ã™ã€‚"
+
+msgid "Continue"
+msgstr "続行"
+
+msgid "Continue to the next step"
msgstr ""
msgid "Continuous Integration and Deployment"
-msgstr ""
+msgstr "継続的インテグレーションã¨ãƒ‡ãƒ—ロイ"
+
+msgid "Contribute to GitLab"
+msgstr "GitLab ã«è²¢çŒ®ã™ã‚‹"
msgid "Contribution"
-msgstr ""
+msgstr "貢献度"
msgid "Contribution guide"
msgstr "貢献者å‘ã‘ガイド"
+msgid "Contributions per group member"
+msgstr "グループメンãƒãƒ¼ã®è²¢çŒ®åº¦"
+
msgid "Contributors"
msgstr "貢献者"
msgid "ContributorsPage|%{startDate} – %{endDate}"
-msgstr ""
+msgstr "%{startDate} – %{endDate}"
msgid "ContributorsPage|Building repository graph."
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªã‚°ãƒ©ãƒ•ã‚’構築ã—ã¦ã„ã¾ã™ã€‚"
msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
-msgstr ""
+msgstr "%{branch_name} ã¸ã®ã‚³ãƒŸãƒƒãƒˆï¼ˆãƒžãƒ¼ã‚¸ã‚³ãƒŸãƒƒãƒˆã‚’除ãã€6,000コミットã¾ã§ï¼‰"
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr "ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„ã€ã“ã®ãƒšãƒ¼ã‚¸ã¯æº–å‚™ãŒæ•´ã†ã¨è‡ªå‹•çš„ã«æ›´æ–°ã•ã‚Œã¾ã™ã€‚"
+
+msgid "Control the display of third party offers."
msgstr ""
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
@@ -1422,83 +2043,116 @@ msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
-msgid "Copy SSH public key to clipboard"
+msgid "Control the maximum concurrency of verification operations for this Geo node"
msgstr ""
+msgid "ConvDev Index"
+msgstr ""
+
+msgid "Copy SSH public key to clipboard"
+msgstr "SSH 公開éµã‚’クリップボードã«ã‚³ãƒ”ー"
+
msgid "Copy URL to clipboard"
msgstr "クリップボードã«URLをコピー"
msgid "Copy branch name to clipboard"
-msgstr ""
+msgstr "ブランãƒåをクリップボードã«ã‚³ãƒ”ー"
msgid "Copy command to clipboard"
-msgstr ""
+msgstr "コマンドをクリップボードã«ã‚³ãƒ”ー"
msgid "Copy commit SHA to clipboard"
msgstr "コミットã®SHAをクリップボードã«ã‚³ãƒ”ー"
+msgid "Copy file path to clipboard"
+msgstr "ファイルパスをクリップボードã«ã‚³ãƒ”ーã™ã‚‹"
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
+msgstr "クリップボードã¸ã‚³ãƒ”ー"
+
+msgid "Copy to clipboard"
+msgstr "クリップボードã«ã‚³ãƒ”ー"
+
+msgid "Copy token to clipboard"
msgstr ""
msgid "Create"
-msgstr ""
+msgstr "作æˆ"
msgid "Create New Directory"
msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’作æˆ"
msgid "Create a new branch"
-msgstr ""
+msgstr "æ–°ã—ã„ブランãƒã‚’作æˆ"
msgid "Create a new branch and merge request"
+msgstr "ブランãƒã®æ–°è¦ä½œæˆã¨ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
+
+msgid "Create a new issue"
msgstr ""
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "%{protocol} ã§ãƒ—ッシュやプルã™ã‚‹ãŸã‚ã®ã‚ãªãŸå€‹äººç”¨ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’作æˆ"
msgid "Create branch"
-msgstr ""
+msgstr "ブランãƒä½œæˆ"
+
+msgid "Create commit"
+msgstr "コミットã®ä½œæˆ"
msgid "Create directory"
msgstr "ディレクトリを作æˆ"
msgid "Create empty repository"
-msgstr ""
+msgstr "空ã®ãƒªãƒã‚¸ãƒˆãƒªã‚’作æˆã™ã‚‹"
msgid "Create epic"
-msgstr ""
+msgstr "エピックã®ä½œæˆ"
msgid "Create file"
+msgstr "ファイルを作æˆ"
+
+msgid "Create group"
msgstr ""
msgid "Create group label"
+msgstr "グループラベルを作æˆ"
+
+msgid "Create issue"
msgstr ""
msgid "Create lists from labels. Issues with that label appear in that list."
-msgstr ""
+msgstr "ラベルã‹ã‚‰ãƒªã‚¹ãƒˆã‚’作æˆã€‚ãã®ãƒ©ãƒ™ãƒ«ã®èª²é¡ŒãŒãƒªã‚¹ãƒˆã«è¡¨ç¤ºã•ã‚Œã¾ã™ã€‚"
msgid "Create merge request"
msgstr "マージリクエストを作æˆ"
msgid "Create merge request and branch"
-msgstr ""
+msgstr "マージリクエストã¨ãƒ–ランãƒã‚’作æˆ"
msgid "Create new branch"
-msgstr ""
+msgstr "æ–°ã—ã„ブランãƒã‚’作æˆ"
msgid "Create new directory"
-msgstr ""
+msgstr "æ–°ã—ã„ディレクトリを作æˆ"
msgid "Create new file"
+msgstr "æ–°è¦ãƒ•ã‚¡ã‚¤ãƒ«ä½œæˆ"
+
+msgid "Create new file or directory"
msgstr ""
msgid "Create new label"
-msgstr ""
+msgstr "ラベルã®æ–°è¦ä½œæˆ"
msgid "Create new..."
msgstr "æ–°è¦ä½œæˆ"
msgid "Create project label"
-msgstr ""
+msgstr "プロジェクトラベルを作æˆ"
msgid "CreateNewFork|Fork"
msgstr "フォーク"
@@ -1509,14 +2163,20 @@ msgstr "ã‚¿ã‚°"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "個人用アクセストークンを作æˆ"
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr "作æˆæ¸ˆã¿"
+
+msgid "Created At"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created by me"
+msgstr "自分ãŒä½œæˆ"
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
-msgstr ""
+msgstr "エピックを作æˆã—ã¦ã„ã¾ã™"
msgid "Cron Timezone"
msgstr "Cron ã®ã‚¿ã‚¤ãƒ ã‚¾ãƒ¼ãƒ³"
@@ -1525,6 +2185,15 @@ msgid "Cron syntax"
msgstr "Cron ã®æ§‹æ–‡"
msgid "Current node"
+msgstr "ç¾åœ¨ã®ãƒŽãƒ¼ãƒ‰"
+
+msgid "CurrentUser|Profile"
+msgstr "プロフィール"
+
+msgid "CurrentUser|Settings"
+msgstr "設定"
+
+msgid "Custom CI config path"
msgstr ""
msgid "Custom notification events"
@@ -1534,6 +2203,12 @@ msgid "Custom notification levels are the same as participating levels. With cus
msgstr "\"カスタム\" ã®é€šçŸ¥ãƒ¬ãƒ™ãƒ«ã®åŸºæœ¬ã¯ \"å‚加\" ã¨åŒã˜ã§ã™ã€‚ã¾ãŸã€ã‚«ã‚¹ã‚¿ãƒ é€šçŸ¥ã«è¨­å®šã™ã‚‹ã“ã¨ã§é¸æŠžã—ãŸã‚«ã‚¹ã‚¿ãƒ ã‚¤ãƒ™ãƒ³ãƒˆã®é€šçŸ¥ã‚’å—ã‘å–ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚ã‚‚ã£ã¨è©³ã—ã知りãŸã„å ´åˆã¯ %{notification_link} を見ã¦ãã ã•ã„。"
msgid "Customize colors"
+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 "Cycle Analytics"
@@ -1560,72 +2235,252 @@ msgstr "ステージング"
msgid "CycleAnalyticsStage|Test"
msgstr "テスト"
-msgid "DashboardProjects|All"
+msgid "Dashboard"
msgstr ""
+msgid "DashboardProjects|All"
+msgstr "ã™ã¹ã¦"
+
msgid "DashboardProjects|Personal"
-msgstr ""
+msgstr "個人"
msgid "Dec"
-msgstr ""
+msgstr "12月"
msgid "December"
-msgstr ""
+msgstr "12月"
+
+msgid "Decline and sign out"
+msgstr "辞退ã—ã¦ã‚µã‚¤ãƒ³ã‚¢ã‚¦ãƒˆ"
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr "Cron 構文ã§ã‚«ã‚¹ã‚¿ãƒ ãªãƒ‘ターンを指定ã™ã‚‹"
msgid "Delete"
msgstr "削除"
+msgid "Delete Snippet"
+msgstr "スニペットを削除"
+
+msgid "Delete list"
+msgstr "リストを削除ã™ã‚‹"
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "デプロイ"
msgid "Deploy Keys"
+msgstr "デプロイキー"
+
+msgid "DeployKeys|+%{count} others"
+msgstr "+ %{count} プロジェクト"
+
+msgid "DeployKeys|Current project"
+msgstr "ç¾åœ¨ã®ãƒ—ロジェクト"
+
+msgid "DeployKeys|Deploy key"
+msgstr "デプロイキー"
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr "有効ãªãƒ‡ãƒ—ロイキー"
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr "デプロイキーã®æœ‰åŠ¹ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿ"
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr "デプロイキーã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿ"
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr "デプロイキーã®å‰Šé™¤ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿ"
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr "他㮠%{count} プロジェクトを展開"
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr "デプロイキーを読込中"
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr "デプロイキーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。上ã®ãƒ•ã‚©ãƒ¼ãƒ ã§ä½œæˆã—ã¦ãã ã•ã„。"
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr "プライベートアクセスãŒå¯èƒ½ãªãƒ‡ãƒ—ロイキー"
+
+msgid "DeployKeys|Project usage"
+msgstr "プロジェクトã®ä½¿ç”¨çŠ¶æ³"
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr "パブリックアクセスãŒå¯èƒ½ãªãƒ‡ãƒ—ロイキー"
+
+msgid "DeployKeys|Read access only"
+msgstr "読ã¿å–り専用"
+
+msgid "DeployKeys|Write access allowed"
+msgstr "書ãè¾¼ã¿ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯"
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr "ã“ã®ãƒ‡ãƒ—ロイキーを削除ã—ã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr "アクティブãªãƒ‡ãƒ—ロイトークン (%{active_tokens})"
+
+msgid "DeployTokens|Add a deploy token"
+msgstr "デプロイトークンを追加"
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr "レジストリイメージã¸ã®èª­ã¿å–り専用アクセスを許å¯ã™ã‚‹"
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr "リãƒã‚¸ãƒˆãƒªã¸ã®èª­ã¿å–り専用アクセスを許å¯ã™ã‚‹"
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr "デプロイトークンをクリップボードã«ã‚³ãƒ”ーã™ã‚‹"
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr "ユーザーåをクリップボードã«ã‚³ãƒ”ーã™ã‚‹"
+
+msgid "DeployTokens|Create deploy token"
+msgstr "デプロイトークンを作æˆ"
+
+msgid "DeployTokens|Created"
+msgstr "作æˆæ—¥"
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr "デプロイトークン"
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr "デプロイトークンã«ã‚ˆã‚‹ãƒªãƒã‚¸ãƒˆãƒªãŠã‚ˆã³ãƒ¬ã‚¸ã‚¹ãƒˆãƒªã‚¤ãƒ¡ãƒ¼ã‚¸ã¸ã®èª­ã¿å–り専用アクセスを許å¯"
+
+msgid "DeployTokens|Expires"
+msgstr "有効期é™"
+
+msgid "DeployTokens|Name"
+msgstr "åå‰"
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr "アプリケーションã®åå‰ã‚’é¸æŠžã™ã‚‹ã¨ã€å›ºæœ‰ã®ãƒ‡ãƒ—ロイトークンãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚"
+
+msgid "DeployTokens|Revoke"
+msgstr "失効"
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr "%{name} ã‚’å–り消ã™"
+
+msgid "DeployTokens|Scopes"
+msgstr "スコープ"
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr "ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。"
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr "ã“ã®ãƒ—ロジェクトã«ã¯ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªãƒ‡ãƒ—ロイトークンã¯ã‚ã‚Šã¾ã›ã‚“。"
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr "ã“ã®ãƒˆãƒ¼ã‚¯ãƒ³ã‚’パスワードã¨ã—ã¦ä½¿ç”¨ã—ã¾ã™ã€‚å†åº¦ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ã¯ã§ããªã„ã®ã§ã€å¿˜ã‚Œãšã«ä¿å­˜ã—ã¦ãã ã•ã„。"
+
+msgid "DeployTokens|Use this username as a login."
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åをログインã«ä½¿ç”¨ã—ã¾ã™ã€‚"
+
+msgid "DeployTokens|Username"
+msgstr "ユーザーå"
+
+msgid "DeployTokens|You are about to revoke"
+msgstr "失効ã—よã†ã¨ã—ã¦ã„ã¾ã™"
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr "æ–°è¦ãƒ‡ãƒ—ロイトークン"
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr "æ–°ã—ã„プロジェクトデプロイトークンãŒä½œæˆã•ã‚Œã¾ã—ãŸã€‚"
+
+msgid "Deprioritize label"
msgstr ""
+msgid "Descending"
+msgstr "é™é †"
+
msgid "Description"
msgstr "説明"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
-msgid "Details"
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
msgstr ""
+msgid "Details"
+msgstr "詳細"
+
msgid "Diffs|No file name available"
+msgstr "使用å¯èƒ½ãªãƒ•ã‚¡ã‚¤ãƒ«åãŒã‚ã‚Šã¾ã›ã‚“"
+
+msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
msgid "Directory name"
msgstr "ディレクトリå"
msgid "Disable"
+msgstr "無効"
+
+msgid "Disable for this project"
+msgstr "ã“ã®ãƒ—ロジェクトã§ã¯ç„¡åŠ¹ã«ã™ã‚‹"
+
+msgid "Disable group Runners"
msgstr ""
+msgid "Discard changes"
+msgstr "変更を破棄ã™ã‚‹"
+
msgid "Discard draft"
-msgstr ""
+msgstr "下書ãを破棄"
msgid "Discover GitLab Geo."
msgstr ""
-msgid "Dismiss Cycle Analytics introduction box"
+msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr ""
+msgid "Dismiss"
+msgstr ""
+
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr "サイクル分æžã®ç´¹ä»‹ã‚’é–‰ã˜ã‚‹"
+
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr "ドメイン"
+
msgid "Don't show again"
msgstr "次回ã‹ã‚‰è¡¨ç¤ºã—ãªã„"
msgid "Done"
-msgstr ""
+msgstr "完了"
msgid "Download"
msgstr "ダウンロード"
@@ -1655,42 +2510,66 @@ msgid "DownloadSource|Download"
msgstr "ダウンロード"
msgid "Downvotes"
-msgstr ""
+msgstr "イマイãƒ"
msgid "Due date"
-msgstr ""
+msgstr "期é™"
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr "編集"
+msgid "Edit Label"
+msgstr "ラベルã®ç·¨é›†"
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "パイプラインスケジュール %{id} を編集"
+msgid "Edit Snippet"
+msgstr "スニペットを編集"
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
msgstr ""
-msgid "Elasticsearch"
+msgid "Edit identity for %{user_name}"
msgstr ""
+msgid "Elasticsearch"
+msgstr "Elasticsearch"
+
msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
+msgstr "メール"
+
+msgid "Email patch"
msgstr ""
msgid "Emails"
+msgstr "メール"
+
+msgid "Embed"
msgstr ""
msgid "Enable"
-msgstr ""
+msgstr "有効化ã™ã‚‹"
msgid "Enable Auto DevOps"
+msgstr "Auto DevOps を有効ã«ã™ã‚‹"
+
+msgid "Enable Pseudonymizer data collection"
msgstr ""
msgid "Enable SAML authentication for this group"
@@ -1708,9 +2587,21 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
-msgid "Enable or disable version check and usage ping."
+msgid "Enable for this project"
+msgstr "ã“ã®ãƒ—ロジェクトã§ã¯æœ‰åŠ¹ã«ã™ã‚‹"
+
+msgid "Enable group Runners"
+msgstr "グループ Runner を有効ã«ã™ã‚‹"
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr "特定ã®ã‚°ãƒ«ãƒ¼ãƒ—機能を有効ã¾ãŸã¯ç„¡åŠ¹ã«ã—ã€ã‚¢ã‚¯ã‚»ã‚¹ãƒ¬ãƒ™ãƒ«ã‚’é¸æŠžã—ã¾ã™ã€‚"
+
+msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
+msgid "Enable or disable version check and usage ping."
+msgstr "ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãƒã‚§ãƒƒã‚¯ã¨ä½¿ç”¨çŠ¶æ³ã® ping を有効ã«ã™ã‚‹ã€‚"
+
msgid "Enable reCAPTCHA or Akismet and set IP limits."
msgstr ""
@@ -1718,56 +2609,92 @@ msgid "Enable the Performance Bar for a given group."
msgstr ""
msgid "Enabled"
-msgstr ""
+msgstr "有効"
+
+msgid "Ends at (UTC)"
+msgstr "終了時刻 (UTC)"
+
+msgid "Environments"
+msgstr "環境"
msgid "Environments|An error occurred while fetching the environments."
-msgstr ""
+msgstr "環境をå–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Environments|An error occurred while making the request."
+msgstr "リクエスト作æˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
msgstr ""
msgid "Environments|Commit"
+msgstr "コミット"
+
+msgid "Environments|Deploy to..."
msgstr ""
msgid "Environments|Deployment"
-msgstr ""
+msgstr "デプロイ"
msgid "Environments|Environment"
-msgstr ""
+msgstr "環境"
msgid "Environments|Environments"
-msgstr ""
+msgstr "環境一覧"
msgid "Environments|Job"
+msgstr "ジョブ"
+
+msgid "Environments|Learn more about stopping environments"
msgstr ""
msgid "Environments|New environment"
-msgstr ""
+msgstr "æ–°ã—ã„環境"
msgid "Environments|No deployments yet"
+msgstr "未デプロイ"
+
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|Pod logs from"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
+msgstr "全件を表示"
+
+msgid "Environments|Stop"
msgstr ""
-msgid "Environments|Updated"
+msgid "Environments|Stop environment"
msgstr ""
+msgid "Environments|Updated"
+msgstr "更新済ã¿"
+
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1781,13 +2708,7 @@ msgid "Epics let you manage your portfolio of projects more efficiently and with
msgstr ""
msgid "Error Reporting and Logging"
-msgstr ""
-
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
+msgstr "エラー報告ã¨ãƒ­ã‚°"
msgid "Error creating epic"
msgstr ""
@@ -1796,10 +2717,10 @@ msgid "Error fetching contributors data."
msgstr ""
msgid "Error fetching labels."
-msgstr ""
+msgstr "ラベルã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Error fetching network graph."
-msgstr ""
+msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚°ãƒ©ãƒ•ã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Error fetching refs"
msgstr ""
@@ -1807,35 +2728,53 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
-msgid "Error occurred when toggling the notification subscription"
+msgid "Error loading branch data. Please try again."
+msgstr "ブランムデータã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+
+msgid "Error loading last commit."
+msgstr "ç›´è¿‘ã®ã‚³ãƒŸãƒƒãƒˆã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "Error loading markdown preview"
msgstr ""
+msgid "Error loading merge requests."
+msgstr "マージリクエストをロード中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "Error loading project data. Please try again."
+msgstr "プロジェクトデータã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+
+msgid "Error occurred when toggling the notification subscription"
+msgstr "通知購読ã®åˆ‡ã‚Šæ›¿ãˆã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
msgid "Error saving label update."
-msgstr ""
+msgstr "ラベルã®æ›´æ–°ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Error updating status for all todos."
-msgstr ""
+msgstr "ã™ã¹ã¦ã® TODO ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’更新中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Error updating todo status."
-msgstr ""
+msgstr "TODO ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’更新中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "Estimated"
+msgstr "見ç©"
msgid "EventFilterBy|Filter by all"
-msgstr ""
+msgstr "ã™ã¹ã¦"
msgid "EventFilterBy|Filter by comments"
-msgstr ""
+msgstr "コメントã§ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼"
msgid "EventFilterBy|Filter by issue events"
-msgstr ""
+msgstr "課題イベントã§ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼"
msgid "EventFilterBy|Filter by merge events"
-msgstr ""
+msgstr "マージイベントã§ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼"
msgid "EventFilterBy|Filter by push events"
-msgstr ""
+msgstr "プッシュイベントã§ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼"
msgid "EventFilterBy|Filter by team"
-msgstr ""
+msgstr "ãƒãƒ¼ãƒ ã§ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼"
msgid "Every day (at 4:00am)"
msgstr "毎日 (åˆå‰4:00)"
@@ -1846,15 +2785,36 @@ msgstr "毎月 (1æ—¥ã®åˆå‰4:00)"
msgid "Every week (Sundays at 4:00am)"
msgstr "毎週 (日曜日ã®åˆå‰4:00)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
+msgstr "展開"
+
+msgid "Expand all"
+msgstr "ã™ã¹ã¦å±•é–‹"
+
+msgid "Expand sidebar"
+msgstr "サイドãƒãƒ¼ã‚’é–‹ã"
+
+msgid "Explore"
msgstr ""
-msgid "Explore projects"
+msgid "Explore GitLab"
msgstr ""
-msgid "Explore public groups"
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
msgstr ""
+msgid "Explore projects"
+msgstr "プロジェクトを探ã™"
+
+msgid "Explore public groups"
+msgstr "公開グループを検索"
+
msgid "External Classification Policy Authorization"
msgstr ""
@@ -1868,53 +2828,65 @@ msgid "External authorization request timeout"
msgstr ""
msgid "ExternalAuthorizationService|Classification Label"
-msgstr ""
+msgstr "分類ラベル"
msgid "ExternalAuthorizationService|Classification label"
-msgstr ""
+msgstr "分類ラベル"
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
-msgid "Failed"
+msgid "Facebook"
msgstr ""
+msgid "Failed"
+msgstr "失敗"
+
msgid "Failed Jobs"
-msgstr ""
+msgstr "失敗ã—ãŸã‚¸ãƒ§ãƒ–"
msgid "Failed to change the owner"
msgstr "オーナーを変更ã§ãã¾ã›ã‚“ã§ã—ãŸ"
+msgid "Failed to check related branches."
+msgstr "関連ã™ã‚‹ãƒ–ランãƒã®ç¢ºèªã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
+
msgid "Failed to remove issue from board, please try again."
-msgstr ""
+msgstr "ボードã®èª²é¡Œã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "Failed to remove the pipeline schedule"
msgstr "パイプラインスケジュールを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ"
msgid "Failed to update issues, please try again."
+msgstr "課題ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+
+msgid "Failure"
+msgstr "失敗"
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
msgstr ""
msgid "Feb"
-msgstr ""
+msgstr "2月"
msgid "February"
-msgstr ""
+msgstr "2月"
msgid "Fields on this page are now uneditable, you can configure"
-msgstr ""
-
-msgid "File name"
-msgstr ""
+msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã®é …ç›®ã¯ç·¨é›†ã§ããªã„設定ã§ã™ã€‚次㮠Kubernetes クラスターを設定ã§ãã¾ã™ã€‚"
msgid "Files"
msgstr "ファイル"
msgid "Files (%{human_size})"
-msgstr ""
+msgstr "ファイル (%{human_size})"
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr "コミットメッセージã§çµžã‚Šè¾¼ã¿"
@@ -1924,19 +2896,52 @@ msgstr "パスã§æ¤œç´¢"
msgid "Find file"
msgstr "ファイルを検索"
-msgid "Finished"
+msgid "Find the downloaded ZIP file and decompress it."
msgstr ""
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
+msgid "Finished"
+msgstr "完了"
+
msgid "FirstPushedBy|First"
msgstr "åˆå›ž"
msgid "FirstPushedBy|pushed by"
msgstr "プッシュã—ãŸäºº"
-msgid "Font Color"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
msgstr ""
+msgid "Font Color"
+msgstr "フォントã®è‰²"
+
msgid "Footer message"
+msgstr "フッターメッセージ"
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "Fork"
@@ -1947,15 +2952,30 @@ msgid "ForkedFromProjectPath|Forked from"
msgstr "フォーク元"
msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
-msgstr ""
+msgstr "%{project_name} ã‹ã‚‰ãƒ•ã‚©ãƒ¼ã‚¯ (削除済)"
msgid "Forking in progress"
-msgstr ""
+msgstr "フォーク中ã§ã™"
msgid "Format"
-msgstr ""
+msgstr "フォーマット"
+
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ".gitlab-ci.yml ã«ã‚¨ãƒ©ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ:"
msgid "From %{provider_title}"
+msgstr "%{provider_title}ã‹ã‚‰"
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
msgstr ""
msgid "From issue creation until deploy to production"
@@ -1965,13 +2985,19 @@ msgid "From merge request merge until deploy to production"
msgstr "マージリクエストãŒãƒžãƒ¼ã‚¸ã•ã‚Œã¦ã‹ã‚‰ãƒ—ロダクションã«ãƒ‡ãƒ—ロイã•ã‚Œã‚‹ã¾ã§"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
-msgstr ""
+msgstr "Kubernetes クラスターã®è©³ç´°ç”»é¢ã‚’介ã—ã¦ã€ã‚¢ãƒ—リケーションリストã‹ã‚‰ Runner をインストールã—ã¾ã™ã€‚"
msgid "GPG Keys"
+msgstr "GPG キー"
+
+msgid "General"
+msgstr "一般"
+
+msgid "General pipelines"
msgstr ""
msgid "Generate a default set of labels"
-msgstr ""
+msgstr "åˆæœŸè¨­å®šãƒ©ãƒ™ãƒ«ã‚»ãƒƒãƒˆã‚’生æˆã™ã‚‹"
msgid "Geo Nodes"
msgstr ""
@@ -1988,7 +3014,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -1998,46 +3027,58 @@ msgid "GeoNodes|Does not match the primary storage configuration"
msgstr ""
msgid "GeoNodes|Failed"
-msgstr ""
+msgstr "失敗"
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
-msgstr ""
+msgstr "æ–°è¦ãƒŽãƒ¼ãƒ‰"
msgid "GeoNodes|Node Authentication was successfully repaired."
msgstr ""
msgid "GeoNodes|Node was successfully removed."
-msgstr ""
+msgstr "ノードã®å‰Šé™¤ã«æˆåŠŸã—ã¾ã—ãŸã€‚"
msgid "GeoNodes|Not checksummed"
msgstr ""
@@ -2048,19 +3089,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2069,16 +3116,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr "ノード読ã¿è¾¼ã¿ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+
msgid "GeoNodes|Something went wrong while removing node"
-msgstr ""
+msgstr "ノードã®å‰Šé™¤ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
-msgstr ""
+msgid "GeoNodes|Storage config"
+msgstr "ストレージã®è¨­å®š"
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2096,13 +3146,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2132,40 +3188,79 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
-msgid "Git repository URL"
+msgid "Geo|Verification capacity"
msgstr ""
+msgid "Git"
+msgstr "Git"
+
+msgid "Git repository URL"
+msgstr "Git リãƒã‚¸ãƒˆãƒª URL"
+
msgid "Git revision"
-msgstr ""
+msgstr "Git リビジョン"
msgid "Git storage health information has been reset"
+msgstr "git ストレージã®æ­£å¸¸æ€§æƒ…å ±ãŒãƒªã‚»ãƒƒãƒˆã•ã‚Œã¾ã—ãŸ"
+
+msgid "Git strategy for pipelines"
msgstr ""
msgid "Git version"
-msgstr ""
+msgstr "Git ãƒãƒ¼ã‚¸ãƒ§ãƒ³"
msgid "GitHub import"
-msgstr ""
+msgstr "GitHub インãƒãƒ¼ãƒˆ"
msgid "GitLab CI Linter has been moved"
-msgstr ""
+msgstr "GitLab CI Linter ãŒç§»å‹•ã—ã¾ã—ãŸ"
msgid "GitLab Geo"
+msgstr "GitLab Geo"
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr "GitLab グループ Runner ã¯ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—内ã®å…¨ã¦ã®ãƒ—ロジェクトã®ã‚³ãƒ¼ãƒ‰ã‚’実行ã§ãã¾ã™ã€‚"
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab project export"
msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
-msgid "Gitaly"
+msgid "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."
msgstr ""
+msgid "GitLab.com import"
+msgstr ""
+
+msgid "Gitaly"
+msgstr "Gitaly"
+
msgid "Gitaly Servers"
+msgstr "Gitaly サーãƒãƒ¼"
+
+msgid "Gitaly|Address"
+msgstr "アドレス"
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
msgstr ""
+msgid "Go Back"
+msgstr "戻る"
+
msgid "Go back"
+msgstr "å‰ã«æˆ»ã‚‹"
+
+msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Go to your fork"
@@ -2174,139 +3269,262 @@ msgstr "自分ã®ãƒ•ã‚©ãƒ¼ã‚¯ã¸ç§»å‹•"
msgid "GoToYourFork|Fork"
msgstr "フォーク"
-msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
msgstr ""
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr "Google èªè¨¼ã¯ %{link_to_documentation} ã§ã¯ã‚ã‚Šã¾ã›ã‚“。ã“ã®ã‚µãƒ¼ãƒ“スã«ã¤ã„ã¦ã¯ GitLab 管ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。"
+
msgid "Got it!"
+msgstr "入手ã—ã¾ã—ょã†ï¼"
+
+msgid "Graph"
+msgstr "グラフ"
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr "CI/CD グループ設定"
+
+msgid "Group Git LFS status:"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Group ID"
+msgstr "グループ ID"
+
+msgid "Group Runners"
+msgstr "グループ Runner"
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr "グループ Maintainer 㯠%{link} ã§ã‚°ãƒ«ãƒ¼ãƒ— Runner を登録ã§ãã¾ã™ã€‚"
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
msgstr ""
msgid "GroupRoadmap|Loading roadmap"
-msgstr ""
+msgstr "ロードマップã®èª­ã¿è¾¼ã¿ä¸­"
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
-msgstr ""
+msgstr "%{group} 内ã®ãƒ—ロジェクトを他ã®ã‚°ãƒ«ãƒ¼ãƒ—ã¨å…±æœ‰ã—ãªã„よã†ã«ã™ã‚‹"
msgid "GroupSettings|Share with group lock"
msgstr ""
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
-msgstr ""
+msgstr "ã“ã®è¨­å®šã¯ %{ancestor_group} ã«é©ç”¨ã•ã‚Œã€ã‚µãƒ–グループã§ä¸Šæ›¸ãã•ã‚Œã¾ã™ã€‚"
msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
-msgstr ""
+msgstr "ã“ã®è¨­å®šã¯ %{ancestor_group} ã«é©ç”¨ã•ã‚Œã¾ã™ã€‚ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã®ãƒ—ロジェクトを他ã®ã‚°ãƒ«ãƒ¼ãƒ—ã¨å…±æœ‰ã™ã‚‹ã«ã¯ã€è¨­å®šã‚’上書ãã™ã‚‹ã‹ã€%{remove_ancestor_share_with_group_lock} ã™ã‚‹ã‚ˆã†ã«ã‚ªãƒ¼ãƒŠãƒ¼ã«ç¢ºèªã—ã¦ãã ã•ã„。"
msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
-msgstr ""
+msgstr "ã“ã®è¨­å®šã¯ %{ancestor_group} ã«é©ç”¨ã•ã‚Œã¾ã™ã€‚設定を上書ãã€ã¾ãŸã¯ %{remove_ancestor_share_with_group_lock} ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
-msgstr ""
+msgstr "ã“ã®è¨­å®šã¯ã‚°ãƒ«ãƒ¼ãƒ—オーナーã«ã‚ˆã£ã¦ä¸Šæ›¸ãã•ã‚Œãªã„é™ã‚Šã€ã™ã¹ã¦ã®ã‚µãƒ–グループã«é©ç”¨ã•ã‚Œã¾ã™ã€‚プロジェクトã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ãŒæ—¢ã«ã‚るグループã¯ã€æ‰‹å‹•ã§å‰Šé™¤ã—ãªã„é™ã‚Šå¼•ã続ãアクセスã§ãã¾ã™ã€‚"
msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr "グループã®ãƒ­ãƒƒã‚¯ã‚’ %{ancestor_group_name} ã®å…±æœ‰ã‹ã‚‰å‰Šé™¤"
+
+msgid "Groups"
msgstr ""
-msgid "GroupsEmptyState|A group is a collection of several projects."
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr "グループã¯ã€%{subgroup_docs_link_start}サブグループ%{subgroup_docs_link_end}を作æˆã™ã‚‹ã“ã¨ã§ãƒã‚¹ãƒˆã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
+
+msgid "GroupsDropdown|Frequently visited"
msgstr ""
-msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr "よã使ã†ã‚°ãƒ«ãƒ¼ãƒ—ã¯ã“ã“ã«è¡¨ç¤ºã•ã‚Œã¾ã™"
+
+msgid "GroupsDropdown|Loading groups"
msgstr ""
-msgid "GroupsEmptyState|No groups found"
+msgid "GroupsDropdown|Search your groups"
msgstr ""
-msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
+msgid "GroupsDropdown|Something went wrong on our end."
msgstr ""
-msgid "GroupsTree|Create a project in this group."
+msgid "GroupsDropdown|Sorry, no groups matched your search"
msgstr ""
-msgid "GroupsTree|Create a subgroup in this group."
+msgid "GroupsDropdown|This feature requires browser localStorage support"
msgstr ""
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr "グループã¯è¤‡æ•°ã®ãƒ—ロジェクトã®é›†åˆä½“ã§ã™ã€‚"
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr "グループã¯ãƒ—ロジェクトを整ç†ã™ã‚‹ãŸã‚ã®ã€ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã®ã‚ˆã†ã«æ©Ÿèƒ½ã—ã¾ã™ã€‚"
+
+msgid "GroupsEmptyState|No groups found"
+msgstr "グループã¯ã‚ã‚Šã¾ã›ã‚“"
+
+msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the 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 ""
+msgstr "グループを編集"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
-msgstr ""
+msgstr "グループã®å”¯ä¸€ã®ã‚ªãƒ¼ãƒŠãƒ¼ã¨ãªã£ã¦ã„ã‚‹ãŸã‚ã€ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã‹ã‚‰é›¢è„±ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。"
msgid "GroupsTree|Filter by name..."
-msgstr ""
+msgstr "åå‰ã§çµžã‚Šè¾¼ã¿..."
msgid "GroupsTree|Leave this group"
-msgstr ""
+msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã‹ã‚‰é›¢è„±ã™ã‚‹"
msgid "GroupsTree|Loading groups"
-msgstr ""
+msgstr "グループã®èª­ã¿è¾¼ã¿ä¸­"
msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr ""
+msgstr "ã™ã¿ã¾ã›ã‚“ã€æ¤œç´¢ã«ä¸€è‡´ã™ã‚‹ã‚°ãƒ«ãƒ¼ãƒ—ãŒå­˜åœ¨ã—ã¾ã›ã‚“"
msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr ""
+msgstr "ã™ã¿ã¾ã›ã‚“ã€æ¤œç´¢ã«ä¸€è‡´ã™ã‚‹ãƒ—ロジェクトã‹ã‚°ãƒ«ãƒ¼ãƒ—ãŒå­˜åœ¨ã—ã¾ã›ã‚“"
msgid "Have your users email"
msgstr ""
msgid "Header message"
-msgstr ""
+msgstr "ヘッダーメッセージ"
msgid "Health Check"
-msgstr ""
+msgstr "正常性ãƒã‚§ãƒƒã‚¯"
msgid "Health information can be retrieved from the following endpoints. More information is available"
-msgstr ""
+msgstr "正常性情報ã¯æ¬¡ã®ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã‹ã‚‰å–å¾—ã§ãã¾ã™ã€‚詳細ãªæƒ…å ±ã¯"
msgid "HealthCheck|Access token is"
-msgstr ""
+msgstr "アクセストークンã¯"
msgid "HealthCheck|Healthy"
-msgstr ""
+msgstr "正常"
msgid "HealthCheck|No Health Problems Detected"
-msgstr ""
+msgstr "正常性ã«å•é¡Œã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "HealthCheck|Unhealthy"
-msgstr ""
+msgstr "異常"
msgid "Help"
-msgstr ""
+msgstr "ヘルプ"
msgid "Help page"
-msgstr ""
+msgstr "ヘルプページ"
msgid "Help page text and support page url."
-msgstr ""
+msgstr "ヘルプページテキストã¨ã‚µãƒãƒ¼ãƒˆãƒšãƒ¼ã‚¸URL。"
msgid "Hide value"
msgid_plural "Hide values"
-msgstr[0] ""
+msgstr[0] "éžè¡¨ç¤º"
+
+msgid "Hide whitespace changes"
+msgstr "空白ã®å¤‰æ›´ã‚’éš ã™"
msgid "History"
-msgstr ""
+msgstr "履歴"
msgid "Housekeeping successfully started"
msgstr "ãƒã‚¦ã‚¹ã‚­ãƒ¼ãƒ”ングã¯æ­£å¸¸ã«èµ·å‹•ã—ã¾ã—ãŸã€‚"
+msgid "I accept the %{terms_link}"
+msgstr "%{terms_link} ã«åŒæ„ã™ã‚‹"
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr "ID"
+
+msgid "IDE|Commit"
+msgstr "コミット"
+
+msgid "IDE|Edit"
+msgstr "編集"
+
+msgid "IDE|Go back"
+msgstr "戻る"
+
+msgid "IDE|Open in file view"
+msgstr "ファイルビューã§é–‹ã"
+
+msgid "IDE|Review"
+msgstr "レビュー"
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2319,23 +3537,62 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr "2-up"
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr "オニオンスキン"
+
+msgid "ImageDiffViewer|Swipe"
+msgstr "スワイプ"
+
msgid "Import"
+msgstr "インãƒãƒ¼ãƒˆ"
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
msgstr ""
msgid "Import all repositories"
+msgstr "ã™ã¹ã¦ã®ãƒªãƒã‚¸ãƒˆãƒªã‚’インãƒãƒ¼ãƒˆ"
+
+msgid "Import an exported GitLab project"
msgstr ""
msgid "Import in progress"
+msgstr "インãƒãƒ¼ãƒˆä¸­ã§ã™"
+
+msgid "Import multiple repositories by uploading a manifest file."
msgstr ""
-msgid "Import repositories from GitHub"
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
msgstr ""
+msgid "Import repositories from GitHub"
+msgstr "リãƒã‚¸ãƒˆãƒªã‚’ GitHub ã‹ã‚‰ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹"
+
msgid "Import repository"
-msgstr "レãƒã‚¸ãƒˆãƒªãƒ¼ã‚’インãƒãƒ¼ãƒˆ"
+msgstr ""
msgid "ImportButtons|Connect repositories from"
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªã¸æŽ¥ç¶š"
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -2346,10 +3603,22 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance"
@@ -2362,6 +3631,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2377,11 +3649,14 @@ msgstr "é–“éš”ã®ãƒ‘ターン"
msgid "Introducing Cycle Analytics"
msgstr "サイクル分æžã®ã”紹介"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
msgid "Issue events"
-msgstr ""
+msgstr "課題イベント"
msgid "IssueBoards|Board"
msgstr ""
@@ -2390,40 +3665,52 @@ msgid "IssueBoards|Boards"
msgstr ""
msgid "Issues"
-msgstr ""
+msgstr "課題"
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
-msgid "Jan"
+msgid "Issues closed"
msgstr ""
+msgid "Jan"
+msgstr "1月"
+
msgid "January"
+msgstr "1月"
+
+msgid "Job"
msgstr ""
+msgid "Job has been erased"
+msgstr "ジョブãŒæ¶ˆåŽ»ã•ã‚Œã¾ã—ãŸ"
+
msgid "Jobs"
-msgstr ""
+msgstr "ジョブ"
msgid "Jul"
-msgstr ""
+msgstr "7月"
msgid "July"
-msgstr ""
+msgstr "7月"
msgid "Jun"
-msgstr ""
+msgstr "6月"
msgid "June"
-msgstr ""
+msgstr "6月"
msgid "Koding"
+msgstr "Koding"
+
+msgid "Koding Dashboard"
msgstr ""
msgid "Kubernetes"
-msgstr ""
+msgstr "Kubernetes"
msgid "Kubernetes Cluster"
-msgstr ""
+msgstr "Kubernetes クラスター"
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
msgstr ""
@@ -2438,11 +3725,14 @@ msgid "Kubernetes cluster was successfully updated."
msgstr ""
msgid "Kubernetes configured"
-msgstr ""
+msgstr "Kubernetes 構æˆæ¸ˆã¿"
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr "LFS"
+
msgid "LFSStatus|Disabled"
msgstr "無効"
@@ -2450,6 +3740,12 @@ msgid "LFSStatus|Enabled"
msgstr "有効"
msgid "Label"
+msgstr "ラベル"
+
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
msgstr ""
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
@@ -2458,20 +3754,26 @@ msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr "ラベル"
+
msgid "Labels"
-msgstr ""
+msgstr "ラベル"
msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr ""
+msgstr "ラベルを%{features} ã«é©ç”¨ã§ãã¾ã™ã€‚グループラベルã¯ã‚°ãƒ«ãƒ¼ãƒ—内ã®ã™ã¹ã¦ã®ãƒ—ロジェクトã§ä½¿ç”¨ã§ãã¾ã™ã€‚"
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
msgid "Labels|Promote Label"
-msgstr ""
+msgstr "ラベルã®æ˜‡æ ¼"
msgid "Last %d day"
msgid_plural "Last %d days"
@@ -2484,22 +3786,25 @@ msgid "Last commit"
msgstr "最新コミット"
msgid "Last edited %{date}"
-msgstr ""
+msgstr "最終編集日 %{date}"
msgid "Last edited by %{name}"
-msgstr ""
+msgstr "最終編集者 %{name}"
msgid "Last update"
-msgstr ""
+msgstr "最終更新"
msgid "Last updated"
msgstr ""
msgid "LastPushEvent|You pushed to"
-msgstr ""
+msgstr "ã“ã“ã¸ãƒ—ッシュã—ã¾ã—ãŸ"
msgid "LastPushEvent|at"
-msgstr ""
+msgstr "ã“ã®æ™‚刻ã«"
+
+msgid "Latest changes"
+msgstr "最新ã®å¤‰æ›´"
msgid "Learn more"
msgstr ""
@@ -2517,7 +3822,7 @@ msgid "Learn more in the|pipeline schedules documentation"
msgstr "詳ã—ãã¯ãƒ‘イプラインスケジュールã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’å‚ç…§"
msgid "Leave"
-msgstr ""
+msgstr "離れる"
msgid "Leave group"
msgstr "グループを離脱"
@@ -2525,120 +3830,225 @@ msgstr "グループを離脱"
msgid "Leave project"
msgstr "プロジェクトを離脱"
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
+msgstr "ライセンス"
+
+msgid "LinkedIn"
msgstr ""
msgid "List"
+msgstr "リスト"
+
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
msgstr ""
msgid "List your GitHub repositories"
+msgstr "GitHub リãƒã‚¸ãƒˆãƒªã‚’一覧表示"
+
+msgid "Loading contribution stats for group members"
msgstr ""
msgid "Loading the GitLab IDE..."
-msgstr ""
+msgstr "GitLab IDE ã®èª­ã¿è¾¼ã¿ä¸­..."
+
+msgid "Loading..."
+msgstr "読ã¿è¾¼ã¿ä¸­..."
msgid "Lock"
-msgstr ""
+msgstr "ロック"
msgid "Lock %{issuableDisplayName}"
-msgstr ""
+msgstr "ロック %{issuableDisplayName}"
msgid "Lock not found"
+msgstr "ロックãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
+
+msgid "Lock to current projects"
msgstr ""
msgid "Locked"
-msgstr ""
+msgstr "ロック中"
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
-msgid "Manage group labels"
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
msgstr ""
-msgid "Manage labels"
+msgid "Manage applications that you've authorized to use your account."
msgstr ""
+msgid "Manage group labels"
+msgstr "グループラベルã®ç®¡ç†"
+
+msgid "Manage labels"
+msgstr "ラベルを管ç†"
+
msgid "Manage project labels"
-msgstr ""
+msgstr "プロジェクトラベルã®ç®¡ç†"
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
-msgid "Mar"
+msgid "Manifest"
msgstr ""
-msgid "March"
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
-msgid "Mark done"
+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"
+msgstr ""
+
+msgid "Mar"
+msgstr "3月"
+
+msgid "March"
+msgstr "3月"
+
+msgid "Mark todo as done"
+msgstr "Todo を完了ã«ã™ã‚‹"
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
msgstr ""
msgid "May"
-msgstr ""
+msgstr "5月"
msgid "Median"
msgstr "中央値"
msgid "Members"
-msgstr ""
+msgstr "メンãƒãƒ¼"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr "マージリクエスト"
+
+msgid "Merge Request:"
+msgstr "マージリクエスト:"
+
msgid "Merge Requests"
+msgstr "マージリクエスト"
+
+msgid "Merge Requests created"
msgstr ""
msgid "Merge events"
-msgstr ""
+msgstr "マージイベント"
msgid "Merge request"
+msgstr "マージリクエスト"
+
+msgid "Merge request approvals"
msgstr ""
+msgid "Merge requests"
+msgstr "マージリクエスト"
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
-msgid "Merged"
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr "æ–°ã—ã„課題ã§ã“ã®æ¤œè¨Žã‚’解決ã™ã‚‹"
+
+msgid "MergeRequests|Saving the comment failed"
msgstr ""
-msgid "Messages"
+msgid "MergeRequests|Toggle comments for this file"
msgstr ""
-msgid "Metrics - Influx"
+msgid "MergeRequests|Updating discussions failed"
+msgstr "検討ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "Merged"
+msgstr "マージ済ã¿"
+
+msgid "Messages"
+msgstr "メッセージ"
+
+msgid "Metrics"
+msgstr "メトリクス"
+
+msgid "Metrics - Influx"
+msgstr "メトリクス - Influx"
+
msgid "Metrics - Prometheus"
msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr "環境"
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2646,11 +4056,14 @@ msgid "Metrics|Must be a valid PromQL query."
msgstr ""
msgid "Metrics|Name"
-msgstr ""
+msgstr "åå‰"
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2658,12 +4071,30 @@ msgid "Metrics|Query"
msgstr ""
msgid "Metrics|Response"
-msgstr ""
+msgstr "レスãƒãƒ³ã‚¹"
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
+msgstr "タイプ"
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
msgstr ""
msgid "Metrics|Unit label"
@@ -2694,52 +4125,67 @@ msgid "Metrics|e.g. req/sec"
msgstr ""
msgid "Milestone"
-msgstr ""
+msgstr "マイルストーン"
+
+msgid "Milestones"
+msgstr "マイルストーン"
msgid "Milestones|Delete milestone"
-msgstr ""
+msgstr "マイルストーンã®å‰Šé™¤"
msgid "Milestones|Delete milestone %{milestoneTitle}?"
-msgstr ""
+msgstr "マイルストーン %{milestoneTitle} を削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
-msgstr ""
+msgstr "マイルストーン %{milestoneTitle} ã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ"
msgid "Milestones|Milestone %{milestoneTitle} was not found"
-msgstr ""
+msgstr "マイルストーン %{milestoneTitle} ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
-msgstr ""
+msgstr "%{milestoneTitle} をグループマイルストーンã«æ˜‡æ ¼ã—ã¾ã™ã‹ï¼Ÿ"
msgid "Milestones|Promote Milestone"
-msgstr ""
+msgstr "マイルストーンã®æ˜‡æ ¼"
msgid "Milestones|This action cannot be reversed."
-msgstr ""
+msgstr "ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。"
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "SSH éµã‚’追加"
msgid "Modal|Cancel"
-msgstr ""
+msgstr "キャンセル"
msgid "Modal|Close"
-msgstr ""
+msgstr "é–‰ã˜ã‚‹"
msgid "Monitoring"
+msgstr "監視"
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
msgstr ""
msgid "More info"
msgstr ""
msgid "More information"
-msgstr ""
+msgstr "詳ã—ã„情報"
msgid "More information is available|here"
+msgstr "ã“ã“ã‚’å‚ç…§"
+
+msgid "Most stars"
msgstr ""
msgid "Move"
-msgstr ""
+msgstr "移動"
msgid "Move issue"
msgstr ""
@@ -2747,27 +4193,66 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr "åå‰"
+
msgid "Name new label"
+msgstr "æ–°ã—ã„ラベルã«åå‰ã‚’ã¤ã‘ã‚‹"
+
+msgid "Name your individual key via a title"
msgstr ""
-msgid "New Issue"
-msgid_plural "New Issues"
-msgstr[0] "æ–°è¦èª²é¡Œ"
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr "ヘルプ"
-msgid "New Kubernetes Cluster"
+msgid "Nav|Home"
+msgstr "ホーム"
+
+msgid "Nav|Sign In / Register"
+msgstr "サインイン / 登録"
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹"
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
msgstr ""
-msgid "New Kubernetes cluster"
+msgid "New Group"
msgstr ""
+msgid "New Identity"
+msgstr "æ–°ã—ã„ ID"
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] "æ–°è¦èª²é¡Œ"
+
+msgid "New Label"
+msgstr "æ–°ã—ã„ラベル"
+
msgid "New Pipeline Schedule"
msgstr "æ–°è¦ãƒ‘イプラインスケジュール"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "æ–°è¦ãƒ–ランãƒ"
msgid "New branch unavailable"
-msgstr ""
+msgstr "æ–°ã—ã„ブランãƒã¯åˆ©ç”¨ã§ãã¾ã›ã‚“"
msgid "New directory"
msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª"
@@ -2779,20 +4264,26 @@ msgid "New file"
msgstr "æ–°è¦ãƒ•ã‚¡ã‚¤ãƒ«"
msgid "New group"
+msgstr "æ–°è¦ã‚°ãƒ«ãƒ¼ãƒ—"
+
+msgid "New identity"
msgstr ""
msgid "New issue"
msgstr "æ–°è¦èª²é¡Œ"
msgid "New label"
-msgstr ""
+msgstr "æ–°ã—ã„ラベル"
msgid "New merge request"
msgstr "æ–°è¦ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
-msgid "New project"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
msgstr ""
+msgid "New project"
+msgstr "æ–°è¦ãƒ—ロジェクト"
+
msgid "New schedule"
msgstr "æ–°è¦ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«"
@@ -2800,55 +4291,94 @@ msgid "New snippet"
msgstr "æ–°è¦ã‚¹ãƒ‹ãƒšãƒƒãƒˆ"
msgid "New subgroup"
-msgstr ""
+msgstr "æ–°è¦ã‚µãƒ–グループ"
msgid "New tag"
msgstr "æ–°è¦ã‚¿ã‚°"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr "ã„ã„ãˆ"
+
msgid "No Label"
msgstr ""
msgid "No assignee"
-msgstr ""
+msgstr "担当者ãªã—"
msgid "No changes"
-msgstr ""
+msgstr "変更ãªã—"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
-msgstr ""
+msgstr "Gitaly サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ログを確èªã—ã¦ãã ã•ã„ï¼"
msgid "No due date"
-msgstr ""
+msgstr "期é™ãªã—"
msgid "No estimate or time spent"
msgstr ""
msgid "No file chosen"
+msgstr "ファイルãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+msgid "No files found"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found."
+msgstr "ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。"
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr "マージリクエストã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+msgid "No messages were logged"
+msgstr "メッセージã¯è¨˜éŒ²ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
-msgstr "レãƒã‚¸ãƒˆãƒªãƒ¼ã¯ã‚ã‚Šã¾ã›ã‚“"
+msgstr "リãƒã‚¸ãƒˆãƒªãŒã‚ã‚Šã¾ã›ã‚“"
msgid "No schedules"
msgstr "スケジュールãªã—"
-msgid "None"
+msgid "No, directly import the existing email addresses and usernames."
msgstr ""
+msgid "None"
+msgstr "ãªã—"
+
msgid "Not allowed to merge"
-msgstr ""
+msgstr "マージã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "Not available"
msgstr "利用ã§ãã¾ã›ã‚“"
msgid "Not available for private projects"
-msgstr ""
+msgstr "プライベートプロジェクトã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“"
msgid "Not available for protected branches"
-msgstr ""
+msgstr "ä¿è­·ã•ã‚ŒãŸãƒ–ランãƒã§ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“"
msgid "Not confidential"
msgstr ""
@@ -2857,7 +4387,7 @@ msgid "Not enough data"
msgstr "データä¸è¶³"
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
-msgstr ""
+msgstr "マスターブランãƒã¯è‡ªå‹•çš„ã«ä¿è­·ã•ã‚Œã‚‹ã“ã¨ã«ç•™æ„ã—ã¦ãã ã•ã„。%{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
msgstr ""
@@ -2871,6 +4401,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "イベント通知"
@@ -2926,31 +4459,31 @@ msgid "NotificationLevel|Watch"
msgstr "ã™ã¹ã¦é€šçŸ¥"
msgid "Notifications"
-msgstr ""
+msgstr "通知"
msgid "Notifications off"
-msgstr ""
+msgstr "通知オフ"
msgid "Notifications on"
-msgstr ""
+msgstr "通知オン"
msgid "Nov"
-msgstr ""
+msgstr "11月"
msgid "November"
-msgstr ""
+msgstr "11月"
msgid "Number of access attempts"
-msgstr ""
+msgstr "アクセス試行回数"
msgid "OK"
-msgstr ""
+msgstr "OK"
msgid "Oct"
-msgstr ""
+msgstr "10月"
msgid "October"
-msgstr ""
+msgstr "10月"
msgid "OfSearchInADropdown|Filter"
msgstr "フィルター"
@@ -2958,27 +4491,72 @@ msgstr "フィルター"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
+msgstr "プロジェクトメンãƒãƒ¼ã®ã¿ã‚³ãƒ¡ãƒ³ãƒˆã§ãã¾ã™"
+
+msgid "Oops, are you sure?"
msgstr ""
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr "Xcode ã§é–‹ã"
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr "オープンã•ã‚ŒãŸã®ã¯"
msgid "Opens in a new window"
+msgstr "æ–°è¦ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã§é–‹ã"
+
+msgid "Operations"
+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 "オプション"
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr "ãã®ä»–ã®ãƒ©ãƒ™ãƒ«"
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -2986,16 +4564,16 @@ msgid "Outbound requests"
msgstr ""
msgid "Overview"
-msgstr ""
+msgstr "概è¦"
msgid "Owner"
msgstr "オーナー"
msgid "Pages"
-msgstr ""
+msgstr "Pages"
msgid "Pagination|Last »"
-msgstr ""
+msgstr "最後 »"
msgid "Pagination|Next"
msgstr ""
@@ -3004,22 +4582,40 @@ msgid "Pagination|Prev"
msgstr ""
msgid "Pagination|« First"
-msgstr ""
+msgstr "« 最åˆ"
msgid "Part of merge request changes"
msgstr ""
msgid "Password"
+msgstr "パスワード"
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
msgstr ""
msgid "Pending"
+msgstr "ä¿ç•™ä¸­"
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
-msgid "Performance optimization"
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
msgstr ""
+msgid "Performance optimization"
+msgstr "パフォーマンスã®æœ€é©åŒ–"
+
+msgid "Permissions"
+msgstr "権é™"
+
msgid "Personal Access Token"
-msgstr ""
+msgstr "個人ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³"
msgid "Pipeline"
msgstr "パイプライン"
@@ -3036,6 +4632,9 @@ msgstr "パイプラインスケジュール"
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr "失敗:"
@@ -3091,19 +4690,19 @@ msgid "Pipelines charts"
msgstr "パイプラインãƒãƒ£ãƒ¼ãƒˆ"
msgid "Pipelines for last month"
-msgstr ""
+msgstr "先月ã®ãƒ‘イプライン"
msgid "Pipelines for last week"
-msgstr ""
+msgstr "先週ã®ãƒ‘イプライン"
msgid "Pipelines for last year"
-msgstr ""
+msgstr "昨年ã®ãƒ‘イプライン"
msgid "Pipelines|Build with confidence"
msgstr ""
msgid "Pipelines|CI Lint"
-msgstr ""
+msgstr "CI Lint"
msgid "Pipelines|Clear Runner Caches"
msgstr ""
@@ -3112,13 +4711,13 @@ msgid "Pipelines|Get started with Pipelines"
msgstr ""
msgid "Pipelines|Loading Pipelines"
-msgstr ""
+msgstr "パイプラインを読ã¿è¾¼ã¿ä¸­"
msgid "Pipelines|Project cache successfully reset."
msgstr ""
msgid "Pipelines|Run Pipeline"
-msgstr ""
+msgstr "パイプライン実行"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
@@ -3132,23 +4731,35 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Create pipeline"
msgstr ""
-msgid "Pipeline|Stop pipeline"
+msgid "Pipeline|Existing branch name or tag"
msgstr ""
-msgid "Pipeline|Stop pipeline #%{pipelineId}?"
-msgstr ""
+msgid "Pipeline|Run Pipeline"
+msgstr "パイプラインを実行"
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Search branches"
+msgstr "ブランãƒã®æ¤œç´¢"
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
+msgid "Pipeline|Stop pipeline"
+msgstr "パイプラインã®åœæ­¢"
+
+msgid "Pipeline|Stop pipeline #%{pipelineId}?"
+msgstr "#%{pipelineId} パイプラインをåœæ­¢ã—ã¾ã™ã‹ï¼Ÿ"
+
+msgid "Pipeline|Variables"
+msgstr "変数"
+
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
-msgstr ""
+msgstr "%{pipelineId} パイプラインをåœæ­¢ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚"
msgid "Pipeline|all"
msgstr "全件"
@@ -3162,18 +4773,42 @@ msgstr "ステージã‚ã‚Š"
msgid "Pipeline|with stages"
msgstr "ステージã‚ã‚Š"
-msgid "PlantUML"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
msgstr ""
+msgid "Planned start date"
+msgstr ""
+
+msgid "PlantUML"
+msgstr "PlantUML"
+
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr "続ã‘ã‚‹å‰ã«ã€åˆ©ç”¨è¦ç´„ã«åŒæ„ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
-msgid "Please solve the reCAPTCHA"
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please solve the reCAPTCHA"
+msgstr "reCAPTCHA を解決ã—ã¦ãã ã•ã„"
+
+msgid "Please try again"
+msgstr "ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„"
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3181,11 +4816,26 @@ msgid "Please wait while we import the repository for you. Refresh at will."
msgstr ""
msgid "Preferences"
+msgstr "基本設定"
+
+msgid "Preferences|Navigation theme"
msgstr ""
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3196,34 +4846,64 @@ msgid "Private projects can be created in your personal namespace with:"
msgstr ""
msgid "Profile"
+msgstr "プロフィール"
+
+msgid "Profile Settings"
msgstr ""
msgid "Profiles|Account scheduled for removal."
msgstr ""
-msgid "Profiles|Delete Account"
+msgid "Profiles|Add key"
msgstr ""
+msgid "Profiles|Change username"
+msgstr "ユーザーåã®å¤‰æ›´"
+
+msgid "Profiles|Current path: %{path}"
+msgstr "ç¾åœ¨ã®ãƒ‘ス: %{path}"
+
+msgid "Profiles|Delete Account"
+msgstr "アカウント削除"
+
msgid "Profiles|Delete account"
-msgstr ""
+msgstr "アカウント削除"
msgid "Profiles|Delete your account?"
-msgstr ""
+msgstr "ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "Profiles|Deleting an account has the following effects:"
-msgstr ""
+msgstr "アカウントを削除ã™ã‚‹ã¨æ¬¡ã®ã‚ˆã†ãªå½±éŸ¿ãŒã‚ã‚Šã¾ã™:"
msgid "Profiles|Invalid password"
-msgstr ""
+msgstr "パスワードãŒæ­£ã—ãã‚ã‚Šã¾ã›ã‚“"
msgid "Profiles|Invalid username"
+msgstr "ユーザーåãŒæ­£ã—ãã‚ã‚Šã¾ã›ã‚“"
+
+msgid "Profiles|Path"
+msgstr "パス"
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr "確èªã®ãŸã‚ %{confirmationValue} を入力ã—ã¦ãã ã•ã„:"
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
+msgid "Profiles|Update username"
+msgstr "ユーザーåã‚’æ›´æ–°"
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr "ユーザーåã®å¤‰æ›´ã«å¤±æ•—ã—ã¾ã—㟠- %{message}"
+
+msgid "Profiles|Username successfully changed"
+msgstr "ユーザーåã¯æ­£å¸¸ã«å¤‰æ›´ã•ã‚Œã¾ã—ãŸ"
+
msgid "Profiles|You don't have access to delete this user."
-msgstr ""
+msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’削除ã™ã‚‹æ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“"
msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
msgstr ""
@@ -3231,17 +4911,26 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
-msgid "Profiles|your account"
+msgid "Profiles|e.g. My MacBook key"
msgstr ""
+msgid "Profiles|your account"
+msgstr "ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ"
+
msgid "Profiling - Performance bar"
msgstr ""
msgid "Programming languages used in this repository"
-msgstr ""
+msgstr "ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã§ä½¿ç”¨ã•ã‚Œã¦ã„るプログラミング言語"
+
+msgid "Progress"
+msgstr "進行状æ³"
+
+msgid "Project"
+msgstr "プロジェクト"
msgid "Project '%{project_name}' is in the process of being deleted."
-msgstr ""
+msgstr "プロジェクト '%{project_name}' ã¯å‰Šé™¤ä¸­ã§ã™ã€‚"
msgid "Project '%{project_name}' queued for deletion."
msgstr "'%{project_name}' プロジェクトã¯å‰Šé™¤å‡¦ç†å¾…ã¡ã§ã™ã€‚"
@@ -3252,17 +4941,20 @@ msgstr "'%{project_name}' プロジェクトã¯æ­£å¸¸ã«ä½œæˆã•ã‚Œã¾ã—ãŸã€‚
msgid "Project '%{project_name}' was successfully updated."
msgstr "'%{project_name}' プロジェクトã¯æ­£å¸¸ã«æ›´æ–°ã•ã‚Œã¾ã—ãŸã€‚"
+msgid "Project Badges"
+msgstr "プロジェクトãƒãƒƒã‚¸"
+
msgid "Project access must be granted explicitly to each user."
msgstr "ユーザーã”ã¨ã«ãƒ—ロジェクトアクセスã®æ¨©é™ã‚’指定ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。"
msgid "Project avatar"
-msgstr ""
+msgstr "プロジェクトアãƒã‚¿ãƒ¼"
msgid "Project avatar in repository: %{link}"
-msgstr ""
+msgstr "リãƒã‚¸ãƒˆãƒªå†…ã®ãƒ—ロジェクトアãƒã‚¿ãƒ¼: %{link}"
msgid "Project details"
-msgstr ""
+msgstr "プロジェクトã®è©³ç´°"
msgid "Project export could not be deleted."
msgstr "プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚’削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
@@ -3276,33 +4968,27 @@ msgstr "プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆãƒªãƒ³ã‚¯ã¯æœŸé™åˆ‡ã‚Œã«ãªã‚Š
msgid "Project export started. A download link will be sent by email."
msgstr "プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚’開始ã—ã¾ã—ãŸã€‚ダウンロードã®ãƒªãƒ³ã‚¯ã¯ãƒ¡ãƒ¼ãƒ«ã§é€ä¿¡ã—ã¾ã™"
-msgid "ProjectActivityRSS|Subscribe"
+msgid "Project name"
msgstr ""
+msgid "ProjectActivityRSS|Subscribe"
+msgstr "講読"
+
msgid "ProjectCreationLevel|Allowed to create projects"
msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr "無効"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "アクセス権é™ã‚’æŒã£ã¦ã„る人"
-
-msgid "ProjectFeature|Only team members"
-msgstr "ãƒãƒ¼ãƒ ãƒ¡ãƒ³ãƒãƒ¼ã®ã¿"
-
msgid "ProjectFileTree|Name"
msgstr "åå‰"
@@ -3312,12 +4998,18 @@ msgstr "記録ãªã—"
msgid "ProjectLifecycle|Stage"
msgstr "ステージ"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚°ãƒ©ãƒ•"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3334,10 +5026,13 @@ msgid "ProjectSettings|Users can only push commits to this repository that were
msgstr ""
msgid "Projects"
+msgstr "プロジェクト"
+
+msgid "Projects shared with %{group_name}"
msgstr ""
msgid "ProjectsDropdown|Frequently visited"
-msgstr ""
+msgstr "よã使ã†ãƒ—ロジェクト"
msgid "ProjectsDropdown|Loading projects"
msgstr ""
@@ -3346,25 +5041,55 @@ msgid "ProjectsDropdown|Projects you visit often will appear here"
msgstr ""
msgid "ProjectsDropdown|Search your projects"
-msgstr ""
+msgstr "プロジェクトを検索"
msgid "ProjectsDropdown|Something went wrong on our end."
msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
+msgstr "検索æ¡ä»¶ã«ä¸€è‡´ã—ãŸãƒ—ロジェクトã¯ã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+
+msgid "PrometheusAlerts|Add alert"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Alert set"
msgstr ""
-msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
msgstr ""
+msgid "PrometheusDashboard|Time"
+msgstr "時間"
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr "%{metrics} ã® %{exporters} ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ"
+
msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
msgstr ""
msgid "PrometheusService|Active"
-msgstr ""
+msgstr "アクティブ"
msgid "PrometheusService|Auto configuration"
msgstr ""
@@ -3388,28 +5113,28 @@ msgid "PrometheusService|Finding and configuring metrics..."
msgstr ""
msgid "PrometheusService|Finding custom metrics..."
-msgstr ""
+msgstr "カスタムメトリクスを検索..."
msgid "PrometheusService|Install Prometheus on clusters"
msgstr ""
msgid "PrometheusService|Manage clusters"
-msgstr ""
+msgstr "クラスター管ç†"
msgid "PrometheusService|Manual configuration"
-msgstr ""
+msgstr "手動構æˆ"
msgid "PrometheusService|Metrics"
msgstr ""
msgid "PrometheusService|Missing environment variable"
-msgstr ""
+msgstr "未設定ã®ç’°å¢ƒå¤‰æ•°"
msgid "PrometheusService|More information"
msgstr ""
msgid "PrometheusService|New metric"
-msgstr ""
+msgstr "æ–°è¦ãƒ¡ãƒˆãƒªã‚¯ã‚¹"
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr ""
@@ -3433,31 +5158,55 @@ msgid "PrometheusService|Waiting for your first deployment to an environment to
msgstr ""
msgid "Promote"
-msgstr ""
+msgstr "昇格"
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
+msgstr "グループマイルストーンã«æ˜‡æ ¼"
+
+msgid "Promote to group label"
msgstr ""
+msgid "Promotions|Don't show me this again"
+msgstr "次回ã‹ã‚‰ã¯ã“ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’表示ã—ãªã„"
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr "ã“ã®æ©Ÿèƒ½ã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
+
+msgid "Promotions|Upgrade plan"
+msgstr "プランをアップグレード"
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
+msgstr "公開 - プロジェクトã¯èªè¨¼ç„¡ã—ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™"
+
+msgid "Public pipelines"
msgstr ""
msgid "Push Rules"
msgstr ""
msgid "Push events"
-msgstr ""
+msgstr "プッシュイベント"
msgid "Push project from command line"
-msgstr ""
+msgstr "コマンドラインã‹ã‚‰ãƒ—ロジェクトをプッシュ"
msgid "Push to create a project"
msgstr ""
@@ -3465,33 +5214,48 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr "プッシュ済ã¿"
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr "続ãを読む"
-msgid "Readme"
-msgstr ""
-
-msgid "Real-time features"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
msgstr ""
-msgid "RefSwitcher|Branches"
-msgstr "ブランãƒ"
+msgid "Readme"
+msgstr "Readme"
-msgid "RefSwitcher|Tags"
-msgstr "ã‚¿ã‚°"
+msgid "Real-time features"
+msgstr "リアルタイム機能"
msgid "Reference:"
+msgstr "å‚ç…§:"
+
+msgid "Refresh"
msgstr ""
msgid "Register / Sign In"
+msgstr "登録 / サインイン"
+
+msgid "Register and see your runners for this group."
msgstr ""
-msgid "Registry"
+msgid "Register and see your runners for this project."
msgstr ""
+msgid "Registry"
+msgstr "レジストリ"
+
msgid "Related Commits"
msgstr "関連ã™ã‚‹ã‚³ãƒŸãƒƒãƒˆ"
@@ -3511,15 +5275,21 @@ msgid "Related Merged Requests"
msgstr "関連ã™ã‚‹ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
msgid "Related merge requests"
-msgstr ""
+msgstr "関連ã™ã‚‹ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
msgid "Remind later"
msgstr "後ã§é€šçŸ¥"
msgid "Remove"
-msgstr ""
+msgstr "削除"
+
+msgid "Remove Runner"
+msgstr "Runner を削除"
msgid "Remove avatar"
+msgstr "ã‚¢ãƒã‚¿ãƒ¼ã‚’削除"
+
+msgid "Remove priority"
msgstr ""
msgid "Remove project"
@@ -3528,10 +5298,19 @@ msgstr "プロジェクトを削除"
msgid "Repair authentication"
msgstr ""
-msgid "Repo by URL"
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
msgstr ""
+msgid "Repo by URL"
+msgstr "リãƒã‚¸ãƒˆãƒª URL"
+
msgid "Repository"
+msgstr "リãƒã‚¸ãƒˆãƒª"
+
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
msgstr ""
msgid "Repository has no locks."
@@ -3540,30 +5319,57 @@ msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
-msgstr ""
+msgid "Repository mirror"
+msgstr "リãƒã‚¸ãƒˆãƒªãƒŸãƒ©ãƒ¼"
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr "アクセス権é™ã‚’リクエストã™ã‚‹"
-msgid "Reset git storage health information"
+msgid "Requests Profiles"
msgstr ""
-msgid "Reset health check access token"
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Reset git storage health information"
+msgstr "git ストレージã®æ­£å¸¸æ€§æƒ…報をリセット"
+
+msgid "Reset health check access token"
+msgstr "正常性ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’リセット"
+
msgid "Reset runners registration token"
+msgstr "Runner 登録トークンをリセット"
+
+msgid "Resolve all discussions in new issue"
msgstr ""
+msgid "Resolve conflicts on source branch"
+msgstr "ソースブランãƒã§ã®ç«¶åˆã‚’解決ã™ã‚‹"
+
msgid "Resolve discussion"
+msgstr "検討を解決"
+
+msgid "Response metrics (Custom)"
msgstr ""
-msgid "Response"
+msgid "Resume"
+msgstr "å†é–‹"
+
+msgid "Retry"
msgstr ""
+msgid "Retry this job"
+msgstr "ジョブをå†è©¦è¡Œã—ã¦ãã ã•ã„"
+
+msgid "Retry verification"
+msgstr "検証をå†è©¦è¡Œã—ã¦ãã ã•ã„"
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] ""
@@ -3574,6 +5380,9 @@ msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã‚’リãƒãƒ¼ãƒˆ"
msgid "Revert this merge request"
msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’リãƒãƒ¼ãƒˆ"
+msgid "Review"
+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 ""
@@ -3583,18 +5392,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
-msgid "Roadmap"
+msgid "Revoke"
msgstr ""
+msgid "Roadmap"
+msgstr "ロードマップ"
+
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr "SAML SSO"
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3605,46 +5432,79 @@ msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from you
msgstr ""
msgid "SSH Keys"
+msgstr "SSH éµ"
+
+msgid "SSL Verification"
msgstr ""
-msgid "Save changes"
+msgid "Save"
msgstr ""
+msgid "Save application"
+msgstr ""
+
+msgid "Save changes"
+msgstr "変更をä¿å­˜"
+
msgid "Save pipeline schedule"
msgstr "パイプラインスケジュールをä¿å­˜"
msgid "Save variables"
-msgstr ""
+msgstr "変数をä¿å­˜ã™ã‚‹"
msgid "Schedule a new pipeline"
msgstr "æ–°ã—ã„パイプラインã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’作æˆ"
msgid "Scheduled"
-msgstr ""
+msgstr "スケジュール済"
msgid "Schedules"
-msgstr ""
+msgstr "スケジュール"
msgid "Scheduling Pipelines"
msgstr "パイプラインスケジューリング"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
-msgid "Search"
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
msgstr ""
+msgid "Scroll to top"
+msgstr ""
+
+msgid "Search"
+msgstr "検索"
+
+msgid "Search branches"
+msgstr "ブランãƒã‚’検索"
+
msgid "Search branches and tags"
msgstr "ブランãƒã¾ãŸã¯ã‚¿ã‚°ã‚’検索"
-msgid "Search milestones"
+msgid "Search files"
+msgstr "ファイルã®æ¤œç´¢"
+
+msgid "Search for projects, issues, etc."
msgstr ""
+msgid "Search merge requests"
+msgstr "マージリクエストを検索"
+
+msgid "Search milestones"
+msgstr "マイルストーンを検索"
+
msgid "Search project"
-msgstr ""
+msgstr "プロジェクトを検索"
msgid "Search users"
-msgstr ""
+msgstr "ユーザーを検索"
msgid "Seconds before reseting failure information"
msgstr ""
@@ -3652,15 +5512,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
+msgstr "セキュリティレãƒãƒ¼ãƒˆ"
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
msgstr ""
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr "é¸æŠž"
+
msgid "Select Archive Format"
msgstr "アーカイブã®ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆã‚’é¸æŠž"
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr "タイムゾーンをé¸æŠž"
@@ -3668,31 +5543,52 @@ msgid "Select an existing Kubernetes cluster or create a new one"
msgstr ""
msgid "Select assignee"
-msgstr ""
+msgstr "担当者をé¸æŠž"
msgid "Select branch/tag"
+msgstr "ブランãƒãƒ»ã‚¿ã‚°é¸æŠž"
+
+msgid "Select project"
+msgstr "プロジェクトã®é¸æŠž"
+
+msgid "Select project and zone to choose machine type"
msgstr ""
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr "ソースブランãƒã‚’é¸æŠž"
+
msgid "Select target branch"
msgstr "ターゲットブランãƒã‚’é¸æŠž"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
msgid "Send email"
-msgstr ""
+msgstr "メールをé€ä¿¡"
msgid "Sep"
-msgstr ""
+msgstr "9月"
msgid "September"
-msgstr ""
+msgstr "9月"
msgid "Server version"
+msgstr "サーãƒãƒ¼ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³"
+
+msgid "Service Desk"
msgstr ""
msgid "Service Templates"
-msgstr ""
+msgstr "サービス テンプレート"
msgid "Service URL"
msgstr ""
@@ -3716,7 +5612,7 @@ msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authe
msgstr ""
msgid "Set up CI/CD"
-msgstr ""
+msgstr "CI/CD を設定"
msgid "Set up Koding"
msgstr "Koding を設定"
@@ -3728,14 +5624,20 @@ msgid "SetPasswordToCloneLink|set a password"
msgstr "パスワードを設定"
msgid "Settings"
-msgstr ""
+msgstr "設定"
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr "共有"
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr "共有 Runner"
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3745,45 +5647,81 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
+msgstr "コマンドを表示"
+
+msgid "Show complete raw log"
msgstr ""
-msgid "Show parent pages"
+msgid "Show latest version"
msgstr ""
+msgid "Show latest version of the diff"
+msgstr ""
+
+msgid "Show parent pages"
+msgstr "親ページを表示"
+
msgid "Show parent subgroups"
+msgstr "親ã®ã‚µãƒ–グループを表示"
+
+msgid "Show whitespace changes"
msgstr ""
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "%d ã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’表示中"
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr "æ•°å­—ã®ã¿ä½¿ç”¨ã§ãã¾ã™"
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr "シングルサインオンã§ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹"
+
+msgid "Sign out"
+msgstr "サインアウト"
+
msgid "Sign-in restrictions"
msgstr ""
msgid "Sign-up restrictions"
-msgstr ""
+msgstr "サインアップã®åˆ¶é™"
msgid "Size and domain settings for static websites"
-msgstr ""
+msgstr "é™çš„ãªã‚¦ã‚§ãƒ–サイトã®ã‚µã‚¤ã‚ºã¨ãƒ‰ãƒ¡ã‚¤ãƒ³ã®è¨­å®š"
msgid "Slack application"
+msgstr "Slack アプリケーション"
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
msgid "Snippets"
-msgstr ""
+msgstr "スニペット"
msgid "Something went wrong on our end"
msgstr ""
@@ -3791,13 +5729,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
+msgstr "ボタンã®åˆ‡ã‚Šæ›¿ãˆä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3806,56 +5750,65 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr "ã“ã®æ¤œè¨Žã‚’解決ã—ã¦ã„ã‚‹ã¨ãã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
msgid "SortOptions|Access level, ascending"
-msgstr ""
+msgstr "アクセスレベル昇順"
msgid "SortOptions|Access level, descending"
-msgstr ""
+msgstr "アクセスレベルé™é †"
msgid "SortOptions|Created date"
-msgstr ""
+msgstr "作æˆæ—¥é †"
msgid "SortOptions|Due date"
-msgstr ""
+msgstr "期é™"
msgid "SortOptions|Due later"
-msgstr ""
+msgstr "期é™ã®é…ã„é †"
msgid "SortOptions|Due soon"
-msgstr ""
+msgstr "期é™ã®è¿‘ã„é †"
msgid "SortOptions|Label priority"
-msgstr ""
+msgstr "ラベルã®å„ªå…ˆåº¦é †"
msgid "SortOptions|Largest group"
-msgstr ""
+msgstr "グループã®å¤§ãã„é †"
msgid "SortOptions|Largest repository"
msgstr ""
msgid "SortOptions|Last created"
-msgstr ""
+msgstr "最新作æˆé †"
msgid "SortOptions|Last joined"
-msgstr ""
+msgstr "æ–°ã—ãå‚加ã—ãŸé †"
msgid "SortOptions|Last updated"
msgstr ""
msgid "SortOptions|Least popular"
-msgstr ""
+msgstr "人気順"
msgid "SortOptions|Less weight"
msgstr ""
msgid "SortOptions|Milestone"
-msgstr ""
+msgstr "マイルストーン順"
msgid "SortOptions|Milestone due later"
msgstr ""
@@ -3867,49 +5820,49 @@ msgid "SortOptions|More weight"
msgstr ""
msgid "SortOptions|Most popular"
-msgstr ""
+msgstr "人気順"
msgid "SortOptions|Name"
-msgstr ""
+msgstr "åå‰"
msgid "SortOptions|Name, ascending"
-msgstr ""
+msgstr "åå‰ã€æ˜‡é †"
msgid "SortOptions|Name, descending"
-msgstr ""
+msgstr "åå‰ã€é™é †"
msgid "SortOptions|Oldest created"
-msgstr ""
+msgstr "å¤ã„é †"
msgid "SortOptions|Oldest joined"
-msgstr ""
+msgstr "å‚加ã—ãŸé †"
msgid "SortOptions|Oldest sign in"
-msgstr ""
+msgstr "サインイン順"
msgid "SortOptions|Oldest updated"
-msgstr ""
+msgstr "å¤ã„æ›´æ–°é †"
msgid "SortOptions|Popularity"
-msgstr ""
+msgstr "人気順"
msgid "SortOptions|Priority"
msgstr ""
msgid "SortOptions|Recent sign in"
-msgstr ""
+msgstr "最近ã®ã‚µã‚¤ãƒ³ã‚¤ãƒ³é †"
msgid "SortOptions|Start later"
-msgstr ""
+msgstr "å¤ã„é †"
msgid "SortOptions|Start soon"
-msgstr ""
+msgstr "æ–°ã—ã„é †"
msgid "SortOptions|Weight"
msgstr ""
msgid "Source"
-msgstr ""
+msgstr "ソース"
msgid "Source (branch or tag)"
msgstr ""
@@ -3918,25 +5871,52 @@ msgid "Source code"
msgstr "ソースコード"
msgid "Source is not available"
-msgstr ""
+msgstr "ソースã¯åˆ©ç”¨ã§ãã¾ã›ã‚“"
msgid "Spam Logs"
-msgstr ""
+msgstr "スパム ログ"
msgid "Spam and Anti-bot Protection"
+msgstr "スパムã¨ã‚¢ãƒ³ãƒãƒœãƒƒãƒˆä¿è­·"
+
+msgid "Specific Runners"
msgstr ""
msgid "Specify the following URL during the Runner setup:"
+msgstr "Runner セットアップã®éš›ã«æ¬¡ã® URL を指定ã—ã¦ãã ã•ã„:"
+
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr "ステージ"
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr "ã™ã¹ã¦ã®å¤‰æ›´ã‚’ステージã™ã‚‹"
+
+msgid "Stage changes"
+msgstr "変更をステージã™ã‚‹"
+
+msgid "Staged"
+msgstr "ステージ済"
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
msgstr ""
msgid "StarProject|Star"
msgstr "スターを付ã‘ã‚‹"
msgid "Starred Projects"
-msgstr ""
+msgstr "スター付ãプロジェクト"
msgid "Starred Projects' Activity"
-msgstr ""
+msgstr "スター付ãプロジェクトã®æ´»å‹•"
msgid "Starred projects"
msgstr ""
@@ -3945,45 +5925,81 @@ msgid "Start a %{new_merge_request} with these changes"
msgstr "ã“ã®å¤‰æ›´ã§ %{new_merge_request} を作æˆã™ã‚‹"
msgid "Start the Runner!"
-msgstr ""
+msgstr "Runner ã‚’èµ·å‹•!"
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
-msgid "Stopped"
+msgid "Stop impersonation"
msgstr ""
+msgid "Stop this environment"
+msgstr "ã“ã®ç’°å¢ƒã‚’åœæ­¢ã™ã‚‹"
+
+msgid "Stopped"
+msgstr "åœæ­¢ä¸­"
+
msgid "Storage"
+msgstr "ストレージ"
+
+msgid "Storage:"
msgstr ""
msgid "Subgroups"
+msgstr "サブグループ"
+
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
msgstr ""
msgid "Switch branch/tag"
msgstr "ブランãƒãƒ»ã‚¿ã‚°åˆ‡ã‚Šæ›¿ãˆ"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
+msgstr "システムフック"
+
+msgid "System Info"
msgstr ""
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
-msgstr[0] ""
+msgstr[0] "タグ(%{tag_count})"
msgid "Tags"
msgstr "ã‚¿ã‚°"
+msgid "Tags:"
+msgstr "ã‚¿ã‚°:"
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -3994,31 +6010,31 @@ msgid "TagsPage|Can't find HEAD commit for this tag"
msgstr ""
msgid "TagsPage|Cancel"
-msgstr ""
+msgstr "キャンセル"
msgid "TagsPage|Create tag"
-msgstr ""
+msgstr "タグ作æˆ"
msgid "TagsPage|Delete tag"
-msgstr ""
+msgstr "タグ削除"
msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
msgstr ""
msgid "TagsPage|Edit release notes"
-msgstr ""
+msgstr "リリースノートを編集"
msgid "TagsPage|Existing branch name, tag, or commit SHA"
-msgstr ""
+msgstr "æ—¢ã«å­˜åœ¨ã™ã‚‹ãƒ–ランãƒãƒ»ã‚¿ã‚°ãƒ»ã‚³ãƒŸãƒƒãƒˆãƒãƒƒã‚·ãƒ¥ã§ã™"
msgid "TagsPage|Filter by tag name"
-msgstr ""
+msgstr "ã‚¿ã‚°åã§çµžã‚Šè¾¼ã¿"
msgid "TagsPage|New Tag"
-msgstr ""
+msgstr "æ–°ã—ã„ã‚¿ã‚°"
msgid "TagsPage|New tag"
-msgstr ""
+msgstr "æ–°ã—ã„ã‚¿ã‚°"
msgid "TagsPage|Optionally, add a message to the tag."
msgstr ""
@@ -4036,18 +6052,18 @@ msgid "TagsPage|Sort by"
msgstr ""
msgid "TagsPage|Tags"
-msgstr ""
+msgstr "タグ一覧"
msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
msgstr ""
msgid "TagsPage|This tag has no release notes."
-msgstr ""
+msgstr "ã“ã®ã‚¿ã‚°ã«ã¯ãƒªãƒªãƒ¼ã‚¹ãƒŽãƒ¼ãƒˆãŒã‚ã‚Šã¾ã›ã‚“。"
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4057,9 +6073,18 @@ msgid "Target Branch"
msgstr "ターゲットブランãƒ"
msgid "Target branch"
-msgstr ""
+msgstr "ターゲットブランãƒ"
msgid "Team"
+msgstr "ãƒãƒ¼ãƒ "
+
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
msgstr ""
msgid "Thanks! Don't show me this again"
@@ -4069,10 +6094,10 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa
msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
-msgstr ""
+msgstr "課題トラッカーã¯ã€ãƒ—ロジェクトを改善ã—ãŸã‚Šè§£æ±ºã—ãŸã‚Šã™ã‚‹ãŸã‚ã«å¿…è¦ãªæƒ…報を追加ã™ã‚‹å ´æ‰€ã§ã™"
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
-msgstr ""
+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 ""
@@ -4099,14 +6124,17 @@ msgid "The maximum file size allowed is 200KB."
msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
-msgstr ""
+msgstr "GitLab ã«ã‚ˆã‚‹ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹è©¦è¡Œå›žæ•°"
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "開発ライフサイクルã®æ®µéšŽ"
@@ -4125,11 +6153,14 @@ msgstr "プロジェクトã¯ã€ãƒ­ã‚°ã‚¤ãƒ³ãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã‚ã‚Œã°èª°ã§ã‚‚ã‚¢
msgid "The project can be accessed without any authentication."
msgstr "プロジェクトã¯ã€ãƒ­ã‚°ã‚¤ãƒ³ãªã—ã«èª°ã§ã‚‚アクセスã§ãã¾ã™ã€‚"
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
-msgstr "ã“ã®ãƒ—ロジェクトã«ãƒ¬ãƒã‚¸ãƒˆãƒªãƒ¼ã¯ã‚ã‚Šã¾ã›ã‚“。"
+msgstr "ã“ã®ãƒ—ロジェクトã«ãƒªãƒã‚¸ãƒˆãƒªã¯ã‚ã‚Šã¾ã›ã‚“。"
msgid "The repository for this project is empty"
-msgstr ""
+msgstr "ã“ã®ãƒ—ロジェクトã«ãƒªãƒã‚¸ãƒˆãƒªã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
@@ -4140,6 +6171,9 @@ msgstr "レビューステージã¨ã¯ã€ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’作æˆã—ã¦
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "ステージングステージã§ã¯ã€ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒãƒžãƒ¼ã‚¸ã•ã‚Œã¦ã‹ã‚‰ã‚³ãƒ¼ãƒ‰ãŒãƒ—ロダクション環境ã«ãƒ‡ãƒ—ロイã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã“ã®ãƒ‡ãƒ¼ã‚¿ã¯æœ€åˆã«ãƒ—ロダクションã«ãƒ‡ãƒ—ロイã—ãŸã¨ãã«è‡ªå‹•çš„ã«è¿½åŠ ã•ã‚Œã¾ã™ã€‚"
@@ -4152,49 +6186,82 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 "得られãŸä¸€é€£ã®ãƒ‡ãƒ¼ã‚¿ã‚’å°ã•ã„é †ã«ä¸¦ã¹ãŸã¨ãã«ä¸­å¤®ã«ä½ç½®ã™ã‚‹å€¤ã€‚例ãˆã°ã€3, 5, 9ã®ä¸­å¤®å€¤ã¯5。3, 5, 7, 8ã®ä¸­å¤®å€¤ã¯ (5+7)/2 = 6。"
+msgstr "得られãŸä¸€é€£ã®ãƒ‡ãƒ¼ã‚¿ã‚’å°ã•ã„é †ã«ä¸¦ã¹ãŸã¨ãã«ä¸­å¤®ã«ä½ç½®ã™ã‚‹å€¤ã€‚例ãˆã°ã€3, 5, 9 ã®ä¸­å¤®å€¤ã¯ 5。3, 5, 7, 8 ã®ä¸­å¤®å€¤ã¯ (5+7)/2 = 6。"
msgid "There are no issues to show"
+msgstr "表示ã™ã‚‹èª²é¡ŒãŒã‚ã‚Šã¾ã›ã‚“"
+
+msgid "There are no labels yet"
msgstr ""
msgid "There are no merge requests to show"
-msgstr ""
+msgstr "表示ã™ã‚‹ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã‚Šã¾ã›ã‚“"
msgid "There are problems accessing Git storage: "
-msgstr ""
+msgstr "Git ストレージã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã«å•é¡ŒãŒã‚ã‚Šã¾ã™: "
+
+msgid "There was an error loading users activity calendar."
+msgstr "ユーザーã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティカレンダーã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "There was an error saving your notification settings."
+msgstr "通知設定をä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "There was an error subscribing to this label."
+msgstr "ラベルã®è³¼èª­ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "There was an error when reseting email token."
+msgstr "メールトークンã®ãƒªã‚»ãƒƒãƒˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "There was an error when subscribing to this label."
+msgstr "ラベルã®è³¼èª­ã™ã‚‹ã¨ãã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-msgid "There was an error loading results"
+msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "There was an error loading users activity calendar."
+msgid "They can be managed using the %{link}."
msgstr ""
-msgid "There was an error saving your notification settings."
+msgid "Third party offers"
msgstr ""
-msgid "There was an error subscribing to this label."
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
msgstr ""
-msgid "There was an error when reseting email token."
+msgid "This application was created by %{link_to_owner}."
msgstr ""
-msgid "There was an error when subscribing to this label."
+msgid "This application will be able to:"
msgstr ""
-msgid "There was an error when unsubscribing from this label."
+msgid "This board's scope is reduced"
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
+msgstr "ディレクトリ"
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
msgstr ""
msgid "This is a confidential issue."
@@ -4210,7 +6277,7 @@ msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
-msgstr ""
+msgstr "ã“ã®èª²é¡Œã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
msgstr ""
@@ -4218,31 +6285,55 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr "ã“ã®ã‚¸ãƒ§ãƒ–ã«ã¯ãƒˆãƒ¬ãƒ¼ã‚¹ãŒã‚ã‚Šã¾ã›ã‚“。"
+
+msgid "This job has been canceled"
+msgstr "ã“ã®ã‚¸ãƒ§ãƒ–ã¯ã‚­ãƒ£ãƒ³ã‚»ãƒ«ã•ã‚Œã¾ã—ãŸ"
+
+msgid "This job has been skipped"
+msgstr "ã“ã®ã‚¸ãƒ§ãƒ–ã¯ã‚¹ã‚­ãƒƒãƒ—ã•ã‚Œã¾ã—ãŸ"
+
msgid "This job has not been triggered yet"
-msgstr ""
+msgstr "ã“ã®ã‚¸ãƒ§ãƒ–ã¯ã¾ã å®Ÿè¡Œã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "This job has not started yet"
-msgstr ""
+msgstr "ジョブã¯ã¾ã é–‹å§‹ã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
msgid "This job requires a manual action"
-msgstr ""
+msgstr "ã“ã®ã‚¸ãƒ§ãƒ–ã¯æ‰‹å‹•ã«ã‚ˆã‚‹å®Ÿè¡Œã‚’求ã‚ã¦ã„ã¾ã™"
msgid "This means you can not push code until you create an empty repository or import existing one."
-msgstr "空レãƒã‚¸ãƒˆãƒªãƒ¼ã‚’作æˆã¾ãŸã¯æ—¢å­˜ãƒ¬ãƒã‚¸ãƒˆãƒªãƒ¼ã‚’インãƒãƒ¼ãƒˆã‚’ã—ãªã‘ã‚Œã°ã€ã‚³ãƒ¼ãƒ‰ã®ãƒ—ッシュã¯ã§ãã¾ã›ã‚“。"
+msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgid "This option is disabled while you still have unstaged changes"
msgstr ""
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr "複数ã®ãƒ—ロジェクト間ã§èª­ã¿è¾¼ã¿ãŒè¨±å¯ã•ã‚Œã¦ã„ãªã„ãŸã‚ã€ã“ã®ãƒšãƒ¼ã‚¸ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。"
+
+msgid "This page will be removed in a future release."
+msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã¯ã€å°†æ¥ã®ãƒªãƒªãƒ¼ã‚¹ã§å‰Šé™¤ã•ã‚Œã‚‹äºˆå®šã§ã™ã€‚"
+
msgid "This project"
+msgstr "プロジェクト"
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
msgid "This repository"
+msgstr "リãƒã‚¸ãƒˆãƒª"
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
msgstr ""
msgid "This will delete the custom metric, Are you sure?"
@@ -4260,12 +6351,15 @@ msgstr "課題ã®å®Ÿè£…ãŒé–‹å§‹ã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“"
msgid "Time between merge request creation and merge/close"
msgstr "マージリクエストãŒä½œæˆã•ã‚Œã¦ã‹ã‚‰ãƒžãƒ¼ã‚¸ã¾ãŸã¯ã‚¯ãƒ­ãƒ¼ã‚ºã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“"
-msgid "Time between updates and capacity settings."
-msgstr ""
-
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
+msgid "Time remaining"
+msgstr "残り時間"
+
+msgid "Time spent"
+msgstr "経éŽæ™‚é–“"
+
msgid "Time tracking"
msgstr ""
@@ -4287,6 +6381,9 @@ msgstr "%sæ—¥å‰"
msgid "Timeago|%s days remaining"
msgstr "残り %s日間"
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr "残り %s時間"
@@ -4302,8 +6399,11 @@ msgstr "%sヶ月å‰"
msgid "Timeago|%s months remaining"
msgstr "残り %sヶ月"
+msgid "Timeago|%s seconds ago"
+msgstr "%s秒å‰"
+
msgid "Timeago|%s seconds remaining"
-msgstr "残り %s 秒"
+msgstr "残り%s秒"
msgid "Timeago|%s weeks ago"
msgstr "%s週間å‰"
@@ -4317,48 +6417,45 @@ msgstr "%så¹´å‰"
msgid "Timeago|%s years remaining"
msgstr "残り %s年間"
+msgid "Timeago|1 day ago"
+msgstr "1æ—¥å‰"
+
msgid "Timeago|1 day remaining"
msgstr "残り 1日間"
+msgid "Timeago|1 hour ago"
+msgstr "1時間å‰"
+
msgid "Timeago|1 hour remaining"
msgstr "残り 1時間"
+msgid "Timeago|1 minute ago"
+msgstr "1分å‰"
+
msgid "Timeago|1 minute remaining"
msgstr "残り 1分間"
+msgid "Timeago|1 month ago"
+msgstr "1ヶ月å‰"
+
msgid "Timeago|1 month remaining"
msgstr "残り 1ヶ月"
+msgid "Timeago|1 week ago"
+msgstr "1週間å‰"
+
msgid "Timeago|1 week remaining"
msgstr "残り 1週間"
+msgid "Timeago|1 year ago"
+msgstr "1å¹´å‰"
+
msgid "Timeago|1 year remaining"
msgstr "残り 1年間"
msgid "Timeago|Past due"
msgstr "期é™ã‚ªãƒ¼ãƒãƒ¼"
-msgid "Timeago|a day ago"
-msgstr "1æ—¥å‰"
-
-msgid "Timeago|a month ago"
-msgstr "1ヶ月å‰"
-
-msgid "Timeago|a week ago"
-msgstr "1週間å‰"
-
-msgid "Timeago|a year ago"
-msgstr "1å¹´å‰"
-
-msgid "Timeago|about %s hours ago"
-msgstr "ç´„%s時間å‰"
-
-msgid "Timeago|about a minute ago"
-msgstr "ç´„1分間å‰"
-
-msgid "Timeago|about an hour ago"
-msgstr "ç´„1時間å‰"
-
msgid "Timeago|in %s days"
msgstr "%s日間以内"
@@ -4398,11 +6495,14 @@ msgstr "1週間以内"
msgid "Timeago|in 1 year"
msgstr "1年以内"
-msgid "Timeago|in a while"
-msgstr ""
+msgid "Timeago|just now"
+msgstr "ãŸã£ãŸä»Š"
+
+msgid "Timeago|right now"
+msgstr "今"
-msgid "Timeago|less than a minute ago"
-msgstr "1分未満"
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4416,12 +6516,15 @@ msgid "Time|s"
msgstr "秒"
msgid "Tip:"
-msgstr ""
+msgstr "ヒント:"
msgid "Title"
-msgstr ""
+msgstr "タイトル"
msgid "To GitLab"
+msgstr "GitLab ã¸"
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
msgstr ""
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
@@ -4433,6 +6536,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4442,19 +6551,43 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
+msgstr "Todo"
+
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr "検討ã®è¡¨ç¤ºãƒ»éžè¡¨ç¤ºã‚’切り替ãˆã‚‹"
+
+msgid "Toggle navigation"
msgstr ""
msgid "Toggle sidebar"
@@ -4466,6 +6599,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr "åˆè¨ˆæ™‚é–“"
@@ -4473,7 +6612,7 @@ msgid "Total test time for all commits/merges"
msgstr "ã™ã¹ã¦ã®ã‚³ãƒŸãƒƒãƒˆ/マージã®åˆè¨ˆãƒ†ã‚¹ãƒˆæ™‚é–“"
msgid "Total: %{total}"
-msgstr ""
+msgstr "åˆè¨ˆ: %{total}"
msgid "Track activity with Contribution Analytics."
msgstr ""
@@ -4484,30 +6623,81 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr "å†è©¦è¡Œ"
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
msgid "Unlock"
-msgstr ""
+msgstr "アンロック"
msgid "Unlocked"
msgstr ""
msgid "Unresolve discussion"
+msgstr "検討を未解決ã«ã™ã‚‹"
+
+msgid "Unstage all changes"
+msgstr "ã™ã¹ã¦ã®å¤‰æ›´ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã‚’å–り消ã™"
+
+msgid "Unstage changes"
+msgstr "ステージã—ãŸå¤‰æ›´ã‚’å–り消ã™"
+
+msgid "Unstaged"
+msgstr "ステージをå–り消ã—"
+
+msgid "Unstaged %{type}"
+msgstr "ステージをå–り消㗠%{type}"
+
+msgid "Unstaged and staged %{type}"
msgstr ""
msgid "Unstar"
msgstr "スターを外ã™"
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
+msgstr "最新"
+
+msgid "Update"
msgstr ""
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr "グループåã€èª¬æ˜Žã€ã‚¢ãƒã‚¿ãƒ¼ã€ãã®ä»–ã®ä¸€èˆ¬è¨­å®šã‚’æ›´æ–°ã—ã¾ã™ã€‚"
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4523,6 +6713,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr "æ–°è¦ãƒ•ã‚¡ã‚¤ãƒ«ã‚’アップロード"
@@ -4530,69 +6723,111 @@ msgid "Upload file"
msgstr "ファイルをアップロード"
msgid "Upload new avatar"
-msgstr ""
+msgstr "æ–°ã—ã„ã‚¢ãƒã‚¿ãƒ¼ã‚’アップロード"
msgid "UploadLink|click to upload"
msgstr "クリックã—ã¦ã‚¢ãƒƒãƒ—ロード"
msgid "Upvotes"
-msgstr ""
+msgstr "ã„ã„ã­"
msgid "Usage statistics"
+msgstr "使用状æ³ã®çµ±è¨ˆ"
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
-msgid "Use the following registration token during setup:"
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
msgstr ""
+msgid "Use the following registration token during setup:"
+msgstr "セットアップã®éš›ã«æ¬¡ã®ç™»éŒ²ãƒˆãƒ¼ã‚¯ãƒ³ã‚’使用ã—ã¦ãã ã•ã„:"
+
msgid "Use your global notification setting"
msgstr "全体通知設定を利用"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
+msgstr "ユーザーã¨IPレートã®åˆ¶é™"
+
+msgid "User map"
msgstr ""
+msgid "Users"
+msgstr "ユーザー"
+
+msgid "Variables"
+msgstr "変数"
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
msgid "Various container registry settings."
-msgstr ""
+msgstr "å„種コンテナレジストリã®è¨­å®šã€‚"
msgid "Various email settings."
-msgstr ""
+msgstr "å„種メール設定。"
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
msgstr ""
+msgid "Verified"
+msgstr "検証済ã¿"
+
msgid "View epics list"
msgstr ""
msgid "View file @ "
-msgstr ""
+msgstr "ファイルを表示 @ "
msgid "View group labels"
+msgstr "グループラベルを表示"
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
msgstr ""
msgid "View labels"
+msgstr "ラベルを表示"
+
+msgid "View log"
msgstr ""
msgid "View open merge request"
msgstr "オープンãªãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’表示"
msgid "View project labels"
-msgstr ""
+msgstr "プロジェクトラベルを表示"
msgid "View replaced file @ "
msgstr ""
msgid "Visibility and access controls"
+msgstr "å¯è¦–性ã¨ã‚¢ã‚¯ã‚»ã‚¹åˆ¶å¾¡"
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
msgstr ""
msgid "VisibilityLevel|Internal"
@@ -4610,7 +6845,7 @@ msgstr "ä¸æ˜Ž"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "ã“ã®ãƒ‡ãƒ¼ã‚¿ã‚’å‚ç…§ã—ãŸã„ã§ã™ã‹ï¼Ÿã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã«ã¯ç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。"
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4620,7 +6855,7 @@ msgid "We want to be sure it is you, please confirm you are not a robot."
msgstr ""
msgid "Web IDE"
-msgstr ""
+msgstr "Web IDE"
msgid "Web terminal"
msgstr ""
@@ -4628,23 +6863,35 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
msgstr ""
-msgid "Wiki"
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr "Runner ãŒãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã‚‹å ´åˆã€ä»–ã®ãƒ—ロジェクトã«å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“"
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
msgstr ""
-msgid "WikiClone|Clone your wiki"
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
+msgid "Wiki"
+msgstr "Wiki"
+
+msgid "WikiClone|Clone your wiki"
+msgstr "Wiki をクローン"
+
msgid "WikiClone|Git Access"
-msgstr ""
+msgstr "Git アクセス"
msgid "WikiClone|Install Gollum"
-msgstr ""
+msgstr "Gollum をインストール"
msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
msgstr ""
@@ -4658,89 +6905,116 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
msgstr ""
-msgid "WikiHistoricalPage|This is an old version of this page."
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
msgstr ""
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
+
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã®å¤ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒè¡¨ç¤ºã•ã‚Œã¦ã„ã¾ã™ã€‚"
+
msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
msgstr ""
msgid "WikiHistoricalPage|history"
-msgstr ""
+msgstr "履歴"
msgid "WikiHistoricalPage|most recent version"
-msgstr ""
+msgstr "最新ãƒãƒ¼ã‚¸ãƒ§ãƒ³"
msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
-msgstr ""
+msgstr "ãã®ä»–ã®ä¾‹ã«ã¤ã„ã¦ã¯ã€%{docs_link} ã‚’å‚ç…§"
msgid "WikiMarkdownDocs|documentation"
-msgstr ""
+msgstr "ドキュメント"
msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
-msgstr ""
+msgstr "ページ(ã¾ãŸã¯æ–°è¦ãƒšãƒ¼ã‚¸) ã«ãƒªãƒ³ã‚¯ã™ã‚‹ã«ã¯ã€%{link_example} ã¨å…¥åŠ›ã™ã‚‹ã ã‘ã§ã™"
msgid "WikiNewPagePlaceholder|how-to-setup"
-msgstr ""
+msgstr "セットアップ方法"
msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
msgstr ""
msgid "WikiNewPageTitle|New Wiki Page"
-msgstr ""
+msgstr "æ–°ã—ã„ Wiki ページ"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
-msgstr ""
+msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã‚’削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr "ページã®å‰Šé™¤"
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr "%{pageTitle} ページを削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "WikiPageConflictMessage|the page"
-msgstr ""
+msgstr "ページ"
msgid "WikiPageCreate|Create %{page_title}"
-msgstr ""
+msgstr "%{page_title} を作æˆ"
msgid "WikiPageEdit|Update %{page_title}"
-msgstr ""
+msgstr "%{page_title} ã‚’æ›´æ–°"
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
-msgstr ""
+msgstr "ページを作æˆ"
msgid "Wiki|Create page"
-msgstr ""
+msgstr "ページを作æˆ"
msgid "Wiki|Edit Page"
-msgstr ""
-
-msgid "Wiki|Empty page"
-msgstr ""
+msgstr "ページを編集"
msgid "Wiki|More Pages"
msgstr ""
msgid "Wiki|New page"
-msgstr ""
+msgstr "æ–°ã—ã„ページ"
msgid "Wiki|Page history"
-msgstr ""
+msgstr "ページã®å±¥æ­´"
msgid "Wiki|Page version"
-msgstr ""
+msgstr "ページã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³"
msgid "Wiki|Pages"
-msgstr ""
+msgstr "Pages"
msgid "Wiki|Wiki Pages"
-msgstr ""
+msgstr "Wikiページ"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
msgstr ""
@@ -4748,7 +7022,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "アクセスリクエストをå–り消ã™"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4766,15 +7049,24 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
msgstr ""
-msgid "You can also create a project from the command line."
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
+msgid "You can also create a project from the command line."
+msgstr "コマンドラインã‹ã‚‰ãƒ—ロジェクトを作æˆã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
+
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4785,6 +7077,9 @@ msgid "You can only add files when you are on a branch"
msgstr "ファイルを追加ã™ã‚‹ã«ã¯ã€ã©ã“ã‹ã®ãƒ–ランãƒã«ã„ãªã‘ã‚Œã°ã„ã‘ã¾ã›ã‚“"
msgid "You can only edit files when you are on a branch"
+msgstr "ファイルを編集ã™ã‚‹ã«ã¯ã€ã©ã“ã‹ã®ãƒ–ランãƒã«ã„ãªã‘ã‚Œã°ã„ã‘ã¾ã›ã‚“"
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
@@ -4793,16 +7088,31 @@ msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr "割り当ã¦ã‚‰ã‚Œã¦ã„るマージリクエストã¯ã‚ã‚Šã¾ã›ã‚“"
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "プロジェクト数ã®ä¸Šé™ã«é”ã—ã¦ã„ã¾ã™"
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4811,6 +7121,9 @@ msgstr "プロジェクトã«ã‚¹ã‚¿ãƒ¼ã‚’ã¤ã‘ãŸã„å ´åˆã¯ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "権é™ãŒå¿…è¦ã§ã™"
@@ -4833,30 +7146,45 @@ msgid "You won't be able to pull or push project code via %{protocol} until you
msgstr "%{set_password_link} ã§ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ‘スワードãŒã‚»ãƒƒãƒˆã•ã‚Œã¦ã„ãªã„ã®ã§ã€ãƒ—ロジェクト㫠%{protocol} ã§ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’プッシュã€ãƒ—ルã§ãã¾ã›ã‚“"
msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
-msgstr "%{add_ssh_key_link} をプロファイルã«è¿½åŠ ã—ã¦ã„ãªã„ã®ã§ã€ãƒ—ロジェクトã«ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’プッシュã€ãƒ—ルã§ãã¾ã›ã‚“"
+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 need to use different branch names to get a valid comparison."
+msgstr "有効ãªæ¯”較を行ã†ãŸã‚ã«ã¯ã€ç•°ãªã‚‹ãƒ–ランãƒåを使用ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
msgstr ""
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr "ã“ã®ãƒ¡ãƒ¼ãƒ«ã¯ %{host} ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆå®›ã«é€ä¿¡ã•ã‚Œã¾ã—ãŸã€‚ %{manage_notifications_link}&middot;%{help_link}"
+
+msgid "YouTube"
msgstr ""
msgid "Your Groups"
-msgstr ""
+msgstr "所属グループ"
msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
msgstr ""
msgid "Your Projects (default)"
-msgstr ""
+msgstr "プロジェクト(デフォルト)"
msgid "Your Projects' Activity"
-msgstr ""
+msgstr "プロジェクトã®æ´»å‹•"
msgid "Your Todos"
+msgstr "ã‚ãªãŸã®Todo"
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
msgstr ""
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
@@ -4866,72 +7194,168 @@ msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
msgstr ""
msgid "Your comment will not be visible to the public."
-msgstr ""
+msgstr "ã“ã®ã‚³ãƒ¡ãƒ³ãƒˆã¯ä¸€èˆ¬ã«ã¯è¡¨ç¤ºã•ã‚Œã¾ã›ã‚“。"
msgid "Your groups"
-msgstr ""
+msgstr "所属グループ"
msgid "Your name"
msgstr "åå‰"
msgid "Your projects"
-msgstr ""
+msgstr "ã‚ãªãŸã®ãƒ—ロジェクト"
+
+msgid "ago"
+msgstr "å‰"
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgid "assign yourself"
-msgstr ""
+msgstr "自分ã«å‰²ã‚Šå½“ã¦"
msgid "branch name"
-msgstr ""
+msgstr "ブランãƒå"
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr "クラス"
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr "説明"
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr "ファイル"
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
+msgstr "インスタンス"
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr "リンク"
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4941,19 +7365,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST detected no security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4962,28 +7383,73 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
msgid "connecting"
-msgstr ""
+msgstr "接続中"
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "æ—¥"
+msgid "deploy token"
+msgstr "デプロイトークン"
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -4995,17 +7461,29 @@ msgstr[0] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr "無効"
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr "ã“ã®ãƒ—ロジェクトã§ã¯"
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
-msgstr ""
+msgid "importing"
+msgstr "インãƒãƒ¼ãƒˆä¸­"
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -5016,15 +7494,18 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
msgid "merge request"
msgid_plural "merge requests"
-msgstr[0] ""
+msgstr[0] "マージリクエスト"
msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
-msgstr ""
+msgstr "ブランãƒã‚’復元ã™ã‚‹ã‹ã€åˆ¥ã® %{missingBranchName} ブランãƒã‚’使用ã—ã¦ãã ã•ã„"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
msgstr ""
@@ -5038,32 +7519,26 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
msgid "mrWidget|Cancel automatic merge"
-msgstr ""
+msgstr "自動マージã®ã‚­ãƒ£ãƒ³ã‚»ãƒ«"
msgid "mrWidget|Check out branch"
-msgstr ""
+msgstr "ブランãƒã®ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆ"
msgid "mrWidget|Checking ability to merge automatically"
msgstr ""
@@ -5083,6 +7558,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr "ã‚ã¨ã§è§£æ±ºã™ã‚‹ãŸã‚ã«èª²é¡Œã‚’作æˆã™ã‚‹"
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5096,7 +7574,7 @@ msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
-msgstr ""
+msgstr "%{branch} ブランãƒãŒãƒ­ãƒ¼ã‚«ãƒ«ãƒªãƒã‚¸ãƒˆãƒªã«å­˜åœ¨ã™ã‚‹å ´åˆã¯ã€ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’手動ã§ãƒžãƒ¼ã‚¸ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
msgstr ""
@@ -5105,10 +7583,10 @@ msgid "mrWidget|Loading deployment statistics"
msgstr ""
msgid "mrWidget|Mentions"
-msgstr ""
+msgstr "メンション"
msgid "mrWidget|Merge"
-msgstr ""
+msgstr "マージ"
msgid "mrWidget|Merge failed."
msgstr ""
@@ -5116,44 +7594,59 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
+msgstr "マージ作業者"
+
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
msgstr ""
msgid "mrWidget|Plain diff"
msgstr ""
msgid "mrWidget|Refresh"
-msgstr ""
+msgstr "æ›´æ–°"
msgid "mrWidget|Refresh now"
-msgstr ""
+msgstr "ã™ãã«æ›´æ–°"
msgid "mrWidget|Refreshing now"
-msgstr ""
+msgstr "更新中"
msgid "mrWidget|Remove Source Branch"
msgstr ""
msgid "mrWidget|Remove source branch"
-msgstr ""
+msgstr "ソースブランãƒã‚’削除ã™ã‚‹"
msgid "mrWidget|Remove your approval"
msgstr ""
msgid "mrWidget|Request to merge"
-msgstr ""
+msgstr "マージをリクエスト"
msgid "mrWidget|Resolve conflicts"
-msgstr ""
+msgstr "競åˆã‚’解決ã™ã‚‹"
msgid "mrWidget|Revert"
-msgstr ""
+msgstr "リãƒãƒ¼ãƒˆ"
msgid "mrWidget|Revert this merge request in a new merge request"
-msgstr ""
+msgstr "æ–°ã—ã„マージリクエストã§ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’リãƒãƒ¼ãƒˆã™ã‚‹"
msgid "mrWidget|Set by"
-msgstr ""
+msgstr "設定者"
msgid "mrWidget|The changes were merged into"
msgstr ""
@@ -5165,31 +7658,31 @@ msgid "mrWidget|The changes will be merged into"
msgstr ""
msgid "mrWidget|The source branch has been removed"
-msgstr ""
+msgstr "ã“ã®ã‚½ãƒ¼ã‚¹ãƒ–ランãƒã¯å‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
msgid "mrWidget|The source branch is being removed"
-msgstr ""
+msgstr "ã“ã®ã‚½ãƒ¼ã‚¹ãƒ–ランãƒã¯å‰Šé™¤ã•ã‚Œã‚ˆã†ã¨ã—ã¦ã„ã¾ã™"
msgid "mrWidget|The source branch will be removed"
-msgstr ""
+msgstr "ã“ã®ã‚½ãƒ¼ã‚¹ãƒ–ランãƒã¯å‰Šé™¤ã•ã‚Œã¾ã™"
msgid "mrWidget|The source branch will not be removed"
-msgstr ""
+msgstr "ã“ã®ã‚½ãƒ¼ã‚¹ãƒ–ランãƒã¯å‰Šé™¤ã•ã‚Œã¾ã›ã‚“"
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr "未解決ã®æ¤œè¨ŽãŒã‚ã‚Šã¾ã™ã€‚ã“れらã®æ¤œè¨Žã‚’解決ã—ã¦ãã ã•ã„。"
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
msgid "mrWidget|This merge request is in the process of being merged"
-msgstr ""
+msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯ãƒžãƒ¼ã‚¸å®Ÿè¡Œä¸­ã§ã™"
msgid "mrWidget|This project is archived, write access has been disabled"
-msgstr ""
-
-msgid "mrWidget|Web IDE"
-msgstr ""
+msgstr "ã“ã®ãƒ—ロジェクトã¯ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã•ã‚Œã¦ã„ã‚‹ãŸã‚ã€æ›¸ãè¾¼ã¿ã¯ç„¡åŠ¹ã§ã™ã€‚"
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5198,16 +7691,16 @@ msgid "mrWidget|You can remove source branch now"
msgstr ""
msgid "mrWidget|branch does not exist."
-msgstr ""
+msgstr "ブランãƒãŒå­˜åœ¨ã—ã¾ã›ã‚“。"
msgid "mrWidget|command line"
-msgstr ""
+msgstr "コマンド ライン"
msgid "mrWidget|into"
msgstr ""
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
-msgstr ""
+msgstr "パイプラインãŒæˆåŠŸã—ãŸã¨ãã¯è‡ªå‹•çš„ã«ãƒžãƒ¼ã‚¸ã•ã‚Œã¾ã™"
msgid "new merge request"
msgstr "æ–°è¦ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
@@ -5216,42 +7709,58 @@ msgid "notification emails"
msgstr "メール通知"
msgid "or"
-msgstr ""
+msgstr "ã¾ãŸã¯"
msgid "parent"
msgid_plural "parents"
msgstr[0] "親"
msgid "password"
-msgstr ""
+msgstr "パスワード"
msgid "personal access token"
-msgstr ""
+msgstr "個人ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³"
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr "残り"
+
msgid "remove due date"
msgstr ""
-msgid "source"
+msgid "remove weight"
msgstr ""
+msgid "source"
+msgstr "ソース"
+
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
-msgid "this document"
+msgid "started"
msgstr ""
+msgid "this document"
+msgstr "ã“ã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ"
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
-msgstr ""
+msgstr "ユーザーå"
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+
diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po
index 91f68dfdee1..36a0d3179b2 100644
--- a/locale/ko/gitlab.po
+++ b/locale/ko/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:34-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Korean\n"
"Language: ko_KR\n"
@@ -15,9 +13,22 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ko\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 12:38\n"
msgid " and"
-msgstr ""
+msgstr " 그리고"
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] " %d í¬ì¸íŠ¸ 저하"
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] " %d í¬ì¸íŠ¸ í–¥ìƒ"
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] "ë³€ê²½ëœ íŒŒì¼ %dê°œ"
msgid "%d commit"
msgid_plural "%d commits"
@@ -25,50 +36,81 @@ msgstr[0] "%d 커밋"
msgid "%d commit behind"
msgid_plural "%d commits behind"
-msgstr[0] ""
+msgstr[0] "%d 커밋 behind"
msgid "%d exporter"
msgid_plural "%d exporters"
-msgstr[0] ""
+msgstr[0] "%d 내보내기"
msgid "%d issue"
msgid_plural "%d issues"
-msgstr[0] ""
+msgstr[0] "%d ì´ìŠˆ"
msgid "%d layer"
msgid_plural "%d layers"
-msgstr[0] ""
+msgstr[0] "%d ë ˆì´ì–´"
msgid "%d merge request"
msgid_plural "%d merge requests"
-msgstr[0] ""
+msgstr[0] "머지 리퀘스트(MR) %d개"
msgid "%d metric"
msgid_plural "%d metrics"
-msgstr[0] ""
+msgstr[0] "%d 측정치"
+
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] "%dê°œì˜ìƒˆ ë¼ì´ì„ ìŠ¤"
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] "%dê°œì˜ ìŠ¤í…Œì´ì§•ëœ 변경사항"
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] "%dê°œì˜ ìŠ¤í…Œì´ì§•ë˜ì§€ ì•Šì€ ë³€ê²½ì‚¬í•­"
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] "ì·¨ì•½ì  %dê°œ"
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] "%s 추가 ì»¤ë°‹ì€ ì„±ëŠ¥ ì´ìŠˆë¥¼ 방지하기 위해 ìƒëžµë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "%{actionText} & %{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr "%{commit_timeago} ì— %{commit_author_link} ë‹˜ì´ ì»¤ë°‹í•˜ì˜€ìŠµë‹ˆë‹¤."
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
-msgstr[0] ""
+msgstr[0] "%{count} ëª…ì˜ ì°¸ì—¬ìž"
+
+msgid "%{filePath} deleted"
+msgstr "%{filePath} ì‚­ì œë¨"
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr "%{group_docs_link_start}그룹%{group_docs_link_end}ì„ ì‚¬ìš©í•˜ë©´ 여러 프로ì íŠ¸ë¥¼ 관리하고 ê³µë™ ìž‘ì—…ì„ ìˆ˜í–‰ í•  수 있습니다. 그룹 회ì›ì€ 모든 프로ì íŠ¸ì— 액세스 í•  수 있습니다."
msgid "%{loadingIcon} Started"
-msgstr ""
+msgstr "%{loadingIcon} 시작ë¨"
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
-msgstr ""
+msgstr "%{lock_path} 경로는 GitLab User %{lock_user_id} ì— ì˜í•´ 잠겼습니다."
+
+msgid "%{name}'s avatar"
+msgstr "%{name} ì˜ ì•„ë°”íƒ€"
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr "%{nip_domain} ì€ ì»¤ìŠ¤í…€ ë„ë©”ì¸ ëŒ€ì‹  사용할 수 있습니다."
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
-msgstr ""
+msgstr "%{default_branch} 로부터 %{number_commits_behind} commits behind, %{number_commits_ahead} commits ahead"
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "%{number_of_failures} / %{maximum_failures} 실패. GitLab ì€ ë‹¤ìŒ ì‹œë„ì—ì„œ 성공하면 ì ‘ê·¼ì„ í—ˆìš©í•  것입니다."
@@ -77,23 +119,71 @@ msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not re
msgstr "%{number_of_failures} / %{maximum_failures} 실패. GitLab ì€ ìžë™ìœ¼ë¡œ 다시 ì‹œë„하지 않습니다. 문제가 í•´ê²°ë˜ë©´ 저장 공간 정보를 초기화 해주세요. "
msgid "%{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{openOrClose} %{noteable}"
+
+msgid "%{percent}%% complete"
+msgstr "%{percent}%% 완료"
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
+msgstr[0] "%{storage_name}: í˜¸ìŠ¤íŠ¸ì˜ storage ì ‘ê·¼ì— ì‹¤íŒ¨ %{failed_attempts} 회"
+
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
msgstr[0] ""
msgid "%{text} is available"
-msgstr ""
+msgstr "%{text} 사용 가능"
+
+msgid "%{title} changes"
+msgstr "%{title} 변경"
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] "%{type} ì—ì„œ ì·¨ì•½ì  %{vulnerabilityCount} 개를 찾았습니다"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "설치 ë°©ë²•ì— ëŒ€í•œ 정보를 얻기 위해 %{link} 를 ì²´í¬ì•„웃하세요."
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr "%{unstaged} ê±´ì´ ìŠ¤í…Œì´ì§•ë˜ì§€ 않았고, %{staged} ê±´ì´ ìŠ¤í…Œì´ì§• ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "+ %{moreCount} more"
-msgstr ""
+msgstr "+ %{moreCount} ë”"
+
+msgid "- Runner is active and can process any new jobs"
+msgstr "- Runner ê°€ 활성화ë˜ì—ˆê³ , 새로운 ìž‘ì—…ì„ ì²˜ë¦¬í•  수 있습니다."
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr "- Runner ê°€ ì¼ì‹œì •ì§€ ë˜ì–´ 새로운 ìž‘ì—…ì„ ë°›ì„ ìˆ˜ 없습니다."
msgid "- show less"
-msgstr ""
+msgstr "-ëœ ë³´ê¸°"
+
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] "%{count} %{type} ê°œì˜ ì¶”ê°€ ì •ë³´"
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] "%{count} ê°œ %{type} ì˜ ìˆ˜ì •ì‚¬í•­"
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] "%d ê°œì˜ ì´ìŠˆ closed"
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] "%dê±´ì˜ ë¨¸ì§€ë¦¬í€˜ìŠ¤íŠ¸ 닫힘"
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] "%dê±´ì˜ ë¨¸ì§€ë¦¬í€˜ìŠ¤íŠ¸ 머지ë¨"
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] "%dê°œì˜ ì˜¤í”ˆëœ ì´ìŠˆ"
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] "%dê°œì˜ ì˜¤í”ˆëœ ë¨¸ì§€ 리퀘스트(MR)"
msgid "1 pipeline"
msgid_plural "%d pipelines"
@@ -103,11 +193,53 @@ msgid "1st contribution!"
msgstr ""
msgid "2FA enabled"
+msgstr "2FA 사용"
+
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr "403|ê¶Œí•œì„ ì–»ìœ¼ë ¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì‹­ì‹œì˜¤."
+
+msgid "403|You don't have the permission to access this page."
+msgstr "403|ì´ íŽ˜ì´ì§€ì— 대한 액세스 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr "404|주소가 정확하고 페ì´ì§€ê°€ ì´ë™í•˜ì§€ 않았는지 확ì¸í•˜ì‹­ì‹œì˜¤."
+
+msgid "404|Page Not Found"
+msgstr "404|페ì´ì§€ë¥¼ ì°¾ì„ ìˆ˜ 없습니다"
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr "404|ì´ê²ƒì´ ì‹¤ìˆ˜ì— ì˜í•œ 것ì´ë¼ê³  ìƒê°í•œë‹¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
msgstr ""
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr "'러너(Runner)'는 ìž‘ì—…ì„ ì‹¤í–‰í•˜ëŠ” 프로세스입니다. 필요한 ë§Œí¼ ëŸ¬ë„ˆë¥¼ ì…‹ì—…í•  수 있습니다."
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "지ì†ì ì¸ í†µí•©ì— ê´€í•œ 그래프 모ìŒ"
@@ -117,33 +249,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr "ìžë™ ë°°í¬ ì •ë³´"
-msgid "Abuse Reports"
+msgid "About this feature"
msgstr ""
+msgid "Abuse Reports"
+msgstr "오남용 리í¬íŠ¸"
+
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr "약관ë™ì˜"
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
+msgstr "액세스 토í°"
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
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 "Account"
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
msgstr ""
-msgid "Account and limit settings"
+msgid "Account"
+msgstr "계정"
+
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "활성"
+msgid "Active Sessions"
+msgstr "í™œì„±í™”ëœ ì„¸ì…˜"
+
msgid "Activity"
msgstr "활ë™"
@@ -168,12 +330,39 @@ msgstr "ë¼ì´ì„ ìŠ¤ 추가"
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "새 디렉토리 추가"
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -190,7 +379,7 @@ msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs
msgstr ""
msgid "AdminHealthPageLink|health page"
-msgstr ""
+msgstr "ìƒíƒœ 페ì´ì§€"
msgid "AdminProjects|Delete"
msgstr ""
@@ -226,10 +415,10 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
msgid "Advanced"
-msgstr ""
+msgstr "고급"
msgid "Advanced settings"
-msgstr ""
+msgstr "고급 설정"
msgid "All"
msgstr "ì „ì²´"
@@ -240,7 +429,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr "ëŒ€ìƒ ë¸Œëžœì¹˜ì— ë¨¸ì§€í•  수 있는 ë©¤ë²„ì˜ ì»¤ë°‹ì„ í—ˆìš©í•©ë‹ˆë‹¤."
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -264,6 +456,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -279,8 +513,11 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr "Alertì„ í•´ì œí•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 페ì´ì§€ë¥¼ 새로 고친 후 다시 ì‹œë„하십시오."
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
-msgstr ""
+msgstr "기능 ê°•ì¡° 표시를 해제하는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 페ì´ì§€ë¥¼ 새로고침하고, 다시 ì‹œë„해주세요."
msgid "An error occurred while fetching markdown preview"
msgstr ""
@@ -294,13 +531,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -325,7 +562,7 @@ msgid "An error occurred while rendering preview broadcast message"
msgstr ""
msgid "An error occurred while retrieving calendar activity"
-msgstr ""
+msgstr "ìº˜ë¦°ë” í™œë™ì„ 가져오는 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while retrieving diff"
msgstr ""
@@ -336,10 +573,25 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
+msgstr "오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
+
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
msgstr ""
msgid "Any Label"
@@ -348,21 +600,39 @@ msgstr ""
msgid "Appearance"
msgstr ""
-msgid "Applications"
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
msgstr ""
+msgid "Applications"
+msgstr "어플리케ì´ì…˜"
+
msgid "Apr"
msgstr ""
msgid "April"
msgstr ""
-msgid "Archived project! Repository is read-only"
-msgstr "프로ì íŠ¸ê°€ ë³´ê´€ë˜ì—ˆìŠµë‹ˆë‹¤! 저장소는 ì½ê¸°ë§Œ 가능합니다."
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ì´ íŒŒì´í”„ë¼ì¸ ìŠ¤ì¼€ì¥´ì„ ì‚­ì œ 하시겠습니까?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "ë“±ë¡ í† í°ì„ 초기화 하시겠습니까?"
@@ -378,6 +648,12 @@ msgstr "확실합니까?"
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr "그룹 관리ìžì—게 그룹 Runner 를 설정하ë„ë¡ ìš”ì²­í•˜ì„¸ìš”"
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -402,12 +678,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr "담당ìž"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "드래그 &amp; 드롭 ë˜ëŠ” %{upload_link}"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -417,41 +708,68 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
msgid "Auto DevOps, runners and job artifacts"
-msgstr ""
+msgstr "ìžë™ DevOps, Runner ìž‘ì—… artifacts"
msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
-msgstr ""
+msgstr "Auto Review Apps 와 Auto Deploy ê°€ ì •ìƒ ë™ìž‘하기 위해서는 %{kubernetes} ê°€ 필요합니다."
msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
-msgstr ""
+msgstr "Auto Review Apps 와 Auto Deploy ê°€ ì •ìƒ ë™ìž‘하기 위해서는 ë„ë©”ì¸ ë„¤ìž„ê³¼ %{kubernetes} ê°€ 필요합니다."
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
-msgstr ""
+msgstr "Auto Review Apps 와 Auto Deploy ê°€ ì •ìƒ ë™ìž‘하기 위해서는 ë„ë©”ì¸ ë„¤ìž„ì´ í•„ìš”í•©ë‹ˆë‹¤."
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
msgstr ""
+msgid "AutoDevOps|Auto DevOps"
+msgstr "Auto DevOps|Auto DevOps"
+
msgid "AutoDevOps|Auto DevOps documentation"
-msgstr ""
+msgstr "Auto DevOps 문서"
msgid "AutoDevOps|Enable in settings"
-msgstr ""
+msgstr "활성화하기"
msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
-msgstr ""
+msgstr "애플리케ì´ì…˜ì˜ 빌드, 테스트, ë°°í¬ê°€ ì‚¬ì „ì— ì •ì˜ëœ CI/CD ì„¤ì •ì— ë”°ë¼ ìžë™ìœ¼ë¡œ 수행ë©ë‹ˆë‹¤."
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
-msgstr ""
+msgstr "ë” ì•Œì•„ë³´ê¸° %{link_to_documentation}"
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
@@ -459,12 +777,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
-msgstr ""
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr "AutoDevOps | Auto DevOps 활성화"
msgid "Available"
msgstr ""
+msgid "Available group Runners : %{runners}"
+msgstr "사용 가능한 그룹 Runner: %{runners}"
+
+msgid "Available group Runners : %{runners}."
+msgstr "사용 가능한 그룹 Runner: %{runners}."
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -474,12 +798,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -498,6 +903,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -522,6 +930,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -534,6 +951,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -610,7 +1039,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -650,22 +1079,22 @@ msgid "Branches|The branch could not be updated automatically because it has div
msgstr ""
msgid "Branches|The default branch cannot be deleted"
-msgstr ""
+msgstr "Default 브랜치는 삭제할 수 없습니다."
msgid "Branches|This branch hasn’t been merged into %{default_branch}."
-msgstr ""
+msgstr "ì´ ë¸Œëžœì¹˜ëŠ” %{default_branch} ë¡œ 병합ë˜ì§€ 않았습니다."
msgid "Branches|To avoid data loss, consider merging this branch before deleting it."
-msgstr ""
+msgstr "ë°ì´í„° ì†ì‹¤ì„ 방지 하려면 삭제하기 ì „ì— ì´ ë¸Œëžœì¹˜ë¥¼ 머지하는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤."
msgid "Branches|To confirm, type %{branch_name_confirmation}:"
-msgstr ""
+msgstr "확ì¸í•˜ë ¤ë©´, %{branch_name_confirmation} 를 ìž…ë ¥ 합니다."
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
msgstr ""
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
-msgstr ""
+msgstr "ë‹¹ì‹ ì€ ë³´í˜¸ëœ %{branch_name} 브랜치를 삭제하려고 합니다."
msgid "Branches|diverged from upstream"
msgstr ""
@@ -691,13 +1120,16 @@ msgstr "íŒŒì¼ ì°¾ì•„ë³´ê¸°"
msgid "Browse files"
msgstr "íŒŒì¼ ì°¾ì•„ë³´ê¸°"
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
msgstr "작성ìž"
msgid "CI / CD"
+msgstr "CI / CD"
+
+msgid "CI / CD Settings"
msgstr ""
msgid "CI/CD"
@@ -709,12 +1141,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr "CI/CD 설정"
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr "CICD|Continuous Integration ê³¼ Delivery 를 사용하기 위해서는 %{ci_file} ì´ ë°˜ë“œì‹œ 명시ë˜ì–´ì•¼ 합니다."
+
+msgid "CICD|Auto DevOps"
+msgstr "CICD|Auto DevOps"
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr "CICD|Auto DevOps는 ì‚¬ì „ì— ì •ì˜ëœ Continuous Integrationê³¼ Delivery ì„¤ì •ì„ ë°”íƒ•ìœ¼ë¡œí•˜ì—¬ ìžë™ìœ¼ë¡œ 빌드, 테스트 그리고 ë°°í¬ë¥¼ 수행합니다."
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr "CICD|Auto DevOps 비활성화"
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr "CICD|Auto DevOps 활성화"
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr "CICD|프로ì íŠ¸ì— %{ci_file} 파ì¼ì´ ì •ì˜ë˜ì–´ìžˆì§€ 않다면, Auto DevOps ê°€ 활성화 í˜¹ì€ ë¹„í™œì„±í™” ë  ìˆ˜ 있ë„ë¡ ì¸ìŠ¤í„´ìŠ¤ ê¸°ë³¸ê°’ì„ ë”°ë¥´ì„¸ìš”."
+
+msgid "CICD|Instance default (%{state})"
+msgstr "CICD|ì¸ìŠ¤í„´ìŠ¤ 기본값 (%{state})"
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr "CICD|Auto DevOpsì— ëŒ€í•´ ë” ì•Œì•„ë³´ê¸°"
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr "CICD|프로ì íŠ¸ ë‚´ì— %{ci_file} 파ì¼ì´ 존재하지 않으면, Auto DevOps 파ì´í”„ë¼ì¸ ì„¤ì •ì´ ì‚¬ìš©ë©ë‹ˆë‹¤."
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "취소"
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -743,19 +1235,19 @@ msgid "ChangeTypeAction|Revert"
msgstr "Revert"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
-msgstr ""
+msgstr "기존 변경 ì‚¬í•­ì„ ë˜ëŒë¦¬ê¸° 위해 새로운 ì»¤ë°‹ì„ ë§Œë“­ë‹ˆë‹¤."
msgid "Changelog"
msgstr "변경사항"
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
-msgstr ""
+msgstr "변경 ì‚¬í•­ì€ <b>source</b> ë¦¬ë¹„ì „ì´ <b>target</b> ë¦¬ë¹„ì „ì— ë¨¸ì§€ëœ ê²ƒì²˜ëŸ¼ 표시ë©ë‹ˆë‹¤."
msgid "Charts"
msgstr "차트"
msgid "Chat"
-msgstr ""
+msgstr "채팅"
msgid "Check interval"
msgstr ""
@@ -770,7 +1262,13 @@ msgid "Cherry-pick this commit"
msgstr "ì´ ì»¤ë°‹ì„ Cherry-pick"
msgid "Cherry-pick this merge request"
-msgstr "ì´ ë¨¸ì§€ 리퀘스트를 Cherry-pick"
+msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR)를 Cherry-pick"
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
msgid "Choose File ..."
msgstr ""
@@ -778,9 +1276,18 @@ 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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -848,50 +1355,71 @@ msgid "CiStatus|running"
msgstr "실행 중"
msgid "CiVariables|Input variable key"
-msgstr ""
+msgstr "입력 변수 키"
msgid "CiVariables|Input variable value"
-msgstr ""
+msgstr "입력 변수 값"
msgid "CiVariables|Remove variable row"
-msgstr ""
+msgstr "변수 행 제거"
msgid "CiVariable|* (All environments)"
-msgstr ""
+msgstr "* (모든 환경)"
msgid "CiVariable|All environments"
-msgstr ""
+msgstr "모든 환경"
msgid "CiVariable|Create wildcard"
msgstr ""
msgid "CiVariable|Error occured while saving variables"
-msgstr ""
+msgstr "CiVariable 변수를 저장 하는 중 오류가 ë°œìƒ í–ˆìŠµë‹ˆë‹¤."
msgid "CiVariable|New environment"
msgstr ""
msgid "CiVariable|Protected"
-msgstr ""
+msgstr "보호ë¨"
msgid "CiVariable|Search environments"
msgstr ""
msgid "CiVariable|Toggle protected"
-msgstr ""
+msgstr "Toggle 보호ë¨"
msgid "CiVariable|Validation failed"
-msgstr ""
+msgstr "유효성 검사 실패"
msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr "circuitbreaker api"
+
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr "검색 입력 지우기"
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
-msgid "Click to expand text"
+msgid "Click to expand it."
msgstr ""
+msgid "Click to expand text"
+msgstr "í…스트를 í´ë¦­í•˜ì—¬ 확장하세요."
+
msgid "Client authentication certificate"
msgstr ""
@@ -901,6 +1429,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -910,6 +1441,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -919,10 +1453,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -937,9 +1474,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -955,6 +1489,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -964,31 +1501,34 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr ""
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ì˜ 세부 ì •ë³´ ìž…ë ¥"
+
+msgid "ClusterIntegration|Environment scope"
+msgstr "환경 범위"
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching projects"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
-msgstr ""
+msgstr "ClusterIntegration|GitLab Runner"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -998,6 +1538,12 @@ msgid "ClusterIntegration|Google Kubernetes Engine project"
msgstr ""
msgid "ClusterIntegration|Helm Tiller"
+msgstr "Helm Tiller"
+
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
@@ -1022,34 +1568,40 @@ msgid "ClusterIntegration|Installing"
msgstr ""
msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° ìžë™í™” 통합"
msgid "ClusterIntegration|Integration status"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster"
+msgid "ClusterIntegration|Jupyter Hostname"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster details"
+msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°"
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° ìƒì„¸"
+
msgid "ClusterIntegration|Kubernetes cluster health"
msgstr ""
msgid "ClusterIntegration|Kubernetes cluster integration"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 통합"
msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
+msgstr "ì´ í”„ë¡œì íŠ¸ì— 대해 Kubernetes í´ëŸ¬ìŠ¤í„° í†µí•©ì´ ë¹„í™œì„±í™”ë˜ì–´ìžˆìŠµë‹ˆë‹¤."
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
+msgstr "ì´ í”„ë¡œì íŠ¸ì— 대해 Kubernetes í´ëŸ¬ìŠ¤í„° í†µí•©ì´ í™œì„±í™”ë˜ì–´ìžˆìŠµë‹ˆë‹¤."
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° í†µí•©ì´ í™œì„±í™”ë˜ì–´ìžˆìŠµë‹ˆë‹¤. ì´ í†µí•©ì„ ë¹„í™œì„±í™”í•´ë„ ê¸°ì¡´ Kubernetes í´ëŸ¬ìŠ¤í„°ì— ì˜í–¥ì„ 주지는 ì•Šê³  GitLabì˜ ì—°ê²°ì„ ì¼ì‹œì ìœ¼ë¡œë§Œ 해제합니다."
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ê°€ Google Kubernetes Engineì—ì„œ ìƒì„± 중입니다..."
msgid "ClusterIntegration|Kubernetes cluster name"
msgstr ""
@@ -1058,25 +1610,31 @@ msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google
msgstr ""
msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 사용하면 리뷰 ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ì‚¬ìš©í•˜ê³ , ì‘ìš© í”„ë¡œê·¸ëž¨ì„ ë°°í¬í•˜ê³ , 파ì´í”„ ë¼ì¸ì„ 실행하는 ë“±ì˜ ìž‘ì—…ì„ ì†ì‰½ê²Œ 수행 í•  수 있습니다. %{link_to_help_page}"
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ëŠ” 어플리케ì´ì…˜ì„ ë°°í¬í•˜ê³  ì´ í”„ë¡œì íŠ¸ì— 대한 리뷰 어플리케ì´ì…˜ì„ 제공하는 ë° ì‚¬ìš©í•  수 있습니다."
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
msgstr ""
-msgid "ClusterIntegration|Learn more about environments"
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
+msgid "ClusterIntegration|Learn more about environments"
+msgstr "í™˜ê²½ì— ëŒ€í•´ ìžì„¸ížˆ 알아보십시오."
+
msgid "ClusterIntegration|Learn more about security configuration"
msgstr ""
msgid "ClusterIntegration|Machine type"
-msgstr ""
+msgstr "머신 타입"
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 만들려면 %{link_to_requirements} ë§í¬ì—ì„œ ê³„ì •ì˜ í•„ìˆ˜ì‚¬í•­ì„ í™•ì¸í•˜ì„¸ìš”."
msgid "ClusterIntegration|Manage"
msgstr ""
@@ -1090,104 +1648,134 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|No machine types matched your search"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|No projects found"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgid "ClusterIntegration|No projects matched your search"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|No zones matched your search"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
+msgid "ClusterIntegration|Note:"
+msgstr "Note:"
+
+msgid "ClusterIntegration|Number of nodes"
+msgstr "노드 수"
+
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ì— 대한 액세스 정보를 입력하십시오. ë„ì›€ì´ í•„ìš”í•˜ì‹œë©´, Kubernetesì— ëŒ€í•œ %{link_to_help_page} 를 참고하세요."
+
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr "ë‹¹ì‹ ì˜ Google ê³„ì •ì´ ë‹¤ìŒì˜ 요구 ì‚¬í•­ì„ ì¶©ì¡±í•˜ëŠ”ì§€ í™•ì¸ í•˜ì„¸ìš”."
msgid "ClusterIntegration|Project namespace"
msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
-msgstr ""
+msgstr "프로ì íŠ¸ 네임 스페ì´ìŠ¤ (optional, unique)"
msgid "ClusterIntegration|Prometheus"
-msgstr ""
+msgstr "Prometheus"
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 통합 알아보기 %{link_to_help_page}"
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 제거"
msgid "ClusterIntegration|Remove integration"
-msgstr ""
+msgstr "통합 제거"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
-msgstr ""
+msgstr "ì´ í”„ë¡œì íŠ¸ì—ì„œ Kubernetes í´ëŸ¬ìŠ¤í„° êµ¬ì„±ì„ ì œê±°í•˜ì‹­ì‹œì˜¤. ì´ë ‡ê²Œí•´ë„ 실제 Kubernetes í´ëŸ¬ìŠ¤í„°ëŠ” ì‚­ì œë˜ì§€ 않습니다."
msgid "ClusterIntegration|Request to begin installing failed"
-msgstr ""
+msgstr "설치 시작 요청 실패"
msgid "ClusterIntegration|Save changes"
+msgstr "변경 ì‚¬í•­ì„ ì €ìž¥"
+
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
msgstr ""
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ì˜ 세부 ì •ë³´ 보기 ë° ìˆ˜ì •"
+
+msgid "ClusterIntegration|Select machine type"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select project"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select project and zone to choose machine type"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select project to choose zone"
msgstr ""
-msgid "ClusterIntegration|Service token"
+msgid "ClusterIntegration|Select zone"
msgstr ""
-msgid "ClusterIntegration|Show"
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
+msgid "ClusterIntegration|Service token"
+msgstr "서비스 토í°"
+
+msgid "ClusterIntegration|Show"
+msgstr "표시"
+
msgid "ClusterIntegration|Something went wrong on our end."
-msgstr ""
+msgstr "문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
-msgstr ""
+msgstr "구글 Kubernetes ì—”ì§„ì— Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 만드는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "ClusterIntegration|Something went wrong while installing %{title}"
-msgstr ""
+msgstr "설치하는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. %{title}"
msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
-msgstr ""
+msgstr "%{link_to_container_project} 프로ì íŠ¸ì— Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 만들기 위해서는 ì´ ê³„ì •ì— ì•„ëž˜ì— ëª…ì‹œëœ ê¶Œí•œì´ í•„ìš”í•©ë‹ˆë‹¤"
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 토글"
msgid "ClusterIntegration|Toggle Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 토글"
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr "ì´ í”„ë¡œì íŠ¸ì— ì—°ê²°ëœ Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 통해 어플리케ì´ì…˜ì„ 리뷰, ë°°í¬í•  수 있고, 파ì´í”„ë¼ì¸ì„ 실행할 수 있습니다. ê·¸ 외ì—ë„ ë§Žì€ ì¼ì„ 훨씬 쉬운 방법으로 수행할 수 있습니다."
+
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
msgid "ClusterIntegration|Zone"
-msgstr ""
+msgstr "Zone"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
-msgstr ""
+msgstr "구글 Kubernetes 컨테ì´ë„ˆ ì—”ì§„ì— ì—‘ì„¸ìŠ¤"
msgid "ClusterIntegration|check the pricing here"
msgstr ""
@@ -1196,24 +1784,33 @@ msgid "ClusterIntegration|documentation"
msgstr ""
msgid "ClusterIntegration|help page"
-msgstr ""
+msgstr "ë„ì›€ë§ íŽ˜ì´ì§€"
msgid "ClusterIntegration|installing applications"
msgstr ""
msgid "ClusterIntegration|meets the requirements"
-msgstr ""
+msgstr "요구 ì‚¬í•­ì„ ì¶©ì¡±"
msgid "ClusterIntegration|properly configured"
+msgstr "ì •ìƒì ìœ¼ë¡œ 구성ë˜ì—ˆìŒ"
+
+msgid "ClusterIntegration|sign up"
+msgstr "ClusterIntegration|가입"
+
+msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr "사ì´ë“œ ë°” 축소"
+
+msgid "Comment & resolve discussion"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1278,6 +1875,9 @@ msgstr ""
msgid "Committed by"
msgstr "커밋한 사용ìž"
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr "비êµ"
@@ -1326,6 +1926,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1348,59 +1951,74 @@ msgid "Connecting..."
msgstr ""
msgid "Container Registry"
-msgstr ""
+msgstr "컨테ì´ë„ˆ 레지스트리"
msgid "ContainerRegistry|Created"
-msgstr ""
+msgstr "만든"
msgid "ContainerRegistry|First log in to GitLab&rsquo;s Container Registry using your GitLab username and password. If you have %{link_2fa} you need to use a %{link_token}:"
-msgstr ""
+msgstr "GitLab ì‚¬ìš©ìž ì´ë¦„ ë° ì•”í˜¸ë¥¼ 사용 하여 GitLabì˜ ì»¨í…Œì´ë„ˆ ë ˆì§€ìŠ¤íŠ¸ë¦¬ì— ì²˜ìŒ ë¡œê·¸ì¸ í•©ë‹ˆë‹¤. %{link_2fa} 를 사용한다면 %{link_token} 를 사용해야 합니다."
msgid "ContainerRegistry|GitLab supports up to 3 levels of image names. The following examples of images are valid for your project:"
msgstr ""
msgid "ContainerRegistry|How to use the Container Registry"
-msgstr ""
+msgstr "컨테ì´ë„ˆ 레지스트리를 사용 하는 방법"
msgid "ContainerRegistry|Learn more about"
-msgstr ""
+msgstr "ë” ì•Œì•„ë³´ê¸°"
msgid "ContainerRegistry|No tags in Container Registry for this container image."
-msgstr ""
+msgstr "컨테ì´ë„ˆ ë ˆì§€ìŠ¤íŠ¸ë¦¬ì— ì´ ì»¨í…Œì´ë„ˆ ì´ë¯¸ì§€ì— 대한 태그가 존재하지 않습니다."
msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
-msgstr ""
+msgstr "로그ì¸í•˜ë©´, ì¼ë°˜ì ì¸ %{build} ë° %{push} ëª…ë ¹ì„ ì‚¬ìš©í•˜ì—¬ 컨테ì´ë„ˆ ì´ë¯¸ì§€ë¥¼ ìžìœ ë¡­ê²Œ ìƒì„±í•˜ê³  업로드할 수 있습니다."
msgid "ContainerRegistry|Remove repository"
-msgstr ""
+msgstr "저장소 제거"
msgid "ContainerRegistry|Remove tag"
-msgstr ""
+msgstr "태그 제거"
msgid "ContainerRegistry|Size"
-msgstr ""
+msgstr "í¬ê¸°"
msgid "ContainerRegistry|Tag"
-msgstr ""
+msgstr "태그"
msgid "ContainerRegistry|Tag ID"
-msgstr ""
+msgstr "태그 ID"
msgid "ContainerRegistry|Use different image names"
-msgstr ""
+msgstr "다른 ì´ë¯¸ì§€ ì´ë¦„ 사용"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
+msgstr "GitLabì— í†µí•© Docker 컨테ì´ë„ˆ 레지스트리를 ì´ìš©í•˜ë©´, 모든 프로ì íŠ¸ëŠ” ìžì‹ ì˜ Docker ì´ë¯¸ì§€ë¥¼ 저장할 수 있는 ê³µê°„ì„ ê°€ì§ˆ 수 있습니다."
+
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
msgstr ""
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr "ê¸°ì—¬ì— ëŒ€í•œ 안내"
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr "기여해 주신 분들"
@@ -1408,7 +2026,7 @@ msgid "ContributorsPage|%{startDate} – %{endDate}"
msgstr ""
msgid "ContributorsPage|Building repository graph."
-msgstr ""
+msgstr "저장소 그래프 작성"
msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
msgstr ""
@@ -1416,12 +2034,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1437,9 +2064,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr "ì»¤ë°‹ì˜ SHA를 í´ë¦½ë³´ë“œë¡œ 복사합니다"
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1452,12 +2091,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "%{protocol}ì„ (를) 통해 Pull 하거나 Push í•  ê°œì¸ ì•¡ì„¸ìŠ¤ 토í°ì„ 만드십시오."
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr "디렉토리 만들기"
@@ -1468,16 +2113,22 @@ msgid "Create epic"
msgstr ""
msgid "Create file"
+msgstr "íŒŒì¼ ë§Œë“¤ê¸°"
+
+msgid "Create group"
msgstr ""
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
msgid "Create merge request"
-msgstr "머지 리퀘스트 만들기"
+msgstr "머지 리퀘스트(MR) 만들기"
msgid "Create merge request and branch"
msgstr ""
@@ -1486,9 +2137,12 @@ msgid "Create new branch"
msgstr ""
msgid "Create new directory"
-msgstr ""
+msgstr "새 디렉토리 만들기"
msgid "Create new file"
+msgstr "새 íŒŒì¼ ë§Œë“¤ê¸°"
+
+msgid "Create new file or directory"
msgstr ""
msgid "Create new label"
@@ -1509,10 +2163,16 @@ msgstr "태그"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "ê°œì¸ ì•¡ì„¸ìŠ¤ í† í° ë§Œë“¤ê¸°"
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr "ìƒì„±ë¨"
+
+msgid "Created At"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1527,6 +2187,15 @@ msgstr "í¬ë¡  구문"
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr "CurrentUser|프로파ì¼"
+
+msgid "CurrentUser|Settings"
+msgstr "CurrentUser|설정"
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr "ì‚¬ìš©ìž ì •ì˜ ì•Œë¦¼ ì´ë²¤íŠ¸"
@@ -1536,9 +2205,15 @@ msgstr "ì‚¬ìš©ìž ì •ì˜ ì•Œë¦¼ ìˆ˜ì¤€ì€ ì°¸ì—¬ 수준과 ë™ì¼í•©ë‹ˆë‹¤. 맞ì
msgid "Customize colors"
msgstr ""
-msgid "Cycle Analytics"
+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 "Cycle Analytics"
+msgstr "사ì´í´ 분ì„"
+
msgid "CycleAnalyticsStage|Code"
msgstr "코드"
@@ -1560,11 +2235,14 @@ msgstr "스테ì´ì§•"
msgid "CycleAnalyticsStage|Test"
msgstr "테스트"
-msgid "DashboardProjects|All"
+msgid "Dashboard"
msgstr ""
+msgid "DashboardProjects|All"
+msgstr "모든"
+
msgid "DashboardProjects|Personal"
-msgstr ""
+msgstr "ê°œì¸"
msgid "Dec"
msgstr ""
@@ -1572,20 +2250,167 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr "취소 ë° ë¡œê·¸ì•„ì›ƒ"
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr "cron êµ¬ë¬¸ì„ ì‚¬ìš©í•˜ì—¬ ì‚¬ìš©ìž ì •ì˜ íŒ¨í„´ ì •ì˜"
msgid "Delete"
msgstr "삭제 "
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "ë°°í¬"
msgid "Deploy Keys"
+msgstr "ë°°í¬ í‚¤"
+
+msgid "DeployKeys|+%{count} others"
+msgstr "DeployKeys|+%{count} 기타"
+
+msgid "DeployKeys|Current project"
+msgstr "DeployKeys|현재 프로ì íŠ¸"
+
+msgid "DeployKeys|Deploy key"
+msgstr "DeployKeys|ë°°í¬ í‚¤"
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr "DeployKeys|í™œì„±í™”ëœ ë°°í¬ í‚¤"
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr "DeployKeys|ë°°í¬í‚¤ 사용 오류"
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr "DeployKeys|ë°°í¬í‚¤ 가져오기 오류"
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr "DeployKeys|ë°°í¬í‚¤ 제거 오류"
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr "DeployKeys|확장 %{count} 기타 프로ì íŠ¸"
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr "DeployKeys|ë°°í¬í‚¤ 로드"
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr "DeployKeys|ë°°í¬í‚¤ê°€ 존재하지 않습니다. ìœ„ì˜ ì–‘ì‹ì„ ì´ìš©í•´ 만드세요."
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr "DeployKeys|비공개ì ìœ¼ë¡œ 엑세스 가능한 ë°°í¬í‚¤"
+
+msgid "DeployKeys|Project usage"
+msgstr "DeployKeys|프로ì íŠ¸ 사용현황"
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr "DeployKeys|공개ì ìœ¼ë¡œ 엑세스 가능한 ë°°í¬í‚¤"
+
+msgid "DeployKeys|Read access only"
+msgstr "DeployKeys|ì½ê¸° 엑세스 ì „ìš©"
+
+msgid "DeployKeys|Write access allowed"
+msgstr "DeployKeys|쓰기 액세스 허용"
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr "DeployKeys|ì´ ë°°í¬í‚¤ë¥¼ 제거하려고 합니다. 확실합니까?"
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
msgstr ""
msgid "Description"
@@ -1594,33 +2419,63 @@ msgstr "설명"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr "ìƒì„¸"
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr "디렉토리 ì´ë¦„"
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr "ì´ í”„ë¡œì íŠ¸ì— 대해 사용 중지"
+
+msgid "Disable group Runners"
+msgstr "그룹 Runner 사용 중지"
+
+msgid "Discard changes"
+msgstr "변경 ì‚¬í•­ì„ ì·¨ì†Œ"
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
-msgid "Dismiss Cycle Analytics introduction box"
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
msgstr ""
+msgid "Dismiss Cycle Analytics introduction box"
+msgstr "사ì´í´ ë¶„ì„ ì†Œê°œ 박스 제거"
+
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr "ë„ë©”ì¸"
+
msgid "Don't show again"
msgstr "다시 표시하지 ì•ŠìŒ"
@@ -1663,16 +2518,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr "ê° Runner는 ë‹¤ìŒ ìƒíƒœ 중 í•˜ë‚˜ì¼ ìˆ˜ 있습니다."
+
msgid "Edit"
msgstr "편집"
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "파ì´í”„ë¼ì¸ 스케줄 편집 %{id}"
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1684,7 +2554,13 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
+msgstr "ì´ë©”ì¼"
+
+msgid "Embed"
msgstr ""
msgid "Enable"
@@ -1693,6 +2569,9 @@ msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1708,6 +2587,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr "ì´ í”„ë¡œì íŠ¸ì— 대해 사용"
+
+msgid "Enable group Runners"
+msgstr "그룹 Runner 사용"
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1720,15 +2611,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr "환경"
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1741,33 +2647,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1783,12 +2710,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1807,6 +2728,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1819,6 +2755,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr "예ìƒ"
+
msgid "EventFilterBy|Filter by all"
msgstr "모든 ê°’ì„ ê¸°ì¤€ìœ¼ë¡œ í•„í„°"
@@ -1846,14 +2785,35 @@ msgstr "매월 (1ì¼ ì˜¤ì „ 4ì‹œ)"
msgid "Every week (Sundays at 4:00am)"
msgstr "매주 (ì¼ìš”ì¼ ì˜¤ì „ 4ì‹œì—)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr "사ì´ë“œë°” 확장"
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
msgid "Explore public groups"
-msgstr ""
+msgstr "공개 그룹 íƒìƒ‰"
msgid "External Classification Policy Authorization"
msgstr ""
@@ -1876,6 +2836,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1885,6 +2848,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr "소유ìžë¥¼ 변경하지 못했습니다"
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1894,6 +2860,12 @@ msgstr "파ì´í”„ë¼ì¸ ìŠ¤ì¼€ì¤„ì„ ì œê±°í•˜ì§€ 못했습니다."
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1903,9 +2875,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr "파ì¼"
@@ -1915,6 +2884,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr "커밋 메시지로 필터"
@@ -1924,6 +2896,12 @@ msgstr "경로로 찾기"
msgid "Find file"
msgstr "íŒŒì¼ ì°¾ê¸°"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1933,12 +2911,39 @@ msgstr "처ìŒ"
msgid "FirstPushedBy|pushed by"
msgstr "푸시한 사용ìž"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "í¬í¬"
@@ -1947,7 +2952,7 @@ msgid "ForkedFromProjectPath|Forked from"
msgstr "í¬í¬í•œ 사용ìž"
msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
-msgstr ""
+msgstr "%{project_name} (ì‚­ì œ) ì—ì„œ í¬í¬ë¨"
msgid "Forking in progress"
msgstr ""
@@ -1955,19 +2960,40 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr "ì´ìŠˆ ìƒì„±ì—ì„œ 프로ë•ì…˜ ë°°í¬ê¹Œì§€"
msgid "From merge request merge until deploy to production"
-msgstr "머지 리퀘스트 머지ì—ì„œ 프로ë•ì…˜ í™˜ê²½ì— ë°°í¬ê¹Œì§€"
+msgstr "머지 리퀘스트(MR) 머지ì—ì„œ 프로ë•ì…˜ í™˜ê²½ì— ë°°í¬ê¹Œì§€"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 세부사항 ë³´ê¸°ì˜ ì• í”Œë¦¬ì¼€ì´ì…˜ 목ë¡ì—ì„œ Runner를 설치하십시오."
msgid "GPG Keys"
+msgstr "GPG 키"
+
+msgid "General"
+msgstr "ì¼ë°˜"
+
+msgid "General pipelines"
msgstr ""
msgid "Generate a default set of labels"
@@ -1988,7 +3014,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2003,31 +3032,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Last event ID processed by cursor"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2048,19 +3089,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2069,16 +3116,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2096,13 +3146,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2132,15 +3188,24 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
msgid "Git revision"
-msgstr ""
+msgstr "Git 리비전"
msgid "Git storage health information has been reset"
msgstr "git storage ìƒíƒœ ì •ë³´ê°€ 초기화ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2153,34 +3218,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "GitLab Runner 섹션"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
+msgstr "Gitaly 서버"
+
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
msgstr ""
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "ë‹¹ì‹ ì˜ í¬í¬ë¡œ ì´ë™í•˜ì„¸ìš”"
msgid "GoToYourFork|Fork"
msgstr "í¬í¬"
-msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgid "Google Code import"
msgstr ""
+msgid "Google Takeout"
+msgstr ""
+
+msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
+msgstr "Google ì¸ì¦ì„ 사용할 수 없습니다. %{link_to_documentation} GitLab 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
+
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr "그래프"
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr "그룹 CI/CD 설정"
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr "그룹 ID"
+
+msgid "Group Runners"
+msgstr "그룹 Runner"
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2192,42 +3323,90 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr ""
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
-msgstr ""
+msgstr "%{group} ë‚´ì˜ í”„ë¡œì íŠ¸ë¥¼ 다른 ê·¸ë£¹ì— ê³µìœ í•  수 없게 합니다."
msgid "GroupSettings|Share with group lock"
-msgstr ""
+msgstr "그룹 lock ì„ ê³µìœ í•©ë‹ˆë‹¤."
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
-msgstr ""
+msgstr "ì´ ì„¤ì •ì€ %{ancestor_group} ì— ì ìš©ë˜ê³  ì´ í•˜ìœ„ 그룹ì—ì„œ ìž¬ì •ì˜ ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}."
-msgstr ""
+msgstr "ì´ ì„¤ì •ì€ %{ancestor_group} ì— ì ìš© ë©ë‹ˆë‹¤. ì´ ê·¸ë£¹ì˜ í”„ë¡œì íŠ¸ë¥¼ 다른 그룹과 공유하려면, 소유ìžì—게 ì„¤ì •ì„ ìž¬ì •ì˜í•˜ê±°ë‚˜ %{remove_ancestor_share_with_group_lock} 하ë„ë¡ ìš”ì²­í•˜ì„¸ìš”."
msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}."
-msgstr ""
+msgstr "ì´ ì„¤ì •ì€ %{ancestor_group} ì— ì ìš©ë©ë‹ˆë‹¤. ì„¤ì •ì„ ìž¬ì •ì˜í•˜ê±°ë‚˜ %{remove_ancestor_share_with_group_lock} í•  수 있습니다."
msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually."
-msgstr ""
+msgstr "ì´ ì„¤ì •ì€ ê·¸ë£¹ 소유ìžì— ì˜í•´ 재정ì˜ë˜ì§€ ì•Šì€ ëª¨ë“  하위 ê·¸ë£¹ì— ì ìš©ë©ë‹ˆë‹¤. ì´ë¯¸ 프로ì íŠ¸ì— 대한 ì ‘ê·¼ ê¶Œí•œì„ ê°€ì§„ ê·¸ë£¹ë“¤ì€ ê¶Œí•œì´ ì§ì ‘ 제거ë˜ì§€ 않는 í•œ 접근할 수 있습니다."
msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group"
-msgstr ""
+msgstr "부모 ê·¸ë£¹ì˜ \"그룹 lock 공유\" ê°€ 사용 가능하ë„ë¡ ì„¤ì •ë˜ì–´ìžˆìœ¼ë©´, 부모 ê·¸ë£¹ì˜ ì†Œìœ ìžë¥¼ ì œì™¸í•˜ê³ ëˆ„êµ¬ë„ ì‚¬ìš©í•˜ì§€ ì•Šë„ë¡ ì„¤ì •í•  수 없습니다."
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
+msgstr "%{ancestor_group_name} ì—ì„œ 그룹 lock 공유 제거"
+
+msgid "Groups"
msgstr ""
-msgid "GroupsEmptyState|A group is a collection of several projects."
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
msgstr ""
-msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgid "GroupsDropdown|Frequently visited"
msgstr ""
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
+msgid "GroupsEmptyState|A group is a collection of several projects."
+msgstr "ê·¸ë£¹ì€ ì—¬ëŸ¬ 프로ì íŠ¸ì˜ 모ìŒìž…니다."
+
+msgid "GroupsEmptyState|If you organize your projects under a group, it works like a folder."
+msgstr "프로ì íŠ¸ë¥¼ ê·¸ë£¹ì— êµ¬ì„±í•˜ë©´, 마치 í´ë”처럼 ë™ìž‘합니다."
+
msgid "GroupsEmptyState|No groups found"
msgstr ""
@@ -2241,7 +3420,7 @@ msgid "GroupsTree|Create a subgroup in this group."
msgstr ""
msgid "GroupsTree|Edit group"
-msgstr ""
+msgstr "그룹 편집"
msgid "GroupsTree|Failed to leave the group. Please make sure you are not the only owner."
msgstr ""
@@ -2250,16 +3429,16 @@ msgid "GroupsTree|Filter by name..."
msgstr ""
msgid "GroupsTree|Leave this group"
-msgstr ""
+msgstr "ì´ ê·¸ë£¹ 떠나기"
msgid "GroupsTree|Loading groups"
-msgstr ""
+msgstr "그룹 로딩중입니다"
msgid "GroupsTree|Sorry, no groups matched your search"
-msgstr ""
+msgstr "죄송합니다, 검색과 ì¼ì¹˜í•˜ëŠ” ê·¸ë£¹ì´ ì—†ìŠµë‹ˆë‹¤"
msgid "GroupsTree|Sorry, no groups or projects matched your search"
-msgstr ""
+msgstr "죄송합니다, 검색과 ì¼ì¹˜í•˜ëŠ” 그룹 í˜¹ì€ í”„ë¡ì íŠ¸ê°€ 없습니다"
msgid "Have your users email"
msgstr ""
@@ -2298,15 +3477,54 @@ msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
-msgid "History"
+msgid "Hide whitespace changes"
msgstr ""
+msgid "History"
+msgstr "ì´ë ¥"
+
msgid "Housekeeping successfully started"
msgstr "Housekeepingì´ ì„±ê³µì ìœ¼ë¡œ 시작ë˜ì—ˆìŠµë‹ˆë‹¤"
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr "IDE|커밋"
+
+msgid "IDE|Edit"
+msgstr "IDE|편집"
+
+msgid "IDE|Go back"
+msgstr "IDE|뒤로"
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr "리뷰"
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2319,15 +3537,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2346,11 +3603,23 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "GitLab CI 와 호환ë˜ëŠ” Runner 설치"
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr "Kubernetesì— Runner 설치"
msgid "Instance"
msgid_plural "Instances"
@@ -2362,9 +3631,12 @@ msgstr ""
msgid "Integrations"
msgstr ""
-msgid "Interested parties can even contribute by pushing commits if they want to."
+msgid "Integrations Settings"
msgstr ""
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr "관심있는 íŒŒí‹°ë“¤ì€ ì›í•˜ëŠ” ê³³ì— ì»¤ë°‹ì„ í‘¸ì‹œí•¨ìœ¼ë¡œì¨ ê¸°ì—¬í•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤."
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -2377,6 +3649,9 @@ msgstr "주기 패턴"
msgid "Introducing Cycle Analytics"
msgstr "Cycle Analytics 소개"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2390,9 +3665,12 @@ msgid "IssueBoards|Boards"
msgstr ""
msgid "Issues"
-msgstr ""
+msgstr "ì´ìŠˆ"
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr "ì´ìŠˆëŠ” 버그, ìž‘ì—… í˜¹ì€ ë…¼ì˜í•  ì•„ì´ë””ì–´ì¼ ìˆ˜ 있습니다. 그리고, ì´ìŠˆëŠ” 검색 ë° í•„í„°ë§ ê°€ëŠ¥í•©ë‹ˆë‹¤."
+
+msgid "Issues closed"
msgstr ""
msgid "Jan"
@@ -2401,6 +3679,12 @@ msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2419,6 +3703,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2426,21 +3713,24 @@ msgid "Kubernetes Cluster"
msgstr ""
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° ìƒì„± ì‹œê°„ì´ ì´ˆê³¼ë˜ì—ˆìŠµë‹ˆë‹¤. %{timeout}"
msgid "Kubernetes cluster integration was not removed."
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° í†µí•©ì´ ì œê±°ë˜ì§€ 않았습니다."
msgid "Kubernetes cluster integration was successfully removed."
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° í†µí•©ì´ ì œê±°ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Kubernetes cluster was successfully updated."
-msgstr ""
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ê°€ 성공ì ìœ¼ë¡œ ì—…ë°ì´íŠ¸ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Kubernetes configured"
msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr "Kubernetes 서비스 í†µí•©ì€ ë” ì´ìƒ 사용ë˜ì§€ 않습니다. %{deprecated_message_content} 새로운 <a href=\"%{url}\"/>Kubernetes í´ëŸ¬ìŠ¤í„°</a> 페ì´ì§€"
+
+msgid "LFS"
msgstr ""
msgid "LFSStatus|Disabled"
@@ -2452,19 +3742,31 @@ msgstr "Enabled"
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
-msgid "Labels"
+msgid "LabelSelect|Labels"
msgstr ""
+msgid "Labels"
+msgstr "ë ˆì´ë¸”"
+
msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr "ë¼ë²¨ì€ ì´ìŠˆì™€ 머지 리퀘스트(MR)ì— ì¹´í…Œê³ ë¼ì´ì¦ˆë¥¼ 위해 ì ìš©ê°€ëŠ¥í•©ë‹ˆë‹¤."
+
+msgid "Labels can be applied to issues and merge requests."
msgstr ""
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
@@ -2501,6 +3803,9 @@ msgstr "푸쉬: "
msgid "LastPushEvent|at"
msgstr "at"
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2525,45 +3830,84 @@ msgstr "그룹 떠나기"
msgid "Leave project"
msgstr "프로ì íŠ¸ì—ì„œ 나가기"
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
msgid "Lock %{issuableDisplayName}"
-msgstr ""
+msgstr "%{issuableDisplayName} 잠금"
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2576,13 +3920,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2595,12 +3960,21 @@ msgid "Median"
msgstr "중앙값"
msgid "Members"
-msgstr ""
+msgstr "회ì›"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr "머지 리퀘스트(MR):"
+
msgid "Merge Requests"
+msgstr "병합 요청"
+
+msgid "Merge Requests created"
msgstr ""
msgid "Merge events"
@@ -2609,13 +3983,40 @@ msgstr "머지 ì´ë²¤íŠ¸"
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr "머지 리퀘스트(MR)는 프로ì íŠ¸ì˜ 변경 ì‚¬í•­ì„ ì œì•ˆí•˜ê³  변경 ì‚¬í•­ì„ ë‹¤ë¥¸ 사람들과 ë…¼ì˜ í•˜ëŠ” 곳입니다."
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
msgid "Merged"
msgstr ""
msgid "Messages"
+msgstr "메시지"
+
+msgid "Metrics"
msgstr ""
msgid "Metrics - Influx"
@@ -2627,18 +4028,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2651,6 +4061,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2663,9 +4076,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2696,6 +4127,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2727,6 +4161,15 @@ msgid "Modal|Close"
msgstr ""
msgid "Monitoring"
+msgstr "모니터ë§"
+
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
msgstr ""
msgid "More info"
@@ -2738,6 +4181,9 @@ msgstr ""
msgid "More information is available|here"
msgstr "여기"
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2747,22 +4193,61 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
+msgstr "새 ë¼ë²¨ ì´ë¦„ 지정"
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "새 ì´ìŠˆ"
-msgid "New Kubernetes Cluster"
-msgstr ""
-
-msgid "New Kubernetes cluster"
+msgid "New Label"
msgstr ""
msgid "New Pipeline Schedule"
msgstr "새로운 파ì´í”„ë¼ì¸ ì¼ì •"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "새 브랜치"
@@ -2781,6 +4266,9 @@ msgstr "새 파ì¼"
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr "새 ì´ìŠˆ"
@@ -2788,7 +4276,10 @@ msgid "New label"
msgstr ""
msgid "New merge request"
-msgstr "새 머지 리퀘스트"
+msgstr "새 머지 리퀘스트(MR)"
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
msgid "New project"
msgstr ""
@@ -2805,28 +4296,64 @@ msgstr ""
msgid "New tag"
msgstr "새 태그 "
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
msgid "No assignee"
-msgstr ""
+msgstr "ë‹´ë‹¹ìž ì—†ìŒ"
msgid "No changes"
msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
-msgstr ""
+msgstr "Gitaly Serverì— ì—°ê²°í•  수 없습니다. 로그를 확ì¸í•˜ì‹­ì‹œì˜¤!"
msgid "No due date"
-msgstr ""
+msgstr "기한 ì—†ìŒ"
msgid "No estimate or time spent"
-msgstr ""
+msgstr "ì˜ˆìƒ ì‹œê°„ì´ë‚˜ ì†Œë¹„ëœ ì‹œê°„ì´ ì—†ìŠµë‹ˆë‹¤."
msgid "No file chosen"
+msgstr "파ì¼ì„ ì„ íƒí•˜ì§€ 않았습니다."
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
msgstr ""
-msgid "No labels created yet."
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2835,11 +4362,14 @@ msgstr "저장소 ì—†ìŒ"
msgid "No schedules"
msgstr "ì¼ì • ì—†ìŒ"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
msgid "Not allowed to merge"
-msgstr ""
+msgstr "머지가 허용ë˜ì§€ ì•ŠìŒ"
msgid "Not available"
msgstr "사용할 수 ì—†ìŒ"
@@ -2851,7 +4381,7 @@ msgid "Not available for protected branches"
msgstr ""
msgid "Not confidential"
-msgstr ""
+msgstr "비밀 아님"
msgid "Not enough data"
msgstr "ë°ì´í„°ê°€ 충분하지 않습니다."
@@ -2871,6 +4401,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "알림 ì´ë²¤íŠ¸"
@@ -2878,19 +4411,19 @@ msgid "NotificationEvent|Close issue"
msgstr "ì´ìŠˆ 닫기"
msgid "NotificationEvent|Close merge request"
-msgstr "머지 리퀘스트 닫기"
+msgstr "머지 리퀘스트(MR) 닫기"
msgid "NotificationEvent|Failed pipeline"
msgstr "실패한 파ì´í”„ë¼ì¸"
msgid "NotificationEvent|Merge merge request"
-msgstr "머지 리퀘스트 머지하기"
+msgstr "머지 리퀘스트(MR) 머지하기"
msgid "NotificationEvent|New issue"
msgstr "새 ì´ìŠˆ"
msgid "NotificationEvent|New merge request"
-msgstr "새 머지 리퀘스트"
+msgstr "새 머지 리퀘스트(MR)"
msgid "NotificationEvent|New note"
msgstr "새 노트"
@@ -2899,7 +4432,7 @@ msgid "NotificationEvent|Reassign issue"
msgstr "ì´ìŠˆ 재지정"
msgid "NotificationEvent|Reassign merge request"
-msgstr "머지 리퀘스트 재 할당"
+msgstr "머지 리퀘스트(MR) 재 할당"
msgid "NotificationEvent|Reopen issue"
msgstr "ì´ìŠˆ 다시 열기"
@@ -2926,7 +4459,7 @@ msgid "NotificationLevel|Watch"
msgstr "Watch"
msgid "Notifications"
-msgstr ""
+msgstr "알림"
msgid "Notifications off"
msgstr ""
@@ -2941,7 +4474,7 @@ msgid "November"
msgstr ""
msgid "Number of access attempts"
-msgstr ""
+msgstr "ì ‘ê·¼ ì‹œë„ íšŸìˆ˜"
msgid "OK"
msgstr ""
@@ -2958,27 +4491,72 @@ msgstr "í•„í„°"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr "열린"
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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 "옵션 "
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -2986,7 +4564,7 @@ msgid "Outbound requests"
msgstr ""
msgid "Overview"
-msgstr ""
+msgstr "개요"
msgid "Owner"
msgstr "소유ìž"
@@ -3010,14 +4588,32 @@ msgid "Part of merge request changes"
msgstr ""
msgid "Password"
+msgstr "패스워드"
+
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
msgstr ""
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3036,6 +4632,9 @@ msgstr "파ì´í”„ë¼ì¸ 스케쥴"
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr "실패 :"
@@ -3091,13 +4690,13 @@ msgid "Pipelines charts"
msgstr "파ì´í”„ë¼ì¸ 차트"
msgid "Pipelines for last month"
-msgstr ""
+msgstr "ì§€ë‚œë‹¬ì˜ íŒŒì´í”„ë¼ì¸"
msgid "Pipelines for last week"
-msgstr ""
+msgstr "ì§€ë‚œì£¼ì˜ íŒŒì´í”„ë¼ì¸"
msgid "Pipelines for last year"
-msgstr ""
+msgstr "ì§€ë‚œí•´ì˜ íŒŒì´í”„ë¼ì¸"
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -3106,10 +4705,10 @@ msgid "Pipelines|CI Lint"
msgstr ""
msgid "Pipelines|Clear Runner Caches"
-msgstr ""
+msgstr "Runner ìºì‹œ 정리"
msgid "Pipelines|Get started with Pipelines"
-msgstr ""
+msgstr "파ì´í”„ ë¼ì¸ìœ¼ë¡œ 시작하기"
msgid "Pipelines|Loading Pipelines"
msgstr ""
@@ -3121,7 +4720,7 @@ msgid "Pipelines|Run Pipeline"
msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
-msgstr ""
+msgstr "Runnerì˜ ìºì‹œë¥¼ ë¹„ìš°ë˜ ì¤‘ 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
msgid "Pipelines|There are currently no %{scope} pipelines."
msgstr ""
@@ -3132,10 +4731,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3144,7 +4755,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3162,16 +4773,40 @@ msgstr "스테ì´ì§•"
msgid "Pipeline|with stages"
msgstr "스테ì´ì§•"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
+msgstr "Play"
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
+msgstr "ReCAPTCHA를 풀어 주십시오."
+
+msgid "Please try again"
msgstr ""
msgid "Please wait while we connect to your repository. Refresh at will."
@@ -3181,24 +4816,51 @@ msgid "Please wait while we import the repository for you. Refresh at will."
msgstr ""
msgid "Preferences"
+msgstr "환경 설정"
+
+msgid "Preferences|Navigation theme"
msgstr ""
msgid "Primary"
msgstr ""
-msgid "Private - Project access must be granted explicitly to each user."
+msgid "Prioritize"
msgstr ""
-msgid "Private - The group and its projects can only be viewed by members."
+msgid "Prioritize label"
msgstr ""
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr "Private - 프로ì íŠ¸ ì ‘ê·¼ì€ ê° ì‚¬ìš©ìžë³„ë¡œ 명시ì ìœ¼ë¡œ 허용ë˜ì–´ì•¼ 합니다."
+
+msgid "Private - The group and its projects can only be viewed by members."
+msgstr "Private - 그룹과 ê·¸ë£¹ì˜ í”„ë¡œì íŠ¸ë“¤ì€ ë©¤ë²„ë“¤ì´ ì—´ëžŒí•  수 있습니다."
+
msgid "Private projects can be created in your personal namespace with:"
msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
+msgstr "ê³„ì •ì´ ì‚­ì œë  ì˜ˆì •ìž…ë‹ˆë‹¤."
+
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
msgstr ""
msgid "Profiles|Delete Account"
@@ -3211,28 +4873,49 @@ msgid "Profiles|Delete your account?"
msgstr ""
msgid "Profiles|Deleting an account has the following effects:"
-msgstr ""
+msgstr "ê³„ì •ì„ ì‚­ì œí•˜ë©´ 다ìŒê³¼ ê°™ì€ ì˜í–¥ì´ 있습니다:"
msgid "Profiles|Invalid password"
-msgstr ""
+msgstr "ìž˜ëª»ëœ íŒ¨ìŠ¤ì›Œë“œ"
msgid "Profiles|Invalid username"
+msgstr "ìž˜ëª»ëœ ì‚¬ìš©ìžì´ë¦„"
+
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
msgstr ""
msgid "Profiles|Type your %{confirmationValue} to confirm:"
+msgstr "확ì¸ì„ 위해 %{confirmationValue} 를 입력하세요."
+
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
-msgid "Profiles|You don't have access to delete this user."
+msgid "Profiles|Update username"
msgstr ""
-msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
msgstr ""
+msgid "Profiles|You don't have access to delete this user."
+msgstr "ì´ ì‚¬ìš©ìžë¥¼ 삭제할 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
+
+msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
+msgstr "ë‹¹ì‹ ì˜ ê³„ì •ì„ ì‚­ì œí•˜ê¸° ì „ì— ì´ ê·¸ë£¹ë“¤ì˜ ì†Œìœ ê¶Œì„ ì´ì „하거나 삭제해야합니다."
+
msgid "Profiles|Your account is currently an owner in these groups:"
+msgstr "ë‹¹ì‹ ì˜ ê³„ì •ì€ í˜„ìž¬ ë‹¤ìŒ ê·¸ë£¹ë“¤ì˜ ì†Œìœ ìžìž…니다:"
+
+msgid "Profiles|e.g. My MacBook key"
msgstr ""
msgid "Profiles|your account"
-msgstr ""
+msgstr "계정"
msgid "Profiling - Performance bar"
msgstr ""
@@ -3240,9 +4923,15 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
-msgid "Project '%{project_name}' is in the process of being deleted."
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
msgstr ""
+msgid "Project '%{project_name}' is in the process of being deleted."
+msgstr "프로ì íŠ¸ '%{project_name}' ì‚­ì œ 중입니다."
+
msgid "Project '%{project_name}' queued for deletion."
msgstr "'%{project_name}'프로ì íŠ¸ê°€ ì‚­ì œ 처리 중입니다."
@@ -3252,14 +4941,17 @@ msgstr "'%{project_name}'프로ì íŠ¸ê°€ 성공ì ìœ¼ë¡œ ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Project '%{project_name}' was successfully updated."
msgstr "'%{project_name}'프로ì íŠ¸ê°€ 성공ì ìœ¼ë¡œ ì—…ë°ì´íŠ¸ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "프로ì íŠ¸ 액세스는 ê° ì‚¬ìš©ìžì—게 명시ì ìœ¼ë¡œ 부여ë˜ì–´ì•¼í•©ë‹ˆë‹¤."
msgid "Project avatar"
-msgstr ""
+msgstr "프로ì íŠ¸ 아바타"
msgid "Project avatar in repository: %{link}"
-msgstr ""
+msgstr "ì €ìž¥ì†Œì˜ í”„ë¡œì íŠ¸ 아바타: %{link}"
msgid "Project details"
msgstr "프로ì íŠ¸ ìƒì„¸"
@@ -3276,6 +4968,9 @@ msgstr "프로ì íŠ¸ 내보내기 ë§í¬ê°€ 만료ë˜ì—ˆìŠµë‹ˆë‹¤. 프로ì íŠ¸
msgid "Project export started. A download link will be sent by email."
msgstr "프로ì íŠ¸ 내보내기가 시작ë˜ì—ˆìŠµë‹ˆë‹¤. 다운로드 ë§í¬ëŠ” ì´ë©”ì¼ë¡œ 전송ë©ë‹ˆë‹¤."
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "구ë…"
@@ -3285,24 +4980,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr "사용 안 함"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "ì ‘ê·¼ ê¶Œí•œì„ ê°€ì§„ 모든 ì´ë“¤"
-
-msgid "ProjectFeature|Only team members"
-msgstr "íŒ€ì› ë§Œ"
-
msgid "ProjectFileTree|Name"
msgstr "ì´ë¦„"
@@ -3312,12 +4998,18 @@ msgstr "Never"
msgid "ProjectLifecycle|Stage"
msgstr "스테ì´ì§•"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "그래프"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3336,25 +5028,58 @@ msgstr ""
msgid "Projects"
msgstr ""
-msgid "ProjectsDropdown|Frequently visited"
+msgid "Projects shared with %{group_name}"
msgstr ""
+msgid "ProjectsDropdown|Frequently visited"
+msgstr "ìžì£¼ 방문"
+
msgid "ProjectsDropdown|Loading projects"
msgstr ""
msgid "ProjectsDropdown|Projects you visit often will appear here"
-msgstr ""
+msgstr "ìžì£¼ 방문하는 프로ì íŠ¸ëŠ” ì—¬ê¸°ì— í‘œì‹œë©ë‹ˆë‹¤"
msgid "ProjectsDropdown|Search your projects"
msgstr ""
msgid "ProjectsDropdown|Something went wrong on our end."
-msgstr ""
+msgstr "문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3435,19 +5160,43 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
+msgstr "Protip:"
+
+msgid "Provider"
msgstr ""
-msgid "Public - The group and any public projects can be viewed without any authentication."
+msgid "Pseudonymizer data collection"
msgstr ""
+msgid "Public - The group and any public projects can be viewed without any authentication."
+msgstr "공개 - ì´ ê·¸ë£¹ê³¼ 모든 공개 프로ì íŠ¸ë“¤ì€ ì–´ë–¤ ì¸ì¦ ì—†ì´ë„ ë³¼ 수 있습니다."
+
msgid "Public - The project can be accessed without any authentication."
+msgstr "공개 - ì´ í”„ë¡œì íŠ¸ëŠ” ì–´ë–¤ ì¸ì¦ ì—†ì´ë„ 접근할 수 있습니다."
+
+msgid "Public pipelines"
msgstr ""
msgid "Push Rules"
@@ -3465,28 +5214,43 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
-msgid "Quick actions can be used in the issues description and comment boxes."
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
msgstr ""
+msgid "Quarters"
+msgstr ""
+
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr "Quick actionì€ ì´ìŠˆ 설명ì´ë‚˜ 댓글 박스ì—ì„œ 사용 가능합니다."
+
msgid "Read more"
msgstr "ë” ì½ê¸°"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr ""
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
-msgstr "브랜치"
-
-msgid "RefSwitcher|Tags"
-msgstr "태그"
-
msgid "Reference:"
msgstr ""
+msgid "Refresh"
+msgstr ""
+
msgid "Register / Sign In"
+msgstr "ë“±ë¡ / 로그ì¸"
+
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
msgstr ""
msgid "Registry"
@@ -3505,10 +5269,10 @@ msgid "Related Jobs"
msgstr "관련 Jobs"
msgid "Related Merge Requests"
-msgstr "관련 머지 리퀘스트"
+msgstr "관련 머지 리퀘스트(MR)"
msgid "Related Merged Requests"
-msgstr "관련 머지 리퀘스트"
+msgstr "관련 머지 리퀘스트(MR)"
msgid "Related merge requests"
msgstr ""
@@ -3519,36 +5283,60 @@ msgstr "ë‚˜ì¤‘ì— ë‹¤ì‹œ 알림"
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr "프로ì íŠ¸ ì‚­ì œ"
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr "액세스 요청"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr "git storage 헬스 정보 초기화"
@@ -3558,21 +5346,42 @@ msgstr "헬스 ì²´í¬ ì ‘ê·¼ í† í° ì´ˆê¸°í™”"
msgid "Reset runners registration token"
msgstr "runner ë“±ë¡ í† í° ì´ˆê¸°í™”"
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
msgid_plural "Reveal values"
-msgstr[0] ""
+msgstr[0] "값 표시"
msgid "Revert this commit"
msgstr "ì´ ì»¤ë°‹ ë˜ëŒë¦¬ê¸°"
msgid "Revert this merge request"
-msgstr "ì´ ë¨¸ì§€ 리퀘스트 ë˜ëŒë¦¬ê¸°"
+msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR) ë˜ëŒë¦¬ê¸°"
+
+msgid "Review"
+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 ""
@@ -3583,18 +5392,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
+msgstr "Runners"
+
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3605,6 +5432,15 @@ msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from you
msgstr ""
msgid "SSH Keys"
+msgstr "SSH 키"
+
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
msgstr ""
msgid "Save changes"
@@ -3614,7 +5450,7 @@ msgid "Save pipeline schedule"
msgstr "파ì´í”„ë¼ì¸ 스케줄 저장"
msgid "Save variables"
-msgstr ""
+msgstr "변수 저장"
msgid "Schedule a new pipeline"
msgstr "새로운 파ì´í”„ë¼ì¸ 스케줄 잡기"
@@ -3628,39 +5464,78 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr "파ì´í”„ë¼ì¸ 스케줄ë§"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr "브랜치 ë° íƒœê·¸ 검색"
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
msgid "Search project"
-msgstr ""
+msgstr "프로ì íŠ¸ 검색"
msgid "Search users"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ê²€ìƒ‰"
msgid "Seconds before reseting failure information"
-msgstr ""
+msgstr "실패 정보를 리셋하기까지 ë‚¨ì€ ì‹œê°„(ì´ˆ)"
msgid "Seconds to wait for a storage access attempt"
+msgstr "저장공간 ì ‘ê·¼ ì‹œë„를 위해 대기할 시간 (ì´ˆ)"
+
+msgid "Secret:"
msgstr ""
-msgid "Secret variables"
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "ì•„ì¹´ì´ë¸Œ í¬ë§· ì„ íƒ"
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr "시간대 ì„ íƒ"
@@ -3668,14 +5543,32 @@ msgid "Select an existing Kubernetes cluster or create a new one"
msgstr ""
msgid "Select assignee"
-msgstr ""
+msgstr "ë‹´ë‹¹ìž ì„ íƒ"
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr "ëŒ€ìƒ ë¸Œëžœì¹˜ ì„ íƒ"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3691,9 +5584,12 @@ msgstr ""
msgid "Server version"
msgstr ""
-msgid "Service Templates"
+msgid "Service Desk"
msgstr ""
+msgid "Service Templates"
+msgstr "서비스 템플릿"
+
msgid "Service URL"
msgstr ""
@@ -3728,14 +5624,20 @@ msgid "SetPasswordToCloneLink|set a password"
msgstr "패스워드 설정"
msgid "Settings"
-msgstr ""
+msgstr "설정"
msgid "Setup a specific Runner automatically"
+msgstr "특정 Runner ìžë™ 설정"
+
+msgid "Share"
msgstr ""
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3745,31 +5647,64 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
-msgid "Show parent pages"
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
msgstr ""
+msgid "Show parent pages"
+msgstr "부모 페ì´ì§€ 보기"
+
msgid "Show parent subgroups"
+msgstr "부모 하위 그룹 보기"
+
+msgid "Show whitespace changes"
msgstr ""
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "%d ê°œì˜ ì´ë²¤íŠ¸ 표시 중"
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3782,41 +5717,59 @@ msgstr ""
msgid "Slack application"
msgstr ""
-msgid "Snippets"
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
+msgid "Snippets"
+msgstr "스니펫"
+
msgid "Something went wrong on our end"
-msgstr ""
+msgstr "문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤"
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
+msgstr "ë²„íŠ¼ì„ í† ê¸€í•˜ë˜ ì¤‘ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
-msgstr ""
+msgstr "프로ì íŠ¸ë¥¼ 가져오는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "Something went wrong while fetching the registry list."
+msgstr "레지스트리 목ë¡ì„ 가져오는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤"
+
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
msgstr ""
msgid "Something went wrong. Please try again."
+msgstr "문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„해주세요."
+
+msgid "Sorry, no epics matched your search"
msgstr ""
msgid "Sort by"
msgstr ""
msgid "SortOptions|Access level, ascending"
-msgstr ""
+msgstr "권한 레벨, 오름차순"
msgid "SortOptions|Access level, descending"
-msgstr ""
+msgstr "권한 레벨, 내림차순"
msgid "SortOptions|Created date"
msgstr ""
@@ -3885,7 +5838,7 @@ msgid "SortOptions|Oldest joined"
msgstr ""
msgid "SortOptions|Oldest sign in"
-msgstr ""
+msgstr "ë¡œê·¸ì¸ ìˆœ"
msgid "SortOptions|Oldest updated"
msgstr ""
@@ -3900,16 +5853,16 @@ msgid "SortOptions|Recent sign in"
msgstr ""
msgid "SortOptions|Start later"
-msgstr ""
+msgstr "ì´ì „ 시작"
msgid "SortOptions|Start soon"
-msgstr ""
+msgstr "최근 시작"
msgid "SortOptions|Weight"
msgstr ""
msgid "Source"
-msgstr ""
+msgstr "소스"
msgid "Source (branch or tag)"
msgstr ""
@@ -3918,17 +5871,44 @@ msgid "Source code"
msgstr "소스 코드"
msgid "Source is not available"
-msgstr ""
+msgstr "소스를 사용할 수 없습니다."
msgid "Spam Logs"
-msgstr ""
+msgstr "스팸 로그"
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "Runner 설정 중 ë‹¤ìŒ URLì„ ì§€ì •í•˜ì„¸ìš”."
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr "별표"
@@ -3939,7 +5919,7 @@ msgid "Starred Projects' Activity"
msgstr ""
msgid "Starred projects"
-msgstr ""
+msgstr "ë³„í‘œëœ í”„ë¡œì íŠ¸"
msgid "Start a %{new_merge_request} with these changes"
msgstr "ì´ ë³€ê²½ 사항으로 %{new_merge_request} ì„ ì‹œìž‘í•˜ì‹­ì‹œì˜¤."
@@ -3950,33 +5930,66 @@ msgstr "Runner 시작!"
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
-msgid "Stopped"
+msgid "Stop impersonation"
msgstr ""
+msgid "Stop this environment"
+msgstr ""
+
+msgid "Stopped"
+msgstr "중지"
+
msgid "Storage"
+msgstr "스토리지"
+
+msgid "Storage:"
msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr "스위치 브랜치/태그"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -3984,44 +5997,47 @@ msgstr[0] ""
msgid "Tags"
msgstr "태그 "
-msgid "TagsPage|Browse commits"
+msgid "Tags:"
msgstr ""
+msgid "TagsPage|Browse commits"
+msgstr "커밋 찾아보기"
+
msgid "TagsPage|Browse files"
-msgstr ""
+msgstr "íŒŒì¼ ëª©ë¡"
msgid "TagsPage|Can't find HEAD commit for this tag"
-msgstr ""
+msgstr "ì´ íƒœê·¸ì— ëŒ€í•œ HEAD ì»¤ë°‹ì„ ì°¾ì„ ìˆ˜ 없습니다."
msgid "TagsPage|Cancel"
-msgstr ""
+msgstr "취소"
msgid "TagsPage|Create tag"
-msgstr ""
+msgstr "태그 ìƒì„±"
msgid "TagsPage|Delete tag"
-msgstr ""
+msgstr "태그 삭졔"
msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
-msgstr ""
+msgstr "'%{tag_name}' 태그를 삭제하면 ë˜ëŒë¦´ 수 없습니다. 괜찮습니까?"
msgid "TagsPage|Edit release notes"
-msgstr ""
+msgstr "Release Note 편집"
msgid "TagsPage|Existing branch name, tag, or commit SHA"
-msgstr ""
+msgstr "기존 브랜치 ì´ë¦„, 태그 ë˜ëŠ” 커밋 SHA"
msgid "TagsPage|Filter by tag name"
-msgstr ""
+msgstr "태그 ì´ë¦„으로 í•„í„°ë§"
msgid "TagsPage|New Tag"
-msgstr ""
+msgstr "새 태그"
msgid "TagsPage|New tag"
-msgstr ""
+msgstr "새 태그"
msgid "TagsPage|Optionally, add a message to the tag."
-msgstr ""
+msgstr "ì„ íƒì ìœ¼ë¡œ íƒœê·¸ì— ë©”ì‹œì§€ë¥¼ 추가하십시오."
msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
msgstr ""
@@ -4030,7 +6046,7 @@ msgid "TagsPage|Release notes"
msgstr ""
msgid "TagsPage|Repository has no tags yet."
-msgstr ""
+msgstr "ì €ìž¥ì†Œì— ì•„ì§ íƒœê·¸ê°€ 없습니다."
msgid "TagsPage|Sort by"
msgstr ""
@@ -4039,7 +6055,7 @@ msgid "TagsPage|Tags"
msgstr ""
msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
-msgstr ""
+msgstr "태그는 특정 지ì ì„ 중요하다고 표시하는 기능입니다."
msgid "TagsPage|This tag has no release notes."
msgstr ""
@@ -4047,7 +6063,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4062,6 +6078,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4078,7 +6103,7 @@ msgid "The X509 Certificate to use when mutual TLS is required to communicate wi
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 "Coding Stage는 첫 번째 커밋ì—서부터 머지 리퀘스트 ìƒì„±ê¹Œì§€ì˜ ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. 첫 번째 머지 ë¦¬í€˜ìŠ¤íŠ¸ì„ ìƒì„±í•˜ë©´ ë°ì´í„°ê°€ ìžë™ìœ¼ë¡œ ì—¬ê¸°ì— ì¶”ê°€ë©ë‹ˆë‹¤."
+msgstr "Coding Stage는 첫 번째 커밋ì—서부터 머지 리퀘스트(MR) ìƒì„±ê¹Œì§€ì˜ ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. 첫 번째 머지 리퀘스트(MR)ì„ ìƒì„±í•˜ë©´ ë°ì´í„°ê°€ ìžë™ìœ¼ë¡œ ì—¬ê¸°ì— ì¶”ê°€ë©ë‹ˆë‹¤."
msgid "The collection of events added to the data gathered for that stage."
msgstr "해당 단계ì—ì„œ 수집 ëœ ë°ì´í„°ê°€ ì´ë²¤íŠ¸ 모ìŒì— 추가ë˜ì—ˆìŠµë‹ˆë‹¤."
@@ -4099,14 +6124,17 @@ msgid "The maximum file size allowed is 200KB."
msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
-msgstr ""
+msgstr "ì €ìž¥ê³µê°„ì— ì ‘ê·¼í•˜ê¸° 위해 GitLabì´ ì‹œë„í•  횟수"
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "개발 ìˆ˜ëª…ì£¼ê¸°ì˜ ë‹¨ê³„."
@@ -4125,6 +6153,9 @@ msgstr "ì´ í”„ë¡œì íŠ¸ëŠ” ë¡œê·¸ì¸ í•œ 사용ìžê°€ë§Œ 액세스 í•  수 있ì
msgid "The project can be accessed without any authentication."
msgstr "ì´ í”„ë¡œì íŠ¸ëŠ” ì¸ì¦ì—†ì´ 액세스 í•  수 있습니다."
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "ì´ í”„ë¡œì íŠ¸ì˜ 저장소가 존재하지 않습니다."
@@ -4135,44 +6166,53 @@ msgid "The repository must be accessible over <code>http://</code>, <code>https:
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 "Review 단계ì—서는 머지 리퀘스트를 작성한 후 ë¨¸ì§€í•˜ê¸°ê¹Œì§€ì˜ ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. ë°ì´í„°ëŠ” 첫 번째 머지 ë¦¬í€˜ìŠ¤íŠ¸ì„ ë¨¸ì§€ í•œ í›„ì— ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
+msgstr "Review 단계ì—서는 머지 리퀘스트(MR)를 작성한 후 ë¨¸ì§€í•˜ê¸°ê¹Œì§€ì˜ ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. ë°ì´í„°ëŠ” 첫 번째 머지 리퀘스트(MR)ì„ ë¨¸ì§€ í•œ í›„ì— ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "Staging 단계ì—서는 MR 머지과 프로ë•ì…˜ í™˜ê²½ì— ì½”ë“œ ë°°í¬ ì‚¬ì´ì˜ ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. ë°ì´í„°ë¥¼ Production í™˜ê²½ì— ì²˜ìŒ ë°°í¬í•˜ë©´ ë°ì´í„°ê°€ ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
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ê°€ 관련 머지 ë¦¬í€˜ìŠ¤íŠ¸ì„ ìœ„í•´ 모든 파ì´í”„ë¼ì¸ì„ 실행하는 ë° ê±¸ë¦¬ëŠ” ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. 첫 번째 파ì´í”„ë¼ì¸ ì‹¤í–‰ì´ ì™„ë£Œë˜ë©´ ë°ì´í„°ê°€ ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
+msgstr "테스트 단계ì—서는 GitLab CIê°€ 관련 머지 리퀘스트(MR)ì„ ìœ„í•´ 모든 파ì´í”„ë¼ì¸ì„ 실행하는 ë° ê±¸ë¦¬ëŠ” ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. 첫 번째 파ì´í”„ë¼ì¸ ì‹¤í–‰ì´ ì™„ë£Œë˜ë©´ ë°ì´í„°ê°€ ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
-msgstr ""
+msgstr "GitLabì´ ì‹¤íŒ¨ 정보를 보관하는 시간(ì´ˆ). ì´ ì‹œê°„ë™ì•ˆ 실패가 ë°œìƒí•˜ì§€ 않으면 ë§ˆìš´íŠ¸ì— ëŒ€í•œ 정보는 리셋ë©ë‹ˆë‹¤."
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
-msgstr ""
+msgstr "GitLabì´ ì €ìž¥ê³µê°„ì— ì ‘ê·¼ì„ ì‹œë„하는 시간(ì´ˆ). ì´ ì‹œê°„ ì´í›„ timeout ì—러가 ë°œìƒí•©ë‹ˆë‹¤."
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 "ê°’ì€ ì¼ë ¨ì˜ 관측 ê°’ 중ì ì— 있습니다. 예를 들어, 3, 5, 9 사ì´ì˜ 중간 ê°’ì€ 5입니다. 3, 5, 7, 8 사ì´ì˜ 중간 ê°’ì€ (5 + 7) / 2 = 6입니다."
msgid "There are no issues to show"
msgstr ""
+msgid "There are no labels yet"
+msgstr ""
+
msgid "There are no merge requests to show"
msgstr ""
msgid "There are problems accessing Git storage: "
msgstr "git storageì— ì ‘ê·¼í•˜ëŠ”ë° ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. "
-msgid "There was an error loading results"
-msgstr ""
-
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -4191,26 +6231,53 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
-msgid "This is a confidential issue."
+msgid "This group"
msgstr ""
-msgid "This is the author's first Merge Request to this project."
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
msgstr ""
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
+msgid "This is a confidential issue."
+msgstr "비밀 ì´ìŠˆìž…니다."
+
+msgid "This is the author's first Merge Request to this project."
+msgstr "ì´ ì €ìžì˜ ì´ í”„ë¡œì íŠ¸ì— 대한 첫번째 머지 리퀘스트(MR) 입니다."
+
msgid "This issue is confidential"
msgstr ""
msgid "This issue is confidential and locked."
-msgstr ""
+msgstr "ì´ ì´ìŠˆëŠ” confidential ì´ê³  잠금 ìƒíƒœìž…니다."
msgid "This issue is locked."
-msgstr ""
+msgstr "ì´ ì´ìŠˆëŠ” lock ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
msgstr ""
@@ -4218,6 +6285,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4225,7 +6301,7 @@ msgid "This job has not started yet"
msgstr ""
msgid "This job is in pending state and is waiting to be picked by a runner"
-msgstr ""
+msgstr "ì´ ìž‘ì—…ì€ ëŒ€ê¸° ìƒíƒœì´ë©° Runnerê°€ 실행하기를 기다리고 있습니다."
msgid "This job requires a manual action"
msgstr ""
@@ -4234,15 +6310,30 @@ msgid "This means you can not push code until you create an empty repository or
msgstr "즉, 빈 저장소를 만들거나 기존 저장소를 가져올 때까지 코드를 Push 할 수 없습니다."
msgid "This merge request is locked."
+msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR)는 잠겨있습니다."
+
+msgid "This option is disabled while you still have unstaged changes"
msgstr ""
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
+msgstr "ì´ ì €ìž¥ì†Œ"
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
msgstr ""
msgid "This will delete the custom metric, Are you sure?"
@@ -4258,28 +6349,31 @@ msgid "Time before an issue starts implementation"
msgstr "ì´ìŠˆê°€ 구현ë˜ê¸° ì „ì˜ ì‹œê°„"
msgid "Time between merge request creation and merge/close"
-msgstr "머지 리퀘스트 ìƒì„±ê³¼ 머지 / 닫기 사ì´ì˜ 시간"
+msgstr "머지 리퀘스트(MR) ìƒì„±ê³¼ 머지 / 닫기 사ì´ì˜ 시간"
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
msgstr ""
msgid "Time until first merge request"
-msgstr "첫 번째 머지 ë¦¬í€˜ìŠ¤íŠ¸ê¹Œì§€ì˜ ì‹œê°„"
+msgstr "첫 번째 머지 리퀘스트(MR)ê¹Œì§€ì˜ ì‹œê°„"
msgid "TimeTrackingEstimated|Est"
-msgstr ""
+msgstr "Est"
msgid "TimeTracking|Estimated:"
-msgstr ""
+msgstr "ì˜ˆìƒ ì‹œê°„:"
msgid "TimeTracking|Spent"
-msgstr ""
+msgstr "소비ë¨"
msgid "Timeago|%s days ago"
msgstr "%s ì¼ ì „"
@@ -4287,6 +6381,9 @@ msgstr "%s ì¼ ì „"
msgid "Timeago|%s days remaining"
msgstr "%s ì¼ ë‚¨ìŒ"
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr "%s 시간 남ìŒ"
@@ -4302,6 +6399,9 @@ msgstr "%s 개월 전"
msgid "Timeago|%s months remaining"
msgstr "%s 개월 남ìŒ"
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr "%s ì´ˆ 남ìŒ"
@@ -4317,48 +6417,45 @@ msgstr "%s ë…„ ì „"
msgid "Timeago|%s years remaining"
msgstr "%s ë…„ 남ìŒ"
+msgid "Timeago|1 day ago"
+msgstr ""
+
msgid "Timeago|1 day remaining"
msgstr "1 ì¼ ë‚¨ìŒ"
+msgid "Timeago|1 hour ago"
+msgstr ""
+
msgid "Timeago|1 hour remaining"
msgstr "1 시간 남ìŒ"
+msgid "Timeago|1 minute ago"
+msgstr ""
+
msgid "Timeago|1 minute remaining"
msgstr "1 분 남ìŒ"
+msgid "Timeago|1 month ago"
+msgstr ""
+
msgid "Timeago|1 month remaining"
msgstr "1 개월 남ìŒ"
+msgid "Timeago|1 week ago"
+msgstr ""
+
msgid "Timeago|1 week remaining"
msgstr "1 주 남ìŒ"
+msgid "Timeago|1 year ago"
+msgstr ""
+
msgid "Timeago|1 year remaining"
msgstr "1 ë…„ 남ìŒ"
msgid "Timeago|Past due"
msgstr "기한 초과"
-msgid "Timeago|a day ago"
-msgstr "1 ì¼ ì „"
-
-msgid "Timeago|a month ago"
-msgstr "1 달 전"
-
-msgid "Timeago|a week ago"
-msgstr "1 ì£¼ì¼ ì „"
-
-msgid "Timeago|a year ago"
-msgstr "1 ë…„ ì „"
-
-msgid "Timeago|about %s hours ago"
-msgstr "약 %s 시간 전"
-
-msgid "Timeago|about a minute ago"
-msgstr "약 1 분 전"
-
-msgid "Timeago|about an hour ago"
-msgstr "약 1 시간 전"
-
msgid "Timeago|in %s days"
msgstr "%s ì¼ ì´ë‚´"
@@ -4398,11 +6495,14 @@ msgstr "1 ì£¼ì¼ ì´ë‚´"
msgid "Timeago|in 1 year"
msgstr "1 ë…„ ì´ë‚´"
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
msgstr ""
-msgid "Timeago|less than a minute ago"
-msgstr "1 분미만"
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4424,6 +6524,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4433,6 +6536,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4442,28 +6551,58 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
-msgid "Toggle sidebar"
+msgid "Todos"
msgstr ""
-msgid "ToggleButton|Toggle Status: OFF"
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
msgstr ""
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr "사ì´ë“œë°” 토글"
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr "토글 ìƒíƒœ: OFF"
+
msgid "ToggleButton|Toggle Status: ON"
+msgstr "토글 ìƒíƒœ: ON"
+
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
msgstr ""
msgid "Total Time"
@@ -4484,12 +6623,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4502,10 +6659,43 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr "별표 제거"
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
+msgstr "최신 ìƒíƒœìž…니다"
+
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -4523,6 +6713,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr "새 íŒŒì¼ ì—…ë¡œë“œ"
@@ -4530,7 +6723,7 @@ msgid "Upload file"
msgstr "íŒŒì¼ ì—…ë¡œë“œ"
msgid "Upload new avatar"
-msgstr ""
+msgstr "새 아바타 업로드"
msgid "UploadLink|click to upload"
msgstr "업로드하려면 í´ë¦­í•˜ì‹­ì‹œì˜¤."
@@ -4541,9 +6734,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "설정 ì¤‘ì— ë‹¤ìŒ ë“±ë¡ í† í° ì´ìš© : "
@@ -4553,12 +6755,24 @@ msgstr "전체 알림 설정 사용"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
-msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
msgstr ""
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr "변수는 runner를 통해 í™˜ê²½ì— ì ìš©ë©ë‹ˆë‹¤. 변수는 ë³´í˜¸ëœ ë¸Œëžœì¹˜ë‚˜ 태그ì—만 노출하여 보호할 수 있습니다. 비밀번호, 암호키, ë˜ëŠ” ì›í•˜ëŠ”대로 변수를 사용할 수 있습니다."
+
msgid "Various container registry settings."
msgstr ""
@@ -4568,7 +6782,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4580,21 +6797,39 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
+msgstr "ë¼ë²¨ë³´ê¸°"
+
+msgid "View log"
msgstr ""
msgid "View open merge request"
-msgstr "열린 머지 리퀘스트보기"
+msgstr "열린 머지 리퀘스트(MR)보기"
msgid "View project labels"
msgstr ""
msgid "View replaced file @ "
-msgstr ""
+msgstr "êµì²´ëœ íŒŒì¼ ë³´ê¸° @ "
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "내부"
@@ -4610,7 +6845,7 @@ msgstr "ì•Œ 수 ì—†ìŒ"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "ì´ ë°ì´í„°ë¥¼ ë³´ê³  싶ì€ê°€ìš”? 관리ìžì—게 액세스 ê¶Œí•œì„ ìš”ì²­í•˜ì„¸ìš”."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4628,29 +6863,41 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
msgstr ""
-msgid "Wiki"
+msgid "When a runner is locked, it cannot be assigned to other projects"
msgstr ""
-msgid "WikiClone|Clone your wiki"
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
msgstr ""
-msgid "WikiClone|Git Access"
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
+msgid "Wiki"
+msgstr "위키"
+
+msgid "WikiClone|Clone your wiki"
+msgstr "위키 Clone"
+
+msgid "WikiClone|Git Access"
+msgstr "Git ì ‘ê·¼"
+
msgid "WikiClone|Install Gollum"
-msgstr ""
+msgstr "Gollum 설치"
msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:"
-msgstr ""
+msgstr "GFMì´ ë¡œì»¬ì—ì„œ ë Œë”ë§ ë˜ë„ë¡ %{markdown} 설치를 추천합니다."
msgid "WikiClone|Start Gollum and edit locally"
-msgstr ""
+msgstr "Gollumì„ ì‹œìž‘í•˜ê³  로컬 편집"
msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
msgstr ""
@@ -4658,89 +6905,116 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
msgstr ""
-msgid "WikiHistoricalPage|This is an old version of this page."
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
msgstr ""
-msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgid "WikiEmptyIssueMessage|issue tracker"
msgstr ""
-msgid "WikiHistoricalPage|history"
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
msgstr ""
-msgid "WikiHistoricalPage|most recent version"
+msgid "WikiEmpty|Create your first page"
msgstr ""
-msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgid "WikiEmpty|Suggest wiki improvement"
msgstr ""
-msgid "WikiMarkdownDocs|documentation"
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
msgstr ""
-msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgid "WikiEmpty|This project has no wiki pages"
msgstr ""
-msgid "WikiNewPagePlaceholder|how-to-setup"
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
+msgid "WikiHistoricalPage|This is an old version of this page."
+msgstr "ì´ íŽ˜ì´ì§€ì˜ old 버전입니다."
+
+msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}."
+msgstr "%{most_recent_link} ì„ ë³´ê±°ë‚˜ %{history_link} 를 찾아볼 수 있습니다."
+
+msgid "WikiHistoricalPage|history"
+msgstr "ì´ë ¥"
+
+msgid "WikiHistoricalPage|most recent version"
+msgstr "최신 버전"
+
+msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
+msgstr "ë” ë§Žì€ ì˜ˆì œëŠ” %{docs_link}"
+
+msgid "WikiMarkdownDocs|documentation"
+msgstr "문서"
+
+msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
+msgstr "(새로운) 페ì´ì§€ì— ì—°ê²° 하려면, %{link_example} ë§í¬ë¥¼ 입력하세요."
+
+msgid "WikiNewPagePlaceholder|how-to-setup"
+msgstr "설정 방법"
+
msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
-msgstr ""
+msgstr "íŒ: 새로운 파ì¼ì— 대한 full path를 지정할 수 있습니다. 존재하지 않는 ë””ë ‰í† ë¦¬ë“¤ì€ ìžë™ì ìœ¼ë¡œ ìƒì„±ë©ë‹ˆë‹¤."
msgid "WikiNewPageTitle|New Wiki Page"
-msgstr ""
+msgstr "새 위키 페ì´ì§€"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
+msgstr "ì´ íŽ˜ì´ì§€ë¥¼ 삭제하시겠습니까?"
+
+msgid "WikiPageConfirmDelete|Delete page"
msgstr ""
-msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
msgstr ""
+msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
+msgstr "ë™ì‹œì— 누군가가 ì´ íŽ˜ì´ì§€ë¥¼ 수정했습니다. %{page_link} 페ì´ì§€ë¥¼ ì²´í¬ì•„웃하여 ë‹¹ì‹ ì˜ ìˆ˜ì •ì‚¬í•­ë“¤ì´ ì˜ë„치 않게 다른 ì´ì˜ ìˆ˜ì •ì‚¬í•­ë“¤ì„ ì œê±°í•˜ì§€ ì•Šë„ë¡ í•´ì£¼ì„¸ìš”."
+
msgid "WikiPageConflictMessage|the page"
-msgstr ""
+msgstr "페ì´ì§€"
msgid "WikiPageCreate|Create %{page_title}"
-msgstr ""
+msgstr "%{page_title} 작성"
msgid "WikiPageEdit|Update %{page_title}"
-msgstr ""
+msgstr "%{page_title} ì—…ë°ì´íŠ¸"
msgid "WikiPage|Page slug"
-msgstr ""
+msgstr "페ì´ì§€ slug"
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
-msgstr ""
+msgstr "페ì´ì§€ 만들기"
msgid "Wiki|Create page"
-msgstr ""
+msgstr "페ì´ì§€ 만들기"
msgid "Wiki|Edit Page"
-msgstr ""
-
-msgid "Wiki|Empty page"
-msgstr ""
+msgstr "페ì´ì§€ 편집"
msgid "Wiki|More Pages"
-msgstr ""
+msgstr "ë” ë§Žì€ íŽ˜ì´ì§€"
msgid "Wiki|New page"
-msgstr ""
+msgstr "새 페ì´ì§€"
msgid "Wiki|Page history"
-msgstr ""
+msgstr "페ì´ì§€ ì´ë ¥"
msgid "Wiki|Page version"
-msgstr ""
+msgstr "페ì´ì§€ 버전"
msgid "Wiki|Pages"
-msgstr ""
+msgstr "페ì´ì§€"
msgid "Wiki|Wiki Pages"
-msgstr ""
+msgstr "위키 페ì´ì§€"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
msgstr ""
@@ -4748,7 +7022,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "액세스 요청 철회"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4766,7 +7049,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4775,9 +7061,15 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
-msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
msgstr ""
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
+msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
+msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ì— Runner를 쉽게 설치할 수 있습니다. %{link_to_help_page}"
+
msgid "You can move around the graph by using the arrow keys."
msgstr ""
@@ -4787,22 +7079,40 @@ msgstr "ë¸Œëžœì¹˜ì— ìžˆì„ ë•Œì—만 파ì¼ì„ 추가 í•  수 있습니다."
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
+msgstr "ì½ê¸° ì „ìš© GitLab ì¸ìŠ¤í„´ìŠ¤ì—는 쓰기가 불가능합니다."
+
+msgid "You do not have any assigned merge requests"
msgstr ""
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "프로ì íŠ¸ ìˆ«ìž í•œë„ì— ë„달했습니다."
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4811,6 +7121,9 @@ msgstr "프로ì íŠ¸ì— 별표를 표시하려면 ë¡œê·¸ì¸ í•´ì•¼ 합니다."
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "ê¶Œí•œì´ í•„ìš”í•©ë‹ˆë‹¤."
@@ -4841,9 +7154,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4859,6 +7181,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4866,10 +7194,10 @@ msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
msgstr ""
msgid "Your comment will not be visible to the public."
-msgstr ""
+msgstr "ëŒ“ê¸€ì´ ê³µê°œì— í‘œì‹œë˜ì§€ 않습니다."
msgid "Your groups"
-msgstr ""
+msgstr "ë‹¹ì‹ ì˜ ê·¸ë£¹"
msgid "Your name"
msgstr "ê·€í•˜ì˜ ì´ë¦„"
@@ -4877,15 +7205,18 @@ msgstr "ê·€í•˜ì˜ ì´ë¦„"
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgid "assign yourself"
-msgstr ""
+msgstr "ìžì‹ ì„ 담당ìžë¡œ 지정"
msgid "branch name"
msgstr ""
@@ -4893,45 +7224,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4941,19 +7365,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST detected no security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4962,15 +7383,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -4980,10 +7440,16 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "ì¼"
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -4995,16 +7461,28 @@ msgstr[0] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5016,6 +7494,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5038,24 +7519,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5083,6 +7558,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5096,7 +7574,7 @@ msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
-msgstr ""
+msgstr "mrWidget %{branch} 브랜치가 로컬 ì €ìž¥ì†Œì— ìžˆìœ¼ë©´ ì´ ë¨¸ì§€ 리퀘스트(MR)를 다ìŒê³¼ ê°™ì´ ìˆ˜ë™ìœ¼ë¡œ 머지할 수 있습니다."
msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
msgstr ""
@@ -5116,9 +7594,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5179,6 +7672,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5188,9 +7684,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5210,34 +7703,43 @@ msgid "mrWidget|to be merged automatically when the pipeline succeeds"
msgstr ""
msgid "new merge request"
-msgstr "새 머지 리퀘스트"
+msgstr "새 머지 리퀘스트(MR)"
msgid "notification emails"
msgstr "알림 ì´ë©”ì¼"
msgid "or"
-msgstr ""
+msgstr "ë˜ëŠ”"
msgid "parent"
msgid_plural "parents"
msgstr[0] "부모"
msgid "password"
-msgstr ""
+msgstr "패스워드"
msgid "personal access token"
-msgstr ""
+msgstr "ê°œì¸ ì—‘ì„¸ìŠ¤ 토í°"
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
-msgid "source"
+msgid "remove weight"
msgstr ""
+msgid "source"
+msgstr "소스"
+
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr "%{slash_command} 소요 ëœ ì‹œê°„ì˜ í•©ê³„ë¥¼ ì—…ë°ì´íŠ¸í•©ë‹ˆë‹¤."
+
+msgid "started"
msgstr ""
msgid "this document"
@@ -5247,11 +7749,18 @@ msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
-msgstr ""
+msgstr "사용ìžëª…"
msgid "uses Kubernetes clusters to deploy your code!"
+msgstr "kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 사용하여 코드를 ë°°í¬í•©ë‹ˆë‹¤!"
+
+msgid "view it on GitLab"
msgstr ""
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+
diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po
index 1b3811198a8..3e1250a3265 100644
--- a/locale/nl_NL/gitlab.po
+++ b/locale/nl_NL/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:39-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
@@ -15,10 +13,26 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: nl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] "%d gewijzigd bestand"
+msgstr[1] "%d gewijzigde bestanden"
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
@@ -54,6 +68,26 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+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] "%s andere commit is weggelaten om prestatieproblemen te voorkomen."
@@ -65,10 +99,19 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{count} deelnemer"
+msgstr[1] "%{count} deelnemers"
+
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
msgid "%{loadingIcon} Started"
msgstr ""
@@ -76,9 +119,15 @@ msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
-msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgid "%{name}'s avatar"
msgstr ""
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr "%{nip_domain} kan worden gebruikt als een alternatief voor een eigen domein."
+
+msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
+msgstr "%{number_commits_behind} commits achter %{default_branch}, %{number_commits_ahead} commits voor"
+
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr ""
@@ -88,23 +137,80 @@ msgstr ""
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+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 "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(bekijk de %{link} voor meer info over hoe je het kan installeren)."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr ""
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] ""
@@ -116,9 +222,51 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -128,33 +276,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr "Over auto deploy"
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr "Misbruik rapporten"
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr "Toegangstokens"
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr "Account"
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "Actief"
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr "Activiteit"
@@ -179,12 +357,39 @@ msgstr "Licentie toevoegen"
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "Nieuwe map toevoegen"
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -251,7 +456,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -275,6 +483,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -290,6 +540,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -305,13 +558,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -347,18 +600,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr "Uiterlijk"
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr "Applicaties"
@@ -368,12 +645,21 @@ msgstr ""
msgid "April"
msgstr ""
-msgid "Archived project! Repository is read-only"
+msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -389,6 +675,12 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -413,12 +705,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr ""
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -428,12 +735,36 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr "Auteur"
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -449,7 +780,10 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
@@ -470,12 +804,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr ""
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -485,12 +825,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -509,6 +930,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -533,6 +957,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -545,6 +978,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -622,7 +1067,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -703,7 +1148,7 @@ msgstr "Door bestanden bladeren"
msgid "Browse files"
msgstr "Door bestanden bladeren"
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -712,6 +1157,9 @@ msgstr "door"
msgid "CI / CD"
msgstr "CI / CD"
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -721,12 +1169,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "Annuleren"
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -784,15 +1292,30 @@ msgstr "Cherry-pick deze commit"
msgid "Cherry-pick this merge request"
msgstr ""
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -898,9 +1421,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -913,6 +1457,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -922,6 +1469,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -931,10 +1481,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -949,9 +1502,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -967,6 +1517,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -976,22 +1529,25 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -1000,7 +1556,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1012,6 +1568,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1039,6 +1601,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1075,7 +1643,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1102,6 +1676,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -1114,9 +1700,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1144,19 +1727,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select zone"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1189,6 +1790,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1219,13 +1823,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1292,6 +1905,9 @@ msgstr ""
msgid "Committed by"
msgstr "Gecommit door"
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr "Vergelijk"
@@ -1340,6 +1956,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1406,15 +2025,30 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr ""
@@ -1430,12 +2064,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1451,9 +2094,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr ""
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1466,12 +2121,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr ""
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr "Maak map aan"
@@ -1484,9 +2145,15 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1505,6 +2172,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1523,10 +2193,16 @@ msgstr ""
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr ""
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1541,6 +2217,15 @@ msgstr ""
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr ""
@@ -1550,6 +2235,12 @@ msgstr ""
msgid "Customize colors"
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 "Cycle Analytics"
msgstr ""
@@ -1574,6 +2265,9 @@ msgstr ""
msgid "CycleAnalyticsStage|Test"
msgstr ""
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1586,15 +2280,36 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] ""
@@ -1603,39 +2318,195 @@ msgstr[1] ""
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr ""
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr ""
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr ""
@@ -1678,16 +2549,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr ""
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1699,15 +2585,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1723,6 +2618,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1735,15 +2642,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1756,33 +2678,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1798,12 +2741,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1822,6 +2759,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1834,6 +2786,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1861,9 +2816,30 @@ msgstr ""
msgid "Every week (Sundays at 4:00am)"
msgstr ""
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1891,6 +2867,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1900,6 +2879,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr ""
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1909,6 +2891,12 @@ msgstr ""
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1918,9 +2906,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr ""
@@ -1930,6 +2915,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
@@ -1939,6 +2927,12 @@ msgstr ""
msgid "Find file"
msgstr ""
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1948,12 +2942,39 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] ""
@@ -1971,9 +2992,24 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
@@ -1986,6 +3022,12 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -2004,7 +3046,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2019,31 +3064,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Last event ID processed by cursor"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2064,19 +3121,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2085,16 +3148,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2112,13 +3178,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2148,6 +3220,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2157,6 +3235,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2169,34 +3250,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
msgid "GoToYourFork|Fork"
msgstr ""
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2208,7 +3355,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2238,6 +3406,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2315,15 +3510,54 @@ msgid_plural "Hide values"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr ""
msgid "Housekeeping successfully started"
msgstr ""
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2336,15 +3570,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2363,10 +3636,22 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance"
@@ -2380,6 +3665,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2395,6 +3683,9 @@ msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2413,12 +3704,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2437,6 +3737,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2461,6 +3764,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr ""
@@ -2470,12 +3776,21 @@ msgstr ""
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2485,6 +3800,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2520,6 +3838,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2544,18 +3865,36 @@ msgstr ""
msgid "Leave project"
msgstr ""
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2565,24 +3904,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2595,13 +3955,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2619,24 +4000,60 @@ msgstr ""
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2646,18 +4063,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2670,6 +4096,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2682,9 +4111,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2715,6 +4162,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2748,6 +4198,15 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2757,6 +4216,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2766,21 +4228,60 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nieuwe issue"
msgstr[1] "Nieuwe issues"
-msgid "New Kubernetes Cluster"
+msgid "New Label"
msgstr ""
-msgid "New Kubernetes cluster"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
msgstr ""
msgid "New branch"
@@ -2801,6 +4302,9 @@ msgstr ""
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -2810,6 +4314,9 @@ msgstr ""
msgid "New merge request"
msgstr ""
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2825,6 +4332,12 @@ msgstr ""
msgid "New tag"
msgstr ""
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2846,7 +4359,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2855,6 +4398,9 @@ msgstr ""
msgid "No schedules"
msgstr ""
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -2891,6 +4437,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -2978,27 +4527,72 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Geopend"
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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 ""
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3032,12 +4626,30 @@ msgstr ""
msgid "Password"
msgstr ""
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3056,6 +4668,9 @@ msgstr ""
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr ""
@@ -3152,10 +4767,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3164,7 +4791,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3182,18 +4809,42 @@ msgstr ""
msgid "Pipeline|with stages"
msgstr ""
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3203,9 +4854,24 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3218,9 +4884,21 @@ msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3239,9 +4917,27 @@ msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3251,6 +4947,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3260,6 +4959,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -3272,6 +4977,9 @@ msgstr ""
msgid "Project '%{project_name}' was successfully updated."
msgstr ""
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -3296,6 +5004,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -3305,24 +5016,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr ""
-
-msgid "ProjectFeature|Everyone with access"
-msgstr ""
-
-msgid "ProjectFeature|Only team members"
-msgstr ""
-
msgid "ProjectFileTree|Name"
msgstr ""
@@ -3332,12 +5034,18 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
-msgid "ProjectNetworkGraph|Graph"
+msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3356,6 +5064,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3374,7 +5085,37 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3455,21 +5196,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3485,28 +5250,43 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr ""
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr ""
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
+msgid "Reference:"
msgstr ""
-msgid "RefSwitcher|Tags"
+msgid "Refresh"
msgstr ""
-msgid "Reference:"
+msgid "Register / Sign In"
msgstr ""
-msgid "Register / Sign In"
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
msgstr ""
msgid "Registry"
@@ -3539,36 +5319,60 @@ msgstr ""
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr ""
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr ""
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr ""
@@ -3578,10 +5382,28 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3595,6 +5417,9 @@ msgstr ""
msgid "Revert this merge request"
msgstr ""
+msgid "Review"
+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 ""
@@ -3604,18 +5429,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3628,6 +5471,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3649,15 +5501,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr ""
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr ""
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3673,15 +5549,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr ""
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr ""
@@ -3694,9 +5585,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3712,6 +5621,9 @@ msgstr ""
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3754,9 +5666,15 @@ msgstr ""
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3766,32 +5684,65 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3804,6 +5755,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3813,13 +5767,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching assignees list"
+msgstr ""
+
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3828,9 +5788,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -3948,9 +5917,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr ""
@@ -3972,33 +5968,66 @@ msgstr ""
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr ""
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4007,6 +6036,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4070,7 +6102,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4085,6 +6117,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4124,12 +6165,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr ""
@@ -4148,6 +6192,9 @@ msgstr ""
msgid "The project can be accessed without any authentication."
msgstr ""
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr ""
@@ -4163,6 +6210,9 @@ msgstr ""
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 ""
@@ -4175,25 +6225,31 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 no issues to show"
msgstr ""
-msgid "There are no merge requests to show"
+msgid "There are no labels yet"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no merge requests to show"
msgstr ""
-msgid "There was an error loading results"
+msgid "There are problems accessing Git storage: "
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -4214,12 +6270,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4241,6 +6324,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4259,15 +6351,30 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4283,10 +6390,13 @@ msgstr ""
msgid "Time between merge request creation and merge/close"
msgstr ""
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4310,6 +6420,9 @@ msgstr ""
msgid "Timeago|%s days remaining"
msgstr ""
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr ""
@@ -4325,6 +6438,9 @@ msgstr ""
msgid "Timeago|%s months remaining"
msgstr ""
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr ""
@@ -4340,46 +6456,43 @@ msgstr ""
msgid "Timeago|%s years remaining"
msgstr ""
-msgid "Timeago|1 day remaining"
-msgstr ""
-
-msgid "Timeago|1 hour remaining"
+msgid "Timeago|1 day ago"
msgstr ""
-msgid "Timeago|1 minute remaining"
+msgid "Timeago|1 day remaining"
msgstr ""
-msgid "Timeago|1 month remaining"
+msgid "Timeago|1 hour ago"
msgstr ""
-msgid "Timeago|1 week remaining"
+msgid "Timeago|1 hour remaining"
msgstr ""
-msgid "Timeago|1 year remaining"
+msgid "Timeago|1 minute ago"
msgstr ""
-msgid "Timeago|Past due"
+msgid "Timeago|1 minute remaining"
msgstr ""
-msgid "Timeago|a day ago"
+msgid "Timeago|1 month ago"
msgstr ""
-msgid "Timeago|a month ago"
+msgid "Timeago|1 month remaining"
msgstr ""
-msgid "Timeago|a week ago"
+msgid "Timeago|1 week ago"
msgstr ""
-msgid "Timeago|a year ago"
+msgid "Timeago|1 week remaining"
msgstr ""
-msgid "Timeago|about %s hours ago"
+msgid "Timeago|1 year ago"
msgstr ""
-msgid "Timeago|about a minute ago"
+msgid "Timeago|1 year remaining"
msgstr ""
-msgid "Timeago|about an hour ago"
+msgid "Timeago|Past due"
msgstr ""
msgid "Timeago|in %s days"
@@ -4421,10 +6534,13 @@ msgstr ""
msgid "Timeago|in 1 year"
msgstr ""
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
msgstr ""
-msgid "Timeago|less than a minute ago"
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
msgstr ""
msgid "Time|hr"
@@ -4449,6 +6565,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4458,6 +6577,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4467,21 +6592,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4491,6 +6640,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr ""
@@ -4509,12 +6664,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4527,12 +6700,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr ""
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4548,6 +6754,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr ""
@@ -4566,9 +6775,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4578,9 +6796,21 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4593,7 +6823,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4605,9 +6838,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr ""
@@ -4620,6 +6865,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr ""
@@ -4635,7 +6886,7 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4653,10 +6904,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4683,7 +6946,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4719,6 +7006,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4734,7 +7027,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4746,9 +7039,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4773,7 +7063,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4791,7 +7090,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4800,6 +7102,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4812,22 +7120,40 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4836,6 +7162,9 @@ msgstr ""
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr ""
@@ -4866,9 +7195,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4884,6 +7222,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4902,10 +7246,13 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4919,45 +7266,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4967,19 +7407,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4988,15 +7425,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5006,11 +7482,17 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5024,16 +7506,28 @@ msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5045,6 +7539,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5068,24 +7565,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5113,6 +7604,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5146,9 +7640,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5209,6 +7718,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5218,9 +7730,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5262,15 +7771,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5283,6 +7801,14 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/pl_PL/gitlab.po b/locale/pl_PL/gitlab.po
index 4ae57235f90..67c1abe6bf7 100644
--- a/locale/pl_PL/gitlab.po
+++ b/locale/pl_PL/gitlab.po
@@ -2,23 +2,43 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:36-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Polish\n"
"Language: pl_PL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=4; plural=((n == 1) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || n%10 == 1 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 12 && n%100 <= 14)) ? 2 : 3));\n"
+"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
"X-Generator: crowdin.com\n"
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: pl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:39\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -68,6 +88,34 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
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] ""
@@ -81,6 +129,9 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -88,12 +139,24 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
@@ -106,6 +169,9 @@ msgstr ""
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+msgstr ""
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] ""
@@ -113,18 +179,90 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{text} is available"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr ""
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] ""
@@ -138,9 +276,51 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -150,33 +330,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr ""
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr ""
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr ""
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr ""
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr ""
@@ -201,12 +411,39 @@ msgstr ""
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr ""
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -273,7 +510,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -297,6 +537,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -312,6 +594,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -327,13 +612,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -369,18 +654,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr ""
@@ -390,12 +699,21 @@ msgstr ""
msgid "April"
msgstr ""
-msgid "Archived project! Repository is read-only"
+msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -411,6 +729,12 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -435,12 +759,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr ""
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -450,12 +789,36 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -471,7 +834,10 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
@@ -492,12 +858,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr ""
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -507,12 +879,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -531,6 +984,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -555,6 +1011,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -567,6 +1032,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -646,7 +1123,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -727,7 +1204,7 @@ msgstr ""
msgid "Browse files"
msgstr ""
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -736,6 +1213,9 @@ msgstr ""
msgid "CI / CD"
msgstr ""
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -745,12 +1225,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr ""
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -808,15 +1348,30 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -922,9 +1477,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -937,6 +1513,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -946,6 +1525,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -955,10 +1537,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -973,9 +1558,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -991,6 +1573,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -1000,22 +1585,25 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching projects"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -1024,7 +1612,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1036,6 +1624,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1063,6 +1657,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1099,7 +1699,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1126,6 +1732,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -1138,9 +1756,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1168,19 +1783,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select project and zone to choose machine type"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone"
+msgstr ""
+
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1213,6 +1846,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1243,13 +1879,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1320,6 +1965,9 @@ msgstr ""
msgid "Committed by"
msgstr ""
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr ""
@@ -1368,6 +2016,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1434,15 +2085,30 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr ""
@@ -1458,12 +2124,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1479,9 +2154,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr ""
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1494,12 +2181,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr ""
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr ""
@@ -1512,9 +2205,15 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1533,6 +2232,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1551,10 +2253,16 @@ msgstr ""
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr ""
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1569,6 +2277,15 @@ msgstr ""
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr ""
@@ -1578,6 +2295,12 @@ msgstr ""
msgid "Customize colors"
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 "Cycle Analytics"
msgstr ""
@@ -1602,6 +2325,9 @@ msgstr ""
msgid "CycleAnalyticsStage|Test"
msgstr ""
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1614,15 +2340,36 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] ""
@@ -1633,39 +2380,195 @@ msgstr[3] ""
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr ""
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr ""
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr ""
@@ -1708,16 +2611,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr ""
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1729,15 +2647,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1753,6 +2680,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1765,15 +2704,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1786,33 +2740,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1828,12 +2803,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1852,6 +2821,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1864,6 +2848,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1891,9 +2878,30 @@ msgstr ""
msgid "Every week (Sundays at 4:00am)"
msgstr ""
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1921,6 +2929,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1930,6 +2941,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr ""
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1939,6 +2953,12 @@ msgstr ""
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1948,9 +2968,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr ""
@@ -1960,6 +2977,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
@@ -1969,6 +2989,12 @@ msgstr ""
msgid "Find file"
msgstr ""
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1978,12 +3004,39 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] ""
@@ -2003,9 +3056,24 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
@@ -2018,6 +3086,12 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -2036,7 +3110,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2051,31 +3128,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2096,19 +3185,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2117,16 +3212,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2144,13 +3242,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2180,6 +3284,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2189,6 +3299,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2201,34 +3314,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
msgid "GoToYourFork|Fork"
msgstr ""
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2240,7 +3419,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2270,6 +3470,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2349,15 +3576,54 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr ""
msgid "Housekeeping successfully started"
msgstr ""
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2370,15 +3636,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2397,10 +3702,22 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance"
@@ -2416,6 +3733,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2431,6 +3751,9 @@ msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2449,12 +3772,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2473,6 +3805,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2497,6 +3832,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr ""
@@ -2506,12 +3844,21 @@ msgstr ""
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2521,6 +3868,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2558,6 +3908,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2582,18 +3935,36 @@ msgstr ""
msgid "Leave project"
msgstr ""
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2603,24 +3974,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2633,13 +4025,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2657,24 +4070,60 @@ msgstr ""
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2684,18 +4133,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2708,6 +4166,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2720,9 +4181,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2753,6 +4232,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2786,6 +4268,15 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2795,6 +4286,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2804,9 +4298,45 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] ""
@@ -2814,13 +4344,16 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "New Kubernetes Cluster"
+msgid "New Label"
msgstr ""
-msgid "New Kubernetes cluster"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
msgstr ""
msgid "New branch"
@@ -2841,6 +4374,9 @@ msgstr ""
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -2850,6 +4386,9 @@ msgstr ""
msgid "New merge request"
msgstr ""
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2865,6 +4404,12 @@ msgstr ""
msgid "New tag"
msgstr ""
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2886,7 +4431,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2895,6 +4470,9 @@ msgstr ""
msgid "No schedules"
msgstr ""
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -2931,6 +4509,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -3018,27 +4599,72 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr ""
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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 ""
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3072,12 +4698,30 @@ msgstr ""
msgid "Password"
msgstr ""
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3096,6 +4740,9 @@ msgstr ""
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr ""
@@ -3192,10 +4839,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3204,7 +4863,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3222,18 +4881,42 @@ msgstr ""
msgid "Pipeline|with stages"
msgstr ""
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3243,9 +4926,24 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3258,9 +4956,21 @@ msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3279,9 +4989,27 @@ msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3291,6 +5019,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3300,6 +5031,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -3312,6 +5049,9 @@ msgstr ""
msgid "Project '%{project_name}' was successfully updated."
msgstr ""
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -3336,6 +5076,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -3345,24 +5088,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr ""
-
-msgid "ProjectFeature|Everyone with access"
-msgstr ""
-
-msgid "ProjectFeature|Only team members"
-msgstr ""
-
msgid "ProjectFileTree|Name"
msgstr ""
@@ -3372,12 +5106,18 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
-msgid "ProjectNetworkGraph|Graph"
+msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3396,6 +5136,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3414,7 +5157,37 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3495,21 +5268,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3525,28 +5322,43 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr ""
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr ""
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
+msgid "Reference:"
msgstr ""
-msgid "RefSwitcher|Tags"
+msgid "Refresh"
msgstr ""
-msgid "Reference:"
+msgid "Register / Sign In"
msgstr ""
-msgid "Register / Sign In"
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
msgstr ""
msgid "Registry"
@@ -3579,36 +5391,60 @@ msgstr ""
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr ""
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr ""
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr ""
@@ -3618,10 +5454,28 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3637,6 +5491,9 @@ msgstr ""
msgid "Revert this merge request"
msgstr ""
+msgid "Review"
+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 ""
@@ -3646,18 +5503,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3670,6 +5545,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3691,15 +5575,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr ""
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr ""
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3715,15 +5623,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr ""
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr ""
@@ -3736,9 +5659,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3754,6 +5695,9 @@ msgstr ""
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3796,9 +5740,15 @@ msgstr ""
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3808,15 +5758,30 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
@@ -3824,18 +5789,36 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3848,6 +5831,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3857,13 +5843,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3872,9 +5864,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -3992,9 +5993,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr ""
@@ -4016,33 +6044,66 @@ msgstr ""
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr ""
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4053,6 +6114,9 @@ msgstr[3] ""
msgid "Tags"
msgstr ""
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4116,7 +6180,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4131,6 +6195,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4170,12 +6243,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr ""
@@ -4194,6 +6270,9 @@ msgstr ""
msgid "The project can be accessed without any authentication."
msgstr ""
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr ""
@@ -4209,6 +6288,9 @@ msgstr ""
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 ""
@@ -4221,25 +6303,31 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 no issues to show"
msgstr ""
-msgid "There are no merge requests to show"
+msgid "There are no labels yet"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no merge requests to show"
msgstr ""
-msgid "There was an error loading results"
+msgid "There are problems accessing Git storage: "
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -4260,12 +6348,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4287,6 +6402,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4305,15 +6429,30 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4329,10 +6468,13 @@ msgstr ""
msgid "Time between merge request creation and merge/close"
msgstr ""
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4356,6 +6498,9 @@ msgstr ""
msgid "Timeago|%s days remaining"
msgstr ""
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr ""
@@ -4371,6 +6516,9 @@ msgstr ""
msgid "Timeago|%s months remaining"
msgstr ""
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr ""
@@ -4386,46 +6534,43 @@ msgstr ""
msgid "Timeago|%s years remaining"
msgstr ""
-msgid "Timeago|1 day remaining"
+msgid "Timeago|1 day ago"
msgstr ""
-msgid "Timeago|1 hour remaining"
+msgid "Timeago|1 day remaining"
msgstr ""
-msgid "Timeago|1 minute remaining"
+msgid "Timeago|1 hour ago"
msgstr ""
-msgid "Timeago|1 month remaining"
-msgstr ""
-
-msgid "Timeago|1 week remaining"
+msgid "Timeago|1 hour remaining"
msgstr ""
-msgid "Timeago|1 year remaining"
+msgid "Timeago|1 minute ago"
msgstr ""
-msgid "Timeago|Past due"
+msgid "Timeago|1 minute remaining"
msgstr ""
-msgid "Timeago|a day ago"
+msgid "Timeago|1 month ago"
msgstr ""
-msgid "Timeago|a month ago"
+msgid "Timeago|1 month remaining"
msgstr ""
-msgid "Timeago|a week ago"
+msgid "Timeago|1 week ago"
msgstr ""
-msgid "Timeago|a year ago"
+msgid "Timeago|1 week remaining"
msgstr ""
-msgid "Timeago|about %s hours ago"
+msgid "Timeago|1 year ago"
msgstr ""
-msgid "Timeago|about a minute ago"
+msgid "Timeago|1 year remaining"
msgstr ""
-msgid "Timeago|about an hour ago"
+msgid "Timeago|Past due"
msgstr ""
msgid "Timeago|in %s days"
@@ -4467,10 +6612,13 @@ msgstr ""
msgid "Timeago|in 1 year"
msgstr ""
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
msgstr ""
-msgid "Timeago|less than a minute ago"
+msgid "Timeago|right now"
+msgstr ""
+
+msgid "Timeout"
msgstr ""
msgid "Time|hr"
@@ -4499,6 +6647,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4508,6 +6659,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4517,21 +6674,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4541,6 +6722,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr ""
@@ -4559,12 +6746,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4577,12 +6782,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr ""
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4598,6 +6836,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr ""
@@ -4616,9 +6857,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4628,9 +6878,21 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4643,7 +6905,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4655,9 +6920,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr ""
@@ -4670,6 +6947,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr ""
@@ -4685,7 +6968,7 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4703,10 +6986,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4733,7 +7028,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4769,6 +7088,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4784,7 +7109,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4796,9 +7121,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4823,7 +7145,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4841,7 +7172,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4850,6 +7184,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4862,22 +7202,40 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4886,6 +7244,9 @@ msgstr ""
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr ""
@@ -4916,9 +7277,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4934,6 +7304,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4952,10 +7328,13 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4971,45 +7350,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -5019,19 +7491,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -5040,15 +7509,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5058,6 +7566,9 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
@@ -5065,6 +7576,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5082,16 +7596,28 @@ msgstr[3] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5103,6 +7629,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5128,24 +7657,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5173,6 +7696,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5206,9 +7732,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5269,6 +7810,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5278,9 +7822,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5324,15 +7865,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5345,6 +7895,16 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index 96a59d6d0d3..6d371f8b818 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:36-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Portuguese, Brazilian\n"
"Language: pt_BR\n"
@@ -15,10 +13,26 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: pt-BR\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:39\n"
msgid " and"
msgstr " e"
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] " piorado em %d ponto"
+msgstr[1] " piorado em %d pontos"
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] " melhorado em %d ponto"
+msgstr[1] " melhorado em %d pontos"
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] "%d arquivo modificado"
+msgstr[1] "%d arquivos modificados"
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
@@ -54,6 +68,26 @@ msgid_plural "%d metrics"
msgstr[0] "%d métrica"
msgstr[1] "%d métricas"
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] "%d nova licença"
+msgstr[1] "%d novas licenças"
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] "%d mudança na lista de commit"
+msgstr[1] "%d mudanças na lista de commit"
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] "%d mudança fora da lista de commit"
+msgstr[1] "%d mudanças fora da lista de commit"
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] "%d vulnerabilidade"
+msgstr[1] "%d vulnerabilidades"
+
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] "%s commit adicional foi omitido para prevenir problemas de performance."
@@ -65,17 +99,32 @@ msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr "%{commit_author_link} fez commit à %{commit_timeago}"
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr "%{counter_storage} (%{counter_repositories} repositórios, %{counter_build_artifacts} artefatos de build, %{counter_lfs_objects} LFS)"
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} participante"
msgstr[1] "%{count} participantes"
+msgid "%{filePath} deleted"
+msgstr "%{filePath} excluído"
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr "%{group_docs_link_start}Grupos%{group_docs_link_end} permitem que você gerencie e colabore em vários projetos. Os membros de um grupo têm acesso a todos os seus projetos."
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} Iniciado"
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr "%{lock_path} está bloqueado pelo usuário do GitLab %{lock_user_id}"
+msgid "%{name}'s avatar"
+msgstr "Avatar de %{name}"
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr "%{nip_domain} pode ser utilizado como um domínio personalizado."
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr "%{number_commits_behind} commits atrás de %{default_branch}, %{number_commits_ahead} commits à frente"
@@ -88,23 +137,80 @@ msgstr "%{number_of_failures} de %{maximum_failures} falhas. O GitLab não tenta
msgid "%{openOrClose} %{noteable}"
msgstr "%{openOrClose} %{noteable}"
+msgid "%{percent}%% complete"
+msgstr "%{percent}%% completado"
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] "%{storage_name}: falha na tentativa de acesso ao storage no host:"
msgstr[1] "%{storage_name}: %{failed_attempts} falhas de acesso ao storage:"
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] "%{text} %{files}"
+msgstr[1] "%{text} %{files} arquivos"
+
msgid "%{text} is available"
msgstr "%{text} está disponível"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(veja o %{link} para informações de como instalar)."
+msgid "%{title} changes"
+msgstr "Alterações de %{title}"
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] "%{type} detectou 1 vulnerabilidade"
+msgstr[1] "%{type} detectou %{vulnerabilityCount} vulnerabilidades"
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr "%{unstaged} mudanças fora e %{staged} mudanças dentro da lista de commit"
msgid "+ %{moreCount} more"
msgstr "%{moreCount} mais"
+msgid "- Runner is active and can process any new jobs"
+msgstr "- O runner está ativo e pode processar novos jobs"
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr "- O runner está pausado e não receberá nenhum novo jobs"
+
msgid "- show less"
msgstr "- exibir menos"
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] "1 adição de %{type}"
+msgstr[1] "%{count} adições %{type}"
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] "1 mudança de %{type}"
+msgstr[1] "%{count} mudanças de %{type}"
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] "1 issue fechada"
+msgstr[1] "%d issues fechadas"
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] "1 merge request fechado"
+msgstr[1] "%d merge requests fechados"
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] "1 merge request com merge"
+msgstr[1] "%d merge requests com merge"
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] "1 issue aberta"
+msgstr[1] "%d issues abertas"
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] "1 merge request aberto"
+msgstr[1] "%d merge request aberto"
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "1 pipeline"
@@ -116,9 +222,51 @@ msgstr "1ª contribuição!"
msgid "2FA enabled"
msgstr "Autenticação de 2 passos ativada"
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr "Entre em contato com o administrador do GitLab para ter permissão."
+
+msgid "403|You don't have the permission to access this page."
+msgstr "Você não tem permissão para acessar essa página."
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr "Certifique-se de que o endereço está correto e a página não foi movida."
+
+msgid "404|Page Not Found"
+msgstr "Página não encontrada"
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr "Por favor, contacte o administrador do GitLab se você acha que isso é um erro."
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> adicionará \"Por <a href=\"#\">@johnsmith</a>\" a todas as issues e comentários originalmente criados por johnsmith@example.com e definirá <a href=\"#\">@johnsmith</a> como o responsável em todas as issues originalmente atribuídas a johnsmith@example.com."
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr "<strong>%{created_count}</strong> criado(s), <strong>%{accepted_count}</strong> aceito(s)."
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr "<strong>%{created_count}</strong> criado, <strong>%{closed_count}</strong> fechado."
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr "<strong>%{pushes}</strong> pushes, mais que <strong>%{commits}</strong> commits por <strong>%{people}</strong> contribuidores."
+
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>Remover</strong> branch de origem"
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr "'Runner' é um processo que executa um job. Você pode configurar quantos Runners você precisar."
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Uma coleção de gráficos sobre Integração Contínua"
@@ -128,33 +276,63 @@ msgstr "Um novo \"branch\" será criado no seu \"fork\" e um novo merge request
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr "Um projeto é onde você armazena seus arquivos (repositório), planeja seu trabalho (issues), e publica sua documentação (wiki), %{among_other_things_link}."
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr "Uma expressão regular que será usada para encontrar a saída do test coverage no rastreador de tarefa. Deixe em branco para desativar"
+
msgid "A user with write access to the source branch selected this option"
msgstr "Um usuário com permissão de escrita no branch de origem selecionou esta opção"
+msgid "About GitLab"
+msgstr "Sobre o GitLab"
+
+msgid "About GitLab CE"
+msgstr "Sobre o GitLab CE"
+
msgid "About auto deploy"
msgstr "Sobre o deploy automático"
+msgid "About this feature"
+msgstr "Sobre essa funcionalidade"
+
msgid "Abuse Reports"
-msgstr "Relatórios de abuso"
+msgstr "Relatórios de Abuso"
msgid "Abuse reports"
-msgstr ""
+msgstr "Relatórios de abuso"
+
+msgid "Accept terms"
+msgstr "Aceitar os temos"
+
+msgid "Accepted MR"
+msgstr "MR aceito"
msgid "Access Tokens"
msgstr "Tokens de acesso"
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr "Acesso negado! Por favor, verifique se você pode adicionar chaves de deploy neste repositório."
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr "Acesso a '%{classification_label}' não permitido"
+
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 "Os acessos à storages com defeito foram temporariamente desabilitados para permitir a sua remontagem. Redefina as informações de armazenamento depois que o problema foi resolvido para permitir o acesso de novo."
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr "Acesse seu runner token, personalize sua configuração de pipeline e visualize o status do seu pipeline e o relatório de coverage."
+
msgid "Account"
msgstr "Conta"
-msgid "Account and limit settings"
-msgstr ""
+msgid "Account and limit"
+msgstr "Conta e limites"
msgid "Active"
msgstr "Ativo"
+msgid "Active Sessions"
+msgstr "Sessões ativas"
+
msgid "Activity"
msgstr "Atividade"
@@ -168,7 +346,7 @@ msgid "Add Contribution guide"
msgstr "Adicionar Guia de contribuição"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr "Adicione o Webhooks de Grupos e GitLab Enterprise Edition."
+msgstr "Adicione Webhooks de grupo e GitLab Enterprise Edition."
msgid "Add Kubernetes cluster"
msgstr "Adicionar cluster Kubernetes"
@@ -179,12 +357,39 @@ msgstr "Adicionar Licença"
msgid "Add Readme"
msgstr "Adicionar leia-me"
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr "Coloque um texto adicional para aparecer em todas as comunicações por email. Limite de %{character_limit} caracteres"
+
+msgid "Add new application"
+msgstr "Adicionar novo aplicativo"
+
msgid "Add new directory"
msgstr "Adicionar novo diretório"
+msgid "Add reaction"
+msgstr "Adicionar reação"
+
msgid "Add todo"
msgstr "Adicionar tarefa"
+msgid "Add user(s) to the group:"
+msgstr "Adicionar usuário(s) ao grupo:"
+
+msgid "Add users to group"
+msgstr "Adicionar usuários ao grupo"
+
+msgid "Additional text"
+msgstr "Texto adicional"
+
+msgid "Admin Area"
+msgstr "Ãrea do Administrador"
+
+msgid "Admin Overview"
+msgstr "Visão Geral do Administrador"
+
+msgid "Admin area"
+msgstr "Ãrea do administrador"
+
msgid "AdminArea|Stop all jobs"
msgstr "Parar todos os processos"
@@ -251,23 +456,26 @@ msgstr "Houve commit com todas as mudanças"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "Todas as funcionalidades estão habilitadas para projetos em branco, a partir de templates ou ao importar, mas você pode desativá-los posteriormente nas configurações do projeto."
-msgid "Allow edits from maintainers."
-msgstr "Permitir as edições dos mantenedores."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr "Permitir commits de membros que podem fazer merge ao branch de destino."
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr "Permitir acesso público aos pipelines e detalhes de trabalho, incluindo logs de saída e artefatos"
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
-msgstr ""
+msgstr "Permitir renderização de diagramas PlantUML em documentos Asciidoc."
msgid "Allow requests to the local network from hooks and services."
-msgstr ""
+msgstr "Permitir requisições de hooks e serviços para a rede local."
msgid "Allows you to add and manage Kubernetes clusters."
msgstr "Permite adicionar e gerenciar clusters do Kubernetes."
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
-msgstr ""
+msgstr "Também chamado de \"Emissor\" ou \"Identificador de confiança em terceiros\""
msgid "Also called \"Relying party service URL\" or \"Reply URL\""
-msgstr ""
+msgstr "Também chamado de \"URL de serviços terceiros\" ou \"URL de resposta\""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr "Alternativamente, você pode usar um %{personal_access_token_link}. Quando você cria seu Token de Acesso Pessoal, você precisará selecionar o escopo do <code>repositório</code>, para que possamos exibir uma lista de seus repositórios públicos e privados que estão disponíveis para se conectar."
@@ -275,6 +483,48 @@ msgstr "Alternativamente, você pode usar um %{personal_access_token_link}. Quan
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "Alternativamente, você pode usar um %{personal_access_token_link}. Quando você cria seu Token de Acesso Pessoal, você precisará selecionar o escopo do <code>repositório</code>, para que possamos exibir uma lista de seus repositórios públicos e privados que estão disponíveis para se importar."
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr "Um aplicativo chamado %{link_to_client} está solicitando acesso à sua conta do GitLab."
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr "Ocorreu um erro ao fazer commit de suas alterações."
+
+msgid "An error has occurred"
+msgstr "Ocorreu um erro"
+
+msgid "An error occured creating the new branch."
+msgstr "Um erro ocorreu ao criar o novo branch."
+
+msgid "An error occured whilst fetching the job trace."
+msgstr "Ocorreu um erro ao carregar o rastro da tarefa."
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr "Ocorreu um erro ao carregar o último pipeline."
+
+msgid "An error occured whilst loading all the files."
+msgstr "Um erro ocorreu no carregamento de todos os arquivos."
+
+msgid "An error occured whilst loading the file content."
+msgstr "Um erro ocorreu no carregamento do conteúdo do arquivo."
+
+msgid "An error occured whilst loading the file."
+msgstr "Um erro ocorreu no carregamento do arquivo."
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr "Ocorreu um erro ao carregar as alterações do merge request."
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr "Ocorreu um erro ao carregar os dados da versão do merge request."
+
+msgid "An error occured whilst loading the merge request."
+msgstr "Ocorreu um erro ao carregar as alterações do merge request."
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr "Ocorreu um erro ao carregar as tarefas de pipeline."
+
msgid "An error occurred previewing the blob"
msgstr "Erro ao pré-visualizar o blob"
@@ -282,13 +532,16 @@ msgid "An error occurred when toggling the notification subscription"
msgstr "Erro ao modificar notificação de assinatura"
msgid "An error occurred when updating the issue weight"
-msgstr "Um erro aconteceu ao atualizar o peso da issue"
+msgstr "Ocorreu um erro ao atualizar o peso do issue"
msgid "An error occurred while adding approver"
-msgstr "Erro ao adicionar o aprovador"
+msgstr "Ocorreu um erro ao adicionar o aprovador"
msgid "An error occurred while detecting host keys"
-msgstr "Erro ao detectar a chave do host"
+msgstr "Ocorreu um erro ao detectar as chaves do host"
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr "Erro ao ignorar alerta. Atualize a página e tente novamente."
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr "Erro ao remover alerta. Atualize a página e tente novamente."
@@ -305,14 +558,14 @@ msgstr "Erro ao recuperar informações da pipeline."
msgid "An error occurred while getting projects"
msgstr "Erro ao recuperar projetos"
-msgid "An error occurred while importing project"
-msgstr "Erro ao importar o projeto"
+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 "Erro ao iniciar o bloqueio do Path"
+msgstr "Ocorreu um erro ao inicializar travas de caminhos"
-msgid "An error occurred while loading commits"
-msgstr "Erro ao carregar os Commits"
+msgid "An error occurred while loading commit signatures"
+msgstr ""
msgid "An error occurred while loading diff"
msgstr "Erro ao carregar o Diff"
@@ -327,7 +580,7 @@ msgid "An error occurred while making the request."
msgstr "Erro ao fazer a requisição."
msgid "An error occurred while removing approver"
-msgstr "Erro ao remover o aprovador"
+msgstr "Ocorreu um erro ao remover o aprovador"
msgid "An error occurred while rendering KaTeX"
msgstr "Erro ao renderizar o KaTeX"
@@ -342,23 +595,47 @@ msgid "An error occurred while retrieving diff"
msgstr "Erro ao recuperar o diff"
msgid "An error occurred while saving LDAP override status. Please try again."
-msgstr "Um erro ocorreu ao sobrescrever o status LDAP."
+msgstr "Ocorreu um erro ao salvar o status de substituição do LDAP. Por favor, tente novamente."
msgid "An error occurred while saving assignees"
msgstr "Erro ao salvar assignees"
+msgid "An error occurred while subscribing to notifications."
+msgstr "Ocorreu um erro ao inscrever às notificações."
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr "Ocorreu um erro ao desinscrever às notificações."
+
msgid "An error occurred while validating username"
msgstr "Erro ao validar o nome de usuário"
msgid "An error occurred. Please try again."
msgstr "Ocorreu um erro. Tente novamente."
+msgid "Anonymous"
+msgstr "Anônimo"
+
+msgid "Anti-spam verification"
+msgstr "Verificação anti-spam"
+
+msgid "Any"
+msgstr "Qualquer um"
+
msgid "Any Label"
msgstr "Qualquer Label"
msgid "Appearance"
msgstr "Aparência"
+msgid "Application"
+msgstr "Aplicativo"
+
+msgid "Application Id"
+msgstr "ID do aplicativo"
+
+msgid "Application: %{name}"
+msgstr "Aplicativo: %{name}"
+
msgid "Applications"
msgstr "Aplicações"
@@ -368,12 +645,21 @@ msgstr "Abr"
msgid "April"
msgstr "Abril"
-msgid "Archived project! Repository is read-only"
-msgstr "Projeto arquivado! O repositório é somente leitura"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr "Projeto arquivado! Repositório e outros recursos de projeto são somente leitura"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Tem certeza que deseja excluir este agendamento de pipeline?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr "Você tem certeza que quer remover %{group_name}?"
+
+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 reset registration token?"
msgstr "Você tem certeza que quer recriar o token de registro?"
@@ -381,7 +667,7 @@ msgid "Are you sure you want to reset the health check token?"
msgstr "Você tem certeza que quer reiniciar o token de status de saúde?"
msgid "Are you sure you want to unlock %{path_lock_path}?"
-msgstr "Tem certeza que deseja desbloquear %{path_lock_path}?"
+msgstr "Você tem certeza que quer destravar %{path_lock_path}?"
msgid "Are you sure?"
msgstr "Você tem certeza?"
@@ -389,8 +675,14 @@ msgstr "Você tem certeza?"
msgid "Artifacts"
msgstr "Artefatos"
+msgid "Ascending"
+msgstr "Ascendente"
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr "Solicite a um mantenedor do grupo para configurar um Runner de grupo."
+
msgid "Assertion consumer service URL"
-msgstr ""
+msgstr "URL de serviço do consumidor de asserção"
msgid "Assign custom color like #FF0000"
msgstr "Coloque uma cor personalizada, como #FF0000"
@@ -405,20 +697,35 @@ msgid "Assign to"
msgstr "Atribuir à"
msgid "Assigned Issues"
-msgstr ""
+msgstr "Problemas Atribuídos"
msgid "Assigned Merge Requests"
-msgstr ""
+msgstr "Merge requests com responsável"
msgid "Assigned to :name"
-msgstr ""
+msgstr "Atribuído a :name"
+
+msgid "Assigned to me"
+msgstr "Atribuído a mim"
msgid "Assignee"
msgstr "Responsável"
+msgid "Assignee boards not available with your current license"
+msgstr "Quadro de responsáveis não disponível com sua licença atual"
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr "Listas de responsáveis mostram todas as issues atribuídas ao usuário selecionado."
+
+msgid "Assignee(s)"
+msgstr "Responsável(is)"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Para anexar arquivo, arraste e solte ou %{upload_link}"
+msgid "Audit Events"
+msgstr "Eventos de Auditoria"
+
msgid "Aug"
msgstr "Ago"
@@ -428,17 +735,41 @@ msgstr "Agosto"
msgid "Authentication Log"
msgstr "Log de autenticação"
+msgid "Authentication log"
+msgstr "Log de autenticação"
+
msgid "Author"
msgstr "Autor"
+msgid "Authorization code:"
+msgstr "Código de autorização:"
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr "A autorização foi concedida digitando seu nome de usuário e senha no aplicativo."
+
+msgid "Authorize"
+msgstr "Autorizar"
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr "Autorizar %{link_to_client} usar sua conta?"
+
+msgid "Authorized At"
+msgstr "Autorizado em"
+
+msgid "Authorized applications (%{size})"
+msgstr "Aplicativos autorizados (%{size})"
+
msgid "Authors: %{authors}"
msgstr "Autores: %{authors}"
+msgid "Auto DevOps"
+msgstr "Auto DevOps"
+
msgid "Auto DevOps enabled"
msgstr "Auto DevOps ativo"
msgid "Auto DevOps, runners and job artifacts"
-msgstr ""
+msgstr "Auto DevOps, runners e artefatos de builds"
msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr "Apps de revisão e deploy automáticos precisam de um %{kubernetes} para funcionar corretamente."
@@ -449,8 +780,11 @@ msgstr "Apps de revisão e deploy automáticos precisam de um nome de domínio e
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Apps de revisão automática e Auto Deploy precisam de um nome de domínio para que funcione corretamente."
-msgid "AutoDevOps|Auto DevOps (Beta)"
-msgstr "Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr "Cancelar automaticamente os pipelines redundantes pendentes"
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr "Auto DevOps"
msgid "AutoDevOps|Auto DevOps documentation"
msgstr "Documentação de auto DevOps"
@@ -470,12 +804,18 @@ msgstr "Você pode automaticamente construir e testar sua aplicação, se você
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr "adicionar um cluster Kubernetes"
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
-msgstr "ativar auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr "ativar Auto DevOps"
msgid "Available"
msgstr "Disponível"
+msgid "Available group Runners : %{runners}"
+msgstr "Runners de grupo disponíveis: %{runners}"
+
+msgid "Available group Runners : %{runners}."
+msgstr "Runners de grupo disponíveis: %{runners}."
+
msgid "Avatar will be removed. Are you sure?"
msgstr "Foto de perfil será removida. Tem certeza?"
@@ -483,19 +823,100 @@ msgid "Average per day: %{average}"
msgstr "Média diária: %{average}"
msgid "Background Color"
-msgstr ""
+msgstr "Cor de Fundo"
+
+msgid "Background Jobs"
+msgstr "Tarefas em Segundo Plano"
+
+msgid "Background color"
+msgstr "Cor do plano de fundo"
msgid "Background jobs"
-msgstr ""
+msgstr "Tarefas em segundo plano"
+
+msgid "Badges"
+msgstr "Selos"
+
+msgid "Badges|A new badge was added."
+msgstr "Novo selo adicionado."
+
+msgid "Badges|Add badge"
+msgstr "Novo selo"
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr "Fala ao adicionar selo, por favor, cheque as URLs inseridas e tente novamente."
+
+msgid "Badges|Badge image URL"
+msgstr "URL da imagem do selo"
+
+msgid "Badges|Badge image preview"
+msgstr "Pré-visualização da imagem do selo"
+
+msgid "Badges|Delete badge"
+msgstr "Apagar selo"
+
+msgid "Badges|Delete badge?"
+msgstr "Apagar selo?"
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr "Erro ao apagar selo, por favor, tente novamente."
+
+msgid "Badges|Group Badge"
+msgstr "Selo de grupo"
+
+msgid "Badges|Link"
+msgstr "Link"
+
+msgid "Badges|No badge image"
+msgstr "Sem imagem"
+
+msgid "Badges|No image to preview"
+msgstr "Sem imagem para pré-visualizar"
+
+msgid "Badges|Project Badge"
+msgstr "Selo de projeto"
+
+msgid "Badges|Reload badge image"
+msgstr "Recarregar imagem do selo"
+
+msgid "Badges|Save changes"
+msgstr "Salvar alterações"
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr "Erro ao salvar selo, por favor, cheque as URLs digitadas e tente novamente."
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr "Badges | As variáveis de %{docsLinkStart}%{docsLinkEnd} oferece suporte a GitLab: %{placeholders}"
+
+msgid "Badges|The badge was deleted."
+msgstr "O selo foi apagado."
+
+msgid "Badges|The badge was saved."
+msgstr "O selo foi salvo."
+
+msgid "Badges|This group has no badges"
+msgstr "Esse grupo não tem selos"
+
+msgid "Badges|This project has no badges"
+msgstr "Esse projeto não tem selos"
+
+msgid "Badges|Your badges"
+msgstr "Seus selos"
msgid "Begin with the selected commit"
msgstr "Comece com o commit selecionado"
+msgid "Below are examples of regex for existing tools:"
+msgstr "Abaixo estão exemplos de regex para ferramentas existentes:"
+
+msgid "Below you will find all the groups that are public."
+msgstr "Abaixo você encontrará todos os grupos que são públicos."
+
msgid "Billing"
msgstr "Cobrança"
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
-msgstr "Grupo %{group_name} está atualmente no plano %{plan_link}."
+msgstr "%{group_name} está atualmente no plano %{plan_link}."
msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
msgstr "Downgrade e upgrade automático para alguns planos ainda não está disponível."
@@ -504,11 +925,14 @@ msgid "BillingPlans|Current plan"
msgstr "Plano atual"
msgid "BillingPlans|Customer Support"
-msgstr "Suporte ao cliente"
+msgstr "Suporte ao Cliente"
msgid "BillingPlans|Downgrade"
msgstr "Downgrade"
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr "Saiba mais sobre cada plano lendo nossa %{faq_link}, ou inicie uma avaliação gratuita de 30 dias do GitLab.com Gold."
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr "Saiba mais sobre cada plano lendo nossa %{faq_link}."
@@ -519,13 +943,13 @@ msgid "BillingPlans|Please contact %{customer_support_link} in that case."
msgstr "Por favor, entre em contato com %{customer_support_link} para resolver seu caso."
msgid "BillingPlans|See all %{plan_name} features"
-msgstr "Veja todas as funcionalidades de %{plan_name}"
+msgstr "Veja todas as funcionalidades do %{plan_name}"
msgid "BillingPlans|This group uses the plan associated with its parent group."
-msgstr "Esse grupo utiliza o plano associado ao seu grupo pai."
+msgstr "Esse grupo usa o plano associado com seu grupo pai."
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
-msgstr "Para gerenciar o plano desse grupo, visite a sessão de cobrança de %{parent_billing_page_link}."
+msgstr "Para gerenciar o plano para esse grupo, visite a seção de cobrança de %{parent_billing_page_link}."
msgid "BillingPlans|Upgrade"
msgstr "Upgrade"
@@ -533,18 +957,39 @@ msgstr "Upgrade"
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr "Você está atualmente no plano %{plan_link}."
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr "Sua avaliação do GitLab.com expirou em %{expiration_date}. %{learn_more_text}"
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr "A sua avaliação Gold irá <strong>expirar após %{expiration_date}</strong>. Você pode saber mais sobre o GitLab.com Gold lendo sobre nossos %{features_link}."
+
+msgid "BillingPlans|features"
+msgstr "recursos"
+
msgid "BillingPlans|frequently asked questions"
-msgstr "perguntas frequentes"
+msgstr "Perguntas frequentes"
msgid "BillingPlans|monthly"
msgstr "mensalmente"
msgid "BillingPlans|paid annually at %{price_per_year}"
-msgstr "pago %{price_per_year} anualmente"
+msgstr "%{price_per_year} pago anualmente"
msgid "BillingPlans|per user"
msgstr "por usuário"
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr "Blog"
+
+msgid "Boards"
+msgstr "Boards"
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr "O branch %{branchName} não foi encontrado no repositório deste projeto."
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] "Branch (%{branch_count})"
@@ -622,8 +1067,8 @@ msgstr "Nenhuma branch para mostrar"
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr "Uma vez que você confirmar e pressionar %{delete_protected_branch}, não pode ser desfeito ou recuperado."
-msgid "Branches|Only a project master or owner can delete a protected branch"
-msgstr "Somente alguém master ou dono do projeto poderá apagar branches protegidas"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr "Somente um mantenedor ou dono do projeto poderá apagar branches protegidas"
msgid "Branches|Overview"
msgstr "Visão Geral"
@@ -659,7 +1104,7 @@ msgid "Branches|Stale branches"
msgstr "Branches obsoletos"
msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
-msgstr "O branch não pode ser atualizado automaticamente porque diverge do seu upstream."
+msgstr "O brach não pôde ser atualizado automaticamente porque ele divergiu de sua contraparte upstream."
msgid "Branches|The default branch cannot be deleted"
msgstr "A branch padrão não pode ser apagada"
@@ -674,13 +1119,13 @@ msgid "Branches|To confirm, type %{branch_name_confirmation}:"
msgstr "Para confirmar, digite %{branch_name_confirmation}:"
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
-msgstr "Para descartar as mudanças locais e sobrescrever a branch com a versão de upstream, apague-o aqui e escolha 'Atualizar agora', acima."
+msgstr "Para descartar as alterações locais e sobrescrever o branch com a versão upstream, exclua-o aqui e escolhe 'Atualize agora' acima."
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
msgstr "Você irá apagar irreparavelmente a branch protegida '%{branch_name}'."
msgid "Branches|diverged from upstream"
-msgstr "divergiu do upstream"
+msgstr "divergido do upstream"
msgid "Branches|merged"
msgstr "merge realizado"
@@ -703,8 +1148,8 @@ msgstr "Acessar arquivos"
msgid "Browse files"
msgstr "Navegar pelos arquivos"
-msgid "Business"
-msgstr ""
+msgid "Business metrics (Custom)"
+msgstr "Métricas de negócios (personalizadas)"
msgid "ByAuthor|by"
msgstr "por"
@@ -712,6 +1157,9 @@ msgstr "por"
msgid "CI / CD"
msgstr "CI / CD"
+msgid "CI / CD Settings"
+msgstr "Configurações de CI / CD"
+
msgid "CI/CD"
msgstr "CI/CD"
@@ -721,26 +1169,86 @@ msgstr "Configuração de CI/CD"
msgid "CI/CD for external repo"
msgstr "CI/CD para um repositório externo"
+msgid "CI/CD settings"
+msgstr "Configurações de CI/CD"
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr "Um arquivo %{ci_file} precisa ser especificado antes de você conseguir utilizar a integração e entrega contínua."
+
+msgid "CICD|Auto DevOps"
+msgstr "Auto DevOps"
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr "Auto DevOps irá automaticamente gerar build, testar e fazer deploy de sua aplicação baseada numa configuração de integração e entrega contínua."
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr "Deploy automático para staging, deploy manual para produção"
+
+msgid "CICD|Continuous deployment to production"
+msgstr "Deploy contínuo para produção"
+
+msgid "CICD|Deployment strategy"
+msgstr "Estratégia de deploy"
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr "Estratégia de deploy precisa de um nome de domínio para funcionar corretamente."
+
+msgid "CICD|Disable Auto DevOps"
+msgstr "Desabilitar DevOps"
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr "Não configure um domínio aqui se você estiver configurando vários clusters de Kubernetes com o Auto DevOps."
+
+msgid "CICD|Enable Auto DevOps"
+msgstr "Ativar DevOps"
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr "Siga o padrão da instância para ter o Auto DevOps ativado ou desativado quando não houver nenhum %{ci_file} específico do projeto."
+
+msgid "CICD|Instance default (%{state})"
+msgstr "Instância padrão (%{state})"
+
msgid "CICD|Jobs"
msgstr "Jobs"
+msgid "CICD|Learn more about Auto DevOps"
+msgstr "Aprender mais sobre Auto DevOps"
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr "As configurações de pipeline do Auto DevOps serão utilizadas quando não existir %{ci_file} no projeto."
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr "Você precisa especificar um domínio se você quiser usar Apps de Revisão Automáticos e Estágios de Deploy automáticos."
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr "Não é possível encontrar o commit HEAD para este branch"
+
msgid "Cancel"
msgstr "Cancelar"
+msgid "Cancel this job"
+msgstr "Cancelar esse job"
+
msgid "Cannot be merged automatically"
-msgstr ""
+msgstr "Não pode ser feito o merge automaticamente"
msgid "Cannot modify managed Kubernetes cluster"
msgstr "Não se pode modificar um cluster Kubernetes gerenciado"
msgid "Certificate fingerprint"
-msgstr ""
+msgstr "Impressão digital do certificado"
msgid "Change Weight"
-msgstr "Alterar peso"
+msgstr "Alterar Peso"
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
-msgstr ""
+msgstr "Altere esse valor para influenciar com que frequência a interface do usuário do GitLab pesquisa atualizações."
msgid "ChangeTypeActionLabel|Pick into branch"
msgstr "Pick para um branch"
@@ -784,26 +1292,41 @@ msgstr "Cherry-pick esse commit"
msgid "Cherry-pick this merge request"
msgstr "Cherry-pick esse merge request"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr "Escolha <strong>Criar arquivo</strong> e aguarde até que o arquivamento seja concluído."
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr "Escolha <strong>Próximo</strong> na parte inferior da página."
+
msgid "Choose File ..."
msgstr "Escolha o arquivo ..."
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."
+msgid "Choose any color."
+msgstr "Escolha qualquer cor."
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr "Escolha entre <code>clone</code> ou <code>fetch</code> para obter o código do aplicativo recente"
+
msgid "Choose file..."
msgstr "Escolha o arquivo..."
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr "Escolha quais grupos você deseja sincronizar nesse nó secundário."
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
-msgstr "Escolha quais repositórios você deseja se conectar e executar CI/CD pipeline."
+msgstr "Escolha quais repositórios você deseja se conectar e executar pipelines de CI/CD."
msgid "Choose which repositories you want to import."
msgstr "Escolha quais repositórios você deseja importar."
msgid "Choose which shards you wish to synchronize to this secondary node."
-msgstr "Escolha quais shards você deseja que sincronizem com esse nó secundário."
+msgstr "Escolha quais shards você deseja sincronizar nesse nó secundário."
msgid "CiStatusLabel|canceled"
msgstr "cancelado"
@@ -818,10 +1341,10 @@ msgid "CiStatusLabel|manual action"
msgstr "ação manual"
msgid "CiStatusLabel|passed"
-msgstr "passou"
+msgstr "bem sucedido"
msgid "CiStatusLabel|passed with warnings"
-msgstr "passou com avisos"
+msgstr "bem sucedido com avisos"
msgid "CiStatusLabel|pending"
msgstr "pendente"
@@ -848,7 +1371,7 @@ msgid "CiStatusText|manual"
msgstr "manual"
msgid "CiStatusText|passed"
-msgstr "passou"
+msgstr "bem sucedido"
msgid "CiStatusText|pending"
msgstr "pendente"
@@ -875,7 +1398,7 @@ msgid "CiVariable|All environments"
msgstr "Todos os ambientes"
msgid "CiVariable|Create wildcard"
-msgstr "Criar um curinga"
+msgstr "Criar curinga"
msgid "CiVariable|Error occured while saving variables"
msgstr "Erro ao salvar variáveis"
@@ -887,7 +1410,7 @@ msgid "CiVariable|Protected"
msgstr "Protegido"
msgid "CiVariable|Search environments"
-msgstr "Procurar ambientes"
+msgstr "Pesquisar ambientes"
msgid "CiVariable|Toggle protected"
msgstr "Alternar proteção"
@@ -898,20 +1421,44 @@ msgstr "Falha na validação"
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "interruptor da api"
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr "está indisponível: %{reason}"
+
+msgid "Clear search input"
+msgstr "Limpar campo de pesquisa"
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr "Clique em qualquer <strong>nome de projeto</strong> na lista a seguir para navegar para o milestone do projeto."
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr "Clique no botão <strong>Baixar</strong> e aguarde a conclusão do download."
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr "Clique no botão <strong>Promover</strong> no canto superior direito para promover a um milestone de grupo."
+
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr "Clique no botão à direita <strong>Selecionar nenhum</strong>, uma vez que só precisamos do \"Google Code Project Hosting\"."
+
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr "Clique no botão abaixo para iniciar o processo de instalação navegando para a página do Kubernetes"
+msgid "Click to expand it."
+msgstr "Clique para expandir."
+
msgid "Click to expand text"
msgstr "Cliquei pra expandir o texto"
msgid "Client authentication certificate"
-msgstr ""
+msgstr "Certificado de autenticação do cliente"
msgid "Client authentication key"
-msgstr ""
+msgstr "Chave de autenticação do cliente"
msgid "Client authentication key password"
-msgstr ""
+msgstr "Senha da chave de autenticação do cliente"
+
+msgid "Clients"
+msgstr "Clientes"
msgid "Clone repository"
msgstr "Clonar repositório"
@@ -922,6 +1469,9 @@ msgstr "Fechar"
msgid "Closed"
msgstr "Fechado"
+msgid "Closed issues"
+msgstr "Issues Fechadas"
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} foi instalado com sucesso no seu cluster Kubernetes"
@@ -931,12 +1481,15 @@ msgstr "API URL"
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr "Adicionar cluster Kubernetes"
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
-msgstr "Adicionar um cluster Kubernetes existente"
-
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Opções avançadas na integração deste cluster Kubernetes"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr "Erro ao recuperar zonas de projeto: %{error}"
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr "Erro ao recuperar seus projetos: %{error}"
+
msgid "ClusterIntegration|Applications"
msgstr "Aplicações"
@@ -949,14 +1502,11 @@ msgstr "Certificado CA"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Pacote de autoridade certificadora (Formato PEM)"
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr "Escolha como configurar a integração do cluster Kubernetes"
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr "Escolha qual dos ambientes do seu projeto usará este cluster Kubernetes."
msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr ""
+msgstr "Controle como seu cluster Kubernetes se integra com o GitLab"
msgid "ClusterIntegration|Copy API URL"
msgstr "Copiar URL da API"
@@ -965,43 +1515,49 @@ msgid "ClusterIntegration|Copy CA Certificate"
msgstr "Copiar certificado CA"
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
-msgstr ""
+msgstr "Copiar o endereço IP de entrada para a área de transferência"
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr "Copiar nome do host Jupyter para a área de transferência"
msgid "ClusterIntegration|Copy Kubernetes cluster name"
-msgstr ""
+msgstr "Copiar nome do cluster Kubernetes"
msgid "ClusterIntegration|Copy Token"
msgstr "Copiar token"
msgid "ClusterIntegration|Create Kubernetes cluster"
-msgstr ""
+msgstr "Integração de Clusters | Criar cluster Kubernetes"
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
-msgstr ""
+msgid "ClusterIntegration|Did you know?"
+msgstr "Você sabia?"
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr ""
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr "Digite detalhes para seu cluster Kubernetes"
-msgid "ClusterIntegration|Create on GKE"
-msgstr "Criar no GKE"
+msgid "ClusterIntegration|Environment scope"
+msgstr "Escopo de ambiente"
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr "Insira os detalhes para o cluster Kubernetes existente"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr "Cada nova conta no Google Cloud Plataform (GCP) recebe US$300 em créditos na %{sign_up_link}. Em parceria com o Google, o Gitlab pode oferecer um adicional de US$200 para novas contas do GCP para começar a usar a integração GKE do GitLab."
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
-msgstr ""
+msgid "ClusterIntegration|Fetching machine types"
+msgstr "Recuperando tipos de máquina"
-msgid "ClusterIntegration|Environment scope"
-msgstr ""
+msgid "ClusterIntegration|Fetching projects"
+msgstr "Recuperando projetos"
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr "Recuperando zonas"
msgid "ClusterIntegration|GitLab Integration"
-msgstr ""
+msgstr "Integração GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "Gitlab Runner"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
-msgstr "ID do projeto no Google Cloud Platform"
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr "Google Cloud Platform projeto"
msgid "ClusterIntegration|Google Kubernetes Engine"
msgstr "Google Kubernetes Engine"
@@ -1012,20 +1568,26 @@ msgstr "Projeto do Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Hide"
+msgstr "Ocultar"
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr "Se você está configurando múltiplos clusters e você está usando auto DevOps, %{help_link_start}leia isso primeiro%{help_link_end}."
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
-msgstr ""
+msgstr "Para mostrar a saúde do cluster, nós precisamos provisionar seu cluster com Prometheus para coletar os dados necessários."
msgid "ClusterIntegration|Ingress"
msgstr "Ingressar"
msgid "ClusterIntegration|Ingress IP Address"
-msgstr ""
+msgstr "Endereço IP de entrada"
msgid "ClusterIntegration|Install"
msgstr "Instalar"
msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
+msgstr "Instalar Prometheus"
msgid "ClusterIntegration|Installed"
msgstr "Instalado"
@@ -1034,73 +1596,97 @@ msgid "ClusterIntegration|Installing"
msgstr "Instalando"
msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
-msgstr ""
+msgstr "Integrar automação de cluster Kubernetes"
msgid "ClusterIntegration|Integration status"
-msgstr ""
+msgstr "Status de integração"
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr "Nome do host Jupyter"
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr "JupyterHub"
msgid "ClusterIntegration|Kubernetes cluster"
-msgstr ""
+msgstr "Cluter Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster details"
-msgstr ""
+msgstr "Detalhes do cluster Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
+msgstr "Saúde do cluster Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster integration"
-msgstr ""
+msgstr "Integração com o cluster Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
+msgstr "Integração com o cluster Kubernetes está desabilitada para esse projeto."
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
+msgstr "Integração com o cluster Kubernetes está ativada para esse projeto."
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
+msgstr "Integração com cluster Kubernetes está ativada para esse projeto. Desabilitar essa integração não afetará seu cluster Kubernetes, somente desligará a conexão do GitLab com o mesmo."
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
-msgstr ""
+msgstr "O cluster Kubernetes está sendo criado no Google Kubernetes Engine..."
msgid "ClusterIntegration|Kubernetes cluster name"
-msgstr ""
+msgstr "Nome do cluster Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
-msgstr ""
+msgstr "Cluster Kubernetes foi criado com sucesso no Google Kubernetes Engine. Atualize a página para ver os detalhes do cluster"
msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr ""
+msgstr "Clusters Kubernetes te permitem usar apps de revisão, publicar suas aplicações, executar pipelines e muito mais de um jeito fácil. %{link_to_help_page}"
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
-msgstr ""
+msgstr "Cluster Kubernetes podem ser usados para publicar aplicações e permitir app de revisão para esse projeto"
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
-msgstr "Leia mais sobre %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr "Saiba mais sobre os %{help_link_start_machine_type}tipos de máquinas%{help_link_end} e seus %{help_link_start_pricing}preços%{help_link_end}."
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr "Saiba mais sobre os %{help_link_start}Kubernetes%{help_link_end}."
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr "Saiba mais sobre as %{help_link_start}zonas%{help_link_end}."
msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
+msgstr "Ler mais sobre ambientes"
msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
+msgstr "Ler mais sobre configurações de segurança"
msgid "ClusterIntegration|Machine type"
msgstr "Tipo de máquina"
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
-msgstr ""
+msgstr "Tenha certeza de que sua conta %{link_to_requirements} para criar clusters Kubernetes"
msgid "ClusterIntegration|Manage"
-msgstr ""
+msgstr "Gerenciar"
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
-msgstr ""
+msgstr "Gerenciar seu cluster Kubernetes visitando %{link_gke}"
msgid "ClusterIntegration|More information"
msgstr "Mais informações"
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
-msgstr "Multiplos clusters Kubernetes estão disponíveis no GitLab Enterprise Edition Premium e Ultimate"
+msgstr "Múltiplos clusters Kubernetes estão disponíveis GitLab Enterprise Edition Premium e Ultimate"
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr "Nenhum tipo de máquina corresponde à sua pesquisa"
+
+msgid "ClusterIntegration|No projects found"
+msgstr "Nenhum projeto encontrado"
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr "Nenhum projeto corresponde à sua pesquisa"
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr "Nenhuma zona corresponde à sua pesquisa"
msgid "ClusterIntegration|Note:"
msgstr "Nota:"
@@ -1109,14 +1695,11 @@ msgid "ClusterIntegration|Number of nodes"
msgstr "Número de nós"
msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
-msgstr ""
+msgstr "Por favor, entre com as informações de acesso para seu cluter Kubernetes. Se precisar de ajuda, você pode ler nosso %{link_to_help_page} em Kubernetes"
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Por favor, tenha certeza que sua conta no Google cumpre com os requisitos:"
-msgid "ClusterIntegration|Project ID"
-msgstr "ID do projeto"
-
msgid "ClusterIntegration|Project namespace"
msgstr "Namespace do projeto"
@@ -1127,16 +1710,16 @@ msgid "ClusterIntegration|Prometheus"
msgstr "Prometheus"
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
-msgstr ""
+msgstr "Leia nosso %{link_to_help_page} em integração de cluter Kubernetes."
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
-msgstr ""
+msgstr "Remover integração com o cluster Kubernetes"
msgid "ClusterIntegration|Remove integration"
msgstr "Remover integração"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
-msgstr ""
+msgstr "Remover configuração desse cluster Kubernetes para esse projeto. Isso não apagará seu cluster Kubernetes atual."
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "Solicitação para início de instalação falhou"
@@ -1144,20 +1727,38 @@ msgstr "Solicitação para início de instalação falhou"
msgid "ClusterIntegration|Save changes"
msgstr "Salvar alterações"
+msgid "ClusterIntegration|Search machine types"
+msgstr "Pesquisar tipos de máquina"
+
+msgid "ClusterIntegration|Search projects"
+msgstr "Pesquisar projetos"
+
+msgid "ClusterIntegration|Search zones"
+msgstr "Pesquisar zonas"
+
msgid "ClusterIntegration|Security"
-msgstr ""
+msgstr "Segurança"
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "Veja e edite os detalhes de seus cluster Kubernates"
-msgid "ClusterIntegration|See machine types"
-msgstr "Ver tipos de máquina"
+msgid "ClusterIntegration|Select machine type"
+msgstr "Selecionar tipo de máquina"
+
+msgid "ClusterIntegration|Select project"
+msgstr "Selecionar projeto"
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr "Selecione projeto e zona para escolher o tipo de máquina"
-msgid "ClusterIntegration|See your projects"
-msgstr "Ver seus projetos"
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr "Selecione o projeto para escolher a zona"
-msgid "ClusterIntegration|See zones"
-msgstr "Ver zonas"
+msgid "ClusterIntegration|Select zone"
+msgstr "Selecione a zona"
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr "Selecione a zone para escolher o tipo de máquina"
msgid "ClusterIntegration|Service token"
msgstr "Token de serviço"
@@ -1175,22 +1776,25 @@ msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Algo deu errado ao instalar %{title}"
msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr ""
+msgstr "O configuração de cluster padrão permite acesso a uma gama de funcionalidades necessárias para gerar build e publicar aplicações em container."
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
-msgstr ""
+msgstr "Essa conta precisa de permissões para criar um cluster Kubernetes no %{link_to_container_project} especificado"
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
-msgstr ""
+msgstr "Alternar cluster Kubernetes"
msgid "ClusterIntegration|Toggle Kubernetes cluster"
-msgstr ""
+msgstr "Alternar cluster Kubernetes"
msgid "ClusterIntegration|Token"
msgstr "Token"
+msgid "ClusterIntegration|Validating project billing status"
+msgstr "Validando status de faturamento do projeto"
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr ""
+msgstr "Com um cluster Kubernetes associado a esse projeto, você pode utilizar apps de revisão, publicar suas aplicações, executar suas pipelines e muito mais de um jeito simples."
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "Sua conta precisa de %{link_to_kubernetes_engine}"
@@ -1202,7 +1806,7 @@ msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr "acesso ao Google Container Engine"
msgid "ClusterIntegration|check the pricing here"
-msgstr ""
+msgstr "verifique os preços aqui"
msgid "ClusterIntegration|documentation"
msgstr "documentação"
@@ -1219,14 +1823,23 @@ msgstr "atende aos requisitos"
msgid "ClusterIntegration|properly configured"
msgstr "configurado corretamente"
+msgid "ClusterIntegration|sign up"
+msgstr "cadastrar"
+
+msgid "Cohorts"
+msgstr "Cohorts"
+
msgid "Collapse"
msgstr "Recolher"
-msgid "Comment and resolve discussion"
-msgstr ""
+msgid "Collapse sidebar"
+msgstr "Minimizar barra lateral"
-msgid "Comment and unresolve discussion"
-msgstr "Comente e marque discussão como não resolvida"
+msgid "Comment & resolve discussion"
+msgstr "Comentar e marcar a discussão como resolvida"
+
+msgid "Comment & unresolve discussion"
+msgstr "Comentar e marcar a discussão como não resolvida"
msgid "Comments"
msgstr "Comentários"
@@ -1292,6 +1905,9 @@ msgstr "Nenhum merge request relacionado foi encontrado"
msgid "Committed by"
msgstr "Commit feito por"
+msgid "Commit…"
+msgstr "Commit…"
+
msgid "Compare"
msgstr "Comparar"
@@ -1302,10 +1918,10 @@ msgid "Compare Revisions"
msgstr "Comparar revisões"
msgid "Compare changes with the last commit"
-msgstr ""
+msgstr "Compare as mudanças do último commit"
msgid "Compare changes with the merge request target branch"
-msgstr ""
+msgstr "Compare as mudanças do merge request com o branch de destino"
msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
msgstr "%{source_branch} e %{target_branch} são o mesmo."
@@ -1323,28 +1939,31 @@ msgid "CompareBranches|There isn't anything to compare."
msgstr "Não há nada para comparar."
msgid "Confidential"
-msgstr ""
+msgstr "Confidencial"
msgid "Confidentiality"
msgstr "Confidencialidade"
msgid "Configure Gitaly timeouts."
-msgstr ""
+msgstr "Configurar timeouts do Gitaly."
msgid "Configure Sidekiq job throttling."
-msgstr ""
+msgstr "Configurar otimização de jobs no Sidekiq."
msgid "Configure automatic git checks and housekeeping on repositories."
-msgstr ""
+msgstr "Configurar housekeeping e checagens do git nos repositórios."
msgid "Configure limits for web and API requests."
-msgstr ""
+msgstr "Configurar limites para web e requisições para API."
+
+msgid "Configure push and pull mirrors."
+msgstr "Configurar push e pull de espelhamentos."
msgid "Configure storage path and circuit breaker settings."
-msgstr ""
+msgstr "Configurar caminho de armazenamento e circuit breaker."
msgid "Configure the way a user creates a new account."
-msgstr ""
+msgstr "Configurar a forma como o usuário cria uma nova conta."
msgid "Connect"
msgstr "Conectar"
@@ -1356,7 +1975,7 @@ msgid "Connect repositories from GitHub"
msgstr "Conectar repositórios do GitHub"
msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
-msgstr ""
+msgstr "Conecte seus repositórios externos e pipelines CI/CD vão ser executados para novos commits. Um projeto GitLab será criado com apenas recursos de CI/CD ativados."
msgid "Connecting..."
msgstr "Conectando..."
@@ -1406,8 +2025,20 @@ msgstr "Use nomes de imagem diferentes"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr "Com o Container Registry do Docker integrado ao Gitlab, todo projeto pode ter seu próprio espaço para guardar suas imagens."
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr "Você pode usar também um %{deploy_token} para acesso somente-leitura às imagens do registry."
+
+msgid "Continue"
+msgstr "Continuar"
+
+msgid "Continue to the next step"
+msgstr "Continuar para a próxima etapa"
+
msgid "Continuous Integration and Deployment"
-msgstr ""
+msgstr "Integração contínua e Implantação"
+
+msgid "Contribute to GitLab"
+msgstr "Contribua para o GitLab"
msgid "Contribution"
msgstr "Contribuições"
@@ -1415,6 +2046,9 @@ msgstr "Contribuições"
msgid "Contribution guide"
msgstr "Guia de contribuição"
+msgid "Contributions per group member"
+msgstr "Contribuições por membro do grupo"
+
msgid "Contributors"
msgstr "Contribuidores"
@@ -1430,14 +2064,23 @@ msgstr "Commits à %{branch_name}, excluindo commits de merge. Limitado à 6000
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr "Por favor, espere um momento, essa página será atualizada automaticamente."
+msgid "Control the display of third party offers."
+msgstr "Controle a exibição de ofertas de terceiros."
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
-msgstr "Controle a concorrência máxima de LFS/preenchimento de repositórios para esse nó secundário"
+msgstr "Controlar a concorrência máxima do enchimento de anexo/LFS para esse nó secundário"
msgid "Control the maximum concurrency of repository backfill for this secondary node"
-msgstr "Controle a concorrência máxima de preenchimento de repositório para esse nó secundário"
+msgstr "Controlar a concorrência máxima do enchimento do repositório para esse nó secundário"
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr "Controlar a concorrência máxima de operações de verificação para esse nó Geo"
+
+msgid "ConvDev Index"
+msgstr ""
msgid "Copy SSH public key to clipboard"
-msgstr "Copiar chave públic SSH para área de transferência"
+msgstr "Copiar chave pública SSH para área de transferência"
msgid "Copy URL to clipboard"
msgstr "Copiar URL para área de transferência"
@@ -1451,9 +2094,21 @@ msgstr "Copiar o comando para área de transferência"
msgid "Copy commit SHA to clipboard"
msgstr "Copiar SHA do commit para a área de transferência"
+msgid "Copy file path to clipboard"
+msgstr "Copiar o local do arquivo para a área de transferência"
+
+msgid "Copy incoming email address to clipboard"
+msgstr "Copiar endereço de e-mail para a área de transferência"
+
msgid "Copy reference to clipboard"
msgstr "Copiar referência para área de transferência"
+msgid "Copy to clipboard"
+msgstr "Copiar para área de transferência"
+
+msgid "Copy token to clipboard"
+msgstr "Copiar token para a área de transferência"
+
msgid "Create"
msgstr "Criar"
@@ -1466,26 +2121,38 @@ msgstr "Criar uma nova branch"
msgid "Create a new branch and merge request"
msgstr "Criar um novo branch e abrir merge request"
+msgid "Create a new issue"
+msgstr "Criar uma nova issue"
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Crie um token de acesso pessoal na sua conta para dar pull ou push via %{protocol}."
msgid "Create branch"
msgstr "Criar a branch"
+msgid "Create commit"
+msgstr "Criar commit"
+
msgid "Create directory"
msgstr "Criar diretório"
msgid "Create empty repository"
-msgstr ""
+msgstr "Criar repositório vazio"
msgid "Create epic"
-msgstr "Criar épico"
+msgstr "Criar epic"
msgid "Create file"
msgstr "Criar arquivo"
+msgid "Create group"
+msgstr "Criar grupo"
+
msgid "Create group label"
-msgstr ""
+msgstr "Criar Label de grupo"
+
+msgid "Create issue"
+msgstr "Criar issue"
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr "Criar lista a partir de labels. Issues com labels aparecem nestas listas."
@@ -1505,6 +2172,9 @@ msgstr "Criar nova pasta"
msgid "Create new file"
msgstr "Criar novo arquivo"
+msgid "Create new file or directory"
+msgstr "Criar novo arquivo ou diretório"
+
msgid "Create new label"
msgstr "Criar nova label"
@@ -1512,7 +2182,7 @@ msgid "Create new..."
msgstr "Criar novo..."
msgid "Create project label"
-msgstr ""
+msgstr "Criar Label de projeto"
msgid "CreateNewFork|Fork"
msgstr "Fork"
@@ -1523,14 +2193,20 @@ msgstr "Tag"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "criar um token de acesso pessoal"
-msgid "Creates a new branch from %{branchName}"
-msgstr "Cria um novo branch de %{branchName}"
+msgid "Created"
+msgstr "Feito"
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
-msgstr ""
+msgid "Created At"
+msgstr "Criado em"
+
+msgid "Created by me"
+msgstr "Criado por mim"
+
+msgid "Created on:"
+msgstr "Criado em:"
msgid "Creating epic"
-msgstr "Criando épico"
+msgstr "Criando epic"
msgid "Cron Timezone"
msgstr "Fuso horário do cron"
@@ -1541,6 +2217,15 @@ msgstr "Sintaxe do cron"
msgid "Current node"
msgstr "Nó atual"
+msgid "CurrentUser|Profile"
+msgstr "Perfil"
+
+msgid "CurrentUser|Settings"
+msgstr "Configurações"
+
+msgid "Custom CI config path"
+msgstr "Caminho de configuração do IC personalizado"
+
msgid "Custom notification events"
msgstr "Eventos de notificação personalizados"
@@ -1548,6 +2233,12 @@ msgid "Custom notification levels are the same as participating levels. With cus
msgstr "Níveis de notificação personalizados são equivalentes a níveis de participação. Com níveis de notificação personalizados você também será notificado sobre eventos selecionados. Para mais informações, visite %{notification_link}."
msgid "Customize colors"
+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 ""
+
+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 "Cycle Analytics"
@@ -1574,6 +2265,9 @@ msgstr "Homologação"
msgid "CycleAnalyticsStage|Test"
msgstr "Teste"
+msgid "Dashboard"
+msgstr "Dashboard"
+
msgid "DashboardProjects|All"
msgstr "Todos"
@@ -1586,15 +2280,36 @@ msgstr "Dez"
msgid "December"
msgstr "Dezembro"
+msgid "Decline and sign out"
+msgstr "Recusar e sair"
+
msgid "Default classification label"
msgstr "Label de classificação padrão"
+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"
+
msgid "Define a custom pattern with cron syntax"
msgstr "Defina um padrão personalizado utilizando a sintaxe do cron"
msgid "Delete"
msgstr "Excluir"
+msgid "Delete Snippet"
+msgstr "Excluir Snippet"
+
+msgid "Delete list"
+msgstr "Excluir lista"
+
+msgid "Deleted"
+msgstr "Excluído"
+
+msgid "Deny"
+msgstr "Recusar"
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "Implantação"
@@ -1603,11 +2318,143 @@ msgstr[1] "Implantações"
msgid "Deploy Keys"
msgstr "Chaves para deploy"
+msgid "DeployKeys|+%{count} others"
+msgstr "+%{count} outros"
+
+msgid "DeployKeys|Current project"
+msgstr "Projeto Atual"
+
+msgid "DeployKeys|Deploy key"
+msgstr "Chave de deploy"
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr "Chaves de deploy ativas"
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr "Erro ao ativar chaves de deploy"
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr "Erro ao obter chaves de deploy"
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr "Erro ao remover chave de deploy"
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr "Expandir %{count} outros projetos"
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr "Carregando chaves de deploy"
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr "Nenhuma chave de deploy encontrada. Crie uma com o formulário acima."
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr "Chaves de deploy acessíveis de forma privada"
+
+msgid "DeployKeys|Project usage"
+msgstr "Uso do projeto"
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr "Chaves de deploy acessíveis publicamente"
+
+msgid "DeployKeys|Read access only"
+msgstr "Apenas acesso de leitura"
+
+msgid "DeployKeys|Write access allowed"
+msgstr "Acesso de edição permitido"
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr "Você removerá essa chave de deploy. Tem certeza?"
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr "Tokens de deploy ativos (%{active_tokens})"
+
+msgid "DeployTokens|Add a deploy token"
+msgstr "Adicionar um token de deploy"
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr "Permite acesso somente-leitura às imagens do registry"
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr "Permite acesso somente-leitura ao repositório"
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr "Copiar token de deploy para área de transferência"
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr "Copiar nome de usuário para área de transferência"
+
+msgid "DeployTokens|Create deploy token"
+msgstr "Criar token de deploy"
+
+msgid "DeployTokens|Created"
+msgstr "Criado"
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr "Tokens de deploy"
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr "Tokens de deploy permitem acesso somente-leitura ao seu repositório e imagens do registry."
+
+msgid "DeployTokens|Expires"
+msgstr "Expira"
+
+msgid "DeployTokens|Name"
+msgstr "Nome"
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr "Escolha um nome para a aplicação e lhe forneceremos um token de deploy exclusivo."
+
+msgid "DeployTokens|Revoke"
+msgstr "Revogar"
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr "Revogar %{name}"
+
+msgid "DeployTokens|Scopes"
+msgstr "Escopos"
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr "Esta ação não pode ser desfeita."
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr "Este projeto não possui tokens de deploy ativos."
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr "Use este token como uma senha. Certifique-se de salvá-lo, pois, não será possível acessá-lo novamente."
+
+msgid "DeployTokens|Use this username as a login."
+msgstr "Utilize este nome de usuário como um login."
+
+msgid "DeployTokens|Username"
+msgstr "Nome de Usuário"
+
+msgid "DeployTokens|You are about to revoke"
+msgstr "Você está prestes a revogar"
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr "Seu novo token de deploy"
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr "Seu novo token de deploy de projeto foi criado."
+
+msgid "Deprioritize label"
+msgstr "Despriorizar label"
+
+msgid "Descending"
+msgstr "Decrescente"
+
msgid "Description"
msgstr "Descrição"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
-msgstr "Modelos de descrição permitem que você defina modelos de contextos específicos para issue e campos de descrição de merge requests para seu projeto."
+msgstr "Os modelos de descrição permitem que você defina modelos específicos de contexto para os campos de descrição de merge request e emissão para seu projeto."
+
+msgid "Description:"
+msgstr "Descrição:"
+
+msgid "Destroy"
+msgstr ""
msgid "Details"
msgstr "Detalhes"
@@ -1615,27 +2462,51 @@ msgstr "Detalhes"
msgid "Diffs|No file name available"
msgstr "Nenhum nome de arquivo disponível"
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr "Algo errado aconteceu ao buscar linhas de comparação."
+
msgid "Directory name"
msgstr "Nome do diretório"
msgid "Disable"
msgstr "Desabilitar"
+msgid "Disable for this project"
+msgstr "Desativar para este projeto"
+
+msgid "Disable group Runners"
+msgstr "Desabilitar runners de grupo"
+
+msgid "Discard changes"
+msgstr "Rejeitar alterações"
+
msgid "Discard draft"
msgstr "Descartar rascunho"
msgid "Discover GitLab Geo."
-msgstr "Descubra Gitlab Geo."
+msgstr "Descubra o GitLab Geo."
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr "Descubra projetos, grupos e snippets. Compartilhe seus projetos com outras pessoas"
+
+msgid "Dismiss"
+msgstr ""
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Ignorar introdução do Cycle Analytics"
msgid "Dismiss Merge Request promotion"
-msgstr "Ignorar anúncio do merge request"
+msgstr "Descartar promoção de Merge Request"
-msgid "Documentation for popular identity providers"
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
msgstr ""
+msgid "Documentation for popular identity providers"
+msgstr "Documentação para provedores de identidade populares"
+
+msgid "Domain"
+msgstr "Domínio"
+
msgid "Don't show again"
msgstr "Não exibir novamente"
@@ -1670,70 +2541,112 @@ msgid "DownloadSource|Download"
msgstr "Baixar"
msgid "Downvotes"
-msgstr ""
+msgstr "Votos negativos"
msgid "Due date"
msgstr "Validade"
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
-msgstr ""
+msgstr "Durante esse processo, você será perguntado por URLs do lado do GitLab. Use as URLs mostradas abaixo."
+
+msgid "Each Runner can be in one of the following states:"
+msgstr "Cada runner pode estar em um dos seguintes estados:"
msgid "Edit"
msgstr "Alterar"
+msgid "Edit Label"
+msgstr "Editar Label"
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "Alterar Agendamento do Pipeline %{id}"
+msgid "Edit Snippet"
+msgstr "Editar Snippet"
+
+msgid "Edit application"
+msgstr "Editar aplicativo"
+
msgid "Edit files in the editor and commit changes here"
msgstr "Alterar arquivos no editor e fazer commit das alterações aqui"
-msgid "Editing"
-msgstr ""
+msgid "Edit group: %{group_name}"
+msgstr "Editar grupo: %{group_name}"
+
+msgid "Edit identity for %{user_name}"
+msgstr "Editar identidade para %{user_name}"
msgid "Elasticsearch"
-msgstr ""
+msgstr "Elasticsearch"
msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
-msgstr ""
+msgstr "Integração com Elasticsearch. Elasticsearch AWS IAM."
msgid "Email"
-msgstr ""
+msgstr "E-mail"
+
+msgid "Email patch"
+msgstr "Patch de email"
msgid "Emails"
msgstr "Emails"
+msgid "Embed"
+msgstr "Embutido"
+
msgid "Enable"
msgstr "Ativar"
msgid "Enable Auto DevOps"
msgstr "Ativar Auto DevOps"
+msgid "Enable Pseudonymizer data collection"
+msgstr "Ativar coleção de dados Pseudonymizer"
+
msgid "Enable SAML authentication for this group"
-msgstr ""
+msgstr "Ativar autenticação SAML para esse grupo"
msgid "Enable Sentry for error reporting and logging."
-msgstr ""
+msgstr "Ativar Sentry para report de erro e log."
msgid "Enable and configure InfluxDB metrics."
-msgstr ""
+msgstr "Habilitar e configurar métricas InfluxDB."
msgid "Enable and configure Prometheus metrics."
-msgstr ""
+msgstr "Ativar e configurar métricas do Prometheus."
msgid "Enable classification control using an external service"
-msgstr ""
+msgstr "Ativar controle de classificação usando um serviço externo"
+
+msgid "Enable for this project"
+msgstr "Ativar para este projeto"
+
+msgid "Enable group Runners"
+msgstr "Ativar grupo de runners"
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr "Ative ou desative certos tipos de funcionalidades e escolha os níveis de acesso."
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr "Ative ou desative a coleção de dados Pseudonymizer"
msgid "Enable or disable version check and usage ping."
-msgstr ""
+msgstr "Ativar ou desativar checagem de versão e uso de ping."
msgid "Enable reCAPTCHA or Akismet and set IP limits."
-msgstr ""
+msgstr "Ativar reCAPTCHA ou Akismet e definir seus limites de IP."
msgid "Enable the Performance Bar for a given group."
-msgstr ""
+msgstr "Ative a barra de desempenho para um determinado grupo."
msgid "Enabled"
-msgstr ""
+msgstr "Habilitado"
+
+msgid "Ends at (UTC)"
+msgstr "Termina em (UTC)"
+
+msgid "Environments"
+msgstr "Ambientes"
msgid "Environments|An error occurred while fetching the environments."
msgstr "Um erro ocorreu ao recuperar ambientes."
@@ -1741,9 +2654,18 @@ msgstr "Um erro ocorreu ao recuperar ambientes."
msgid "Environments|An error occurred while making the request."
msgstr "Um erro ocorreu ao fazer a requisição."
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr "Commit"
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr "Deploy"
@@ -1756,56 +2678,71 @@ msgstr "Ambientes"
msgid "Environments|Job"
msgstr "Job"
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr "Novo ambiente"
msgid "Environments|No deployments yet"
msgstr "Nenhum deploy"
-msgid "Environments|Open"
-msgstr "Abrir"
+msgid "Environments|No pod name has been specified"
+msgstr "Nenhum nome pod foi especificado"
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr "Abrir ambiente ao vivo"
-msgid "Environments|Re-deploy"
-msgstr "Refazer"
+msgid "Environments|Pod logs from"
+msgstr "Logs de pod de"
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
msgid "Environments|Read more about environments"
msgstr "Ler mais sobre ambiente"
-msgid "Environments|Rollback"
-msgstr "Rollback"
+msgid "Environments|Rollback environment"
+msgstr ""
msgid "Environments|Show all"
msgstr "Mostrar tudo"
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Atualizado"
msgid "Environments|You don't have any environments right now."
msgstr "Você não tem nenhum ambiente."
+msgid "Epic"
+msgstr "Epic"
+
msgid "Epic will be removed! Are you sure?"
-msgstr "Épico será removido! Tem certeza?"
+msgstr "Epic será removido! Tem certeza?"
msgid "Epics"
-msgstr "Épicos"
+msgstr "Epics"
msgid "Epics Roadmap"
-msgstr ""
+msgstr "Roadmap de epics"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr "Epics permite que você gerencie seu portfólio de projetos de forma mais eficiente e com menos esforço"
+msgstr "Epics permitem gerenciar seu portfólio de projetos de forma mais eficiente e com menos esforço"
msgid "Error Reporting and Logging"
-msgstr ""
-
-msgid "Error checking branch data. Please try again."
-msgstr "Erro ao verificar dados do branch. Favor tentar novamente."
-
-msgid "Error committing changes. Please try again."
-msgstr "Erro ao realizar o commit das alterações. Favor tentar novamente."
+msgstr "Relatório e registro de erros"
msgid "Error creating epic"
-msgstr "Erro ao criar épico"
+msgstr "Erro ao criar epic"
msgid "Error fetching contributors data."
msgstr "Erro ao recuperar informações de contribuintes."
@@ -1822,6 +2759,21 @@ msgstr "Erro ao recuperar refs"
msgid "Error fetching usage ping data."
msgstr "Erro ao recupera dados de ping."
+msgid "Error loading branch data. Please try again."
+msgstr "Erro ao carregar dados de branch. Por favor, tente novamente."
+
+msgid "Error loading last commit."
+msgstr "Erro ao carregar último commit."
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr "Erro ao carregar merge requests."
+
+msgid "Error loading project data. Please try again."
+msgstr "Erro ao carregar dados do projeto. Por favor, tente novamente."
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Erro ao alterar configuração de notificação de assinatura"
@@ -1834,6 +2786,9 @@ msgstr "Erro ao atualizar status para todas as tarefas."
msgid "Error updating todo status."
msgstr "Erro ao atualizar status das tarefas."
+msgid "Estimated"
+msgstr "Estimativa"
+
msgid "EventFilterBy|Filter by all"
msgstr "EventFilterBy|Filtrar por tudo"
@@ -1861,9 +2816,30 @@ msgstr "Todos os meses (no dia primeiro às 4:00)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Toda semana (domingos às 4:00)"
+msgid "Everyone can contribute"
+msgstr "Todos podem contribuir"
+
msgid "Expand"
msgstr "Expandir"
+msgid "Expand all"
+msgstr "Expandir tudo"
+
+msgid "Expand sidebar"
+msgstr "Expandir barra lateral"
+
+msgid "Explore"
+msgstr "Explorar"
+
+msgid "Explore GitLab"
+msgstr "Explorar o GitLab"
+
+msgid "Explore Groups"
+msgstr "Explorar Grupos"
+
+msgid "Explore groups"
+msgstr "Explorar grupos"
+
msgid "Explore projects"
msgstr "Explorar projetos"
@@ -1871,25 +2847,28 @@ msgid "Explore public groups"
msgstr "Explorar grupos públicos"
msgid "External Classification Policy Authorization"
-msgstr ""
+msgstr "Autorização de Política de Classificação Externa"
msgid "External authentication"
-msgstr ""
+msgstr "Autenticação externa"
msgid "External authorization denied access to this project"
-msgstr "O acesso a este projeto foi negado para autorização externa"
+msgstr "Autorização externa negou acesso a este projeto"
msgid "External authorization request timeout"
-msgstr ""
+msgstr "A requisição de autorização externa esgotou o tempo limite"
msgid "ExternalAuthorizationService|Classification Label"
-msgstr ""
+msgstr "Label de classificação"
msgid "ExternalAuthorizationService|Classification label"
-msgstr ""
+msgstr "Label de classificação"
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
-msgstr ""
+msgstr "Quando nenhum label de classificação está definido, o label padrão `%{default_label}` será usado."
+
+msgid "Facebook"
+msgstr "Facebook"
msgid "Failed"
msgstr "Falha"
@@ -1900,14 +2879,23 @@ msgstr "Jobs falharam"
msgid "Failed to change the owner"
msgstr "Erro ao alterar o proprietário"
+msgid "Failed to check related branches."
+msgstr "Falha ao procurar por branches relacionadas."
+
msgid "Failed to remove issue from board, please try again."
-msgstr ""
+msgstr "Falha ao remover issue do board, por favor, tente novamente."
msgid "Failed to remove the pipeline schedule"
msgstr "Erro ao excluir o agendamento do pipeline"
msgid "Failed to update issues, please try again."
-msgstr ""
+msgstr "Falha ao atualizar Issues. Por favor, tente novamente."
+
+msgid "Failure"
+msgstr "Falha"
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr "Mais rápido, uma vez que reutiliza o espaço de trabalho do projeto (voltando a clonar, se não existir)"
msgid "Feb"
msgstr "Fev"
@@ -1918,9 +2906,6 @@ msgstr "Fevereiro"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "Campos nessa página não são mais editáveis, você pode configurar"
-msgid "File name"
-msgstr "Nome do arquivo"
-
msgid "Files"
msgstr "Arquivos"
@@ -1928,6 +2913,9 @@ msgid "Files (%{human_size})"
msgstr "Arquivos (%{human_size})"
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr "Preencha nos campos abaixo, ative o <strong>%{enable_label}</strong> e pressione <strong>%{save_changes}</strong>"
+
+msgid "Filter"
msgstr ""
msgid "Filter by commit message"
@@ -1939,6 +2927,12 @@ msgstr "Localizar por caminho"
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>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr "Finalizado"
@@ -1948,12 +2942,39 @@ msgstr "Primeiro"
msgid "FirstPushedBy|pushed by"
msgstr "publicado por"
-msgid "Font Color"
+msgid "FogBugz Email"
+msgstr "E-mail do FogBugz"
+
+msgid "FogBugz Import"
msgstr ""
-msgid "Footer message"
+msgid "FogBugz Password"
+msgstr "Senha do FogBugz"
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
msgstr ""
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr "Cor da Fonte"
+
+msgid "Footer message"
+msgstr "Mensagem do Rodapé"
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr "Para projetos internos, qualquer usuário conectado pode visualizar pipelines e acessar detalhes do job (logs de saída e artefatos)"
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr "Para projetos privados, qualquer membro (guest ou superior) pode visualizar pipelines e acessar detalhes do trabalho (logs de saída e artefatos)"
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr "Para projetos públicos, qualquer pessoa pode visualizar pipelines e acessar detalhes do trabalho (logs de saída e artefatos)"
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "Fork"
@@ -1971,9 +2992,24 @@ msgstr "Fork em andamento"
msgid "Format"
msgstr "Formato"
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr "Erros encontrados em seu .gitlab-ci.yml:"
+
msgid "From %{provider_title}"
msgstr "De %{provider_title}"
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr "Da abertura de tarefas até a implantação para a produção"
@@ -1981,154 +3017,190 @@ msgid "From merge request merge until deploy to production"
msgstr "Do merge request até a implantação em produção"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
-msgstr ""
+msgstr "Na visualização de detalhes do cluster do Kubernetes, instale o Runner pela lista de aplicativos"
msgid "GPG Keys"
msgstr "Chaves GPG"
+msgid "General"
+msgstr "Geral"
+
+msgid "General pipelines"
+msgstr "Pipelines Gerais"
+
msgid "Generate a default set of labels"
msgstr "Gerar labels padrão"
msgid "Geo Nodes"
-msgstr "Nós de geo"
+msgstr "Nós do Geo"
msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
-msgstr ""
+msgstr "Geo permite que você replique sua instância do GitLab para outras localizações geográficas."
msgid "GeoNodeSyncStatus|Node is failing or broken."
-msgstr "Nó está falhando ou quebrado."
+msgstr "Nó está falho ou quebrado."
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
-msgstr "Nó está lento, sobrecarregado, ou acabou de recuperar após uma interrupção."
+msgstr "O nó está lento, sobrecarregado ou acabou de se recuperar de uma falta de energia."
msgid "GeoNodes|Checksummed"
-msgstr ""
+msgstr "Checksum verificado"
+
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr "Os dados estão desatualizados de %{timeago}"
-msgid "GeoNodes|Database replication lag:"
-msgstr "Atraso na replicação do banco de dados:"
+msgid "GeoNodes|Data replication lag"
+msgstr "Atraso de replicação de dados"
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
-msgstr "Desabilitar um nó para o processo de sincronização. Você tem certeza?"
+msgstr "A desabilitação de um nó interrompe o processo de sincronização. Você tem certeza?"
msgid "GeoNodes|Does not match the primary storage configuration"
-msgstr "Não corresponde á configuração de armazenamento primário"
+msgstr "Não corresponde à configuração de armazenamento primário"
msgid "GeoNodes|Failed"
-msgstr "Falha"
+msgstr "Falhou"
msgid "GeoNodes|Full"
msgstr "Completo"
+msgid "GeoNodes|GitLab version"
+msgstr "Versão do GitLab"
+
msgid "GeoNodes|GitLab version does not match the primary node version"
-msgstr "Versão do GitLab não corresponde a versão do nó primário"
+msgstr "A versão do GitLab não corresponde à versão do nó primário"
+
+msgid "GeoNodes|Health status"
+msgstr "Status de saúde"
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr "O ID do último evento processado por cursor"
-msgid "GeoNodes|GitLab version:"
-msgstr "Versão do GitLab:"
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr "O ID do último evento visto do primário"
-msgid "GeoNodes|Health status:"
-msgstr "Saúde dos serviços:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr "Aprenda mais sobre progresso de soma de verificação de repositório"
-msgid "GeoNodes|Last event ID processed by cursor:"
-msgstr "Último ID de evento processado pelo cursor:"
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr "Aprenda mais sobre verificação de repositório"
-msgid "GeoNodes|Last event ID seen from primary:"
-msgstr "Último ID de evento visto pelo primário:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr "Aprenda mais sobre progresso de soma de verificação de Wiki"
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr "Aprenda mais sobre verificação de Wiki"
msgid "GeoNodes|Loading nodes"
msgstr "Carregando nós"
-msgid "GeoNodes|Local Attachments:"
-msgstr "Anexos locais:"
+msgid "GeoNodes|Local LFS objects"
+msgstr "Objetos LFS locais"
-msgid "GeoNodes|Local LFS objects:"
-msgstr "Objetos LFS locais:"
+msgid "GeoNodes|Local attachments"
+msgstr "Anexos locais"
-msgid "GeoNodes|Local job artifacts:"
-msgstr "Artefatos de processos locais:"
+msgid "GeoNodes|Local job artifacts"
+msgstr "Artefatos de tarefa locais"
msgid "GeoNodes|New node"
msgstr "Novo nó"
msgid "GeoNodes|Node Authentication was successfully repaired."
-msgstr ""
+msgstr "Autenticação de Nó foi corrigida com sucesso."
msgid "GeoNodes|Node was successfully removed."
-msgstr ""
+msgstr "O nó foi removido com sucesso."
msgid "GeoNodes|Not checksummed"
-msgstr ""
+msgstr "Soma de verificação não conferida"
msgid "GeoNodes|Out of sync"
msgstr "Fora de sincronia"
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
-msgstr ""
+msgstr "A remoção de um nó interrompe o processo de sincronização. Você tem certeza?"
-msgid "GeoNodes|Replication slot WAL:"
-msgstr ""
+msgid "GeoNodes|Replication slot WAL"
+msgstr "WAL de slots de replicação"
-msgid "GeoNodes|Replication slots:"
-msgstr ""
+msgid "GeoNodes|Replication slots"
+msgstr "Slots de replicação"
-msgid "GeoNodes|Repositories checksummed:"
-msgstr ""
+msgid "GeoNodes|Repositories"
+msgstr "Repositórios"
-msgid "GeoNodes|Repositories:"
-msgstr ""
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr "Repositórios com soma de verificação para conferência com suas contrapartes nos nós Secundários"
-msgid "GeoNodes|Repository checksums verified:"
-msgstr ""
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr "Repositórios verificados com suas contrapartes no nó Primário"
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr "Progresso de soma de verificação de repositório"
+
+msgid "GeoNodes|Repository verification progress"
+msgstr "Progresso de verificação de repositório"
msgid "GeoNodes|Selective"
-msgstr ""
+msgstr "Seletivo"
msgid "GeoNodes|Something went wrong while changing node status"
-msgstr ""
+msgstr "Alguma coisa deu errada ao mudar o status do nó"
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr "Alguma coisa deu errada ao recuperar nós"
msgid "GeoNodes|Something went wrong while removing node"
-msgstr ""
+msgstr "Alguma coisa deu errada ao remover nó"
msgid "GeoNodes|Something went wrong while repairing node"
-msgstr ""
+msgstr "Alguma coisa deu errada ao corrigir nó"
-msgid "GeoNodes|Storage config:"
-msgstr ""
+msgid "GeoNodes|Storage config"
+msgstr "Configuração de armazenamento"
-msgid "GeoNodes|Sync settings:"
-msgstr ""
+msgid "GeoNodes|Sync settings"
+msgstr "Configurações de sincronização"
msgid "GeoNodes|Synced"
-msgstr ""
+msgstr "Sincronizado"
msgid "GeoNodes|Unused slots"
-msgstr ""
+msgstr "Slots não usados"
msgid "GeoNodes|Unverified"
-msgstr ""
+msgstr "Não verificado"
msgid "GeoNodes|Used slots"
-msgstr ""
+msgstr "Slots usados"
msgid "GeoNodes|Verified"
-msgstr ""
+msgstr "Verificado"
-msgid "GeoNodes|Wiki checksums verified:"
-msgstr ""
+msgid "GeoNodes|Wiki checksum progress"
+msgstr "Progresso de soma de verificação de wiki"
-msgid "GeoNodes|Wikis checksummed:"
-msgstr ""
+msgid "GeoNodes|Wiki verification progress"
+msgstr "Progresso de verificação de wiki"
+
+msgid "GeoNodes|Wikis"
+msgstr "Wikis"
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr "Checksum realizado nas wikis para verificação com suas contrapartes nos nós secundários"
-msgid "GeoNodes|Wikis:"
-msgstr "Wikis:"
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr "Wikis verificados com suas contrapartes no nó Primário"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
-msgstr "Você configurou Geo nodes usando uma conexão HTTP insegura. Recomendamos o uso de HTTPS."
+msgstr "Você tem nós Geo configurados usando uma conexão HTTP insegura. Recomendamos o uso de HTTPS."
msgid "Geo|All projects"
msgstr "Todos os projetos"
msgid "Geo|File sync capacity"
-msgstr "Capacidade de sincronização de arquivos"
+msgstr "Capacidade de sincronização de arquivo"
msgid "Geo|Groups to synchronize"
msgstr "Grupos para sincronizar"
@@ -2137,19 +3209,25 @@ msgid "Geo|Projects in certain groups"
msgstr "Projetos em certos grupos"
msgid "Geo|Projects in certain storage shards"
-msgstr ""
+msgstr "Projetos em certos pedaços de armazenamento"
msgid "Geo|Repository sync capacity"
-msgstr "Capacidade de sincronização do repositório"
+msgstr "Capacidade de sincronização de repositório"
msgid "Geo|Select groups to replicate."
msgstr "Selecione grupos para replicar."
msgid "Geo|Shards to synchronize"
-msgstr ""
+msgstr "Shards para sincronizar"
+
+msgid "Geo|Verification capacity"
+msgstr "Capacidade de verificação"
+
+msgid "Git"
+msgstr "Git"
msgid "Git repository URL"
-msgstr ""
+msgstr "URL do repositório Git"
msgid "Git revision"
msgstr "Revisão do Git"
@@ -2157,62 +3235,152 @@ msgstr "Revisão do Git"
msgid "Git storage health information has been reset"
msgstr "Informações sobre o status de saúde do storage Git foram reiniciadas"
+msgid "Git strategy for pipelines"
+msgstr "Estratégia Git para pipelines"
+
msgid "Git version"
msgstr "Versão do Git"
msgid "GitHub import"
-msgstr ""
+msgstr "Importação do GitHub"
msgid "GitLab CI Linter has been moved"
-msgstr ""
+msgstr "Linter do GitLab CI foi movido"
msgid "GitLab Geo"
+msgstr "GitLab Geo"
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr "Os GitLab Group Runners podem executar código para todos os projetos neste grupo."
+
+msgid "GitLab Import"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "Seção GitLab Runner"
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
+msgstr "URL de logon único do GitLab"
+
+msgid "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."
+msgstr "O GitLab executará um trabalho em segundo plano que produzirá CSVs com pseudônimos do banco de dados do GitLab que serão carregados no diretório de armazenamento de objetos configurado."
+
+msgid "GitLab.com import"
msgstr ""
msgid "Gitaly"
-msgstr ""
+msgstr "Gitaly"
msgid "Gitaly Servers"
msgstr "Servidores Gitaly"
+msgid "Gitaly|Address"
+msgstr "Endereço"
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr "Voltar"
+
msgid "Go back"
msgstr "Voltar"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "Ir para seu fork"
msgid "GoToYourFork|Fork"
msgstr "Fork"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "Autenticação do Google não está %{link_to_documentation}. Peça ao administrador do Gitlab se você deseja usar esse serviço."
msgid "Got it!"
msgstr "Entendi!"
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr "Gráfico"
+
+msgid "Group"
+msgstr "Grupo"
+
+msgid "Group CI/CD settings"
+msgstr "Configurações de CI/CD do grupo"
+
+msgid "Group Git LFS status:"
msgstr ""
+msgid "Group ID"
+msgstr "ID do grupo"
+
+msgid "Group Runners"
+msgstr "Group Runners"
+
+msgid "Group avatar"
+msgstr "Avatar do grupo"
+
+msgid "Group details"
+msgstr "Detalhes do grupo"
+
+msgid "Group info:"
+msgstr "Info. do grupo:"
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr "Os mantenedores podem registrar grupos de runners em %{link}"
+
+msgid "Group: %{group_name}"
+msgstr "Grupo: %{group_name}"
+
msgid "GroupRoadmap|From %{dateWord}"
-msgstr ""
+msgstr "A partir de %{dateWord}"
msgid "GroupRoadmap|Loading roadmap"
-msgstr ""
+msgstr "Carregando roadmap"
msgid "GroupRoadmap|Something went wrong while fetching epics"
-msgstr ""
+msgstr "Alguma coisa deu errada ao recuperar epics"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr "Desculpe, nenhum epic corresponde à sua pesquisa"
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr "O roadmap mostra o progresso de seus epics ao longo de uma linha do tempo"
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Para visualizar o roadmap, adicione uma data de início ou término planejada a um dos seus epics nesse grupo ou em seus subgrupos. Na visão mensal, apenas epics no mês passado, no mês atual e nos próximos 5 meses são exibidos &ndash; de %{startDate} a %{endDate}."
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Para visualizar o roadmap, adicione uma data de início ou término planejada a um dos seus epics nesse grupo ou em seus subgrupos. Na visão trimestral, apenas epics no trimestre passado, no trimestre atual e nos próximos 4 trimestres são exibidos &ndash; de %{startDate} a %{endDate}."
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Para visualizar o roadmap, adicione uma data de início ou término planejada a um dos seus epics nesse grupo ou em seus subgrupos. Na visão semanal, apenas epics na semana passada, na semana atual e nas próximas 4 semanas são exibidos &ndash; de %{startDate} a %{endDate}."
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Para ampliar sua pesquisa, alterar ou remover filtros. Na visão mensal, apenas epics no mês passado, no mês atual e nos próximos 5 meses são exibidos &ndash; de %{startDate} a %{endDate}."
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Para ampliar sua pesquisa, alterar ou remover filtros. Na visão trimestral, apenas epics no trimestre passado, no trimestre atual e nos próximos 4 trimestre são exibidos &ndash; de %{startDate} a %{endDate}."
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Para ampliar sua pesquisa, alterar ou remover filtros. Na visão semanal, apenas epics na semana passada, na semana atual e nas próximas 4 semanas são exibidos &ndash; de %{startDate} a %{endDate}."
msgid "GroupRoadmap|Until %{dateWord}"
-msgstr ""
+msgstr "Até %{dateWord}"
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Bloquear compartilhamento de projetos do grupo %{group} com outros grupos"
@@ -2238,6 +3406,33 @@ msgstr "não pode ser desativado quando a configuração \"Travar compartilhamen
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr "retirar a trava de compartilhamento de grupo de %{ancestor_group_name}"
+msgid "Groups"
+msgstr "Grupos"
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr "Grupos também podem ser aninhados criando %{subgroup_docs_link_start}subgrupos%{subgroup_docs_link_end}."
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr "Visitados frequentemente"
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr "Grupos que você visita frequentemente aparecerão aqui"
+
+msgid "GroupsDropdown|Loading groups"
+msgstr "Carregando grupos"
+
+msgid "GroupsDropdown|Search your groups"
+msgstr "Procure seus grupos"
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr "Algo deu errado do nosso lado."
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr "Desculpe, nenhum grupo corresponde à sua pesquisa"
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr "Esse recurso requer um navegador com suporte a localStorage"
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr "Um grupo é uma coleção de vários projetos."
@@ -2278,10 +3473,10 @@ msgid "GroupsTree|Sorry, no groups or projects matched your search"
msgstr "Desculpe, nenhum grupo ou projeto correspondem à sua pesquisa"
msgid "Have your users email"
-msgstr "E-mail para abertura de issues"
+msgstr "Tem o e-mail de seus usuários"
msgid "Header message"
-msgstr ""
+msgstr "Mensagem de cabeçalho"
msgid "Health Check"
msgstr "Status de Saúde"
@@ -2302,49 +3497,127 @@ msgid "HealthCheck|Unhealthy"
msgstr "Não saudável"
msgid "Help"
-msgstr ""
+msgstr "Ajuda"
msgid "Help page"
-msgstr ""
+msgstr "Página de ajuda"
msgid "Help page text and support page url."
-msgstr ""
+msgstr "Texto da página de ajuda e Url da página de suporte."
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "Ocultar valor"
msgstr[1] "Ocultar valores"
+msgid "Hide whitespace changes"
+msgstr "Ocultar as alterações de espaço em branco"
+
msgid "History"
msgstr "Histórico"
msgid "Housekeeping successfully started"
msgstr "Manutenção iniciada com sucesso"
+msgid "I accept the %{terms_link}"
+msgstr "Eu aceito o %{terms_link}"
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr "Termos de Serviço e Política de Privacidade"
+
+msgid "ID"
+msgstr "ID"
+
+msgid "IDE|Commit"
+msgstr "Commit"
+
+msgid "IDE|Edit"
+msgstr "Editar"
+
+msgid "IDE|Go back"
+msgstr "Voltar"
+
+msgid "IDE|Open in file view"
+msgstr "Abrir na visualização de arquivos"
+
+msgid "IDE|Review"
+msgstr "Revisar"
+
+msgid "Identifier"
+msgstr "Identificador"
+
+msgid "Identities"
+msgstr "Identidades"
+
msgid "Identity provider single sign on URL"
-msgstr ""
+msgstr "URL de logon único de provedor de identidade"
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr "Se desativado, o nível de acesso irá depender das permissões do usuário no projeto."
+
+msgid "If enabled"
+msgstr "Se ativado"
msgid "If enabled, access to projects will be validated on an external service using their classification label."
-msgstr ""
+msgstr "Se ativado, o acesso aos projetos será validado em um serviço externo usando seu rótulo de classificação."
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
-msgstr ""
+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 already have files you can push them using the %{link_to_cli} below."
-msgstr ""
+msgstr "Se você já tem arquivos, pode realizar um push usando o %{link_to_cli} abaixo."
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
-msgstr ""
+msgstr "Se o seu repositório HTTP não estiver acessível publicamente, adicione informações de autenticação à URL:<code>https://username:password@gitlab.company.com/group/project.git</code>."
+
+msgid "ImageDiffViewer|2-up"
+msgstr "2-up"
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr "Onion skin"
+
+msgid "ImageDiffViewer|Swipe"
+msgstr "Deslizar"
msgid "Import"
msgstr "Importar"
+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 all repositories"
msgstr "Importar todos repositórios"
+msgid "Import an exported GitLab project"
+msgstr "Importar um projeto GitLab exportado"
+
msgid "Import in progress"
msgstr "Importação em andamento"
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr "Importar vários repositórios fazendo o upload de um arquivo manifest."
+
+msgid "Import project"
+msgstr "Importar projeto"
+
+msgid "Import projects from Bitbucket"
+msgstr "Importar projetos do Bitbucket"
+
+msgid "Import projects from FogBugz"
+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 GitHub"
msgstr "Importar repositórios do GitHub"
@@ -2352,23 +3625,35 @@ msgid "Import repository"
msgstr "Importar repositório"
msgid "ImportButtons|Connect repositories from"
-msgstr ""
+msgstr "Conectar repositórios de"
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr "Melhorar issue boards com o GitLab Enterprise Edition."
+msgstr "Melhore os quadros de issues com GitLab Enterprise Edition."
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
-msgstr "Melhore a gerência de issues com pesos no GitLab Enterprise Edition."
+msgstr "Melhore o gerenciamento de issues com o peso de issues e GitLab Enterprise Edition."
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
-msgstr "Encontre o que precisa mais facilmente com a pesquisa global avançada com GitLab Enterprise Edition."
+msgstr "Melhore a pesquisa com Advanced Global Search e GitLab Enterprise Edition."
+
+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 "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr "Inclua um contrato de Termos de Serviço e uma Política de Privacidade que todos os usuários devem aceitar."
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr "Em linha"
+
+msgid "Install GitLab Runner"
+msgstr "Instalar o GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "Instalar Runner no Kubernates"
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "Instalar um Runner compatível com o GitLab CI"
-
msgid "Instance"
msgid_plural "Instances"
msgstr[0] "Instância"
@@ -2378,10 +3663,13 @@ msgid "Instance does not support multiple Kubernetes clusters"
msgstr "A instância não suporta múltiplos clusters Kubernetes"
msgid "Integrations"
-msgstr ""
+msgstr "Integrações"
+
+msgid "Integrations Settings"
+msgstr "Configurações de Integrações"
msgid "Interested parties can even contribute by pushing commits if they want to."
-msgstr ""
+msgstr "As partes interessadas podem até contribuir enviando commits, caso queiram."
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "Interno - O grupo e projetos internos podem ser visualizados por qualquer usuário autenticado."
@@ -2395,8 +3683,11 @@ msgstr "Padrão de intervalo"
msgid "Introducing Cycle Analytics"
msgstr "Apresentando a Análise de Ciclo"
+msgid "Issue Boards"
+msgstr "Quadros de issues"
+
msgid "Issue board focus mode"
-msgstr "Focus mode no issue board"
+msgstr "Modo de foco no quadro de issues"
msgid "Issue events"
msgstr "Eventos de issue"
@@ -2405,13 +3696,16 @@ msgid "IssueBoards|Board"
msgstr "Board"
msgid "IssueBoards|Boards"
-msgstr "Boards"
+msgstr "Quadros"
msgid "Issues"
msgstr "Issues"
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
-msgstr ""
+msgstr "Issues podem ser bugs, tarefas ou ideias a serem discutidas. Além disso, issues são pesquisáveis e filtráveis."
+
+msgid "Issues closed"
+msgstr "Issues fechadas"
msgid "Jan"
msgstr "Jan"
@@ -2419,6 +3713,12 @@ msgstr "Jan"
msgid "January"
msgstr "Janeiro"
+msgid "Job"
+msgstr "Trabalho"
+
+msgid "Job has been erased"
+msgstr "O job foi apagado"
+
msgid "Jobs"
msgstr "Jobs"
@@ -2435,6 +3735,9 @@ msgid "June"
msgstr "Junho"
msgid "Koding"
+msgstr "Koding"
+
+msgid "Koding Dashboard"
msgstr ""
msgid "Kubernetes"
@@ -2444,22 +3747,25 @@ msgid "Kubernetes Cluster"
msgstr "Cluster Kubernetes"
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
-msgstr ""
+msgstr "O tempo de criação do cluster de Kubernetes excedeu o tempo limite; %{timeout}"
msgid "Kubernetes cluster integration was not removed."
-msgstr ""
+msgstr "A integração do cluster do Kubernetes não foi removida."
msgid "Kubernetes cluster integration was successfully removed."
-msgstr ""
+msgstr "A integração do cluster do Kubernetes foi removida com sucesso."
msgid "Kubernetes cluster was successfully updated."
-msgstr ""
+msgstr "O cluster do Kubernetes foi atualizado com sucesso."
msgid "Kubernetes configured"
-msgstr ""
+msgstr "Kubernetes configurado"
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
-msgstr ""
+msgstr "Integração de serviço Kubernetes foi depreciada. %{deprecated_message_content} seus clusters Kubernetes usando a nova página <a href=\"%{url}\"/>Clusters Kubernetes</a>"
+
+msgid "LFS"
+msgstr "LFS"
msgid "LFSStatus|Disabled"
msgstr "Desabilitado"
@@ -2470,26 +3776,38 @@ msgstr "Habilitado"
msgid "Label"
msgstr "Label"
+msgid "Label actions dropdown"
+msgstr "Dropdown de ações de etiqueta"
+
+msgid "Label lists show all issues with the selected label."
+msgstr "Listas de etiquetas mostram todas as issues com a etiqueta selecionada."
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
-msgstr ""
+msgstr "%{firstLabelName} +%{remainingLabelCount} mais"
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
-msgstr ""
+msgstr "%{labelsString} e %{remainingLabelCount} mais"
+
+msgid "LabelSelect|Labels"
+msgstr "Etiquetas"
msgid "Labels"
msgstr "Etiquetas"
msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr ""
+msgstr "Labels podem ser aplicadas a %{features}. Labels de grupo estão disponíveis para qualquer projeto dentro do grupo."
msgid "Labels can be applied to issues and merge requests to categorize them."
-msgstr ""
+msgstr "Labels podem ser aplicadas a issues e merge requests para categorizá-los."
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr "Etiquetas podem ser aplicadas a issues e merge requests."
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
-msgstr ""
+msgstr "<span>Promover label</span> %{labelTitle} <span>para Label do Grupo?</span>"
msgid "Labels|Promote Label"
-msgstr ""
+msgstr "Promover Label"
msgid "Last %d day"
msgid_plural "Last %d days"
@@ -2520,14 +3838,17 @@ msgstr "Você fez o push para"
msgid "LastPushEvent|at"
msgstr "em"
+msgid "Latest changes"
+msgstr "Últimas modificações"
+
msgid "Learn more"
msgstr "Saiba mais"
msgid "Learn more about Kubernetes"
-msgstr ""
+msgstr "Saiba mais sobre o Kubernetes"
msgid "Learn more about protected branches"
-msgstr ""
+msgstr "Saiba mais sobre branches protegidos"
msgid "Learn more in the"
msgstr "Saiba mais em"
@@ -2544,18 +3865,36 @@ 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 ""
+
msgid "License"
msgstr "Licença"
+msgid "LinkedIn"
+msgstr "LinkedIn"
+
msgid "List"
msgstr "Lista"
+msgid "List Your Gitea Repositories"
+msgstr "Listar os seus Repositórios do Gitea"
+
+msgid "List available repositories"
+msgstr "Listar repositórios disponíveis"
+
msgid "List your GitHub repositories"
-msgstr ""
+msgstr "Listar os seus repositórios no GitHub"
+
+msgid "Loading contribution stats for group members"
+msgstr "Carregando estados de contribuição para membros de grupo"
msgid "Loading the GitLab IDE..."
msgstr "Carregando IDE do GitLab..."
+msgid "Loading..."
+msgstr "Carregando..."
+
msgid "Lock"
msgstr "Bloquear"
@@ -2565,34 +3904,73 @@ msgstr "Bloquear %{issuableDisplayName}"
msgid "Lock not found"
msgstr "Bloqueio não encontrado"
+msgid "Lock to current projects"
+msgstr "Travar para projetos existentes"
+
msgid "Locked"
msgstr "Bloqueado"
msgid "Locked Files"
-msgstr "Arquivos bloqueados"
+msgstr "Arquivos travados"
+
+msgid "Locked to current projects"
+msgstr "Travado para projetos existentes"
msgid "Locks give the ability to lock specific file or folder."
-msgstr ""
+msgstr "Travas possibilitam travar um arquivo ou uma pasta específica."
-msgid "Login"
-msgstr "Entrar"
+msgid "Logs"
+msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr "Faça com que todos em sua equipe sejam mais produtivos, independentemente da localização deles. O GitLab Geo cria espelhos somente leitura de sua instância do GitLab para que você possa reduzir o tempo necessário para clonar e buscar grandes repositórios."
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
msgstr ""
-msgid "Manage all notifications"
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
+msgid "Manage access"
+msgstr "Gerenciar acesso"
+
+msgid "Manage all notifications"
+msgstr "Gerenciar todas as notificações"
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr "Gerencie aplicativos que podem utilizar o GitLab como um provedor OAuth e aplicativos que você autorizou a utilizar sua conta."
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr "Gerencie aplicativos que você autorizou a utilizar sua conta."
+
msgid "Manage group labels"
-msgstr ""
+msgstr "Gerenciar Labels de grupo"
msgid "Manage labels"
msgstr "Gerenciar etiquetas"
msgid "Manage project labels"
-msgstr ""
+msgstr "Gerenciar Labels de projetos"
msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr "Gerencie a qualidade de membros do seu grupo ao adicionar outro nível de segurança com o SAML."
+
+msgid "Manifest"
+msgstr "Manifesto"
+
+msgid "Manifest file import"
+msgstr "Importação de arquivo de manifesto"
+
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
msgstr ""
msgid "Mar"
@@ -2601,8 +3979,11 @@ msgstr "Mar"
msgid "March"
msgstr "Março"
-msgid "Mark done"
-msgstr "Marcar como feito"
+msgid "Mark todo as done"
+msgstr "Marcar como concluído"
+
+msgid "Markdown enabled"
+msgstr "Markdown habilitado"
msgid "Maximum git storage failures"
msgstr "Máximo de falhas do git storage"
@@ -2617,57 +3998,105 @@ msgid "Members"
msgstr "Membros"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
-msgstr ""
+msgstr "Membros serão encaminhados quando fizerem login no seu grupo. Obtenha isso pelo seu provedor de identidade, também chamado de \"SSO Service Location\", \"SAM Token Issuance Endpoint\", ou \"SAML 2.0/W-Federation URL\"."
+
+msgid "Merge Request"
+msgstr "Merge Request"
+
+msgid "Merge Request:"
+msgstr "Merge Request:"
msgid "Merge Requests"
msgstr "Merge Requests"
+msgid "Merge Requests created"
+msgstr "Merge Requests criadas"
+
msgid "Merge events"
msgstr "Eventos de merge"
msgid "Merge request"
msgstr "Merge requests"
+msgid "Merge request approvals"
+msgstr "Aprovações de merge request"
+
+msgid "Merge requests"
+msgstr "Merge requests"
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "A tela de Merge request é um lugar para propor mudanças em um projeto e discutir essas mudanças com outros"
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr "Resolver essa discussão em um novo issue"
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr "Falha ao salvar comentário"
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr "Ativar/desativar comentários para este arquivo"
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr "A atualização de discussões falhou"
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr "Visualizar o arquivo @ %{commitId}"
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr "Ver arquivo substituído @ %{commitId}"
+
msgid "Merged"
msgstr "Merge realizado"
msgid "Messages"
msgstr "Mensagens"
+msgid "Metrics"
+msgstr "Métricas"
+
msgid "Metrics - Influx"
-msgstr ""
+msgstr "Métricas - Influx"
msgid "Metrics - Prometheus"
-msgstr ""
+msgstr "Métricas - Prometheus"
msgid "Metrics|Business"
-msgstr ""
+msgstr "Negócios"
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr "Confira a documentação de CI/CD sobre como implantar em um ambiente"
msgid "Metrics|Create metric"
-msgstr ""
+msgstr "Criar métricas"
msgid "Metrics|Edit metric"
-msgstr ""
+msgstr "Editar métricas"
+
+msgid "Metrics|Environment"
+msgstr "Ambiente"
msgid "Metrics|For grouping similar metrics"
-msgstr ""
+msgstr "Para agrupar métricas similares"
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
-msgstr ""
+msgstr "Rotula do eixo vertical do gráfico. Geralmente, o tipo da unidade que está sendo mapeada. O eixo horizontal (eixo X) representa sempre o tempo."
+
+msgid "Metrics|Learn about environments"
+msgstr "Aprenda sobre ambientes"
msgid "Metrics|Legend label (optional)"
-msgstr ""
+msgstr "Rótulo de legenda (opcional)"
msgid "Metrics|Must be a valid PromQL query."
-msgstr ""
+msgstr "Deve ser uma consulta PromQL válida."
msgid "Metrics|Name"
-msgstr ""
+msgstr "Nome"
msgid "Metrics|New metric"
+msgstr "Nova métrica"
+
+msgid "Metrics|No deployed environments"
msgstr ""
msgid "Metrics|Prometheus Query Documentation"
@@ -2680,16 +4109,34 @@ msgid "Metrics|Response"
msgstr ""
msgid "Metrics|System"
+msgstr "Sistema"
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
msgstr ""
msgid "Metrics|Type"
+msgstr "Tipo"
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
msgstr ""
msgid "Metrics|Unit label"
msgstr ""
msgid "Metrics|Used as a title for the chart"
-msgstr ""
+msgstr "Usado como um título para o gráfico"
msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
msgstr ""
@@ -2715,48 +4162,63 @@ msgstr ""
msgid "Milestone"
msgstr "Milestone"
+msgid "Milestones"
+msgstr "Milestones"
+
msgid "Milestones|Delete milestone"
-msgstr ""
+msgstr "Excluir Milestone"
msgid "Milestones|Delete milestone %{milestoneTitle}?"
-msgstr ""
+msgstr "Excluir Milestone %{milestoneTitle}?"
msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
-msgstr ""
+msgstr "Falha ao excluir Milestone %{milestoneTitle}"
msgid "Milestones|Milestone %{milestoneTitle} was not found"
-msgstr ""
+msgstr "Milestone %{milestoneTitle} não foi encontrado"
msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
-msgstr ""
+msgstr "Promover Milestone %{milestoneTitle} de projeto para Milestone de grupo?"
msgid "Milestones|Promote Milestone"
-msgstr ""
+msgstr "Promover Milestone"
msgid "Milestones|This action cannot be reversed."
-msgstr ""
+msgstr "Essa ação não pode ser revertida."
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "adicione uma chave SSH"
msgid "Modal|Cancel"
-msgstr ""
+msgstr "Cancelar"
msgid "Modal|Close"
-msgstr ""
+msgstr "Fechar"
msgid "Monitoring"
msgstr "Monitoramento"
+msgid "Months"
+msgstr "Meses"
+
+msgid "More"
+msgstr "Mais"
+
+msgid "More actions"
+msgstr "Mais ações"
+
msgid "More info"
-msgstr ""
+msgstr "Mais informações"
msgid "More information"
-msgstr ""
+msgstr "Mais informações"
msgid "More information is available|here"
msgstr "Mais informações estão disponíveis|aqui"
+msgid "Most stars"
+msgstr "Mais estrelas"
+
msgid "Move"
msgstr "Mover"
@@ -2764,25 +4226,64 @@ msgid "Move issue"
msgstr "Mover issue"
msgid "Multiple issue boards"
-msgstr "Múltiplos issue boards"
+msgstr ""
+
+msgid "Name"
+msgstr "Nome"
msgid "Name new label"
msgstr "Nome da nova label"
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr "Nome:"
+
+msgid "Nav|Help"
+msgstr "Ajuda"
+
+msgid "Nav|Home"
+msgstr "Início"
+
+msgid "Nav|Sign In / Register"
+msgstr "Entrar / Registar"
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr "Saia e faça login com uma conta diferente"
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nova Issue"
msgstr[1] "Novas Issues"
-msgid "New Kubernetes Cluster"
-msgstr "Novo cluster Kubernetes"
-
-msgid "New Kubernetes cluster"
-msgstr "Novo cluster Kubernetes"
+msgid "New Label"
+msgstr ""
msgid "New Pipeline Schedule"
msgstr "Novo Agendamento de Pipeline"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "Novo branch"
@@ -2793,7 +4294,7 @@ msgid "New directory"
msgstr "Novo diretório"
msgid "New epic"
-msgstr "Novo épico"
+msgstr ""
msgid "New file"
msgstr "Novo arquivo"
@@ -2801,6 +4302,9 @@ msgstr "Novo arquivo"
msgid "New group"
msgstr "Novo grupo"
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr "Nova issue"
@@ -2810,6 +4314,9 @@ msgstr "Nova label"
msgid "New merge request"
msgstr "Novo merge request"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr "Novo projeto"
@@ -2825,6 +4332,12 @@ msgstr "Novo subgrupo"
msgid "New tag"
msgstr "Nova tag"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr "Não"
+
msgid "No Label"
msgstr ""
@@ -2846,7 +4359,37 @@ msgstr "Sem estimativa de tempo gasto"
msgid "No file chosen"
msgstr "Nenhum arquivo escolhido"
-msgid "No labels created yet."
+msgid "No files found"
+msgstr "Nenhum arquivo encontrado"
+
+msgid "No files found."
+msgstr "Nenhum arquivo encontrado."
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr "Nenhum merge requests encontrado"
+
+msgid "No messages were logged"
+msgstr "Nenhuma mensagem foi registrada"
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2855,6 +4398,9 @@ msgstr "Nenhum repositório"
msgid "No schedules"
msgstr "Nenhum agendamento"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr "Nenhum"
@@ -2865,10 +4411,10 @@ msgid "Not available"
msgstr "Não disponível"
msgid "Not available for private projects"
-msgstr ""
+msgstr "Não disponível para projetos privados"
msgid "Not available for protected branches"
-msgstr ""
+msgstr "Não disponível para Branches protegidas"
msgid "Not confidential"
msgstr "Não confidencial"
@@ -2877,18 +4423,21 @@ msgid "Not enough data"
msgstr "Dados insuficientes"
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
-msgstr ""
+msgstr "Observe que o branch master é automaticamente protegido. %{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
msgstr ""
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "Nota: Como administrador, você pode configurar o %{github_integration_link}, que permitirá o login via GitHub e permitirá a importação de repositórios sem gerar um Token de Acesso Pessoal."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr "Nota: Considere pedir ao seu administrador do GitLab para configurar %{github_integration_link}, o que permitirá o login via GitHub e permitir a importação de repositórios sem gerar um Token de Acesso Pessoal."
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
msgstr ""
msgid "Notification events"
@@ -2964,7 +4513,7 @@ msgid "Number of access attempts"
msgstr "Número de tentativas de acesso"
msgid "OK"
-msgstr "OK"
+msgstr ""
msgid "Oct"
msgstr "Out"
@@ -2978,33 +4527,78 @@ msgstr "Filtrar"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
+msgstr "Configurações de integração on-line do IDE."
+
+msgid "Only comments from the following commit are shown below"
msgstr ""
msgid "Only project members can comment."
msgstr "Somente membros do projeto podem comentar."
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
-msgstr "Abrir"
+msgstr ""
+
+msgid "Open in Xcode"
+msgstr "Abrir no Xcode"
+
+msgid "Open sidebar"
+msgstr "Abrir barra lateral"
+
+msgid "Open source software to collaborate on code"
+msgstr ""
msgid "Opened"
msgstr "Aberto"
+msgid "Opened MR"
+msgstr "MR Aberto"
+
+msgid "Opened issues"
+msgstr "Issues abertas"
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Aberto"
msgid "Opens in a new window"
msgstr "Abrir em nova janela"
+msgid "Operations"
+msgstr "Operações"
+
+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 "Opções"
-msgid "Otherwise it is recommended you start with one of the options below."
+msgid "Or you can choose one of the suggested colors below"
msgstr ""
-msgid "Outbound requests"
+msgid "Other Labels"
+msgstr "Outros Labels"
+
+msgid "Other information"
msgstr ""
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr "Caso contrário, é recomendado que você inicie com uma das opções abaixo."
+
+msgid "Outbound requests"
+msgstr "Pedidos de saída"
+
msgid "Overview"
msgstr "Visão geral"
@@ -3012,7 +4606,7 @@ msgid "Owner"
msgstr "Proprietário"
msgid "Pages"
-msgstr ""
+msgstr "Páginas"
msgid "Pagination|Last »"
msgstr "Último >>"
@@ -3027,19 +4621,37 @@ msgid "Pagination|« First"
msgstr "<< Primeiro"
msgid "Part of merge request changes"
-msgstr ""
+msgstr "Parte das mudanças do merge request"
msgid "Password"
msgstr "Senha"
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr "Cole a sua chave SSH pública, que geralmente é encontrada no arquivo '~/.ssh/id_rsa.pub' e começa com 'ssh-rsa'. Não use a sua chave SSH privada."
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr "Pausar"
+
msgid "Pending"
+msgstr "Pendente"
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr "Execute opções avançadas, como alterar o caminho, transferir ou remover o grupo."
+
msgid "Performance optimization"
-msgstr ""
+msgstr "Otimização de performance"
+
+msgid "Permissions"
+msgstr "Permissões"
msgid "Personal Access Token"
-msgstr ""
+msgstr "Token de Acesso Pessoal"
msgid "Pipeline"
msgstr "Pipeline"
@@ -3054,7 +4666,10 @@ msgid "Pipeline Schedules"
msgstr "Agendamentos da Pipeline"
msgid "Pipeline quota"
-msgstr "Cota de pipeline"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
msgid "PipelineCharts|Failed:"
msgstr "Falhou:"
@@ -3123,52 +4738,64 @@ msgid "Pipelines|Build with confidence"
msgstr "Construa com confiança"
msgid "Pipelines|CI Lint"
-msgstr ""
+msgstr "CI Lint"
msgid "Pipelines|Clear Runner Caches"
-msgstr ""
+msgstr "Limpar cache dos Runners"
msgid "Pipelines|Get started with Pipelines"
msgstr "Saiba como funcionam as pipelines"
msgid "Pipelines|Loading Pipelines"
-msgstr ""
+msgstr "Carregando Pipelines"
msgid "Pipelines|Project cache successfully reset."
-msgstr ""
+msgstr "Cache do projeto redefinido com sucesso."
msgid "Pipelines|Run Pipeline"
-msgstr ""
+msgstr "Executar Pipeline"
msgid "Pipelines|Something went wrong while cleaning runners cache."
-msgstr ""
+msgstr "Algo deu errado ao limpar o cache dos runners."
msgid "Pipelines|There are currently no %{scope} pipelines."
-msgstr ""
+msgstr "Atualmente, não há pipelines de %{scope}."
msgid "Pipelines|There are currently no pipelines."
-msgstr ""
+msgstr "Atualmente não há pipelines."
msgid "Pipelines|This project is not currently set up to run pipelines."
-msgstr ""
+msgstr "Este projeto não está atualmente configurado para executar pipelines."
-msgid "Pipeline|Retry pipeline"
-msgstr ""
+msgid "Pipeline|Create for"
+msgstr "Criar para"
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
-msgstr ""
+msgid "Pipeline|Create pipeline"
+msgstr "Criar pipeline"
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr "Nome de branch ou tag existente"
+
+msgid "Pipeline|Run Pipeline"
+msgstr "Executar Pipeline"
+
+msgid "Pipeline|Search branches"
+msgstr "Pesquisar branches"
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr "Especifique valores de variáveis ​​a serem usados ​​nesta execução. Os valores especificados em %{settings_link} serão usados ​​por padrão."
msgid "Pipeline|Stop pipeline"
-msgstr ""
+msgstr "Parar pipeline"
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
-msgstr ""
+msgstr "Parar pipeline #%{pipelineId}?"
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
-msgstr ""
+msgid "Pipeline|Variables"
+msgstr "Variáveis"
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
-msgstr ""
+msgstr "Você está prestes a interromper o pipeline %{pipelineId}."
msgid "Pipeline|all"
msgstr "todos"
@@ -3182,29 +4809,68 @@ msgstr "com etapa"
msgid "Pipeline|with stages"
msgstr "com etapas"
-msgid "PlantUML"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
msgstr ""
+msgid "Planned start date"
+msgstr ""
+
+msgid "PlantUML"
+msgstr "PlantUML"
+
msgid "Play"
msgstr "Iniciar"
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
-msgstr "Por favor, <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener no referrer\">ative a cobrança para um de seus projetos para ser possível criar um cluster Kubernetes</a>, depois tente novamente."
+msgid "Please accept the Terms of Service before continuing."
+msgstr "Por favor, aceite os Termos de Serviço antes de continuar."
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr "Por favor selecione pelo menos um filtro para ver os resultados"
msgid "Please solve the reCAPTCHA"
msgstr "Por favor, resolva o reCAPTCHA"
+msgid "Please try again"
+msgstr "Por favor, tente novamente"
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
msgid "Please wait while we import the repository for you. Refresh at will."
-msgstr ""
+msgstr "Por favor, aguarde enquanto importamos o repositório para você. Atualize à vontade."
msgid "Preferences"
msgstr "Preferências"
+msgid "Preferences|Navigation theme"
+msgstr "Tema de navegação"
+
msgid "Primary"
-msgstr "Primário"
+msgstr ""
+
+msgid "Prioritize"
+msgstr "Priorizar"
+
+msgid "Prioritize label"
+msgstr "Priorizar label"
+
+msgid "Prioritized Labels"
+msgstr "Labels Priorizadas"
+
+msgid "Prioritized label"
+msgstr "Label priorizada"
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Privado - O acesso ao projeto deve ser concedido explicitamente para cada usuário."
@@ -3213,14 +4879,26 @@ msgid "Private - The group and its projects can only be viewed by members."
msgstr "Privado - O grupo e seus projetos só podem ser vistos por seus membros."
msgid "Private projects can be created in your personal namespace with:"
-msgstr ""
+msgstr "Projetos privados podem ser criados em seu namespace pessoal com:"
msgid "Profile"
msgstr "Perfil"
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr "Conta agendada para remoção."
+msgid "Profiles|Add key"
+msgstr "Adicionar chave"
+
+msgid "Profiles|Change username"
+msgstr "Alterar nome de usuário"
+
+msgid "Profiles|Current path: %{path}"
+msgstr "Caminho atual: %{path}"
+
msgid "Profiles|Delete Account"
msgstr "Excluir conta"
@@ -3239,9 +4917,27 @@ msgstr "Senha inválida"
msgid "Profiles|Invalid username"
msgstr "Nome de usuário inválido"
+msgid "Profiles|Path"
+msgstr "Caminho"
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr "Isso não se parece com uma chave pública SSH, você tem certeza que gostaria de adicioná-la?"
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Escreva %{confirmationValue} para confirmar:"
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr "Geralmente se inicia com \"ssh-rsa …\""
+
+msgid "Profiles|Update username"
+msgstr "Atualizar nome de usuário"
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr "Falha na alteração de nome de usuário - %{message}"
+
+msgid "Profiles|Username successfully changed"
+msgstr "Alteração de nome de usuário realizada com sucesso"
+
msgid "Profiles|You don't have access to delete this user."
msgstr "Você não tem permissão para apagar esse usuário."
@@ -3251,15 +4947,24 @@ msgstr "Você precisa delegar outro usuário para ser dono ou apagar esses grupo
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Sua conta é atualmente proprietária dos seguintes grupos:"
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr "sua conta"
msgid "Profiling - Performance bar"
-msgstr ""
+msgstr "Barra de performance"
msgid "Programming languages used in this repository"
msgstr "Linguagens de programação usadas nesse repositório"
+msgid "Progress"
+msgstr "Progresso"
+
+msgid "Project"
+msgstr "Projeto"
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "O projeto '%{project_name}' está sendo excluído."
@@ -3272,6 +4977,9 @@ msgstr "Projeto '%{project_name}' criado com sucesso."
msgid "Project '%{project_name}' was successfully updated."
msgstr "Projeto '%{project_name}' atualizado com sucesso."
+msgid "Project Badges"
+msgstr "Selos de projeto"
+
msgid "Project access must be granted explicitly to each user."
msgstr "Acesso ao projeto deve ser concedido explicitamente para cada usuário."
@@ -3296,32 +5004,26 @@ msgstr "O link para a exportação do projeto expirou. Favor gerar uma nova expo
msgid "Project export started. A download link will be sent by email."
msgstr "Exportação do projeto iniciada. Um link para baixá-la será enviado por email."
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "Inscreva-se"
msgid "ProjectCreationLevel|Allowed to create projects"
-msgstr "Permitido a criação de projetos"
+msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
-msgstr "Proteção de criação de projeto padrão"
+msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
-msgstr "Desenvolvedores + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
-msgid "ProjectCreationLevel|Masters"
-msgstr "Masters"
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
msgid "ProjectCreationLevel|No one"
-msgstr "Ninguém"
-
-msgid "ProjectFeature|Disabled"
-msgstr "Desabilitado"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "Todos que possuem acesso"
-
-msgid "ProjectFeature|Only team members"
-msgstr "Apenas membros do time"
+msgstr ""
msgid "ProjectFileTree|Name"
msgstr "Nome"
@@ -3332,30 +5034,39 @@ msgstr "Nunca"
msgid "ProjectLifecycle|Stage"
msgstr "Etapa"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "Ãrvore"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
-msgstr "Fale com um administrador para mudar essa configuração."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
-msgstr "Esse repositório só aceita push de commits assinados."
+msgstr ""
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
-msgstr "Essa configuração é aplicada em nível de servidor e pode ser sobrescrita por qualquer administrador."
+msgstr ""
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
-msgstr "Essa configuração está aplicada à nivel de servidor mas foi sobrescrita para esse projeto."
+msgstr ""
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
-msgstr "Essa configuração será aplicada à todos os projetos, a não ser que seja sobrescrita pelo administrador."
+msgstr ""
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
-msgstr "Usuários só podem fazer push de commits para esse repositório se os commits estiverem assinados com um de seus próprios e-mails verificados."
+msgstr "Usuários só podem fazer push para este repositório com commits que contenham um de seus e-mails verificados."
msgid "Projects"
msgstr "Projetos"
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr "Visitados frequentemente"
@@ -3374,29 +5085,59 @@ msgstr "Algo deu errado do nosso lado."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Desculpe, nenhum projeto corresponde a sua pesquisa"
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr "Esta funcionalidade necessita de suporte à localStorage do navegador"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
-msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgid "PrometheusAlerts|Alert set"
msgstr ""
-msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgid "PrometheusAlerts|Edit alert"
msgstr ""
-msgid "PrometheusService|Active"
+msgid "PrometheusAlerts|Error creating alert"
msgstr ""
-msgid "PrometheusService|Auto configuration"
+msgid "PrometheusAlerts|Error deleting alert"
msgstr ""
-msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgid "PrometheusAlerts|Error fetching alert"
msgstr ""
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr "Tempo"
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr "%{exporters} com %{metrics} foram encontrados"
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr "<p class=\"text-tertiary\">Nenhuma <a href=\"%{docsUrl}\">métrica comum</a> foi encontrada</p>"
+
+msgid "PrometheusService|Active"
+msgstr "Ativo"
+
+msgid "PrometheusService|Auto configuration"
+msgstr "Configuração automática"
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr "Fazer deploy automático e configurar o Prometheus nos seus clusters para monitorar o ambiente do seu projeto"
+
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
msgstr "Por padrão, Prometheus escuta em 'http://localhost:9090'. Não é recomendado mudar o endereço padrão e sua porta, porque pode conflitar com outros serviços que estão executando no sevidor do Gitlab."
msgid "PrometheusService|Common metrics"
-msgstr ""
+msgstr "Métricas comuns"
msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
msgstr ""
@@ -3411,13 +5152,13 @@ msgid "PrometheusService|Finding custom metrics..."
msgstr ""
msgid "PrometheusService|Install Prometheus on clusters"
-msgstr ""
+msgstr "Instale o Prometheus nos clusters"
msgid "PrometheusService|Manage clusters"
-msgstr ""
+msgstr "Gerenciar clusters"
msgid "PrometheusService|Manual configuration"
-msgstr ""
+msgstr "Configuração manual"
msgid "PrometheusService|Metrics"
msgstr "Métricas"
@@ -3435,7 +5176,7 @@ msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example
msgstr "URL da API base do Prometheus. como http://prometheus.example.com/"
msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
-msgstr ""
+msgstr "Prometheus está sendo automaticamente gerenciado nos seus clusters"
msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
msgstr ""
@@ -3444,46 +5185,79 @@ msgid "PrometheusService|Time-series monitoring service"
msgstr "Serviço de monitoramento de tempo-de-série"
msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
-msgstr ""
+msgstr "Para ativar a configuração manual, desinstale o Prometheus dos seus clusters"
msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
-msgstr ""
+msgstr "Para ativar a instalação do Prometheus nos seus clusters, desative a configuração manual abaixo"
msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
-msgstr ""
+msgstr "PrometheusService| Aguardando sua primeira implantação em um ambiente para encontrar métricas comuns"
msgid "Promote"
+msgstr "Promover"
+
+msgid "Promote these project milestones into a group milestone."
+msgstr "Promova esses milestones de projeto em milestones de grupo."
+
+msgid "Promote to Group Milestone"
+msgstr "Promover para Milestone de Grupo"
+
+msgid "Promote to group label"
+msgstr "Promover para etiqueta de grupo"
+
+msgid "Promotions|Don't show me this again"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
msgstr ""
-msgid "Promote to Group Milestone"
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
msgstr ""
msgid "Protip:"
msgstr "Dicas:"
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Público - O grupo e seus projetos podem ser visualizados por todos sem autenticação."
msgid "Public - The project can be accessed without any authentication."
msgstr "Público - O projeto pode ser acessado sem nenhuma autenticação."
+msgid "Public pipelines"
+msgstr "Pipelines públicos"
+
msgid "Push Rules"
-msgstr "Regras de push"
+msgstr ""
msgid "Push events"
msgstr "Eventos de push"
msgid "Push project from command line"
-msgstr ""
+msgstr "Fazer push do projeto por linha de comando"
msgid "Push to create a project"
-msgstr ""
+msgstr "Push para criar um projeto"
msgid "PushRule|Committer restriction"
-msgstr "Restrição de commit"
+msgstr ""
+
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "Ações rápidas podem ser usadas nas descrições das issues e nas caixas de comentário."
@@ -3491,24 +5265,30 @@ msgstr "Ações rápidas podem ser usadas nas descrições das issues e nas caix
msgid "Read more"
msgstr "Leia mais"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "Leia-me"
msgid "Real-time features"
-msgstr ""
-
-msgid "RefSwitcher|Branches"
-msgstr "Branches"
-
-msgid "RefSwitcher|Tags"
-msgstr "Tags"
+msgstr "Recursos em tempo real"
msgid "Reference:"
msgstr "Referência:"
+msgid "Refresh"
+msgstr ""
+
msgid "Register / Sign In"
msgstr "Registrar/Login"
+msgid "Register and see your runners for this group."
+msgstr "Registre-se e veja seus runners para este grupo."
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
msgid "Registry"
msgstr "Registro"
@@ -3531,7 +5311,7 @@ msgid "Related Merged Requests"
msgstr "Merge Requests Relacionados"
msgid "Related merge requests"
-msgstr ""
+msgstr "Merge requests relacionados"
msgid "Remind later"
msgstr "Lembrar mais tarde"
@@ -3539,14 +5319,23 @@ msgstr "Lembrar mais tarde"
msgid "Remove"
msgstr "Remover"
+msgid "Remove Runner"
+msgstr "Remover Runner"
+
msgid "Remove avatar"
msgstr "Remover imagem"
+msgid "Remove priority"
+msgstr "Remover prioridade"
+
msgid "Remove project"
msgstr "Remover projeto"
msgid "Repair authentication"
-msgstr "Reparar autenticação"
+msgstr ""
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
msgid "Repo by URL"
msgstr ""
@@ -3554,21 +5343,36 @@ msgstr ""
msgid "Repository"
msgstr "Repositório"
-msgid "Repository has no locks."
+msgid "Repository Settings"
msgstr ""
-msgid "Repository maintenance"
+msgid "Repository URL"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository has no locks."
msgstr ""
+msgid "Repository maintenance"
+msgstr "Manutenção do repositório"
+
+msgid "Repository mirror"
+msgstr "Espelhamento do repositório"
+
msgid "Repository storage"
+msgstr "Armazenamento do Repositório"
+
+msgid "RepositorySettingsAccessLevel|Select"
msgstr ""
msgid "Request Access"
msgstr "Solicitar acesso"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr "Exija que todos os usuários aceitem Termos de Serviço e Política de Privacidade quando acessarem o GitLab."
+
msgid "Reset git storage health information"
msgstr "Reiniciar informações de status do storage Git"
@@ -3578,12 +5382,30 @@ msgstr "Recriar o token de status de saúde"
msgid "Reset runners registration token"
msgstr "Recriar o token de registro de runners"
-msgid "Resolve discussion"
+msgid "Resolve all discussions in new issue"
msgstr ""
-msgid "Response"
+msgid "Resolve conflicts on source branch"
+msgstr "Resolver conflitos na branch de origem"
+
+msgid "Resolve discussion"
+msgstr "Resolver discussão"
+
+msgid "Response metrics (Custom)"
msgstr ""
+msgid "Resume"
+msgstr "Continuar"
+
+msgid "Retry"
+msgstr "Tentar novamente"
+
+msgid "Retry this job"
+msgstr "Tentar novamente este trabalho"
+
+msgid "Retry verification"
+msgstr "Tentar novamente a verificação"
+
msgid "Reveal value"
msgid_plural "Reveal values"
msgstr[0] "Mostrar valor"
@@ -3595,13 +5417,19 @@ msgstr "Reverter este commit"
msgid "Revert this merge request"
msgstr "Reverter esse merge request"
+msgid "Review"
+msgstr "Revisar"
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
msgid "Reviewing"
-msgstr ""
+msgstr "Revisão"
msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr "Revisando (merge request !%{mergeRequestId})"
+
+msgid "Revoke"
msgstr ""
msgid "Roadmap"
@@ -3610,12 +5438,27 @@ msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
-msgid "Runners"
+msgid "Runner token"
msgstr ""
+msgid "Runners"
+msgstr "Runners"
+
+msgid "Runners API"
+msgstr "Runners de API"
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr "Os corredores podem ser colocados em usuários, servidores e até mesmo em sua máquina local."
+
msgid "Running"
msgstr "Executando"
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3628,6 +5471,15 @@ msgstr ""
msgid "SSH Keys"
msgstr "Chaves SSH"
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr "Salvar alterações"
@@ -3649,15 +5501,39 @@ msgstr "Agendamentos"
msgid "Scheduling Pipelines"
msgstr "Agendando pipelines"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
-msgstr "Issue board de escopo"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr "Rolar até o final"
+
+msgid "Scroll to top"
+msgstr "Voltar ao topo"
msgid "Search"
msgstr "Pesquisar"
+msgid "Search branches"
+msgstr "Pesquisar branches"
+
msgid "Search branches and tags"
msgstr "Procurar branch e tags"
+msgid "Search files"
+msgstr "Procurar arquivos"
+
+msgid "Search for projects, issues, etc."
+msgstr "Pesquise por projetos, issues, etc."
+
+msgid "Search merge requests"
+msgstr "Pesquisar merge requests"
+
msgid "Search milestones"
msgstr "Pesquisar milestones"
@@ -3673,20 +5549,35 @@ msgstr "Segundos antes de redefinir as informações de falha"
msgid "Seconds to wait for a storage access attempt"
msgstr "Segundo de espera para tentativa de acesso ao storage"
-msgid "Secret variables"
-msgstr "Variáveis secretas"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr ""
msgid "Security report"
-msgstr "Relatório de segurança"
+msgstr ""
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr "Selecionar"
msgid "Select Archive Format"
msgstr "Selecionar Formato do Arquivo"
+msgid "Select a namespace to fork the project"
+msgstr "Selecione um namespace para realizar o fork do projeto"
+
msgid "Select a timezone"
msgstr "Selecionar fuso horário"
msgid "Select an existing Kubernetes cluster or create a new one"
-msgstr ""
+msgstr "Selecione um cluster existente do Kubernetes ou crie um novo"
msgid "Select assignee"
msgstr "Selecione o responsável"
@@ -3694,11 +5585,29 @@ msgstr "Selecione o responsável"
msgid "Select branch/tag"
msgstr "Selecionar o branch/tag"
+msgid "Select project"
+msgstr "Selecionar projeto"
+
+msgid "Select project and zone to choose machine type"
+msgstr "Selecione projeto e zona para escolher o tipo de máquina"
+
+msgid "Select project to choose zone"
+msgstr "Selecione o projeto para escolher a zona"
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr "Selecionar branch de origem"
+
msgid "Select target branch"
msgstr "Selecionar branch de destino"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
-msgstr "Sincronização seletiva"
+msgstr ""
msgid "Send email"
msgstr "Enviar e-mail"
@@ -3712,6 +5621,9 @@ msgstr "Setembro"
msgid "Server version"
msgstr "Versão do servidor"
+msgid "Service Desk"
+msgstr "Balcão de Atendimento"
+
msgid "Service Templates"
msgstr "Modelos de serviço"
@@ -3719,22 +5631,22 @@ msgid "Service URL"
msgstr ""
msgid "Session expiration, projects limit and attachment size."
-msgstr ""
+msgstr "Expiração de sessão, limite de projetos e tamanho de anexo."
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Defina uma senha para sua conta para aceitar ou entregar código via %{protocol}."
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
-msgstr ""
+msgstr "Definir padrão e restringir os níveis de visibilidade. Configurar fontes de importação e protocolo de acesso git."
msgid "Set max session time for web terminal."
-msgstr ""
+msgstr "Defina o tempo máximo da sessão para o terminal da web."
msgid "Set notification email for abuse reports."
-msgstr ""
+msgstr "Definir notificação por e-mail para relatórios de abuso."
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
-msgstr ""
+msgstr "Definir requisitos para um usuário entrar. Ative a autenticação obrigatória de dois fatores."
msgid "Set up CI/CD"
msgstr "Configurar CI/CD"
@@ -3752,11 +5664,17 @@ msgid "Settings"
msgstr "Configurações"
msgid "Setup a specific Runner automatically"
-msgstr ""
+msgstr "Configurar um Runner específico automaticamente"
+
+msgid "Share"
+msgstr "Compartilhar"
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr "Runners Compartilhados"
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3766,7 +5684,19 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
+msgstr "Exibir comando"
+
+msgid "Show complete raw log"
+msgstr "Visualizar raw log completo"
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
msgstr ""
msgid "Show parent pages"
@@ -3775,51 +5705,81 @@ msgstr "Mostrar páginas acima"
msgid "Show parent subgroups"
msgstr "Mostrar subgrupos acima"
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Mostrando %d evento"
msgstr[1] "Mostrando %d eventos"
-msgid "Sidebar|Change weight"
-msgstr "Mudar peso"
+msgid "Side-by-side"
+msgstr ""
-msgid "Sidebar|No"
-msgstr "Não"
+msgid "Sidebar|Change weight"
+msgstr ""
msgid "Sidebar|None"
-msgstr "Nenhum"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
msgid "Sidebar|Weight"
-msgstr "Peso"
+msgstr ""
-msgid "Sign-in restrictions"
+msgid "Sign in"
msgstr ""
-msgid "Sign-up restrictions"
+msgid "Sign in / Register"
msgstr ""
-msgid "Size and domain settings for static websites"
+msgid "Sign in to %{group_name}"
msgstr ""
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr "Sair"
+
+msgid "Sign-in restrictions"
+msgstr "Restrições de login"
+
+msgid "Sign-up restrictions"
+msgstr "Restrições de cadastro"
+
+msgid "Size and domain settings for static websites"
+msgstr "Configurações de tamanho e domínio para sites estáticos"
+
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr "Snippets"
msgid "Something went wrong on our end"
-msgstr ""
+msgstr "Algo deu errado do nosso lado"
msgid "Something went wrong on our end."
+msgstr "Algo deu errado do nosso lado."
+
+msgid "Something went wrong on our end. Please try again!"
msgstr ""
msgid "Something went wrong when toggling the button"
+msgstr "Algo deu errado ao alternar o botão"
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3828,9 +5788,18 @@ msgstr "Algo deu errado ao recuperar os projetos."
msgid "Something went wrong while fetching the registry list."
msgstr "Algo deu errado ao recuperar a lista de registro."
+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 resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr "Algo deu errado. Por favor, tente novamente."
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr "Ordenar por"
@@ -3874,7 +5843,7 @@ msgid "SortOptions|Least popular"
msgstr "Menos populares"
msgid "SortOptions|Less weight"
-msgstr "Menos peso"
+msgstr ""
msgid "SortOptions|Milestone"
msgstr "Milestone"
@@ -3886,7 +5855,7 @@ msgid "SortOptions|Milestone due soon"
msgstr "Milestone de fim mais próximo"
msgid "SortOptions|More weight"
-msgstr "Mais peso"
+msgstr ""
msgid "SortOptions|Most popular"
msgstr "Mais populares"
@@ -3928,7 +5897,7 @@ msgid "SortOptions|Start soon"
msgstr "Iniciar mais próximo"
msgid "SortOptions|Weight"
-msgstr "Peso"
+msgstr ""
msgid "Source"
msgstr "Origem"
@@ -3946,19 +5915,46 @@ msgid "Spam Logs"
msgstr "Logs de spam"
msgid "Spam and Anti-bot Protection"
-msgstr ""
+msgstr "Proteção contra spam e anti-bot"
+
+msgid "Specific Runners"
+msgstr "Runners Específicos"
msgid "Specify the following URL during the Runner setup:"
msgstr "Especifique a seguinte URL durante a configuração do Runner:"
+msgid "Squash commits"
+msgstr "Squash commits"
+
+msgid "Stage"
+msgstr "Colocar na lista para commit"
+
+msgid "Stage & Commit"
+msgstr "Colocar na lista para commit e fazer commit"
+
+msgid "Stage all changes"
+msgstr "Colocar tudo na lista para commit"
+
+msgid "Stage changes"
+msgstr "Mudanças na lista de commit"
+
+msgid "Staged"
+msgstr "Na lista para commit"
+
+msgid "Staged %{type}"
+msgstr "%{type} na lista para commit"
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr "Coloque uma estrela em um label para dar prioridade. Altere a ordem das labels priorizadas arrastando-as para alterar sua prioridade."
+
msgid "StarProject|Star"
-msgstr "Marcar"
+msgstr "Marcar como favorito"
msgid "Starred Projects"
-msgstr ""
+msgstr "Projetos favoritos"
msgid "Starred Projects' Activity"
-msgstr ""
+msgstr "Atividade dos projetos favoritos"
msgid "Starred projects"
msgstr "Projetos favoritos"
@@ -3972,33 +5968,66 @@ msgstr "Inicie o Runner!"
msgid "Started"
msgstr "Iniciado"
+msgid "Starts at (UTC)"
+msgstr "Começa em (UTC)"
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr "Status"
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr "Parar este ambiente"
+
msgid "Stopped"
msgstr "Parado"
msgid "Storage"
msgstr "Armazenamento"
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr "Subgrupos"
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr "Inscrever-se"
+
+msgid "Subscribe at group level"
+msgstr "Inscrever-se no nível de grupo"
+
+msgid "Subscribe at project level"
+msgstr "Inscrever-se no nível do projeto"
+
msgid "Switch branch/tag"
msgstr "Trocar branch/tag"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr "Hooks do sistema"
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] "Tag (%{tag_count})"
@@ -4007,6 +6036,9 @@ msgstr[1] "Tags (%{tag_count})"
msgid "Tags"
msgstr "Tags"
+msgid "Tags:"
+msgstr "Tags:"
+
msgid "TagsPage|Browse commits"
msgstr "Navegar nos commits"
@@ -4070,8 +6102,8 @@ msgstr "Essa tag não tem release notes."
msgid "TagsPage|Use git tag command to add a new one:"
msgstr "Use o comando \"git tag\" para adiciona uma nova tag:"
-msgid "TagsPage|Write your release notes or drag files here..."
-msgstr "Escreve seu release notes ou arraste o arquivo aqui..."
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
msgid "TagsPage|protected"
msgstr "protegido"
@@ -4080,16 +6112,25 @@ msgid "Target Branch"
msgstr "Branch de destino"
msgid "Target branch"
-msgstr ""
+msgstr "Branch de destino"
msgid "Team"
msgstr "Equipe"
+msgid "Terms of Service Agreement and Privacy Policy"
+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 "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
-msgstr "Obrigado! Não mostrar novamente"
+msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
-msgstr "A pesquisa global avançada no GitLab é um serviço de pesquisa poderoso que economiza seu tempo. Ao invés de criar códigos duplicados e perder seu tempo, você pode agora pesquisar códigos de outros times que podem ajudar em seu projeto."
+msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr "Issue Tracker é o lugar para adicionar coisas que precisam ser melhoradas ou resolvidas em um projeto"
@@ -4113,23 +6154,26 @@ msgid "The fork relationship has been removed."
msgstr "O relacionamento como fork foi removido."
msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
-msgstr ""
+msgstr "A importação expirará após %{timeout}. Para repositórios que demoram mais tempo, use a combinação clone/push."
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 milestone, ou sua adição a uma lista no seu Issue Board. Comece a criar issues para ver dados para esta etapa."
msgid "The maximum file size allowed is 200KB."
-msgstr ""
+msgstr "O tamanho máximo do arquivo é de 200KB."
msgid "The number of attempts GitLab will make to access a storage."
msgstr "O número de tentativas que gitlab fará para acessar um storage."
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr "O número de falhas para que o GitLab desabilite o acesso ao storage. O número de falhas pode ser redefinido na interface do administrador: %{link_to_health_page} ou %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "A fase do ciclo de vida do desenvolvimento."
@@ -4148,14 +6192,17 @@ msgstr "O projeto pode ser acessado por qualquer usuário autenticado."
msgid "The project can be accessed without any authentication."
msgstr "O projeto pode ser acessado sem a necessidade de autenticação."
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "Não existe repositório para este projeto."
msgid "The repository for this project is empty"
-msgstr ""
+msgstr "O repositório para este projeto está vazio"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
-msgstr ""
+msgstr "O repositório deve ser acessível por <code>http://</code>, <code>https://</code> ou <code>git://</code>."
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 "A etapa de revisão mostra o tempo de criação de uma solicitação de incorporação até sua aceitação. Os dados serão automaticamente adicionados depois que sua primeira solicitação de incorporação for aceita."
@@ -4163,6 +6210,9 @@ msgstr "A etapa de revisão mostra o tempo de criação de uma solicitação de
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "A etapa de homologação mostra o tempo entre o aceite da solicitação de incorporação e a implantação do código no ambiente de produção. Os dados serão automaticamente adicionados depois que você implantar em produção pela primeira vez."
@@ -4175,27 +6225,33 @@ msgstr "Tempo em segundos para o GitLab manter as informações de falha. Se nen
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "Tempo em segundos que o GitLab tentará acessar o storage. Depois desse tempo, um erro de tempo excedido será disparado."
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr "O tempo necessário por cada entrada de dados reunida por essa etapa."
+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>:</code>. 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 ""
+
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 "O valor situado no ponto médio de uma série de valores observados. Ex., entre 3, 5, 9, a mediana é 5. Entre 3, 5, 7, 8, a mediana é (5+7)/2 = 6."
msgid "There are no issues to show"
msgstr "Não há issues para mostrar"
+msgid "There are no labels yet"
+msgstr "Não há etiquetas ainda"
+
msgid "There are no merge requests to show"
msgstr "Não há merge requests pra mostrar"
msgid "There are problems accessing Git storage: "
msgstr "Há problemas para acessar o storage Git: "
-msgid "There was an error loading results"
-msgstr ""
-
msgid "There was an error loading users activity calendar."
msgstr "Erro ao carregar calendário de atividades."
@@ -4214,12 +6270,39 @@ msgstr "Erro ao se inscrever nessa label."
msgid "There was an error when unsubscribing from this label."
msgstr "Erro ao se anular a inscrição dessa label."
-msgid "This board\\'s scope is reduced"
-msgstr "O escopo desse board está reduzido"
+msgid "They can be managed using the %{link}."
+msgstr "Eles podem ser gerenciados usando o %{link}."
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr "Esta instância do GitLab ainda não fornece nenhum Runner compartilhado. Os administradores da instância podem registrar os Runners compartilhados na área de administração."
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
msgid "This directory"
msgstr "Esse diretório"
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr "Este grupo não fornece nenhum grupo de Runners ainda."
+
msgid "This is a confidential issue."
msgstr "Essa issue é confidencial."
@@ -4236,19 +6319,28 @@ msgid "This issue is locked."
msgstr "Essa issue está bloqueada."
msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
-msgstr ""
+msgstr "Esta tarefa depende de um usuário para acionar seu processo. Geralmente eles são usados ​​para implantar código em ambientes de produção"
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
-msgstr ""
+msgstr "Esta tarefa depende das tarefas do upstream que precisam ser bem-sucedidos para que essa tarefa seja acionada"
+
+msgid "This job does not have a trace."
+msgstr "Esta tarefa não possui um traço."
+
+msgid "This job has been canceled"
+msgstr "Esta etapa foi cancelada"
+
+msgid "This job has been skipped"
+msgstr "Este trabalho foi pulado"
msgid "This job has not been triggered yet"
-msgstr ""
+msgstr "Esta build ainda não foi acionada"
msgid "This job has not started yet"
msgstr "Esse processo ainda não começou"
msgid "This job is in pending state and is waiting to be picked by a runner"
-msgstr ""
+msgstr "Esta tarefa está em estado pendente e está esperando para ser escolhido por um runner"
msgid "This job requires a manual action"
msgstr "Este Job exige uma ação manual"
@@ -4259,20 +6351,35 @@ msgstr "Isto significa que você não pode entregar código até que crie um rep
msgid "This merge request is locked."
msgstr "Esse merge request está bloqueado."
+msgid "This option is disabled while you still have unstaged changes"
+msgstr "Esta opção ficará desativada enquanto você ainda tiver alterações fora da lista para commit"
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr "Esta página não está disponível porque você não tem permissão para ler informações de vários projetos."
+msgid "This page will be removed in a future release."
+msgstr "Esta página será removida em uma versão futura."
+
msgid "This project"
msgstr "Esse projeto"
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr "Este projeto não pertence a um grupo e, portanto, não pode usar os Runners do grupo."
+
msgid "This repository"
msgstr "Esse repositório"
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
-msgstr "Esta ação excluirá uma métrica personalizada, você tem certeza?"
+msgstr ""
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr "Esses e-mails se tornarão issues automaticamente (com os comentários se tornando uma conversa de e-mail) listadas aqui."
+msgstr "Esses e-mails se tornam automaticamente issues (com os comentários se tornando a conversa por e-mail) listados aqui."
msgid "Time before an issue gets scheduled"
msgstr "Tempo até que uma issue seja agendada"
@@ -4283,105 +6390,111 @@ msgstr "Tempo até que uma issue comece a ser implementado"
msgid "Time between merge request creation and merge/close"
msgstr "Tempo entre a criação da solicitação de incorporação e a aceitação/fechamento"
-msgid "Time between updates and capacity settings."
-msgstr ""
-
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
+msgid "Time remaining"
+msgstr "Tempo restante"
+
+msgid "Time spent"
+msgstr "Tempo gasto"
+
msgid "Time tracking"
-msgstr ""
+msgstr "Acompanhamento de tempo"
msgid "Time until first merge request"
msgstr "Tempo até a primeira solicitação de incorporação"
msgid "TimeTrackingEstimated|Est"
-msgstr ""
+msgstr "Est"
msgid "TimeTracking|Estimated:"
-msgstr ""
+msgstr "Estimado:"
msgid "TimeTracking|Spent"
-msgstr ""
+msgstr "Gasto"
msgid "Timeago|%s days ago"
-msgstr "há %s dias"
+msgstr "%s dias atrás"
msgid "Timeago|%s days remaining"
msgstr "%s dias restantes"
+msgid "Timeago|%s hours ago"
+msgstr "%s horas atrás"
+
msgid "Timeago|%s hours remaining"
msgstr "%s horas restantes"
msgid "Timeago|%s minutes ago"
-msgstr "há %s minutos"
+msgstr "%s minutos atrás"
msgid "Timeago|%s minutes remaining"
msgstr "%s minutos restantes"
msgid "Timeago|%s months ago"
-msgstr "há %s meses"
+msgstr "%s meses atrás"
msgid "Timeago|%s months remaining"
msgstr "%s meses restantes"
+msgid "Timeago|%s seconds ago"
+msgstr "%s segundos atrás"
+
msgid "Timeago|%s seconds remaining"
msgstr "%s segundos restantes"
msgid "Timeago|%s weeks ago"
-msgstr "há %s semanas"
+msgstr "%s semanas atrás"
msgid "Timeago|%s weeks remaining"
msgstr "%s semanas restantes"
msgid "Timeago|%s years ago"
-msgstr "há %s anos"
+msgstr "%s anos atrás"
msgid "Timeago|%s years remaining"
msgstr "%s anos restantes"
+msgid "Timeago|1 day ago"
+msgstr "1 dia atrás"
+
msgid "Timeago|1 day remaining"
msgstr "1 dia restante"
+msgid "Timeago|1 hour ago"
+msgstr "1 hora atrás"
+
msgid "Timeago|1 hour remaining"
msgstr "1 hora restante"
+msgid "Timeago|1 minute ago"
+msgstr "1 minuto atrás"
+
msgid "Timeago|1 minute remaining"
msgstr "1 minuto restante"
+msgid "Timeago|1 month ago"
+msgstr "1 mês atrás"
+
msgid "Timeago|1 month remaining"
msgstr "1 mês restante"
+msgid "Timeago|1 week ago"
+msgstr "1 semana atrás"
+
msgid "Timeago|1 week remaining"
msgstr "1 semana restante"
+msgid "Timeago|1 year ago"
+msgstr "1 ano atrás"
+
msgid "Timeago|1 year remaining"
msgstr "1 ano restante"
msgid "Timeago|Past due"
msgstr "Venceu"
-msgid "Timeago|a day ago"
-msgstr "há um dia"
-
-msgid "Timeago|a month ago"
-msgstr "há um mês"
-
-msgid "Timeago|a week ago"
-msgstr "há uma semana"
-
-msgid "Timeago|a year ago"
-msgstr "há um ano"
-
-msgid "Timeago|about %s hours ago"
-msgstr "há cerca de %s horas"
-
-msgid "Timeago|about a minute ago"
-msgstr "há cerca de um minuto"
-
-msgid "Timeago|about an hour ago"
-msgstr "há cerca de uma hora"
-
msgid "Timeago|in %s days"
msgstr "em %s dias"
@@ -4421,11 +6534,14 @@ msgstr "em 1 semana"
msgid "Timeago|in 1 year"
msgstr "em 1 ano"
-msgid "Timeago|in a while"
-msgstr "há algum tempo"
+msgid "Timeago|just now"
+msgstr "agora"
+
+msgid "Timeago|right now"
+msgstr "agora mesmo"
-msgid "Timeago|less than a minute ago"
-msgstr "há menos de um minuto"
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4441,13 +6557,16 @@ msgid "Time|s"
msgstr "s"
msgid "Tip:"
-msgstr ""
+msgstr "Dica:"
msgid "Title"
-msgstr "Título"
+msgstr ""
msgid "To GitLab"
-msgstr ""
+msgstr "Para o GitLab"
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr "Para adicionar uma chave SSH, você precisa %{generate_link_start}gerar uma%{link_end} ou usar uma %{existing_link_start}chave existente%{link_end}."
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4458,13 +6577,22 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
-msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
-msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr "Para importar repositórios do GitHub, você pode usar um %{personal_access_token_link}. Ao criar seu Token de Acesso Pessoal, você precisará selecionar o escopo <code>repo</code>, para que possamos exibir uma lista de seus repositórios públicos e privados que estão disponíveis para importação."
+
+msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
+msgstr "Para importar repositórios do GitHub, primeiro você precisa autorizar o GitLab a acessar a lista de seus repositórios do GitHub:"
+
msgid "To import an SVN repository, check out %{svn_link}."
+msgstr "Para importar um repositório SVN, confira %{svn_link}."
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
msgstr ""
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
@@ -4473,15 +6601,36 @@ msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
-msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr "Para começar a servir suas tarefas, você pode adicionar Runners ao seu grupo"
+
+msgid "To this GitLab instance"
msgstr ""
+msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
+msgstr "Para validar suas configurações de CI do GitLab, vá para 'CI/CD → Pipelines' dentro do seu projeto e clique no botão 'CI Lint'."
+
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr "Pendente"
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr "Alternar barra lateral"
+
+msgid "Toggle discussion"
+msgstr "Alternar discussão"
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr "Ativar/Desativar barra lateral"
@@ -4491,6 +6640,12 @@ msgstr "Mudar Status: Desligado"
msgid "ToggleButton|Toggle Status: ON"
msgstr "Mudar Status: Ligado"
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr "Contribuições totais"
+
msgid "Total Time"
msgstr "Tempo Total"
@@ -4501,22 +6656,40 @@ msgid "Total: %{total}"
msgstr "Total: %{total}"
msgid "Track activity with Contribution Analytics."
-msgstr "Acompanhe a atividade com o Contribution Analytics."
+msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
-msgstr "Acompanhe grupos de questões que compartilhem um tema, em projetos e milestones"
+msgstr ""
msgid "Track time with quick actions"
+msgstr "Acompanhe o tempo com ações rápidas"
+
+msgid "Trending"
msgstr ""
msgid "Trigger this manual action"
+msgstr "Acionar esta ação manual"
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
msgstr ""
+msgid "Try again"
+msgstr "Tente novamente"
+
msgid "Turn on Service Desk"
-msgstr "Ativar Service Desk"
+msgstr "Ativar o Balcão de Atendimento"
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr "Não é possível carregar o diff. %{button_try_again}"
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
msgid "Unknown"
-msgstr "Desconhecido"
+msgstr ""
msgid "Unlock"
msgstr "Desbloquear"
@@ -4525,28 +6698,64 @@ msgid "Unlocked"
msgstr "Desbloqueado"
msgid "Unresolve discussion"
-msgstr ""
+msgstr "Reabrir discussão"
+
+msgid "Unstage all changes"
+msgstr "Retirar tudo da lista para commit"
+
+msgid "Unstage changes"
+msgstr "Retirar mudanças da lista de commit"
+
+msgid "Unstaged"
+msgstr "Fora da lista de commit"
+
+msgid "Unstaged %{type}"
+msgstr "%{type} fora da lista para commit"
+
+msgid "Unstaged and staged %{type}"
+msgstr "%{type} dentro e fora da lista para commit"
msgid "Unstar"
-msgstr "Desmarcar"
+msgstr "Desmarcar como favorito"
+
+msgid "Unsubscribe"
+msgstr "Cancelar inscrição"
+
+msgid "Unsubscribe at group level"
+msgstr "Cancelar inscrição no nível do grupo"
+
+msgid "Unsubscribe at project level"
+msgstr "Cancelar inscrição no nível do projeto"
+
+msgid "Unverified"
+msgstr "Não verificado"
msgid "Up to date"
msgstr "Atualizado"
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr "Atualize o nome do seu grupo, descrição, avatar e outras configurações gerais."
+
msgid "Upgrade your plan to activate Advanced Global Search."
-msgstr "Atualize seu plano para ativar a Pesquisa Global Avançada."
+msgstr "Aprimore seu plano para ativar a Pesquisa Global Avançada."
msgid "Upgrade your plan to activate Contribution Analytics."
-msgstr "Atualize seu plano para ativar o Contribution Analytics."
+msgstr "Aprimore seu plano para ativar a Análise de Contribuição."
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr "Atualize seu plano para ativar Webhooks do grupo."
+msgstr ""
msgid "Upgrade your plan to activate Issue weight."
-msgstr "Atualize seu plano para ativar peso nas issues."
+msgstr ""
msgid "Upgrade your plan to improve Issue boards."
-msgstr "Atualize seu plano para melhorar os issue boards."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
msgid "Upload New File"
msgstr "Enviar Novo Arquivo"
@@ -4561,13 +6770,22 @@ msgid "UploadLink|click to upload"
msgstr "clique para fazer upload"
msgid "Upvotes"
-msgstr ""
+msgstr "Votos positivos"
msgid "Usage statistics"
+msgstr "Estatísticas de uso"
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
-msgstr "Use o Service Desk para se conectar com seus usuários (por exemplo, para oferecer suporte ao cliente) por email dentro do GitLab"
+msgstr "Use o Balcão de Atendimento para se conectar com seus usuários (por exemplo, para oferecer suporte ao cliente) por email dentro do GitLab"
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr "Use milestones de grupo para gerenciar problemas de vários projetos no mesmo milestone."
+
+msgid "Use one line per URI"
+msgstr ""
msgid "Use the following registration token during setup:"
msgstr "Use o seguinte token de registro durante a configuração:"
@@ -4578,24 +6796,39 @@ msgstr "Utilizar configuração de notificação global"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr "Configurações do Usuário"
+
msgid "User and IP Rate Limits"
+msgstr "Limites de Taxa de Usuário e IP"
+
+msgid "User map"
msgstr ""
-msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgid "Users"
msgstr ""
+msgid "Variables"
+msgstr "Variáveis"
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr "As variáveis ​​são aplicadas aos ambientes por meio do runner. Eles podem ser protegidos apenas expondo-os a branches protegidas ou tags. Você pode usar variáveis ​​para senhas, chaves secretas ou o que você quiser."
+
msgid "Various container registry settings."
-msgstr ""
+msgstr "Várias configurações de registry container."
msgid "Various email settings."
-msgstr ""
+msgstr "Várias configurações de email."
msgid "Various settings that affect GitLab performance."
-msgstr ""
+msgstr "Várias configurações que afetam o desempenho do GitLab."
-msgid "View and edit lines"
+msgid "Verification information"
msgstr ""
+msgid "Verified"
+msgstr "Verificado"
+
msgid "View epics list"
msgstr ""
@@ -4603,21 +6836,39 @@ msgid "View file @ "
msgstr "Ver arquivo @ "
msgid "View group labels"
+msgstr "Visualizar labels do grupo"
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
msgstr ""
+msgid "View jobs"
+msgstr "Visualizar tarefas"
+
msgid "View labels"
msgstr "Visualizar etiquetas"
+msgid "View log"
+msgstr "Visualizar log"
+
msgid "View open merge request"
msgstr "Ver merge request aberto"
msgid "View project labels"
-msgstr ""
+msgstr "Ver labels do projeto"
msgid "View replaced file @ "
msgstr "Ver arquivo substituído @ "
msgid "Visibility and access controls"
+msgstr "Visibilidade e controles de acesso"
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
msgstr ""
msgid "VisibilityLevel|Internal"
@@ -4635,8 +6886,8 @@ msgstr "Desconhecido"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Precisa visualizar os dados? Solicite acesso ao administrador."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
-msgstr "Não foi possível verificar se um dos seus projetos no GCP possui o faturamento ativado. Por favor, tente novamente."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "Esta etapa não possui dados suficientes para exibição."
@@ -4645,20 +6896,32 @@ msgid "We want to be sure it is you, please confirm you are not a robot."
msgstr "Queremos ter certeza de que é você, confirme que você não é um robô."
msgid "Web IDE"
-msgstr ""
+msgstr "IDE Web"
msgid "Web terminal"
-msgstr ""
+msgstr "Terminal Web"
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr "Webhooks permitem que você acione uma URL se, por exemplo, quando um novo código for feito push ou uma nova issue criada. Você pode configurar os webhooks para escutar eventos específicos como push, issue ou merge request. Webhooks de grupo aplicarão para todos os projetos no grupo, permitindo você padronizar o funcionamento em todo o grupo."
+msgstr "Webhooks permitem que você acione uma URL se, por exemplo, um novo código for pushado ou se uma nova issue for criada. Você pode configurar os webhooks para interagir com eventos específicos como pushes, issues ou merge requests. Webhooks de grupos serão aplicados para todos os projetos em um grupo, permitindo que você padronize o funcionamento em todo o grupo."
+
+msgid "Weeks"
+msgstr ""
msgid "Weight"
-msgstr "Peso"
+msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
msgstr ""
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr "Quando um runner está bloqueado, não pode ser atribuído a outros projetos"
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr "Quando ativado, os usuários não podem usar o GitLab até que os termos tenham sido aceitos."
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr "Ao deixar o URL em branco, a classificação das etiquetas ainda podem ser especificadas sem desativar os recursos do projeto ou executar verificações de autorização externas."
+
msgid "Wiki"
msgstr "Wiki"
@@ -4678,13 +6941,37 @@ msgid "WikiClone|Start Gollum and edit locally"
msgstr "Inicie o Gollum e edite localmente"
msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
-msgstr ""
+msgstr "Dica: Você pode mover esta página adicionando o caminho para o início do título."
msgid "WikiEdit|There is already a page with the same title in that path."
-msgstr ""
+msgstr "Já existe uma página com o mesmo título nesse caminho."
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr "Sugira uma melhoria na wiki"
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
-msgstr "Você não tem permissão para criar páginas web"
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr "Você deve ser um membro do projeto para adicionar páginas na wiki. Se você tiver sugestões de como melhorar o wiki para este projeto, considere a possibilidade de abrir uma issue no %{issues_link}."
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr "issue tracker"
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr "Um wiki é onde você pode armazenar todos os detalhes sobre o seu projeto. Isso pode incluir por que você criou, seus princípios, como usá-lo e assim por diante."
+
+msgid "WikiEmpty|Create your first page"
+msgstr "Crie sua primeira página"
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr "Sugira a melhoria do wiki"
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr "O wiki permite que você escreva documentação para seu projeto"
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr "Este projeto não tem páginas wiki"
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr "Você deve ser um membro do projeto para adicionar páginas wiki."
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "Essa é uma versão antiga dessa página."
@@ -4719,6 +7006,12 @@ msgstr "Nova página Wiki"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr "Quer mesmo apagar essa página?"
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr "Excluir página"
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr "Excluir página %{pageTitle}?"
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr "Alguém editou essa página ao mesmo tempo que você. Por favor olhe %{page_link} e tenha certeza de que suas mudanças não removerão as mudanças deles."
@@ -4734,8 +7027,8 @@ msgstr "Atualizar %{page_title}"
msgid "WikiPage|Page slug"
msgstr "Nome amigável da página"
-msgid "WikiPage|Write your content or drag files here..."
-msgstr "Escreve seu conteudo ou arraste arquivos aqui..."
+msgid "WikiPage|Write your content or drag files here…"
+msgstr "Escreva seu conteúdo ou arraste arquivos aqui…"
msgid "Wiki|Create Page"
msgstr "Criar página"
@@ -4746,9 +7039,6 @@ msgstr "Criar página"
msgid "Wiki|Edit Page"
msgstr "Ediar página"
-msgid "Wiki|Empty page"
-msgstr "Página vazia"
-
msgid "Wiki|More Pages"
msgstr "Mais páginas"
@@ -4768,13 +7058,22 @@ msgid "Wiki|Wiki Pages"
msgstr "Páginas Wiki"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr "Com a análise de contribuição, você pode ter uma visão geral da atividade de issues, merge requests e eventos push de sua organização e seus membros."
+msgstr ""
msgid "Withdraw Access Request"
msgstr "Remover Requisição de Acesso"
-msgid "Write a commit message..."
-msgstr "Escrever uma mensagem de commit..."
+msgid "Yes"
+msgstr "Sim"
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "Você vai remover %{group_name}. Grupos removidos NÃO PODEM ser restaurados! Você está ABSOLUTAMENTE certo?"
@@ -4789,9 +7088,12 @@ msgid "You are going to transfer %{project_full_name} to another owner. Are you
msgstr "Você irá transferir %{project_full_name} para outro proprietário. Tem certeza ABSOLUTA?"
msgid "You are on a read-only GitLab instance."
+msgstr "Você está em uma instância somente-leitura do GitLab."
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4800,6 +7102,12 @@ msgstr "Você também pode criar um projeto a partir da linha de comando."
msgid "You can also star a label to make it a priority label."
msgstr "Você também pode marcar uma label para torná-la uma label de prioridade."
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr "Você também pode testar o seu .gitlab-ci.yml no %{linkStart}Lint%{linkEnd}"
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr "Você pode instalar facilmente um Runner em um cluster Kubernetes. %{link_to_help_page}"
@@ -4812,23 +7120,41 @@ msgstr "Você somente pode adicionar arquivos quando estiver em um branch"
msgid "You can only edit files when you are on a branch"
msgstr "Você só pode editar arquivos quando estiver em um branch"
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr "Você pode resolver o conflito de merge usando o modo Interativo, escolhendo os botões %{use_ours} ou %{use_theirs} ou editando os arquivos diretamente. Confirme essas alterações em %{branch_name}"
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
-msgstr "Você não pode escrever numa instância secundária de somente leitura do GitLab Geo. Por favor use %{link_to_primary_node}."
+msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr "Você não pode escrever nesta instância somente-leitura do GitLab."
+msgid "You do not have any assigned merge requests"
+msgstr "Você não atribuiu nenhum merge request"
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr "Você não tem permissão"
+msgid "You have not created any merge requests"
+msgstr "Você não criou merge request"
+
msgid "You have reached your project limit"
msgstr "Você atingiu o limite de seu projeto"
-msgid "You must have master access to force delete a lock"
-msgstr "Você deve ter o acesso master para apagar um bloqueio"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr "Você deve aceitar nossos Termos de Serviço e política de privacidade para registrar uma conta"
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr "Você deve ter o acesso de mantenedor para apagar um bloqueio"
msgid "You must sign in to star a project"
msgstr "Você deve estar autenticado para marcar um projeto"
@@ -4836,6 +7162,9 @@ msgstr "Você deve estar autenticado para marcar um projeto"
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "Você precisa de permissão."
@@ -4866,27 +7195,42 @@ msgstr "Você não poderá fazer push ou pull do código via SSH enquanto não a
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "Você precisará usar nomes de branch diferentes para obter uma comparação válida."
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr "Você está recebendo este e-mail devido à sua conta no %{host}. %{manage_notifications_link} &middot; %{help_link}"
+
+msgid "YouTube"
msgstr ""
msgid "Your Groups"
-msgstr ""
+msgstr "Seus Grupos"
msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
-msgstr ""
+msgstr "As informações do cluster do Kubernetes nesta página ainda são editáveis, mas é recomendado que você desative e reconfigure"
msgid "Your Projects (default)"
-msgstr ""
+msgstr "Seus projetos (padrão)"
msgid "Your Projects' Activity"
-msgstr ""
+msgstr "Atividade dos seus projetos"
msgid "Your Todos"
+msgstr "Seus lembretes"
+
+msgid "Your applications (%{size})"
msgstr ""
-msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgid "Your authorized applications"
msgstr ""
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr "Você pode fazer commit de suas alterações para %{branch_name} porque um merge request está aberto."
+
msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
msgstr "Commit com suas alterações realizado. Commit %{commitId} %{commitStats}"
@@ -4902,10 +7246,13 @@ msgstr "Seu nome"
msgid "Your projects"
msgstr "Seus projetos"
+msgid "ago"
+msgstr "atrás"
+
msgid "among other things"
-msgstr ""
+msgstr "entre outras coisas"
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4917,7 +7264,37 @@ msgid "branch name"
msgstr "nome da branch"
msgid "by"
-msgstr "por"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
@@ -4925,39 +7302,102 @@ msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
-msgstr "Qualidade de código"
+msgstr ""
+
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|DAST is loading"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4967,19 +7407,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST detected no security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4988,29 +7425,74 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr "na pipeline"
+
msgid "command line instructions"
-msgstr ""
+msgstr "instruções da linha de comando"
msgid "connecting"
-msgstr ""
+msgstr "conectando"
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "dia"
msgstr[1] "dias"
+msgid "deploy token"
+msgstr "token de deploy"
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5024,17 +7506,29 @@ msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr ""
-msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgid "disabled"
+msgstr "desabilitado"
+
+msgid "done"
msgstr ""
+msgid "enabled"
+msgstr "habilitado"
+
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr "%{slash_command} irá atualizar o tempo estimado com o último comando."
+
+msgid "for this project"
+msgstr "para este projeto"
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
-msgstr ""
+msgid "importing"
+msgstr "importando"
msgid "is invalid because there is downstream lock"
msgstr ""
@@ -5045,199 +7539,214 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
msgid "merge request"
msgid_plural "merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "merge request"
+msgstr[1] "merge requests"
msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
-msgstr ""
+msgstr "Por favor, restaurar ou usar um branch %{missingBranchName} diferente"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} Memória %{metricsLinkEnd} uso %{emphasisStart} diminuição %{emphasisEnd} de %{memoryFrom}MB para %{memoryTo}MB"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} Memória %{metricsLinkEnd} uso %{emphasisStart} aumento %{emphasisEnd} de %{memoryFrom}MB para %{memoryTo}MB"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} Memória %{metricsLinkEnd} uso é %{emphasisStart} inalterado %{emphasisEnd} em %{memoryFrom}MB"
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
-msgstr ""
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr "Permite commits de membros que podem fazer merge ao branch de destino"
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
msgid "mrWidget|Cancel automatic merge"
-msgstr ""
+msgstr "Cancelar merge request automático"
msgid "mrWidget|Check out branch"
-msgstr ""
+msgstr "Fazer checkout de branch"
msgid "mrWidget|Checking ability to merge automatically"
-msgstr ""
+msgstr "Verificando a capacidade de merge automaticamente"
msgid "mrWidget|Cherry-pick"
-msgstr ""
+msgstr "Cherry-pick"
msgid "mrWidget|Cherry-pick this merge request in a new merge request"
-msgstr ""
+msgstr "Fazer cherry-pick desse merge request em um novo merge request"
msgid "mrWidget|Closed"
-msgstr ""
+msgstr "Fechado"
msgid "mrWidget|Closed by"
-msgstr ""
+msgstr "Fechado por"
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr "Criar uma issue para resolvê-los mais tarde"
+
msgid "mrWidget|Deployment statistics are not available currently"
-msgstr ""
+msgstr "Estatísticas de deploy não estão disponíveis atualmente"
msgid "mrWidget|Did not close"
-msgstr ""
+msgstr "mrWidget|Não foi possível fechar"
msgid "mrWidget|Email patches"
-msgstr ""
+msgstr "Email patches"
msgid "mrWidget|Failed to load deployment statistics"
-msgstr ""
+msgstr "Falha ao carregar estatísticas de deploy"
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
-msgstr ""
+msgstr "Se o branch %{branch} existir em seu repositório local, você poderá fazer o merge request manualmente usando o"
msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
-msgstr ""
+msgstr "Se o branch %{missingBranchName} existir em seu repositório local, você poderá fazer merge request manualmente usando a linha de comando"
msgid "mrWidget|Loading deployment statistics"
-msgstr ""
+msgstr "Carregando estatísticas de implantação"
msgid "mrWidget|Mentions"
-msgstr ""
+msgstr "Menções"
msgid "mrWidget|Merge"
-msgstr ""
+msgstr "Fazer merge"
msgid "mrWidget|Merge failed."
-msgstr ""
+msgstr "Falha ao fazer merge."
msgid "mrWidget|Merge locally"
+msgstr "Fazer merge localmente"
+
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
msgid "mrWidget|Merged by"
+msgstr "Merge realizado por"
+
+msgid "mrWidget|No Approval required"
msgstr ""
-msgid "mrWidget|Plain diff"
+msgid "mrWidget|No Approval required; you can still approve"
msgstr ""
+msgid "mrWidget|Open in Web IDE"
+msgstr "Abrir na Web IDE"
+
+msgid "mrWidget|Plain diff"
+msgstr "Diff em texto"
+
msgid "mrWidget|Refresh"
-msgstr ""
+msgstr "Atualizar"
msgid "mrWidget|Refresh now"
-msgstr ""
+msgstr "Atualizar agora"
msgid "mrWidget|Refreshing now"
-msgstr ""
+msgstr "Atualizando agora"
msgid "mrWidget|Remove Source Branch"
-msgstr ""
+msgstr "Remover branch de origem"
msgid "mrWidget|Remove source branch"
-msgstr ""
+msgstr "Remover branch de origem"
msgid "mrWidget|Remove your approval"
msgstr ""
msgid "mrWidget|Request to merge"
-msgstr ""
+msgstr "Solicitar merge"
msgid "mrWidget|Resolve conflicts"
-msgstr ""
+msgstr "Resolver conflitos"
msgid "mrWidget|Revert"
-msgstr ""
+msgstr "Reverter"
msgid "mrWidget|Revert this merge request in a new merge request"
-msgstr ""
+msgstr "Reverter esse merge request com um novo merge request"
msgid "mrWidget|Set by"
-msgstr ""
+msgstr "Definir por"
msgid "mrWidget|The changes were merged into"
-msgstr ""
+msgstr "Houve merge das alterações em"
msgid "mrWidget|The changes were not merged into"
-msgstr ""
+msgstr "Não houve merge para as mudanças em"
msgid "mrWidget|The changes will be merged into"
-msgstr ""
+msgstr "Será feito merge das alterações em"
msgid "mrWidget|The source branch has been removed"
-msgstr ""
+msgstr "O branch de origem foi removido"
msgid "mrWidget|The source branch is being removed"
-msgstr ""
+msgstr "O branch de origem está sendo removido"
msgid "mrWidget|The source branch will be removed"
-msgstr ""
+msgstr "O branch de origem será removido"
msgid "mrWidget|The source branch will not be removed"
-msgstr ""
+msgstr "O branch de origem não será removido"
msgid "mrWidget|There are merge conflicts"
-msgstr ""
+msgstr "Existem conflitos de merge"
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr "Há discussões não resolvidas. Por favor, resolva estas discussões"
msgid "mrWidget|This merge request failed to be merged automatically"
-msgstr ""
+msgstr "Falha ao realizar merge automaticamente"
msgid "mrWidget|This merge request is in the process of being merged"
-msgstr ""
+msgstr "Esse merge request está em processamento"
msgid "mrWidget|This project is archived, write access has been disabled"
-msgstr ""
-
-msgid "mrWidget|Web IDE"
-msgstr ""
+msgstr "Este projeto está arquivado, a escrita foi desativada"
msgid "mrWidget|You can merge this merge request manually using the"
-msgstr ""
+msgstr "Você pode fazer merge manualmente usando o"
msgid "mrWidget|You can remove source branch now"
-msgstr ""
+msgstr "Agora você pode remover o branch de origem"
msgid "mrWidget|branch does not exist."
-msgstr ""
+msgstr "branch não existe."
msgid "mrWidget|command line"
-msgstr ""
+msgstr "linha de comando"
msgid "mrWidget|into"
-msgstr ""
+msgstr "dentro"
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
-msgstr ""
+msgstr "para ser realizado merge automaticamente quando o pipeline for bem sucedido"
msgid "new merge request"
msgstr "novo merge request"
@@ -5262,18 +7771,27 @@ msgstr "token de acesso pessoal"
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr "restante"
+
msgid "remove due date"
msgstr "remover a data de vencimento"
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr "origem"
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "%{slash_command} irá atualizar a soma do tempo gasto."
-msgid "this document"
+msgid "started"
msgstr ""
+msgid "this document"
+msgstr "este documento"
+
msgid "to help your contributors communicate effectively!"
msgstr "para ajudar seus contribuintes à se comunicar de maneira eficaz!"
@@ -5281,8 +7799,16 @@ msgid "username"
msgstr "nome do usuário"
msgid "uses Kubernetes clusters to deploy your code!"
+msgstr "use clusters Kubernetes para deploy do seu código!"
+
+msgid "view it on GitLab"
msgstr ""
msgid "with %{additions} additions, %{deletions} deletions."
-msgstr ""
+msgstr "com %{additions} adições, %{deletions} remoções."
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] "dentro de %d minuto "
+msgstr[1] "dentro de %d minutos "
diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po
index a5e145a06ed..e5627672761 100644
--- a/locale/ru/gitlab.po
+++ b/locale/ru/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:35-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Russian\n"
"Language: ru_RU\n"
@@ -15,9 +13,31 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ru\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:39\n"
msgid " and"
-msgstr " и"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
msgid "%d commit"
msgid_plural "%d commits"
@@ -35,10 +55,10 @@ msgstr[3] "на %d коммитов позади"
msgid "%d exporter"
msgid_plural "%d exporters"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%d ÑкÑпортер"
+msgstr[1] "%d ÑкÑпортера"
+msgstr[2] "%d ÑкÑпортеров"
+msgstr[3] "%d ÑкÑпортеров"
msgid "%d issue"
msgid_plural "%d issues"
@@ -63,6 +83,34 @@ msgstr[3] "%d запроÑов на ÑлиÑние"
msgid "%d metric"
msgid_plural "%d metrics"
+msgstr[0] "%d метрика"
+msgstr[1] "%d метрики"
+msgstr[2] "%d метрик"
+msgstr[3] "%d метрик"
+
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] "%d запиÑанное изменение"
+msgstr[1] "%d запиÑанных изменений"
+msgstr[2] "%d запиÑанных изменений"
+msgstr[3] "%d запиÑанных изменений"
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] "%d не запиÑанное изменение"
+msgstr[1] "%d не запиÑанных изменений"
+msgstr[2] "%d не запиÑанных изменений"
+msgstr[3] "%d не запиÑанных изменений"
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -76,9 +124,12 @@ msgstr[2] "%s дополнительных коммитов было пропуÑ
msgstr[3] "%s дополнительных коммитов было пропущено Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¾Ñ‚Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñтью."
msgid "%{actionText} & %{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr "%{commit_author_link} Ñоздан %{commit_timeago}"
+
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
msgstr ""
msgid "%{count} participant"
@@ -88,12 +139,24 @@ msgstr[1] "%{count} учаÑтника"
msgstr[2] "%{count} учаÑтников"
msgstr[3] "%{count} учаÑтников"
-msgid "%{loadingIcon} Started"
+msgid "%{filePath} deleted"
msgstr ""
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
+msgid "%{loadingIcon} Started"
+msgstr "%{loadingIcon} Запущено"
+
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr "%{lock_path} заблокирован пользователем GitLab %{lock_user_id}"
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr "%{nip_domain} может иÑпользоватьÑÑ ÐºÐ°Ðº альтернатива пользовательÑкому домену."
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr "на %{number_commits_behind} коммитов позади %{default_branch}, на %{number_commits_ahead} коммитов впереди"
@@ -104,7 +167,10 @@ msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not re
msgstr "%{number_of_failures} из %{maximum_failures} возможных неудачных попыток. GitLab не будет автоматичеÑки повторÑÑ‚ÑŒ попытку. СброÑьте информацию хранилища поÑле уÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹."
msgid "%{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{openOrClose} %{noteable}"
+
+msgid "%{percent}%% complete"
+msgstr "%{percent}%% выполнено"
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
@@ -113,18 +179,90 @@ msgstr[1] "%{storage_name}: %{failed_attempts} неудачные попытки
msgstr[2] "%{storage_name}: %{failed_attempts} неудачных попыток доÑтупа к хранилищу:"
msgstr[3] "%{storage_name}: %{failed_attempts} неудачных попыток доÑтупа к хранилищу:"
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{text} is available"
msgstr "%{text} доÑтупен"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(перейдите по ÑÑылке %{link} Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ об уÑтановке)."
+msgid "%{title} changes"
+msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² %{title}"
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr "%{unstaged} не зафикÑированных и %{staged} зафикÑированных изменений"
msgid "+ %{moreCount} more"
msgstr "+ ещё %{moreCount}"
+msgid "- Runner is active and can process any new jobs"
+msgstr "- Runner активен и может обрабатывать любые новые заданиÑ"
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr "- Runner приоÑтановлен и не получит новые заданиÑ"
+
msgid "- show less"
msgstr "- Ñвернуть"
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] "1 дополнение типа %{type}"
+msgstr[1] "%{count} дополнений типа %{type}"
+msgstr[2] "%{count} дополнений типа %{type}"
+msgstr[3] "%{count} дополнений типа %{type}"
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] "1 Ð¼Ð¾Ð´Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ñ‚Ð¸Ð¿Ð° %{type}"
+msgstr[1] "%{count} модификаций типа %{type}"
+msgstr[2] "%{count} модификаций типа %{type}"
+msgstr[3] "%{count} модификаций типа %{type}"
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] "1 закрытое обÑуждение"
+msgstr[1] "%d закрытых обÑуждений"
+msgstr[2] "%d закрытых обÑуждений"
+msgstr[3] "%d закрытых обÑуждений"
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] "1 закрытый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgstr[1] "%d закрытых запроÑов на ÑлиÑние"
+msgstr[2] "%d закрытых запроÑов на ÑлиÑние"
+msgstr[3] "%d закрытых запроÑов на ÑлиÑние"
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] "1 объединенный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ ÑлиÑние"
+msgstr[1] "%d объединенных запроÑов не ÑлиÑние"
+msgstr[2] "%d объединенных запроÑов не ÑлиÑние"
+msgstr[3] "%d объединенных запроÑов не ÑлиÑние"
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] "1 открытое обÑуждение"
+msgstr[1] "%d открытых обÑуждений"
+msgstr[2] "%d открытых обÑуждений"
+msgstr[3] "%d открытых обÑуждений"
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] "1 открытый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgstr[1] "%d открытых запроÑов на ÑлиÑние"
+msgstr[2] "%d открытых запроÑов на ÑлиÑние"
+msgstr[3] "%d открытых запроÑов на ÑлиÑние"
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "1 ÑÐ±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ"
@@ -138,9 +276,51 @@ msgstr "Первый вклад!"
msgid "2FA enabled"
msgstr "Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°"
-msgid "<strong>Removes</strong> source branch"
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr "ПожалуйÑта ÑвÑжитеÑÑŒ Ñ Ð²Ð°ÑˆÐ¸Ð¼ админиÑтратором GitLab Ñервера чтобы получить разрешение к данному реÑурÑу."
+
+msgid "403|You don't have the permission to access this page."
+msgstr "У Ð’Ð°Ñ Ð½ÐµÑ‚ права доÑтупа к Ñтой Ñтранице."
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr "ПожалуйÑта убедитеÑÑŒ что Ð°Ð´Ñ€ÐµÑ Ñтраницы верный и то что Ñтраница не была перемещена."
+
+msgid "404|Page Not Found"
+msgstr "Страница Ðе Ðайдена"
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
msgstr ""
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
+msgid "<strong>Removes</strong> source branch"
+msgstr "<strong>УдалÑет</strong> иÑходную ветку"
+
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr "«Runner» - Ñто процеÑÑ, который выполнÑет задание (обработчиков заданий). Ð’Ñ‹ можете наÑтроить Ñтолько таких процеÑÑов, Ñколько вам нужно."
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Графики непрерывной интеграции (CI)"
@@ -148,40 +328,70 @@ msgid "A new branch will be created in your fork and a new merge request will be
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 "Ð’ проекте вы размещаете Ñвои файлы (репозиторий), планируете Ñвою работу (обÑуждениÑ), и публикуете документацию (wiki), %{among_other_things_link}."
+
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
msgstr ""
msgid "A user with write access to the source branch selected this option"
+msgstr "Пользователь Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸ÐµÐ¼ на запиÑÑŒ в ветку иÑточника выбрал Ñтот вариант"
+
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
msgstr ""
msgid "About auto deploy"
msgstr "Об автоматичеÑком развёртывании"
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr "Отчёты о Жалобах"
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr "ПринÑÑ‚ÑŒ уÑловиÑ"
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr "Токены ДоÑтупа"
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr "Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ"
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "Ðктивный"
+msgid "Active Sessions"
+msgstr "Ðктивные ÑеÑÑии"
+
msgid "Activity"
msgstr "ÐктивноÑÑ‚ÑŒ"
msgid "Add"
-msgstr "Добавить"
+msgstr ""
msgid "Add Changelog"
msgstr "Добавить Журнал Изменений"
@@ -190,7 +400,7 @@ msgid "Add Contribution guide"
msgstr "Добавить РуководÑтво учаÑтника"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr "Добавить групповые веб-обработчики и GitLab Enterprise Edition."
+msgstr ""
msgid "Add Kubernetes cluster"
msgstr "Добавить Kubernetes клаÑтер"
@@ -201,11 +411,38 @@ msgstr "Добавить Лицензию"
msgid "Add Readme"
msgstr "Добавить Информацию"
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "Добавить новый каталог"
+msgid "Add reaction"
+msgstr "Добавить реакцию"
+
msgid "Add todo"
-msgstr "Добавить todo"
+msgstr "Добавить в дела"
+
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
msgid "AdminArea|Stop all jobs"
msgstr "ОÑтановить вÑе заданиÑ"
@@ -259,7 +496,7 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ, введите %{username}"
msgid "Advanced"
-msgstr "Дополнительно"
+msgstr "РаÑширенный режим"
msgid "Advanced settings"
msgstr "РаÑширенные наÑтройки"
@@ -271,13 +508,16 @@ msgid "All changes are committed"
msgstr "Ð’Ñе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð°Ñ„Ð¸ÐºÑированы"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
+msgstr "Ð’Ñе функции ÑиÑтемы включаютÑÑ Ð´Ð»Ñ Ð¿ÑƒÑÑ‚Ñ‹Ñ… проектов, Ñозданных из шаблонов или импортированных, но вы можете отключить их впоÑледÑтвии в наÑтройках проекта."
+
+msgid "Allow commits from members who can merge to the target branch."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
-msgstr ""
+msgstr "Разрешить рендеринг диаграмм PlantUML в документах Asciidoc."
msgid "Allow requests to the local network from hooks and services."
msgstr ""
@@ -297,6 +537,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr "Произошла ошибка при предварительном проÑмотре объекта"
@@ -304,13 +586,16 @@ msgid "An error occurred when toggling the notification subscription"
msgstr "Произошла ошибка при переключении подпиÑки на оповещениÑ"
msgid "An error occurred when updating the issue weight"
-msgstr "Произошла ошибка при обновлении веÑа обÑуждениÑ"
+msgstr ""
msgid "An error occurred while adding approver"
-msgstr "Произошла ошибка при добавлении утверждающего"
+msgstr ""
msgid "An error occurred while detecting host keys"
-msgstr "Произошла ошибка при обнаружении ключей хоÑта"
+msgstr ""
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr "При отключении Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð¾ÑˆÐ»Ð° ошибка. Обновите Ñтраницу и повторите попытку."
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr "Произошла ошибка при отключении ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾ функции. Обновите Ñтраницу и повторите попытку."
@@ -327,14 +612,14 @@ msgstr "Произошла ошибка при получении Ñборочн
msgid "An error occurred while getting projects"
msgstr "Произошла ошибка при получении ÑпиÑка проектов"
-msgid "An error occurred while importing project"
-msgstr "Произошла ошибка при импорте проекта"
+msgid "An error occurred while importing project: ${details}"
+msgstr ""
msgid "An error occurred while initializing path locks"
-msgstr "Произошла ошибка при инициализации блокировок пути"
+msgstr ""
-msgid "An error occurred while loading commits"
-msgstr "Произошла ошибка при загрузке коммитов"
+msgid "An error occurred while loading commit signatures"
+msgstr ""
msgid "An error occurred while loading diff"
msgstr "Произошла ошибка при загрузке различий"
@@ -349,7 +634,7 @@ msgid "An error occurred while making the request."
msgstr "Произошла ошибка при выполнении запроÑа."
msgid "An error occurred while removing approver"
-msgstr "Произошла ошибка при удалении утверждающего"
+msgstr ""
msgid "An error occurred while rendering KaTeX"
msgstr "Произошла ошибка при рендеринге KaTeX"
@@ -364,10 +649,16 @@ msgid "An error occurred while retrieving diff"
msgstr "Произошла ошибка при получении различий"
msgid "An error occurred while saving LDAP override status. Please try again."
-msgstr "Произошла ошибка при Ñохранении ÑтатуÑа наÑтроенного LDAP. ПожалуйÑта, попробуйте еще раз."
+msgstr ""
msgid "An error occurred while saving assignees"
-msgstr "Произошла ошибка при Ñохранении иÑполнителÑ"
+msgstr "Произошла ошибка при Ñохранении ответÑтвенных"
+
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
msgid "An error occurred while validating username"
msgstr "Произошла ошибка при проверке имени пользователÑ"
@@ -375,12 +666,30 @@ msgstr "Произошла ошибка при проверке имени поÐ
msgid "An error occurred. Please try again."
msgstr "Произошла ошибка. ПожалуйÑта, попробуйте Ñнова."
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
-msgstr "Ð›ÑŽÐ±Ð°Ñ ÐœÐµÑ‚ÐºÐ°"
+msgstr ""
msgid "Appearance"
msgstr "Оформление"
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr "ПриложениÑ"
@@ -390,12 +699,21 @@ msgstr "Ðпр."
msgid "April"
msgstr "Ðпрель"
-msgid "Archived project! Repository is read-only"
-msgstr "Ðрхивный проект! Репозиторий доÑтупен только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr "Ðрхивный проект! Репозиторий и другие реÑурÑÑ‹ проекта доÑтупны только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ð’Ñ‹ дейÑтвительно хотите удалить Ñто раÑпиÑание Ñборочной линии?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Ð’Ñ‹ уверены, что хотите ÑброÑить Ñтот региÑтрационный токен?"
@@ -403,7 +721,7 @@ msgid "Are you sure you want to reset the health check token?"
msgstr "Ð’Ñ‹ уверены, что хотите ÑброÑить Ñтот токен проверки работоÑпоÑобноÑти?"
msgid "Are you sure you want to unlock %{path_lock_path}?"
-msgstr "Ð’Ñ‹ дейÑтвительно хотите разблокировать %{path_lock_path}?"
+msgstr ""
msgid "Are you sure?"
msgstr "Вы уверены?"
@@ -411,6 +729,12 @@ msgstr "Вы уверены?"
msgid "Artifacts"
msgstr "Ðртефакты"
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -427,20 +751,35 @@ msgid "Assign to"
msgstr "Ðазначить"
msgid "Assigned Issues"
-msgstr ""
+msgstr "Ðазначенные обÑуждениÑ"
msgid "Assigned Merge Requests"
-msgstr ""
+msgstr "Ðазначенные запроÑÑ‹ на ÑлиÑние"
msgid "Assigned to :name"
-msgstr ""
+msgstr "Ðазначено на :name"
+
+msgid "Assigned to me"
+msgstr "Ðазначить мне"
msgid "Assignee"
+msgstr "ОтветÑтвенный"
+
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
msgstr ""
+msgid "Assignee(s)"
+msgstr "ОтветÑтвенный(ные)"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Приложить файл через drag &amp; drop или %{upload_link}"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr "Ðвг."
@@ -450,17 +789,41 @@ msgstr "ÐвгуÑÑ‚"
msgid "Authentication Log"
msgstr "Журнал аутентификации"
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr "Ðвтор"
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr "Ðвторы: %{authors}"
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr "Auto DevOps включен"
msgid "Auto DevOps, runners and job artifacts"
-msgstr ""
+msgstr "ÐвтоматичеÑкий DevOps, обработчики и артефакты заданий"
msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr "ÐŸÑ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкого ревью и автоматичеÑкого Ñ€Ð°Ð·Ð²Ñ‘Ñ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ñ‚Ñ€ÐµÐ±ÑƒÑŽÑ‚ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ %{kubernetes} Ð´Ð»Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ работы."
@@ -471,8 +834,11 @@ msgstr "ÐŸÑ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкого ревью и
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "ÐŸÑ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкого ревью и автоматичеÑкого Ñ€Ð°Ð·Ð²Ñ‘Ñ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ñ‚Ñ€ÐµÐ±ÑƒÑŽÑ‚ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð¸ домена Ð´Ð»Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ работы."
-msgid "AutoDevOps|Auto DevOps (Beta)"
-msgstr "Auto DevOps (бета)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr "ÐвтоматичеÑкий DevOps"
msgid "AutoDevOps|Auto DevOps documentation"
msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Ð¿Ð¾ Auto DevOps"
@@ -492,12 +858,18 @@ msgstr "Ð’Ñ‹ можете автоматичеÑки Ñобирать и теÑÑ
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr "добавите клаÑтер Kubernetes"
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
-msgstr "включен Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr " включить ÐвтоматичеÑкий DevOps"
msgid "Available"
msgstr "ДоÑтупен"
+msgid "Available group Runners : %{runners}"
+msgstr "ДоÑтупные обработчики заданий Ð´Ð»Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹: %{runners}"
+
+msgid "Available group Runners : %{runners}."
+msgstr "ДоÑтупные обработчики заданий Ð´Ð»Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹: %{runners}."
+
msgid "Avatar will be removed. Are you sure?"
msgstr "Ðватар будет удален. Ð’Ñ‹ уверены?"
@@ -507,65 +879,170 @@ msgstr "Ð’ Ñреднем за день: %{average}"
msgid "Background Color"
msgstr ""
-msgid "Background jobs"
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
msgstr ""
+msgid "Background jobs"
+msgstr "Фоновые заданиÑ"
+
+msgid "Badges"
+msgstr "Значки"
+
+msgid "Badges|A new badge was added."
+msgstr "Ðовый значок был добавлен."
+
+msgid "Badges|Add badge"
+msgstr "Добавить значок"
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr "Добавление значка не удалоÑÑŒ, пожалуйÑта, проверьте введенный URL и попробуйте еще раз."
+
+msgid "Badges|Badge image URL"
+msgstr "URL-Ð°Ð´Ñ€ÐµÑ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐºÐ°"
+
+msgid "Badges|Badge image preview"
+msgstr "Предварительный проÑмотра Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐºÐ°"
+
+msgid "Badges|Delete badge"
+msgstr "Удалить значок"
+
+msgid "Badges|Delete badge?"
+msgstr "Удалить значок?"
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr "Удаление значка не удалоÑÑŒ, повторите попытку."
+
+msgid "Badges|Group Badge"
+msgstr "Значок Группы"
+
+msgid "Badges|Link"
+msgstr "СÑылка"
+
+msgid "Badges|No badge image"
+msgstr "Ðет Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐºÐ°"
+
+msgid "Badges|No image to preview"
+msgstr "Ðет Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð²Ð°Ñ€Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ проÑмотра"
+
+msgid "Badges|Project Badge"
+msgstr "Значок Проекта"
+
+msgid "Badges|Reload badge image"
+msgstr "Обновить изображение значка"
+
+msgid "Badges|Save changes"
+msgstr "Сохранить изменениÑ"
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr "Сохранение значка не удалоÑÑŒ, проверьте введенные URL-адреÑа и повторите попытку."
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr "%{docsLinkStart}переменные%{docsLinkEnd} GitLab поддерживает: %{placeholders}"
+
+msgid "Badges|The badge was deleted."
+msgstr "Значок был удален."
+
+msgid "Badges|The badge was saved."
+msgstr "Значок был Ñохранен."
+
+msgid "Badges|This group has no badges"
+msgstr "Эта группа не имеет значков"
+
+msgid "Badges|This project has no badges"
+msgstr "Этот проект не имеет значков"
+
+msgid "Badges|Your badges"
+msgstr "Ваши значки"
+
msgid "Begin with the selected commit"
msgstr "Ðачать Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð¾Ð³Ð¾ коммита"
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
-msgstr "Тариф"
+msgstr ""
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
-msgstr "%{group_name} иÑпользует тарифный план %{plan_link}."
+msgstr ""
msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
-msgstr "Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкое повышение или понижение недоÑтупно Ð´Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… тарифных планов."
+msgstr ""
msgid "BillingPlans|Current plan"
-msgstr "Текущий тарифный план"
+msgstr ""
msgid "BillingPlans|Customer Support"
-msgstr "Поддержка Клиентов"
+msgstr ""
msgid "BillingPlans|Downgrade"
-msgstr "Понизить"
+msgstr ""
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
-msgstr "Узнайте больше о каждом тарифном плане, прочитав нашу Ñтраницу %{faq_link}."
+msgstr ""
msgid "BillingPlans|Manage plan"
-msgstr "Управление тарифным планом"
+msgstr ""
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
-msgstr "ПожалуйÑта, в Ñтом Ñлучае обратитеÑÑŒ в %{customer_support_link}."
+msgstr ""
msgid "BillingPlans|See all %{plan_name} features"
-msgstr "ПоÑмотреть возможноÑти %{plan_name} полноÑтью"
+msgstr ""
msgid "BillingPlans|This group uses the plan associated with its parent group."
-msgstr "Эта группа иÑпользует тарифный план, ÑвÑзанный Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑкой группой."
+msgstr ""
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
-msgstr "Ð”Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñ‚Ð°Ñ€Ð¸Ñ„Ð½Ñ‹Ð¼ планом Ñтой группы поÑетите раздел тарификации %{parent_billing_page_link}."
+msgstr ""
msgid "BillingPlans|Upgrade"
-msgstr "ПовыÑить"
+msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
-msgstr "Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹ иÑпользуете тарифный план %{plan_link}."
+msgstr ""
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
msgid "BillingPlans|frequently asked questions"
-msgstr "чаÑто задаваемых вопроÑов"
+msgstr ""
msgid "BillingPlans|monthly"
-msgstr "ежемеÑÑчно"
+msgstr ""
msgid "BillingPlans|paid annually at %{price_per_year}"
-msgstr "оплачиваетÑÑ ÐµÐ¶ÐµÐ³Ð¾Ð´Ð½Ð¾ в размере %{price_per_year}"
+msgstr ""
msgid "BillingPlans|per user"
-msgstr "за пользователÑ"
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr "ДоÑки"
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
@@ -596,13 +1073,13 @@ msgid "Branches"
msgstr "Ветки"
msgid "Branches|Active"
-msgstr ""
+msgstr "Ðктивные"
msgid "Branches|Active branches"
-msgstr ""
+msgstr "Ðктивные ветки"
msgid "Branches|All"
-msgstr ""
+msgstr "Ð’Ñе"
msgid "Branches|Cant find HEAD commit for this branch"
msgstr "Ðевозможно найти HEAD-коммит Ñтой ветки"
@@ -646,44 +1123,44 @@ msgstr "Ðет веток Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ"
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr "Как только вы подтвердите и нажмёте %{delete_protected_branch}, данные будут удалены без возможноÑти воÑÑтановлениÑ."
-msgid "Branches|Only a project master or owner can delete a protected branch"
-msgstr "Только маÑтер или владелец проекта может удалить защищённую ветку"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr ""
msgid "Branches|Overview"
-msgstr ""
+msgstr "Обзор"
msgid "Branches|Protected branches can be managed in %{project_settings_link}."
-msgstr ""
+msgstr "Управление защищёнными ветками возможно в %{project_settings_link}."
msgid "Branches|Show active branches"
-msgstr ""
+msgstr "Показать активные ветки"
msgid "Branches|Show all branches"
-msgstr ""
+msgstr "Показать вÑе ветки"
msgid "Branches|Show more active branches"
-msgstr ""
+msgstr "Показать больше активных веток"
msgid "Branches|Show more stale branches"
-msgstr ""
+msgstr "Показать больше заÑтаревших веток"
msgid "Branches|Show overview of the branches"
-msgstr ""
+msgstr "Показать опиÑание веток"
msgid "Branches|Show stale branches"
-msgstr ""
+msgstr "Показать заÑтаревшие ветки"
msgid "Branches|Sort by"
msgstr "Сортировать по"
msgid "Branches|Stale"
-msgstr ""
+msgstr "ЗаÑтаревшие"
msgid "Branches|Stale branches"
-msgstr ""
+msgstr "ЗаÑтаревшие ветки"
msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
-msgstr "Ветвь не может быть обновлена автоматичеÑки, потому что она имеет раÑÑ…Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑким репозиторием."
+msgstr ""
msgid "Branches|The default branch cannot be deleted"
msgstr "Ветка \"по умолчанию\" не может быть удалена"
@@ -698,13 +1175,13 @@ msgid "Branches|To confirm, type %{branch_name_confirmation}:"
msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ, введите %{branch_name_confirmation}:"
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
-msgstr "Чтобы отменить локальные Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ перезапиÑать ветвь верÑией из родительÑкого репозиториÑ, удалите её здеÑÑŒ и выберите \"Обновить ÑейчаÑ\" выше."
+msgstr ""
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ безвозвратно удалить защищённую ветку %{branch_name}."
msgid "Branches|diverged from upstream"
-msgstr "раÑходитÑÑ Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑким репозиторием"
+msgstr ""
msgid "Branches|merged"
msgstr "влита"
@@ -727,7 +1204,7 @@ msgstr "ПроÑмотр файлов"
msgid "Browse files"
msgstr "ПроÑмотр файлов"
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -736,23 +1213,86 @@ msgstr "по автору"
msgid "CI / CD"
msgstr "CI / CD"
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
-msgstr "CI/CD"
+msgstr ""
msgid "CI/CD configuration"
msgstr "ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ CI/CD"
msgid "CI/CD for external repo"
-msgstr "CI/CD Ð´Ð»Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ³Ð¾ репозиториÑ"
+msgstr ""
+
+msgid "CI/CD settings"
+msgstr "ÐаÑтройки CI/CD"
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr "ÐвтоматичеÑкий DevOps"
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr "Отключить Auto DevOps"
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr "Включить Auto DevOps"
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr "ЭкземплÑÑ€ по умолчанию (%{state})"
msgid "CICD|Jobs"
msgstr "ЗаданиÑ"
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "Отмена"
+msgid "Cancel this job"
+msgstr "Отменить Ñто задание"
+
msgid "Cannot be merged automatically"
-msgstr ""
+msgstr "Ðет возможноÑти объединить автоматичеÑки"
msgid "Cannot modify managed Kubernetes cluster"
msgstr "Ðевозможно изменить управлÑемый клаÑтер Kubernetes"
@@ -761,10 +1301,10 @@ msgid "Certificate fingerprint"
msgstr ""
msgid "Change Weight"
-msgstr "Изменить ВеÑ"
+msgstr ""
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
-msgstr ""
+msgstr "Измените Ñто значение, чтобы уÑтановить как чаÑто GitLab UI запрашивает обновлениÑ."
msgid "ChangeTypeActionLabel|Pick into branch"
msgstr "Выбрать в ветке"
@@ -808,23 +1348,38 @@ msgstr "Подобрать в Ñтом коммите"
msgid "Cherry-pick this merge request"
msgstr "Подобрать Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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}), чтобы увидеть, что изменилоÑÑŒ или Ñоздать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние."
+msgstr "Выберите ветку/тег (например, %{master}) или введите коммит (например, %{sha}), чтобы увидеть, что изменилоÑÑŒ или Ñоздать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние."
+
+msgid "Choose any color."
+msgstr "Выберите любой цвет."
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
msgid "Choose file..."
msgstr "Выберите файл..."
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
-msgstr "Выберите группы, которые вы хотите Ñинхронизировать Ñ Ñтим вторичным узлом."
+msgstr ""
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr ""
msgid "Choose which repositories you want to import."
-msgstr ""
+msgstr "Выберите, какие репозитории вы хотите импортировать."
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
@@ -899,19 +1454,19 @@ msgid "CiVariable|All environments"
msgstr "Ð’Ñе Ñреды"
msgid "CiVariable|Create wildcard"
-msgstr "Создать шаблон"
+msgstr ""
msgid "CiVariable|Error occured while saving variables"
msgstr "Произошла ошибка при Ñохранении переменных"
msgid "CiVariable|New environment"
-msgstr "ÐÐ¾Ð²Ð°Ñ Ñреда"
+msgstr ""
msgid "CiVariable|Protected"
msgstr "Защищено"
msgid "CiVariable|Search environments"
-msgstr "ПоиÑк Ñред"
+msgstr ""
msgid "CiVariable|Toggle protected"
msgstr "Включить защиту"
@@ -922,9 +1477,30 @@ msgstr "Проверка не удалаÑÑŒ"
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "CircuitBreaker API"
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr "ОчиÑтить Ñтроку поиÑка"
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr "Выберите из ÑпиÑка любой <strong>проект</strong>, чтобы перейти к Ñтапу проекта."
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 "Ðажмите кнопку ниже, чтобы начать процеÑÑ ÑƒÑтановки, Ð¿ÐµÑ€ÐµÐ¹Ð´Ñ Ð½Ð° Ñтраницу Kubernetes"
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr "Ðажмите, чтобы раÑкрыть текÑÑ‚"
@@ -937,6 +1513,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr "Клонировать репозиторий"
@@ -944,7 +1523,10 @@ msgid "Close"
msgstr "Закрыть"
msgid "Closed"
-msgstr "Закрыто"
+msgstr ""
+
+msgid "Closed issues"
+msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} уÑпешно уÑтановлены на вашем клаÑтере Kubernetes"
@@ -955,12 +1537,15 @@ msgstr "ÐÐ´Ñ€ÐµÑ API"
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr "Добавить клаÑтер Kubernetes"
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
-msgstr "Добавить ÑущеÑтвующий клаÑтер Kubernetes"
-
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Дополнительные опции Ð´Ð»Ñ Ñтой интеграции Ñ ÐºÐ»Ð°Ñтером Kubernetes"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr "Произошла ошибка при попытке Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð·Ð¾Ð½ проекта: %{error}"
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr "Произошла ошибка при попытке Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð²Ð°ÑˆÐ¸Ñ… проектов: %{error}"
+
msgid "ClusterIntegration|Applications"
msgstr "ПриложениÑ"
@@ -973,9 +1558,6 @@ msgstr "Сертификат удоÑтоверÑющего центра"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Комплект Ñертификатов удоÑтоверÑющего центра (формат PEM)"
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr "Выберите ÑпоÑоб наÑтройки интеграции Ñ ÐºÐ»Ð°Ñтером Kubernetes"
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr "Выберите, какие Ñреды вашего проекта будут иÑпользовать Ñтот клаÑтер Kubernetes."
@@ -991,6 +1573,9 @@ msgstr "Копировать Сертификат УдоÑтоверÑющего
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr "Скопировать IP-Ð°Ð´Ñ€ÐµÑ Ingress в буфер обмена"
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr "Скопировать Ð¸Ð¼Ñ ÐºÐ»Ð°Ñтера Kubernetes"
@@ -1000,32 +1585,35 @@ msgstr "Скопировать Токен"
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr "Создать клаÑтер Kubernetes"
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
-msgstr "Создать клаÑтер при помощи Google Kubernetes Engine"
-
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr "Создайте новый клаÑтер Kubernetes в Google Kubernetes Engine прÑмо из GitLab"
-
-msgid "ClusterIntegration|Create on GKE"
-msgstr "Создать в GKE"
-
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr "Укажите параметры ÑущеÑтвующего клаÑтера Kubernetes"
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr "Введите ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ вашем клаÑтере Kubernetes"
msgid "ClusterIntegration|Environment scope"
+msgstr "Среда окружениÑ"
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
+msgid "ClusterIntegration|Fetching machine types"
+msgstr "Получение типов машин клаÑтера"
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr "Получение проектов"
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr "Получение зон"
+
msgid "ClusterIntegration|GitLab Integration"
msgstr "Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ Ñ GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
-msgstr "Идентификатор проекта в Google Cloud Platform"
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
msgstr "Google Kubernetes Engine"
@@ -1036,6 +1624,12 @@ msgstr "Проект Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1063,6 +1657,12 @@ msgstr "Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ð¸ клаÑтеров Kuber
msgid "ClusterIntegration|Integration status"
msgstr " Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¸Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ð¸"
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr "Ð˜Ð¼Ñ Ñ…Ð¾Ñта ÑервиÑа Jupyter"
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr "Хаб ÑервиÑа Jupyter"
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "КлаÑтер Kubernetes"
@@ -1099,20 +1699,26 @@ msgstr "КлаÑтер Kubernetes позволÑет вам иÑпользова
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr "КлаÑтеры Kubernetes могут иÑпользоватьÑÑ Ð´Ð»Ñ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ð¹ и предоÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ð¹ Review Ð´Ð»Ñ Ñтого проекта"
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
-msgstr "Узнайте больше на %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr ""
msgid "ClusterIntegration|Learn more about environments"
msgstr "Узнайте больше о Ñредах"
msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
+msgstr "Узнайте больше о наÑтройке безопаÑноÑти"
msgid "ClusterIntegration|Machine type"
msgstr "Тип машины"
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
-msgstr ""
+msgstr "УбедитеÑÑŒ, что ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ %{link_to_requirements} Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÐºÐ»Ð°Ñтеров"
msgid "ClusterIntegration|Manage"
msgstr "Управление"
@@ -1124,7 +1730,19 @@ msgid "ClusterIntegration|More information"
msgstr "Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
-msgstr "ÐеÑколько клаÑтеров Kubernetes доÑтупно в GitLab Entreprise Edition Premium и Ultimate"
+msgstr ""
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr "Ðет типов машин, ÑоответÑтвующих вашему поиÑковому запроÑу"
+
+msgid "ClusterIntegration|No projects found"
+msgstr "Проекты не найдены"
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr "Ðет проектов, ÑоответÑтвующих вашему поиÑковому запроÑу"
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr "Ðет зон, ÑоответÑтвующих вашему поиÑковому запроÑу"
msgid "ClusterIntegration|Note:"
msgstr "Примечание:"
@@ -1138,9 +1756,6 @@ msgstr "ПожалуйÑта, укажите параметры доÑтупа Ð
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "ПожалуйÑта, убедитеÑÑŒ, что ваш аккаунт Google отвечает Ñледующим требованиÑм:"
-msgid "ClusterIntegration|Project ID"
-msgstr "ID проекта"
-
msgid "ClusterIntegration|Project namespace"
msgstr "ПроÑтранÑтво имён проекта"
@@ -1148,10 +1763,10 @@ msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr "ПроÑтранÑтво имен проекта (необÑзательное, уникальное)"
msgid "ClusterIntegration|Prometheus"
-msgstr ""
+msgstr "Prometheus"
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
-msgstr ""
+msgstr "Прочтите нашу документацию %{link_to_help_page} по интеграции клаÑтера Kubernetes."
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr "Удалить интеграцию клаÑтера Kubernetes"
@@ -1168,20 +1783,38 @@ msgstr "Ðе удалоÑÑŒ выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° запуÑк п
msgid "ClusterIntegration|Save changes"
msgstr "Сохранить изменениÑ"
+msgid "ClusterIntegration|Search machine types"
+msgstr "ПоиÑк типов машин клаÑтера"
+
+msgid "ClusterIntegration|Search projects"
+msgstr "ПоиÑк проектов"
+
+msgid "ClusterIntegration|Search zones"
+msgstr "ПоиÑк зон"
+
msgid "ClusterIntegration|Security"
-msgstr ""
+msgstr "БезопаÑноÑÑ‚ÑŒ"
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "ПроÑмотр и редактирование информации о вашем клаÑтере Kubernetes"
-msgid "ClusterIntegration|See machine types"
-msgstr "См. типы машин"
+msgid "ClusterIntegration|Select machine type"
+msgstr "Выберите тип машины"
+
+msgid "ClusterIntegration|Select project"
+msgstr "Выберите проект"
-msgid "ClusterIntegration|See your projects"
-msgstr "См. ваши проекты"
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr "Выберите проект и зону, чтобы выбрать тип машины"
-msgid "ClusterIntegration|See zones"
-msgstr "См. зоны"
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr "Выберите проект, чтобы выбрать зону"
+
+msgid "ClusterIntegration|Select zone"
+msgstr "Выберете зону"
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr "Выберите зону, чтобы выбрать тип машины"
msgid "ClusterIntegration|Service token"
msgstr "Служебный токен"
@@ -1199,10 +1832,10 @@ msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Произошли ошибки во Ð²Ñ€ÐµÐ¼Ñ ÑƒÑтановки %{title}"
msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr ""
+msgstr "По умолчанию ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтера предоÑтавлÑет доÑтуп к широкому набору функциональных возможноÑтей, необходимых Ð´Ð»Ñ ÑƒÑпешного ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¸ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ð¹ в контейнерах."
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
-msgstr ""
+msgstr " У Ñтой учетной запиÑи должны быть Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð½Ð° Ñоздание клаÑтера Kubernetes в %{link_to_container_project} указанных ниже"
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr "Переключить КлаÑтер Kubernetes"
@@ -1213,6 +1846,9 @@ msgstr "Переключить клаÑтер Kubernetes"
msgid "ClusterIntegration|Token"
msgstr "Токен"
+msgid "ClusterIntegration|Validating project billing status"
+msgstr "Проверка ÑтатуÑа тарификации проекта"
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "ЕÑли привÑзать клаÑтер Kubernetes к Ñтому проекту, вы Ñ Ð»Ñ‘Ð³ÐºÐ¾Ñтью Ñможете иÑпользовать Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ€ÐµÐ²ÑŒÑŽ, развертывать ваши приложениÑ, запуÑкать Ñборочные линии и многое другое."
@@ -1226,7 +1862,7 @@ msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr "доÑтуп к Google Kubernetes Engine"
msgid "ClusterIntegration|check the pricing here"
-msgstr ""
+msgstr "поÑмотрите цены здеÑÑŒ"
msgid "ClusterIntegration|documentation"
msgstr "документациÑ"
@@ -1243,14 +1879,23 @@ msgstr "отвечает требованиÑм"
msgid "ClusterIntegration|properly configured"
msgstr "правильно наÑтроен"
+msgid "ClusterIntegration|sign up"
+msgstr "зарегиÑтрироватьÑÑ"
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr "Свернуть"
-msgid "Comment and resolve discussion"
-msgstr "Прокомментировать и закрыть диÑкуÑÑию"
+msgid "Collapse sidebar"
+msgstr "Свернуть боковую панель"
-msgid "Comment and unresolve discussion"
-msgstr "Прокомментировать и переоткрыть диÑкуÑÑию"
+msgid "Comment & resolve discussion"
+msgstr ""
+
+msgid "Comment & unresolve discussion"
+msgstr ""
msgid "Comments"
msgstr "Комментарии"
@@ -1279,7 +1924,7 @@ msgid "Commit message"
msgstr "ОпиÑание коммита"
msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
-msgstr ""
+msgstr "СтатиÑтика коммитов Ð´Ð»Ñ %{ref} в период Ñ %{start_time} по %{end_time}"
msgid "Commit to %{branchName} branch"
msgstr ""
@@ -1297,46 +1942,49 @@ msgid "Commits feed"
msgstr "Лента коммитов"
msgid "Commits per day hour (UTC)"
-msgstr ""
+msgstr "Коммиты по чаÑам Ð´Ð½Ñ (UTC)"
msgid "Commits per day of month"
-msgstr ""
+msgstr "Коммиты по днÑм меÑÑца"
msgid "Commits per weekday"
-msgstr ""
+msgstr "Коммиты по днÑм недели"
msgid "Commits|An error occurred while fetching merge requests data."
msgstr "Произошла ошибка при получении данных запроÑа на ÑлиÑниÑ."
msgid "Commits|Commit: %{commitText}"
-msgstr ""
+msgstr "Коммит: %{commitText}"
msgid "Commits|History"
msgstr "ИÑториÑ"
msgid "Commits|No related merge requests found"
-msgstr ""
+msgstr "Ðе найдено ÑвÑзанных запроÑов на ÑлиÑние"
msgid "Committed by"
msgstr "ЗафикÑировано автором"
+msgid "Commit…"
+msgstr "Коммит…"
+
msgid "Compare"
msgstr "Сравнить"
msgid "Compare Git revisions"
-msgstr ""
+msgstr "Сравнение верÑий Git"
msgid "Compare Revisions"
-msgstr ""
+msgstr "Сравнить верÑии"
msgid "Compare changes with the last commit"
-msgstr ""
+msgstr "Сравнить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ Ð¿Ð¾Ñледним коммитом"
msgid "Compare changes with the merge request target branch"
msgstr ""
msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
-msgstr ""
+msgstr "%{source_branch} и %{target_branch} одинаковы."
msgid "CompareBranches|Compare"
msgstr "Сравнить"
@@ -1351,21 +1999,24 @@ msgid "CompareBranches|There isn't anything to compare."
msgstr "Ðечего Ñравнивать."
msgid "Confidential"
-msgstr ""
+msgstr "Конфиденциально"
msgid "Confidentiality"
msgstr "КонфиденциальноÑÑ‚ÑŒ"
msgid "Configure Gitaly timeouts."
-msgstr ""
+msgstr "ÐаÑтройка таймаутов Gitaly."
msgid "Configure Sidekiq job throttling."
-msgstr ""
+msgstr "ÐаÑтройте фактор уÑÐºÐ¾Ñ€ÐµÐ½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ очереди иÑполнителей фоновых заданий Sidekiq."
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr ""
msgid "Configure limits for web and API requests."
+msgstr "ÐаÑтройка ограничений Ð´Ð»Ñ Web и API запроÑов."
+
+msgid "Configure push and pull mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
@@ -1375,19 +2026,19 @@ msgid "Configure the way a user creates a new account."
msgstr ""
msgid "Connect"
-msgstr ""
+msgstr "Подключить"
msgid "Connect all repositories"
msgstr ""
msgid "Connect repositories from GitHub"
-msgstr ""
+msgstr "Подключить репозитории Ñ GitHub"
msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
msgstr ""
msgid "Connecting..."
-msgstr "Подключение..."
+msgstr ""
msgid "Container Registry"
msgstr "РееÑÑ‚Ñ€ Контейнеров"
@@ -1434,15 +2085,30 @@ msgstr "ИÑпользовать различные имена образов"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr "Когда рееÑÑ‚Ñ€ контейнеров Docker интегрирован Ñ GitLab, каждый проект может иметь Ñвое ÑобÑтвенное проÑтранÑтво Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÐµÐ³Ð¾ Docker образов."
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr "Ð’Ñ‹ также можете иÑпользовать %{deploy_token} Ð´Ð»Ñ Ð´Ð¾Ñтупа в режиме только чтение к рееÑтру образов."
+
+msgid "Continue"
+msgstr "Продолжить"
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
+msgstr "ÐÐµÐ¿Ñ€ÐµÑ€Ñ‹Ð²Ð½Ð°Ñ Ð¸Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ Ð¸ развертывание"
+
+msgid "Contribute to GitLab"
msgstr ""
msgid "Contribution"
-msgstr ""
+msgstr "УчаÑтие"
msgid "Contribution guide"
msgstr "РуководÑтво учаÑтника"
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr "УчаÑтники"
@@ -1458,14 +2124,23 @@ msgstr "Коммиты в %{branch_name}, за иÑключением комми
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr "ПожалуйÑта подождите, Ñта Ñтраница автоматичеÑки обновитÑÑ Ð¿Ð¾ готовноÑти."
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
-msgstr "Контролировать макÑимальное количеÑтво потоков фоновой загрузки LFS/вложений Ð´Ð»Ñ Ñтого вторичного узла"
+msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
-msgstr "Контролировать макÑимальное количеÑтво потоков фоновой загрузки хранилища Ð´Ð»Ñ Ñтого вторичного узла"
+msgstr ""
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
msgid "Copy SSH public key to clipboard"
-msgstr "Скопировать публичный ключ SSH в буфер обмена"
+msgstr ""
msgid "Copy URL to clipboard"
msgstr "Копировать URL в буфер обмена"
@@ -1479,9 +2154,21 @@ msgstr "Копировать команду в буфер обмена"
msgid "Copy commit SHA to clipboard"
msgstr "Копировать SHA коммита в буфер обмена"
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr "Скопировать ÑÑылку в буфер обмена"
+msgid "Copy to clipboard"
+msgstr "Скопировать в буфер обмена"
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr "Создать"
@@ -1494,25 +2181,37 @@ msgstr "Создать новую ветку"
msgid "Create a new branch and merge request"
msgstr "Создать новую ветку и Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Создать личный токен на аккаунте Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ отправки через %{protocol}."
msgid "Create branch"
msgstr "Создать ветку"
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr "Создать каталог"
msgid "Create empty repository"
-msgstr ""
+msgstr "Создать пуÑтой репозиторий"
msgid "Create epic"
-msgstr "Создать Ñпик"
+msgstr ""
msgid "Create file"
msgstr "Создать файл"
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
+msgstr "Создать метку группы"
+
+msgid "Create issue"
msgstr ""
msgid "Create lists from labels. Issues with that label appear in that list."
@@ -1533,6 +2232,9 @@ msgstr "Создать новый каталог"
msgid "Create new file"
msgstr "Создать новый файл"
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr "Создать новую метку"
@@ -1540,7 +2242,7 @@ msgid "Create new..."
msgstr "Ðовый"
msgid "Create project label"
-msgstr ""
+msgstr "Создать метку проекта"
msgid "CreateNewFork|Fork"
msgstr "Ответвить"
@@ -1551,14 +2253,20 @@ msgstr "Тег"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "Ñоздать перÑональный токен доÑтупа"
-msgid "Creates a new branch from %{branchName}"
-msgstr "Создает новую ветку из %{branchName}"
+msgid "Created"
+msgstr "Создан"
+
+msgid "Created At"
+msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
-msgstr "Создает новую ветку из %{branchName} и перенаправлÑет на Ñоздание нового запроÑа на ÑлиÑние"
+msgid "Created by me"
+msgstr "Создано мной"
+
+msgid "Created on:"
+msgstr ""
msgid "Creating epic"
-msgstr "Создание Ñпика"
+msgstr ""
msgid "Cron Timezone"
msgstr "Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð·Ð¾Ð½Ð° Cron"
@@ -1567,7 +2275,16 @@ msgid "Cron syntax"
msgstr "СинтакÑÐ¸Ñ Cron"
msgid "Current node"
-msgstr "Текущий узел"
+msgstr ""
+
+msgid "CurrentUser|Profile"
+msgstr "Профиль"
+
+msgid "CurrentUser|Settings"
+msgstr "ÐаÑтройки"
+
+msgid "Custom CI config path"
+msgstr ""
msgid "Custom notification events"
msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð½Ð°Ñтраиваемых уведомлений"
@@ -1578,6 +2295,12 @@ msgstr "ÐаÑтраиваемые уровни уведомлений аналÐ
msgid "Customize colors"
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 "Cycle Analytics"
msgstr "Ðналитика Цикла"
@@ -1602,6 +2325,9 @@ msgstr "Приёмка"
msgid "CycleAnalyticsStage|Test"
msgstr "ТеÑтирование"
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr "Ð’Ñе"
@@ -1614,8 +2340,17 @@ msgstr "Дек."
msgid "December"
msgstr "Декабрь"
+msgid "Decline and sign out"
+msgstr "Отклонить и выйти"
+
msgid "Default classification label"
-msgstr "Метка клаÑÑификации по умолчанию"
+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 ""
msgid "Define a custom pattern with cron syntax"
msgstr "Определить наÑтраиваемый шаблон Ñ ÑинтакÑиÑом cron"
@@ -1623,6 +2358,18 @@ msgstr "Определить наÑтраиваемый шаблон Ñ Ñинт
msgid "Delete"
msgstr "Удалить"
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr "Удалить ÑпиÑок"
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "Развертывание"
@@ -1633,11 +2380,143 @@ msgstr[3] "РазвертываниÑ"
msgid "Deploy Keys"
msgstr "Ключи РазвертываниÑ"
+msgid "DeployKeys|+%{count} others"
+msgstr "+%{count} других"
+
+msgid "DeployKeys|Current project"
+msgstr "Текущий проект"
+
+msgid "DeployKeys|Deploy key"
+msgstr "Ключ развертываниÑ"
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr "Примененные ключи развертываниÑ"
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr "Ошибка Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÐºÐ»ÑŽÑ‡Ð° развертываниÑ"
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr "Ошибка Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÐºÐ»ÑŽÑ‡Ð° развертываниÑ"
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr "Ошибка ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ ÐºÐ»ÑŽÑ‡Ð° развертываниÑ"
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr "Развернуть в %{count} других проектах"
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr "Загрузка ключей развертываниÑ"
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr "Ðе найдено ключей развертываниÑ. Создайте первый Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ данной формы."
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr "Приватные ключи развертываниÑ"
+
+msgid "DeployKeys|Project usage"
+msgstr "ИÑпользование в проекте"
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr "Публичные ключи развертываниÑ"
+
+msgid "DeployKeys|Read access only"
+msgstr "ДоÑтуп только на чтение"
+
+msgid "DeployKeys|Write access allowed"
+msgstr "Разрешен доÑтуп Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи"
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ удалить Ñтот ключ развертываниÑ. Ð’Ñ‹ уверены?"
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr "Ðктивные токены Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ (%{active_tokens})"
+
+msgid "DeployTokens|Add a deploy token"
+msgstr "Создать токен развертываниÑ"
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr "Разрешает доÑтуп только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ðº региÑтру образов"
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr "Разрешает доÑтуп только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ðº репозиторию"
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr "Копировать токен Ñ€Ð°Ð·Ð²Ñ‘Ñ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð² буфер обмена"
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr "Копировать Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð² буфер обмена"
+
+msgid "DeployTokens|Create deploy token"
+msgstr "Создать токен развертываниÑ"
+
+msgid "DeployTokens|Created"
+msgstr "Создан"
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr "Токены развертываниÑ"
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr "Токены Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð´Ð°ÑŽÑ‚ доÑтуп только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ðº вашим репозиториÑм и региÑтру образов."
+
+msgid "DeployTokens|Expires"
+msgstr "ИÑтекает"
+
+msgid "DeployTokens|Name"
+msgstr "Ðаименование"
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr "Выберите Ð¸Ð¼Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ, и мы предоÑтавим вам уникальный токен развертываниÑ."
+
+msgid "DeployTokens|Revoke"
+msgstr "Отозвать"
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr "Отозвать %{name}"
+
+msgid "DeployTokens|Scopes"
+msgstr "ОблаÑти"
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr "Это дейÑтвие Ð½ÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ."
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr "Этот проект не имеет активных токенов развертываниÑ."
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr "ИÑпользуйте Ñтот токен в качеÑтве паролÑ. УбедитеÑÑŒ, что вы Ñохраните его - вы не Ñможете получить доÑтуп к нему Ñнова."
+
+msgid "DeployTokens|Use this username as a login."
+msgstr "ИÑпользуйте Ñто Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÐºÐ°Ðº логин."
+
+msgid "DeployTokens|Username"
+msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+
+msgid "DeployTokens|You are about to revoke"
+msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ отозвать"
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr "Ваш Ðовый Токен РазвертываниÑ"
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr "Создан новый токен Ð´Ð»Ñ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð°."
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr "ОпиÑание"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
-msgstr "Шаблоны опиÑаний позволÑÑŽÑ‚ вам определить контекÑтно-завиÑимые шаблоны Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¾Ð±Ñуждений и запроÑов на ÑлиÑние в вашем проекте."
+msgstr ""
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
msgid "Details"
msgstr "ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
@@ -1645,32 +2524,56 @@ msgstr "ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
msgid "Diffs|No file name available"
msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° недоÑтупно"
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr "Ð˜Ð¼Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð°"
msgid "Disable"
msgstr "Отключить"
+msgid "Disable for this project"
+msgstr "Отключить Ð´Ð»Ñ Ñтого проекта"
+
+msgid "Disable group Runners"
+msgstr "Выключить групповые обработчиков заданий"
+
+msgid "Discard changes"
+msgstr "Отменить изменениÑ"
+
msgid "Discard draft"
msgstr "Удалить черновик"
msgid "Discover GitLab Geo."
-msgstr "Откройте Ð´Ð»Ñ ÑÐµÐ±Ñ GitLab Geo."
+msgstr ""
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Отключить блок Ð²Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð² Ðналитику Цикла"
msgid "Dismiss Merge Request promotion"
-msgstr "Отключить анонÑÑ‹ Ð´Ð»Ñ Ð—Ð°Ð¿Ñ€Ð¾Ñов на ÑлиÑние"
+msgstr ""
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr "Домен"
+
msgid "Don't show again"
msgstr "Ðе показывать Ñнова"
msgid "Done"
-msgstr ""
+msgstr "Выполнено"
msgid "Download"
msgstr "Скачать"
@@ -1700,24 +2603,39 @@ msgid "DownloadSource|Download"
msgstr "Скачать"
msgid "Downvotes"
-msgstr ""
+msgstr "ГолоÑа \"против\""
msgid "Due date"
-msgstr "Срок"
+msgstr "Плановый Ñрок"
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr "Редактировать"
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "Изменить раÑпиÑание Ñборочной линии %{id}"
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr "Редактируйте файлы в редакторе и зафикÑируйте Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð´ÐµÑÑŒ"
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1727,32 +2645,53 @@ msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
+msgstr "Ð­Ð»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð°"
+
+msgid "Email patch"
msgstr ""
msgid "Emails"
msgstr "Email-адреÑа"
+msgid "Embed"
+msgstr "Ð’Ñтроить"
+
msgid "Enable"
msgstr "Включить"
msgid "Enable Auto DevOps"
msgstr "Включить Auto DevOps"
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
msgid "Enable Sentry for error reporting and logging."
-msgstr ""
+msgstr "Включить Sentry Ð´Ð»Ñ Ð¾Ñ‚Ñ‡ÐµÑ‚Ð¾Ð² об ошибках и журналированиÑ."
msgid "Enable and configure InfluxDB metrics."
-msgstr ""
+msgstr "Включить и наÑтроить метрики InfluxDB."
msgid "Enable and configure Prometheus metrics."
-msgstr ""
+msgstr "Включить и наÑтроить метрики Prometheus."
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr "Включить Ð´Ð»Ñ Ñтого проекта"
+
+msgid "Enable group Runners"
+msgstr "Включить групповые обработчики заданий"
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1760,20 +2699,35 @@ msgid "Enable reCAPTCHA or Akismet and set IP limits."
msgstr ""
msgid "Enable the Performance Bar for a given group."
-msgstr ""
+msgstr "Включите панель производительноÑти Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð¹ группы."
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr "ЗаканчиваетÑÑ Ð² (UTC)"
+
+msgid "Environments"
+msgstr "Среды"
+
msgid "Environments|An error occurred while fetching the environments."
msgstr "Произошла ошибка при получении окружений."
msgid "Environments|An error occurred while making the request."
msgstr "Произошла ошибка во Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа."
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr "Коммит"
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr "Развертывание"
@@ -1786,56 +2740,71 @@ msgstr "ОкружениÑ"
msgid "Environments|Job"
msgstr "Задание"
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr "Ðовое окружение"
msgid "Environments|No deployments yet"
msgstr "Еще нет развертываний"
-msgid "Environments|Open"
-msgstr "Открыть"
+msgid "Environments|No pod name has been specified"
+msgstr ""
-msgid "Environments|Re-deploy"
-msgstr "Переразвернуть"
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
msgid "Environments|Read more about environments"
msgstr "Подробнее об окружениÑÑ…"
-msgid "Environments|Rollback"
-msgstr "Откатить"
+msgid "Environments|Rollback environment"
+msgstr ""
msgid "Environments|Show all"
msgstr "Показать вÑе"
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Обновлено"
msgid "Environments|You don't have any environments right now."
msgstr "Ð’Ñ‹ пока не наÑтроили ни одного окружениÑ."
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
-msgstr "Эпик будет удален! Вы уверены?"
+msgstr ""
msgid "Epics"
-msgstr "Эпики"
+msgstr ""
msgid "Epics Roadmap"
msgstr ""
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr "Эпики позволÑÑ‚ вам управлÑÑ‚ÑŒ портфелем проектов более Ñффективно и Ñ Ð¼ÐµÐ½ÑŒÑˆÐ¸Ð¼Ð¸ уÑилиÑми"
-
-msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr "Ошибка проверки данных ветки. ПожалуйÑта, попробуйте еще раз."
-
-msgid "Error committing changes. Please try again."
-msgstr "Ошибка при Ñохранении изменений. ПожалуйÑта, попробуйте еще раз."
+msgid "Error Reporting and Logging"
+msgstr "Отчеты об ошибках и журналирование"
msgid "Error creating epic"
-msgstr "Ошибка ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñпика"
+msgstr ""
msgid "Error fetching contributors data."
msgstr "Ошибка Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… учаÑтников."
@@ -1852,6 +2821,21 @@ msgstr "Ошибка Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÑÑылок"
msgid "Error fetching usage ping data."
msgstr "Ошибка при получении данных об иÑпользовании ping."
+msgid "Error loading branch data. Please try again."
+msgstr "Ошибка загрузки данных ветки. ПожалуйÑта, попробуйте еще раз."
+
+msgid "Error loading last commit."
+msgstr "Ошибка загрузки поÑледнего коммита."
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr "Ошибка загрузки данных проекта. ПожалуйÑта, попробуйте еще раз."
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Произошла ошибка при переключении подпиÑки на оповещение"
@@ -1859,10 +2843,13 @@ msgid "Error saving label update."
msgstr "Ошибка при обновлении метки."
msgid "Error updating status for all todos."
-msgstr "Ошибка при обновлении ÑтатуÑа Ð´Ð»Ñ Ð²Ñех todo."
+msgstr "Ошибка при обновлении ÑтатуÑа Ð´Ð»Ñ Ð²Ñех дел."
msgid "Error updating todo status."
-msgstr "Ошибка при обновлении ÑтатуÑа todo."
+msgstr "Ошибка при обновлении ÑтатуÑа дела."
+
+msgid "Estimated"
+msgstr ""
msgid "EventFilterBy|Filter by all"
msgstr "Фильтр по вÑему"
@@ -1891,9 +2878,30 @@ msgstr "ЕжемеÑÑчно (каждое 1-е чиÑло в 4:00)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Еженедельно (по воÑкреÑениÑм в 4:00)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr "Развернуть"
+msgid "Expand all"
+msgstr "Развернуть вÑе"
+
+msgid "Expand sidebar"
+msgstr "Развернуть боковую панель"
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr "Обзор проектов"
@@ -1921,15 +2929,21 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
-msgid "Failed"
+msgid "Facebook"
msgstr ""
+msgid "Failed"
+msgstr "Ðеудачно"
+
msgid "Failed Jobs"
msgstr "Ðевыполненные ЗаданиÑ"
msgid "Failed to change the owner"
msgstr "Ðе удалоÑÑŒ изменить владельца"
+msgid "Failed to check related branches."
+msgstr "Ðе удалоÑÑŒ проверить ÑвÑзанные ветки."
+
msgid "Failed to remove issue from board, please try again."
msgstr "Ошибка при удалении обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ñ Ð´Ð¾Ñки, повторите попытку."
@@ -1939,6 +2953,12 @@ msgstr "Ðе удалоÑÑŒ удалить раÑпиÑание Ñборочно
msgid "Failed to update issues, please try again."
msgstr "Ошибка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾Ð±Ñуждений, пожалуйÑта, попробуйте Ñнова."
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr "Фев."
@@ -1948,9 +2968,6 @@ msgstr "Февраль"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "ÐŸÐ¾Ð»Ñ Ð½Ð° Ñтой Ñтранице ÑÐµÐ¹Ñ‡Ð°Ñ Ð½ÐµÐ´Ð¾Ñтупны Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ, вы можете наÑтроить"
-msgid "File name"
-msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°"
-
msgid "Files"
msgstr "Файлы"
@@ -1960,6 +2977,9 @@ msgstr "Файлов (%{human_size})"
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr "Фильтр по комментариÑми к коммитам"
@@ -1969,21 +2989,54 @@ msgstr "ПоиÑк по пути"
msgid "Find file"
msgstr "Ðайти файл"
-msgid "Finished"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Finished"
+msgstr "Завершено"
+
msgid "FirstPushedBy|First"
msgstr "Первый"
msgid "FirstPushedBy|pushed by"
msgstr "отправлено автором"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "Ответвление"
@@ -2003,7 +3056,22 @@ msgstr "ВыполнÑетÑÑ Ð¾Ñ‚Ð²ÐµÑ‚Ð²Ð»ÐµÐ½Ð¸Ðµ"
msgid "Format"
msgstr "Формат"
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
+msgstr "Из %{provider_title}"
+
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
msgstr ""
msgid "From issue creation until deploy to production"
@@ -2018,25 +3086,34 @@ msgstr ""
msgid "GPG Keys"
msgstr "GPG Ключи"
+msgid "General"
+msgstr "ОÑновныe"
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr "Создать Ñтандартный набор меток"
msgid "Geo Nodes"
-msgstr "ГеографичеÑкие Узлы"
+msgstr ""
msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
-msgstr "Ðа узле Ñбой или он не работает."
+msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
-msgstr "Узел функционирует медленно, перегужен или только что воÑÑтановлен поÑле ÑбоÑ."
+msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2051,31 +3128,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2096,19 +3185,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
+msgstr ""
+
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2117,16 +3212,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2144,13 +3242,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2160,7 +3264,7 @@ msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
-msgstr "Объем хранилища Ð´Ð»Ñ Ñинхронизации файлов"
+msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
@@ -2172,14 +3276,20 @@ msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
-msgstr "Объем хранилища Ð´Ð»Ñ Ñинхронизации репозиториÑ"
+msgstr ""
msgid "Geo|Select groups to replicate."
-msgstr "Выберите группы Ð´Ð»Ñ Ñ€ÐµÐ¿Ð»Ð¸ÐºÐ°Ñ†Ð¸Ð¸."
+msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr "URL-Ð°Ð´Ñ€ÐµÑ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Git"
@@ -2189,46 +3299,115 @@ msgstr "Ð ÐµÐ²Ð¸Ð·Ð¸Ñ git"
msgid "Git storage health information has been reset"
msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ ÑтабильноÑти Git хранилища была Ñброшена"
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr "ВерÑÐ¸Ñ Git"
msgid "GitHub import"
-msgstr ""
+msgstr "Импорт из GitHub"
msgid "GitLab CI Linter has been moved"
-msgstr ""
+msgstr "GitLab CI Linter перемещен"
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "Ð¡ÐµÐºÑ†Ð¸Ñ Gitlab Runner"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr "Групповые Обработчики Заданий (GitLab Group Runners) могут выполнÑÑ‚ÑŒ код Ð´Ð»Ñ Ð²Ñех проектов в Ñтой группе."
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
-msgid "Gitaly"
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
msgstr ""
+msgid "Gitaly"
+msgstr "Gitaly"
+
msgid "Gitaly Servers"
msgstr "Серверы Gitaly"
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr "ВернутьÑÑ Ð½Ð°Ð·Ð°Ð´"
+
msgid "Go back"
msgstr "ВернутьÑÑ"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "Перейти к вашему ответвлению"
msgid "GoToYourFork|Fork"
msgstr "Ответвление"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "ÐÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Google не %{link_to_documentation}. ПопроÑите Ñвоего админиÑтратора GitLab, еÑли вы хотите воÑпользоватьÑÑ Ñтим ÑервиÑом."
msgid "Got it!"
msgstr "ПонÑтно!"
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr "Групповые Обработчики Заданий (GitLab Group Runner)"
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2240,7 +3419,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2270,6 +3470,33 @@ msgstr "не может быть отменена до тех пор пока г
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr "удалить возможноÑÑ‚ÑŒ поделитьÑÑ Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð¾Ð²Ð¾Ð¹ блокировкой из %{ancestor_group_name}"
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr "Группа - Ñто набор из неÑкольких проектов."
@@ -2310,7 +3537,7 @@ msgid "GroupsTree|Sorry, no groups or projects matched your search"
msgstr "К Ñожалению, по вашему запроÑу групп или проектов не найдено"
msgid "Have your users email"
-msgstr "Ð­Ð»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð° Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ð¹ пользователей"
+msgstr ""
msgid "Header message"
msgstr ""
@@ -2334,20 +3561,23 @@ msgid "HealthCheck|Unhealthy"
msgstr "ÐеÑтабильный"
msgid "Help"
-msgstr ""
+msgstr "Помощь"
msgid "Help page"
-msgstr ""
+msgstr "Страница Ñправки"
msgid "Help page text and support page url."
-msgstr ""
+msgstr "ТекÑÑ‚ Ñтраницы Ñправки и Url-Ð°Ð´Ñ€ÐµÑ Ñтраницы поддержки."
msgid "Hide value"
msgid_plural "Hide values"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Скрыть значение"
+msgstr[1] "Скрыть значениÑ"
+msgstr[2] "Скрыть значениÑ"
+msgstr[3] "Скрыть значениÑ"
+
+msgid "Hide whitespace changes"
+msgstr ""
msgid "History"
msgstr "ИÑториÑ"
@@ -2355,9 +3585,45 @@ msgstr "ИÑториÑ"
msgid "Housekeeping successfully started"
msgstr "ОчиÑтка уÑпешно запущена"
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr "ID"
+
+msgid "IDE|Commit"
+msgstr "Коммит"
+
+msgid "IDE|Edit"
+msgstr "Редактировать"
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr "Обзор"
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2365,23 +3631,62 @@ msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commit
msgstr ""
msgid "If you already have files you can push them using the %{link_to_cli} below."
-msgstr ""
+msgstr "ЕÑли у Ð²Ð°Ñ ÑƒÐ¶Ðµ еÑÑ‚ÑŒ файлы иÑходных кодов вы можете отправить их Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ ÑÑылки %{link_to_cli} ниже."
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
+msgstr "Импортировать"
+
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
msgstr ""
msgid "Import all repositories"
+msgstr "Импортировать вÑе репозитории"
+
+msgid "Import an exported GitLab project"
msgstr ""
msgid "Import in progress"
msgstr "ВыполнÑетÑÑ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚"
-msgid "Import repositories from GitHub"
+msgid "Import multiple repositories by uploading a manifest file."
msgstr ""
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr "Импорт репозиториев из GitHub"
+
msgid "Import repository"
msgstr "Импорт репозиториÑ"
@@ -2389,35 +3694,50 @@ msgid "ImportButtons|Connect repositories from"
msgstr ""
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr "Улучшить доÑки обÑуждений Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ GitLab Enterprise Edition."
+msgstr ""
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
-msgstr "Улучшить управление обÑуждениÑми возможноÑтью Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð²ÐµÑа обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¸ GitLab Enterprise Edition."
+msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
-msgstr "Улучшить поиÑк при помощи РаÑширенного Глобального ПоиÑка и GitLab Enterprise Edition."
+msgstr ""
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
msgid "Install Runner on Kubernetes"
msgstr "УÑтановить Runner на Kubernetes"
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "УÑтановите Gitlab Runner ÑовмеÑтимый Ñ Gitlab CI"
-
msgid "Instance"
msgid_plural "Instances"
-msgstr[0] "ЭкземплÑÑ€"
-msgstr[1] "ЭкземплÑра"
-msgstr[2] "ЭкземплÑров"
-msgstr[3] "ЭкземплÑры"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
msgid "Instance does not support multiple Kubernetes clusters"
msgstr "ЭкземплÑÑ€ не поддерживает неÑколько клаÑтеров Kubernetes"
msgid "Integrations"
+msgstr "Интеграции"
+
+msgid "Integrations Settings"
msgstr ""
msgid "Interested parties can even contribute by pushing commits if they want to."
-msgstr ""
+msgstr "Желающие могут внеÑти Ñвой вклад, отправив коммит, еÑли захотÑÑ‚."
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "Внутренний - Группу и включённые в неё проекты может видеть любой зарегиÑтрированный пользователь."
@@ -2431,8 +3751,11 @@ msgstr "Шаблон интервала"
msgid "Introducing Cycle Analytics"
msgstr "Внедрение Цикла Ðналитик"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
-msgstr "Режим фокуÑировки над доÑкой обÑуждений"
+msgstr ""
msgid "Issue events"
msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¾Ð±Ñуждений"
@@ -2441,12 +3764,15 @@ msgid "IssueBoards|Board"
msgstr "ДоÑка"
msgid "IssueBoards|Boards"
-msgstr "ДоÑки"
+msgstr ""
msgid "Issues"
msgstr "ОбÑуждениÑ"
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr "ОбÑуждениÑми могут быть ошибки, задачи или идеи. Также, по обÑуждениÑм можно выполнÑÑ‚ÑŒ поиÑк и отбор."
+
+msgid "Issues closed"
msgstr ""
msgid "Jan"
@@ -2455,6 +3781,12 @@ msgstr "Янв."
msgid "January"
msgstr "Январь"
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr "Фоновое задание было удалено"
+
msgid "Jobs"
msgstr "ЗаданиÑ"
@@ -2471,6 +3803,9 @@ msgid "June"
msgstr "Июнь"
msgid "Koding"
+msgstr "Koding"
+
+msgid "Koding Dashboard"
msgstr ""
msgid "Kubernetes"
@@ -2497,6 +3832,9 @@ msgstr "Kubernetes наÑтроен"
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Отключено"
@@ -2506,26 +3844,38 @@ msgstr "Включено"
msgid "Label"
msgstr "Метка"
-msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgid "Label actions dropdown"
msgstr ""
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
+msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
+msgstr "%{firstLabelName} + ещё %{remainingLabelCount}"
+
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
+msgstr "%{labelsString} и еще %{remainingLabelCount} других"
+
+msgid "LabelSelect|Labels"
msgstr ""
msgid "Labels"
msgstr "Метки"
msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr ""
+msgstr "Метки могут быть применены к %{features}. Групповые метки доÑтупны Ð´Ð»Ñ Ð»ÑŽÐ±Ð¾Ð³Ð¾ проекта внутри группы."
msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr "Метки могут применÑÑ‚ÑŒÑÑ Ðº обÑуждениÑм и запроÑам на ÑлиÑние Ð´Ð»Ñ Ð¸Ñ… категоризации."
+
+msgid "Labels can be applied to issues and merge requests."
msgstr ""
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
-msgstr ""
+msgstr "<span>ПовыÑить метку</span> %{labelTitle} <span>до групповой метки?</span>"
msgid "Labels|Promote Label"
-msgstr ""
+msgstr "ПеренеÑти Метку"
msgid "Last %d day"
msgid_plural "Last %d days"
@@ -2558,11 +3908,14 @@ msgstr "Вы отправили в"
msgid "LastPushEvent|at"
msgstr "в"
+msgid "Latest changes"
+msgstr "ПоÑледние изменениÑ"
+
msgid "Learn more"
-msgstr ""
+msgstr "Подробнее"
msgid "Learn more about Kubernetes"
-msgstr ""
+msgstr "Подробнее о Kubernates"
msgid "Learn more about protected branches"
msgstr ""
@@ -2582,65 +3935,125 @@ msgstr "Покинуть группу"
msgid "Leave project"
msgstr "Покинуть проект"
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
-msgstr "ЛицензиÑ"
+msgstr ""
+
+msgid "LinkedIn"
+msgstr ""
msgid "List"
msgstr "СпиÑок"
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
+msgstr "СпиÑок ваших репозиториев на GitHub"
+
+msgid "Loading contribution stats for group members"
msgstr ""
msgid "Loading the GitLab IDE..."
msgstr "Загрузка GitLab IDE..."
+msgid "Loading..."
+msgstr "Загрузка..."
+
msgid "Lock"
msgstr "Блокировка"
msgid "Lock %{issuableDisplayName}"
-msgstr ""
+msgstr "Заблокировать %{issuableDisplayName}"
msgid "Lock not found"
+msgstr "Блокировка не найдена"
+
+msgid "Lock to current projects"
msgstr ""
msgid "Locked"
msgstr "Заблокировано"
msgid "Locked Files"
-msgstr "Заблокированные Файлы"
+msgstr ""
+
+msgid "Locked to current projects"
+msgstr ""
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
-msgstr "Войти"
+msgid "Logs"
+msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
+msgstr "Управление уведомлениÑми"
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
msgstr ""
-msgid "Manage group labels"
+msgid "Manage applications that you've authorized to use your account."
msgstr ""
+msgid "Manage group labels"
+msgstr "Управление метками группы"
+
msgid "Manage labels"
-msgstr ""
+msgstr "Управление метками"
msgid "Manage project labels"
-msgstr ""
+msgstr "Управление метками проекта"
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr "Мар."
msgid "March"
msgstr "Март"
-msgid "Mark done"
-msgstr ""
+msgid "Mark todo as done"
+msgstr "Отметить как Ñделанное"
+
+msgid "Markdown enabled"
+msgstr "Включен режим Markdown"
msgid "Maximum git storage failures"
msgstr "МакÑимальное количеÑтво Ñбоев хранилища git"
@@ -2657,45 +4070,90 @@ msgstr "УчаÑтники"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние:"
+
msgid "Merge Requests"
msgstr "ЗапроÑÑ‹ на СлиÑние"
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ ÑлиÑний"
msgid "Merge request"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr "ЗапроÑÑ‹ на ÑлиÑние"
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr "ЗапроÑÑ‹ на ÑлиÑние- Ñто меÑто, где можно предлагать Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²Ð½Ð¾Ñимые в проект, и обÑуждать Ñти Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸"
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
msgstr ""
-msgid "Merged"
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "Merged"
+msgstr "Слито"
+
msgid "Messages"
msgstr "СообщениÑ"
-msgid "Metrics - Influx"
+msgid "Metrics"
msgstr ""
+msgid "Metrics - Influx"
+msgstr "Метрики - Influx"
+
msgid "Metrics - Prometheus"
-msgstr ""
+msgstr "Метрики - Prometheus"
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2708,6 +4166,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2720,9 +4181,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2751,25 +4230,28 @@ msgid "Metrics|e.g. req/sec"
msgstr ""
msgid "Milestone"
-msgstr ""
+msgstr "Этап"
+
+msgid "Milestones"
+msgstr "Этапы"
msgid "Milestones|Delete milestone"
-msgstr ""
+msgstr "Удалить Ñтап"
msgid "Milestones|Delete milestone %{milestoneTitle}?"
-msgstr ""
+msgstr "Удалить Ñтап %{milestoneTitle}?"
msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
-msgstr ""
+msgstr "Ðе удалоÑÑŒ удалить Ñтап %{milestoneTitle}"
msgid "Milestones|Milestone %{milestoneTitle} was not found"
-msgstr ""
+msgstr "Этап %{milestoneTitle} не найден"
msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
-msgstr ""
+msgstr "ПовыÑить %{milestoneTitle} до группового Ñтапа?"
msgid "Milestones|Promote Milestone"
-msgstr ""
+msgstr "ПовыÑить Ñтап"
msgid "Milestones|This action cannot be reversed."
msgstr ""
@@ -2778,33 +4260,81 @@ msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "добавить ключ SSH"
msgid "Modal|Cancel"
-msgstr ""
+msgstr "Отмена"
msgid "Modal|Close"
-msgstr ""
+msgstr "Закрыть"
msgid "Monitoring"
msgstr "Мониторинг"
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
msgid "More information"
-msgstr ""
+msgstr "Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
msgid "More information is available|here"
msgstr "Больше информации доÑтупно|тут"
-msgid "Move"
+msgid "Most stars"
msgstr ""
+msgid "Move"
+msgstr "ПеремеÑтить"
+
msgid "Move issue"
-msgstr ""
+msgstr "ПеремеÑтить обÑуждение"
msgid "Multiple issue boards"
-msgstr "Сводные доÑки обÑуждений"
+msgstr ""
+
+msgid "Name"
+msgstr "ИмÑ"
msgid "Name new label"
+msgstr "Ðазвать новую метку"
+
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr "Помощь"
+
+msgid "Nav|Home"
+msgstr "ГлавнаÑ"
+
+msgid "Nav|Sign In / Register"
+msgstr "Вход / РегиÑтрациÑ"
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
msgstr ""
msgid "New Issue"
@@ -2814,15 +4344,18 @@ msgstr[1] "Ðовых ОбÑуждениÑ"
msgstr[2] "Ðовых ОбÑуждений"
msgstr[3] "Ðовые ОбÑуждениÑ"
-msgid "New Kubernetes Cluster"
-msgstr ""
-
-msgid "New Kubernetes cluster"
-msgstr ""
+msgid "New Label"
+msgstr "ÐÐ¾Ð²Ð°Ñ Ð¼ÐµÑ‚ÐºÐ°"
msgid "New Pipeline Schedule"
msgstr "Ðовое РаÑпиÑание Сборочной Линии"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ‚ÐºÐ°"
@@ -2833,7 +4366,7 @@ msgid "New directory"
msgstr "Ðовый каталог"
msgid "New epic"
-msgstr "Ðовый Ñпик"
+msgstr ""
msgid "New file"
msgstr "Ðовый файл"
@@ -2841,15 +4374,21 @@ msgstr "Ðовый файл"
msgid "New group"
msgstr "ÐÐ¾Ð²Ð°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð°"
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr "Ðовое обÑуждение"
msgid "New label"
-msgstr ""
+msgstr "ÐÐ¾Ð²Ð°Ñ Ð¼ÐµÑ‚ÐºÐ°"
msgid "New merge request"
msgstr "Ðовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr "Ðовый проект"
@@ -2865,28 +4404,64 @@ msgstr "ÐÐ¾Ð²Ð°Ñ Ð¿Ð¾Ð´Ð³Ñ€ÑƒÐ¿Ð¿Ð°"
msgid "New tag"
msgstr "Ðовый тег"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr "Ðет"
+
msgid "No Label"
msgstr ""
msgid "No assignee"
-msgstr ""
+msgstr "Ðет ответÑтвенного"
msgid "No changes"
-msgstr ""
+msgstr "Ðет изменений"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
msgid "No due date"
-msgstr ""
+msgstr "Плановый Ñрок не указан"
msgid "No estimate or time spent"
msgstr ""
msgid "No file chosen"
+msgstr "Файл не выбран"
+
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr "Файлы не найдены."
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
msgstr ""
-msgid "No labels created yet."
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2895,29 +4470,32 @@ msgstr "Ðет репозиториÑ"
msgid "No schedules"
msgstr "Ðет раÑпиÑаний"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr "ПуÑто"
msgid "Not allowed to merge"
-msgstr ""
+msgstr "СлиÑние не допуÑкаетÑÑ"
msgid "Not available"
msgstr "ÐедоÑтупно"
msgid "Not available for private projects"
-msgstr ""
+msgstr "ÐедоÑтупно Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð²Ð°Ñ‚Ð½Ñ‹Ñ… проектов"
msgid "Not available for protected branches"
-msgstr ""
+msgstr "ÐедоÑтупно Ð´Ð»Ñ Ð·Ð°Ñ‰Ð¸Ñ‰ÐµÐ½Ð½Ñ‹Ñ… веток"
msgid "Not confidential"
-msgstr ""
+msgstr "Ðе конфиденциально"
msgid "Not enough data"
msgstr "ÐедоÑтаточно данных"
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
-msgstr ""
+msgstr "Обратите внимание, что маÑтер ветка автоматичеÑки защищена. %{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
msgstr ""
@@ -2931,6 +4509,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾ ÑобытиÑÑ…"
@@ -2989,10 +4570,10 @@ msgid "Notifications"
msgstr "УведомлениÑ"
msgid "Notifications off"
-msgstr ""
+msgstr "Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ñ‹"
msgid "Notifications on"
-msgstr ""
+msgstr "Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ñ‹"
msgid "Nov"
msgstr "ÐоÑб."
@@ -3018,17 +4599,44 @@ msgstr "Фильтр"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr "Только учаÑтники проекта могут оÑтавлÑÑ‚ÑŒ комментарии."
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr "Открыть в Xcode"
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
-msgstr "Открыт"
+msgstr ""
+
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
msgid "OpenedNDaysAgo|Opened"
msgstr "Открыто"
@@ -3036,15 +4644,33 @@ msgstr "Открыто"
msgid "Opens in a new window"
msgstr "ОткроетÑÑ Ð² новом окне"
+msgid "Operations"
+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 "ÐаÑтройки"
-msgid "Otherwise it is recommended you start with one of the options below."
+msgid "Or you can choose one of the suggested colors below"
msgstr ""
-msgid "Outbound requests"
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
msgstr ""
+msgid "Otherwise it is recommended you start with one of the options below."
+msgstr "Ð’ противном Ñлучае рекомендуетÑÑ Ð½Ð°Ñ‡Ð°Ñ‚ÑŒ Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ из перечиÑленных ниже вариантов."
+
+msgid "Outbound requests"
+msgstr "ИÑходÑщие запроÑÑ‹"
+
msgid "Overview"
msgstr "Обзор"
@@ -3052,7 +4678,7 @@ msgid "Owner"
msgstr "Владелец"
msgid "Pages"
-msgstr ""
+msgstr "Страницы"
msgid "Pagination|Last »"
msgstr "ПоÑледнÑÑ Â»"
@@ -3067,19 +4693,37 @@ msgid "Pagination|« First"
msgstr "« ПерваÑ"
msgid "Part of merge request changes"
-msgstr ""
+msgstr "ЧаÑÑ‚ÑŒ изменений запроÑа на ÑлиÑние"
msgid "Password"
msgstr "Пароль"
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr "ПриоÑтановить"
+
msgid "Pending"
+msgstr "В ожидании"
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
-msgid "Performance optimization"
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
msgstr ""
+msgid "Performance optimization"
+msgstr "ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти"
+
+msgid "Permissions"
+msgstr "Права доÑтупа"
+
msgid "Personal Access Token"
-msgstr ""
+msgstr "ПерÑональный Токен ДоÑтупа"
msgid "Pipeline"
msgstr "Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ"
@@ -3094,7 +4738,10 @@ msgid "Pipeline Schedules"
msgstr "РаÑпиÑÐ°Ð½Ð¸Ñ Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ñ‹Ñ… Линий"
msgid "Pipeline quota"
-msgstr "Квота Ñборочной линии"
+msgstr ""
+
+msgid "Pipeline triggers"
+msgstr ""
msgid "PipelineCharts|Failed:"
msgstr "Ðеудача:"
@@ -3169,46 +4816,58 @@ msgid "Pipelines|Clear Runner Caches"
msgstr ""
msgid "Pipelines|Get started with Pipelines"
-msgstr ""
+msgstr "Ðачало работы Ñо Ñборочными линиÑми"
msgid "Pipelines|Loading Pipelines"
-msgstr ""
+msgstr "ЗагружаютÑÑ Ñборочные линии"
msgid "Pipelines|Project cache successfully reset."
-msgstr ""
+msgstr "КÑш проекта уÑпешно очищен."
msgid "Pipelines|Run Pipeline"
-msgstr ""
+msgstr "ЗапуÑтить Ñборочную линию"
msgid "Pipelines|Something went wrong while cleaning runners cache."
-msgstr ""
+msgstr "Что-то пошло не так, при очиÑтке кÑша обработчиков заданий."
msgid "Pipelines|There are currently no %{scope} pipelines."
-msgstr ""
+msgstr "Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÑ‚ %{scope} Ñборочных линий."
msgid "Pipelines|There are currently no pipelines."
-msgstr ""
+msgstr "Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÑ‚ Ñборочных линий."
msgid "Pipelines|This project is not currently set up to run pipelines."
+msgstr "Этот проект в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½Ðµ наÑтроен Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка Ñборочных линий."
+
+msgid "Pipeline|Create for"
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create pipeline"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Existing branch name or tag"
msgstr ""
-msgid "Pipeline|Stop pipeline"
+msgid "Pipeline|Run Pipeline"
+msgstr "ЗапуÑтить Ñборочную линию"
+
+msgid "Pipeline|Search branches"
+msgstr "ПоиÑк веток"
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
+msgid "Pipeline|Stop pipeline"
+msgstr "ОÑтановить Ñборочную линию"
+
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
-msgstr ""
+msgstr "ОÑтановить Ñборочную линию #%{pipelineId}?"
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
-msgstr ""
+msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ оÑтановить Ñборочную линию %{pipelineId}."
msgid "Pipeline|all"
msgstr "вÑе"
@@ -3222,30 +4881,69 @@ msgstr "Ñо Ñтадией"
msgid "Pipeline|with stages"
msgstr "Ñо ÑтадиÑми"
-msgid "PlantUML"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
msgstr ""
+msgid "Planned start date"
+msgstr ""
+
+msgid "PlantUML"
+msgstr "PlantUML"
+
msgid "Play"
+msgstr "ЗапуÑк"
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr "ПожалуйÑта, выберите по крайней мере один фильтр, чтобы увидеть результаты"
+
msgid "Please solve the reCAPTCHA"
msgstr "ПожалуйÑта, решите reCAPTCHA"
+msgid "Please try again"
+msgstr "ПожалуйÑта, попробуйте ещё раз"
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
msgid "Please wait while we import the repository for you. Refresh at will."
-msgstr ""
+msgstr "ПожалуйÑта, подождите пока мы импортируем к ваш репозиторий. ОбновлÑйте Ñтраницу по желанию."
msgid "Preferences"
msgstr "ПредпочтениÑ"
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Приватный - ДоÑтуп к проекту должен предоÑтавлÑÑ‚ÑŒÑÑ Ñвно каждому пользователю."
@@ -3253,14 +4951,26 @@ msgid "Private - The group and its projects can only be viewed by members."
msgstr "ÐŸÑ€Ð¸Ð²Ð°Ñ‚Ð½Ð°Ñ - Группу и включённые в неё проекты могут видеть только члены группы."
msgid "Private projects can be created in your personal namespace with:"
-msgstr ""
+msgstr "Личные проекты могут быть Ñозданы в вашем перÑональном проÑтранÑтве Ñ:"
msgid "Profile"
msgstr "Профиль"
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr "Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ запланирована к удалению."
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr "Изменить Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+
+msgid "Profiles|Current path: %{path}"
+msgstr "Текущий путь: %{path}"
+
msgid "Profiles|Delete Account"
msgstr "Удалить Учетную запиÑÑŒ"
@@ -3279,9 +4989,27 @@ msgstr "Ðеверный пароль"
msgid "Profiles|Invalid username"
msgstr "Ðеверное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+msgid "Profiles|Path"
+msgstr "Путь"
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Введите значение %{confirmationValue} Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ:"
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr "Обновить Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr "Ошибка Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð¸ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ - %{message}"
+
+msgid "Profiles|Username successfully changed"
+msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÑƒÑпешно изменено"
+
msgid "Profiles|You don't have access to delete this user."
msgstr "У Ð²Ð°Ñ Ð½ÐµÑ‚ прав на удаление Ñтого пользователÑ."
@@ -3291,15 +5019,24 @@ msgstr "Перед удалением учётной запиÑи, вам нео
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ ÑвлÑетÑÑ Ð²Ð»Ð°Ð´ÐµÐ»ÑŒÑ†ÐµÐ¼ Ñледующих групп:"
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr "ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ"
msgid "Profiling - Performance bar"
-msgstr ""
+msgstr "Профилирование - панель производительноÑти"
msgid "Programming languages used in this repository"
+msgstr "Языки программированиÑ, иÑпользуемые в Ñтом репозитории"
+
+msgid "Progress"
msgstr ""
+msgid "Project"
+msgstr "Проект"
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Проект '%{project_name}' находитÑÑ Ð² процеÑÑе удалениÑ."
@@ -3312,6 +5049,9 @@ msgstr "Проект '%{project_name}' уÑпешно Ñоздан."
msgid "Project '%{project_name}' was successfully updated."
msgstr "Проект '%{project_name}' уÑпешно обновлен."
+msgid "Project Badges"
+msgstr "Значки Проекта"
+
msgid "Project access must be granted explicitly to each user."
msgstr "ДоÑтуп к проекту должен предоÑтавлÑÑ‚ÑŒÑÑ Ñвно каждому пользователю."
@@ -3336,6 +5076,9 @@ msgstr "ИÑтек Ñрок дейÑÑ‚Ð²Ð¸Ñ ÑÑылки на проект. СÐ
msgid "Project export started. A download link will be sent by email."
msgstr "Ðачат ÑкÑпорт проекта. СÑылка Ð´Ð»Ñ ÑÐºÐ°Ñ‡Ð¸Ð²Ð°Ð½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ отправлена по Ñлектронной почте."
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "ПодпиÑатьÑÑ"
@@ -3345,24 +5088,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr "Отключено"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "Ð’Ñе Ñ Ð´Ð¾Ñтупом"
-
-msgid "ProjectFeature|Only team members"
-msgstr "Только члены команды"
-
msgid "ProjectFileTree|Name"
msgstr "Ðаименование"
@@ -3372,30 +5106,39 @@ msgstr "Ðикогда"
msgid "ProjectLifecycle|Stage"
msgstr "Этап"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "Граф"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
-msgstr "ОбратитеÑÑŒ к админиÑтратору Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтой наÑтройки."
+msgstr ""
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
-msgstr "Только подпиÑанные коммиты могут быть помещены в Ñтот репозиторий."
+msgstr ""
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
-msgstr "Эта наÑтройка применÑетÑÑ Ð½Ð° уровне Ñервера и может быть переопределена админиÑтратором."
+msgstr ""
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
-msgstr "Эта наÑтройка применÑетÑÑ Ð½Ð° уровне Ñервера, но была переопределена Ð´Ð»Ñ Ñтого проекта."
+msgstr ""
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
-msgstr "Эта наÑтройка будет применена Ð´Ð»Ñ Ð²Ñех проектов, еÑли иное поведение не переопределено админиÑтратором."
+msgstr ""
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
-msgstr "Пользователи могут отправлÑÑ‚ÑŒ коммиты в данный репозиторий, только в Ñлучае еÑли коммит подпиÑан одним из подтвержденных адреÑов почты."
+msgstr ""
msgid "Projects"
msgstr "Проекты"
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr "ЧаÑто поÑещаемые"
@@ -3414,15 +5157,45 @@ msgstr "У Ð½Ð°Ñ Ñ‡Ñ‚Ð¾-то пошло не так."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "К Ñожалению, по вашему запроÑу проекты не найдены"
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr "Эта функциональноÑÑ‚ÑŒ требует поддержки localStorage в вашем браузере"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
-msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgid "PrometheusAlerts|Alert set"
msgstr ""
-msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
msgstr ""
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr "ВремÑ"
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr "Были найдены %{exporters} Ñ %{metrics}"
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr "<p class=\"text-tertiary\">Ðи одной <a href=\"%{docsUrl}\">общей метрики</a> не найдено</p>"
+
msgid "PrometheusService|Active"
msgstr ""
@@ -3481,7 +5254,7 @@ msgid "PrometheusService|These metrics will only be monitored after your first d
msgstr ""
msgid "PrometheusService|Time-series monitoring service"
-msgstr ""
+msgstr "Служба мониторинга временных Ñ€Ñдов"
msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
msgstr ""
@@ -3493,15 +5266,36 @@ msgid "PrometheusService|Waiting for your first deployment to an environment to
msgstr ""
msgid "Promote"
-msgstr ""
+msgstr "ПовыÑить"
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
+msgstr "ПовыÑить до Группового Этапа"
+
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
msgstr ""
msgid "Protip:"
+msgstr "ПодÑказка:"
+
+msgid "Provider"
+msgstr "Провайдер"
+
+msgid "Pseudonymizer data collection"
msgstr ""
msgid "Public - The group and any public projects can be viewed without any authentication."
@@ -3510,8 +5304,11 @@ msgstr "Публичный - Группу и включённые в неё пр
msgid "Public - The project can be accessed without any authentication."
msgstr "Публичный - ДоÑтуп к проекту возможен без какой-либо проверки подлинноÑти."
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
-msgstr "Правила Отправки"
+msgstr ""
msgid "Push events"
msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸"
@@ -3523,30 +5320,45 @@ msgid "Push to create a project"
msgstr ""
msgid "PushRule|Committer restriction"
-msgstr "ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚ÐµÑ€Ð°"
+msgstr ""
-msgid "Quick actions can be used in the issues description and comment boxes."
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr "БыÑтрые дейÑÑ‚Ð²Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ иÑпользоватьÑÑ Ð² опиÑании обÑуждений и комментариÑÑ…."
+
msgid "Read more"
msgstr "Подробнее"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "ИнÑтрукциÑ"
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
-msgstr "Ветки"
-
-msgid "RefSwitcher|Tags"
-msgstr "Теги"
-
msgid "Reference:"
+msgstr "СÑылка:"
+
+msgid "Refresh"
msgstr ""
msgid "Register / Sign In"
+msgstr "РегиÑÑ‚Ñ€Ð°Ñ†Ð¸Ñ / Вход"
+
+msgid "Register and see your runners for this group."
+msgstr "ЗарегиÑтрируйте и проÑмотрите ваши обработчики заданий Ð´Ð»Ñ Ñтой группы."
+
+msgid "Register and see your runners for this project."
msgstr ""
msgid "Registry"
@@ -3577,9 +5389,15 @@ msgid "Remind later"
msgstr "Ðапомнить позже"
msgid "Remove"
+msgstr "Удалить"
+
+msgid "Remove Runner"
msgstr ""
msgid "Remove avatar"
+msgstr "Удалить аватар"
+
+msgid "Remove priority"
msgstr ""
msgid "Remove project"
@@ -3588,27 +5406,45 @@ msgstr "Удалить проект"
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr "Репозиторий"
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr "СброÑить информацию о работоÑпоÑобноÑти Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ git"
@@ -3616,20 +5452,38 @@ msgid "Reset health check access token"
msgstr "СброÑить ключ доÑтупа проверки работоÑпоÑобноÑти"
msgid "Reset runners registration token"
-msgstr "СброÑить ключ региÑтрации Gitlab Runners"
+msgstr "СброÑить ключ региÑтрации обработчиков заданий"
+
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
msgid "Resolve discussion"
msgstr "Закрыть диÑкуÑÑию"
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr "Повторить"
+
+msgid "Retry this job"
+msgstr "Повторить Ñто фоновое задание"
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
msgid_plural "Reveal values"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Показать значение"
+msgstr[1] "Показать значениÑ"
+msgstr[2] "Показать значениÑ"
+msgstr[3] "Показать значениÑ"
msgid "Revert this commit"
msgstr "Отменить Ñто коммит"
@@ -3637,6 +5491,9 @@ msgstr "Отменить Ñто коммит"
msgid "Revert this merge request"
msgstr "Отменить Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "Review"
+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 ""
@@ -3646,16 +5503,34 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
-msgid "Runners"
+msgid "Runner token"
msgstr ""
+msgid "Runners"
+msgstr "Обработчики заданий"
+
+msgid "Runners API"
+msgstr "API обработчиков заданий"
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr "Обработчики заданий могут запуÑкатьÑÑ Ñƒ отдельных пользователей, Ñерверах и даже на вашей локальной машине."
+
msgid "Running"
+msgstr "ВыполнÑетÑÑ"
+
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
msgstr ""
msgid "SAML Single Sign On"
@@ -3670,6 +5545,15 @@ msgstr ""
msgid "SSH Keys"
msgstr "SSH Ключи"
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr "Сохранить изменениÑ"
@@ -3677,13 +5561,13 @@ msgid "Save pipeline schedule"
msgstr "Сохранить раÑпиÑание Ñборочной лини"
msgid "Save variables"
-msgstr ""
+msgstr "Сохранить переменные"
msgid "Schedule a new pipeline"
msgstr "РаÑпиÑание новой Ñборочной линии"
msgid "Scheduled"
-msgstr ""
+msgstr "Запланировано"
msgid "Schedules"
msgstr "РаÑпиÑаниÑ"
@@ -3691,23 +5575,47 @@ msgstr "РаÑпиÑаниÑ"
msgid "Scheduling Pipelines"
msgstr "Планирование Сборочных Линий"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
-msgstr "ТематичеÑкие доÑки обÑуждений"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
msgid "Search"
+msgstr "ПоиÑк"
+
+msgid "Search branches"
msgstr ""
msgid "Search branches and tags"
msgstr "Ðайти ветки и теги"
-msgid "Search milestones"
+msgid "Search files"
msgstr ""
-msgid "Search project"
+msgid "Search for projects, issues, etc."
+msgstr "ПоиÑк проектов, обÑуждений и Ñ‚.д."
+
+msgid "Search merge requests"
msgstr ""
+msgid "Search milestones"
+msgstr "ПоиÑк Ñтапов"
+
+msgid "Search project"
+msgstr "ПоиÑк проекта"
+
msgid "Search users"
-msgstr ""
+msgstr "ПоиÑк пользователей"
msgid "Seconds before reseting failure information"
msgstr "Секунд до очиÑтки информации о ÑбоÑÑ…"
@@ -3715,35 +5623,68 @@ msgstr "Секунд до очиÑтки информации о ÑбоÑÑ…"
msgid "Seconds to wait for a storage access attempt"
msgstr "Секунд задержки между попытками доÑтупа к хранилищу"
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Выбрать формат архива"
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr "Выбор временной зоны"
msgid "Select an existing Kubernetes cluster or create a new one"
-msgstr ""
+msgstr "Выбрать ÑущеÑтвующий клаÑтер Kubernetes или Ñоздать новый"
msgid "Select assignee"
-msgstr ""
+msgstr "Выбрать ответÑтвенного"
msgid "Select branch/tag"
+msgstr "Выбрать ветку/тег"
+
+msgid "Select project"
+msgstr "Выбрать проект"
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
msgstr ""
msgid "Select target branch"
msgstr "Выбор целевой ветки"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
msgid "Send email"
-msgstr ""
+msgstr "Отправить Ñлектронное пиÑьмо"
msgid "Sep"
msgstr "Сент."
@@ -3752,6 +5693,9 @@ msgid "September"
msgstr "СентÑбрь"
msgid "Server version"
+msgstr "ВерÑÐ¸Ñ Ñервера"
+
+msgid "Service Desk"
msgstr ""
msgid "Service Templates"
@@ -3770,16 +5714,16 @@ msgid "Set default and restrict visibility levels. Configure import sources and
msgstr ""
msgid "Set max session time for web terminal."
-msgstr ""
+msgstr "УÑтановить макÑимальное Ð²Ñ€ÐµÐ¼Ñ ÑеанÑа Ð´Ð»Ñ Ð²ÐµÐ±-терминала."
msgid "Set notification email for abuse reports."
-msgstr ""
+msgstr "ÐаÑтроить ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾ Ñлектронной почте Ð´Ð»Ñ ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾ злоупотреблениÑÑ…."
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
msgstr ""
msgid "Set up CI/CD"
-msgstr ""
+msgstr "ÐаÑтройка CI/CD"
msgid "Set up Koding"
msgstr "ÐаÑтройка Koding"
@@ -3794,11 +5738,17 @@ msgid "Settings"
msgstr "ÐаÑтройки"
msgid "Setup a specific Runner automatically"
-msgstr ""
+msgstr "ÐаÑтроить конкретный обработчик заданий автоматичеÑки"
+
+msgid "Share"
+msgstr "ПоделитьÑÑ"
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr "Общие обработчики заданий"
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3808,7 +5758,19 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
+msgstr "Показать команду"
+
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
msgstr ""
msgid "Show parent pages"
@@ -3817,6 +5779,9 @@ msgstr "Показать родительÑкие Ñтраницы"
msgid "Show parent subgroups"
msgstr "Показать родительÑкие подгруппы"
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Показано %d Ñобытие"
@@ -3824,46 +5789,73 @@ msgstr[1] "Показано %d ÑобытиÑ"
msgstr[2] "Показано %d Ñобытий"
msgstr[3] "Показано %d Ñобытий"
-msgid "Sidebar|Change weight"
-msgstr "Изменить веÑ"
+msgid "Side-by-side"
+msgstr ""
-msgid "Sidebar|No"
-msgstr "Ðет"
+msgid "Sidebar|Change weight"
+msgstr ""
msgid "Sidebar|None"
-msgstr "ОтÑутÑтвует"
+msgstr ""
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
msgid "Sidebar|Weight"
-msgstr "ВеÑ"
+msgstr ""
-msgid "Sign-in restrictions"
+msgid "Sign in"
msgstr ""
-msgid "Sign-up restrictions"
+msgid "Sign in / Register"
msgstr ""
-msgid "Size and domain settings for static websites"
+msgid "Sign in to %{group_name}"
msgstr ""
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr "Выйти"
+
+msgid "Sign-in restrictions"
+msgstr "ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñ…Ð¾Ð´Ð°"
+
+msgid "Sign-up restrictions"
+msgstr "ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ñ€ÐµÐ³Ð¸Ñтрации"
+
+msgid "Size and domain settings for static websites"
+msgstr "ÐаÑтройки размера и доменных имён Ð´Ð»Ñ ÑтатичеÑких веб-Ñайтов"
+
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr "Сниппеты"
msgid "Something went wrong on our end"
-msgstr ""
+msgstr "Что-то пошло не так Ñ Ð½Ð°ÑˆÐµÐ¹ Ñтороны"
msgid "Something went wrong on our end."
+msgstr "Что-то пошло не так Ñ Ð½Ð°ÑˆÐµÐ¹ Ñтороны."
+
+msgid "Something went wrong on our end. Please try again!"
msgstr ""
msgid "Something went wrong when toggling the button"
+msgstr "Что-то пошло не так при переключении кнопки"
+
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3872,7 +5864,16 @@ msgstr "Что-то пошло не так при получении проекÑ
msgid "Something went wrong while fetching the registry list."
msgstr "Что-то пошло не так при получении ÑпиÑка рееÑтров."
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
+msgstr "Произошла ошибка. Попробуйте позже."
+
+msgid "Sorry, no epics matched your search"
msgstr ""
msgid "Sort by"
@@ -3888,7 +5889,7 @@ msgid "SortOptions|Created date"
msgstr "Дата ÑозданиÑ"
msgid "SortOptions|Due date"
-msgstr "Срок"
+msgstr "Плановый Ñрок"
msgid "SortOptions|Due later"
msgstr "Срок позже"
@@ -3918,7 +5919,7 @@ msgid "SortOptions|Least popular"
msgstr "Ðаименее популÑрный"
msgid "SortOptions|Less weight"
-msgstr "Меньший веÑ"
+msgstr ""
msgid "SortOptions|Milestone"
msgstr "Веха"
@@ -3930,7 +5931,7 @@ msgid "SortOptions|Milestone due soon"
msgstr "Веха, наÑÑ‚ÑƒÐ¿Ð°ÑŽÑ‰Ð°Ñ Ñ€Ð°Ð½ÑŒÑˆÐµ"
msgid "SortOptions|More weight"
-msgstr "Больший веÑ"
+msgstr ""
msgid "SortOptions|Most popular"
msgstr "Ðаиболее популÑрный"
@@ -3972,13 +5973,13 @@ msgid "SortOptions|Start soon"
msgstr "Ðачатые недавно"
msgid "SortOptions|Weight"
-msgstr "ВеÑ"
+msgstr ""
msgid "Source"
msgstr "ИÑточник"
msgid "Source (branch or tag)"
-msgstr ""
+msgstr "ИÑточник (ветка или тег)"
msgid "Source code"
msgstr "ИÑходный код"
@@ -3990,19 +5991,46 @@ msgid "Spam Logs"
msgstr "Спам Логи"
msgid "Spam and Anti-bot Protection"
-msgstr ""
+msgstr "Защита от Ñпама и ботов"
+
+msgid "Specific Runners"
+msgstr "Конкретные обработчики заданий"
msgid "Specify the following URL during the Runner setup:"
msgstr "Укажите Ñледующий URL во Ð²Ñ€ÐµÐ¼Ñ Ð½Ð°Ñтройки Gitlab Runner:"
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr "Отметить"
msgid "Starred Projects"
-msgstr ""
+msgstr "Избранные проекты"
msgid "Starred Projects' Activity"
-msgstr ""
+msgstr "ÐктивноÑÑ‚ÑŒ в избранных проектах"
msgid "Starred projects"
msgstr "Отмеченные проекты"
@@ -4014,45 +6042,81 @@ msgid "Start the Runner!"
msgstr "ЗапуÑтить GitLab Runner!"
msgid "Started"
+msgstr "Запущен"
+
+msgid "Starts at (UTC)"
msgstr ""
msgid "State your message to activate"
msgstr ""
msgid "Status"
+msgstr "СтатуÑ"
+
+msgid "Stop impersonation"
msgstr ""
+msgid "Stop this environment"
+msgstr "ОÑтановить Ñту Ñреду"
+
msgid "Stopped"
msgstr "ОÑтановлен"
msgid "Storage"
+msgstr "Хранилище"
+
+msgid "Storage:"
msgstr ""
msgid "Subgroups"
msgstr "Подгруппы"
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr "ПодпиÑатьÑÑ"
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr "Переключить ветка/тег"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr "СиÑтемные Обработчики"
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Тег (%{tag_count})"
+msgstr[1] "Тегов (%{tag_count})"
+msgstr[2] "Тегов (%{tag_count})"
+msgstr[3] "Тегов (%{tag_count})"
msgid "Tags"
msgstr "Теги"
+msgid "Tags:"
+msgstr "Теги:"
+
msgid "TagsPage|Browse commits"
msgstr "ПроÑмотреть коммиты"
@@ -4116,8 +6180,8 @@ msgstr "Этот тег не Ñодержит заметок к релизу."
msgid "TagsPage|Use git tag command to add a new one:"
msgstr "ИÑпользуйте команду git tag, чтобы добавить новый тег:"
-msgid "TagsPage|Write your release notes or drag files here..."
-msgstr "Ðапишите Ñвои заметки к релизу или перетащите файлы Ñюда..."
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr ""
msgid "TagsPage|protected"
msgstr "защищенный"
@@ -4126,19 +6190,28 @@ msgid "Target Branch"
msgstr "Ветка"
msgid "Target branch"
-msgstr ""
+msgstr "Ð¦ÐµÐ»ÐµÐ²Ð°Ñ Ð²ÐµÑ‚ÐºÐ°"
msgid "Team"
msgstr "Команда"
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
-msgstr "СпаÑибо! Больше не показывайте мне Ñто Ñообщение"
+msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
-msgstr "РаÑширенный глобальный поиÑк в GitLab - Ñто мощный инÑтрумент Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка, который Ñокращает ваше времÑ. ВмеÑто ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð´ÑƒÐ±Ð»Ð¸Ñ€ÑƒÑŽÑ‰ÐµÐ³Ð¾ кода и траты времени, вы можете иÑкать код внутри других команд, который поможет вам в вашем проекте."
+msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
-msgstr ""
+msgstr "Трекер обÑуждений - Ñто меÑто, где можно добавить вещи, которые необходимо улучшить или решить в проекте"
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
@@ -4159,23 +6232,26 @@ msgid "The fork relationship has been removed."
msgstr "СвÑзь Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ð²Ð»ÐµÐ½Ð¸ÐµÐ¼ удалена."
msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
-msgstr ""
+msgstr "Импорт будет отключен поÑле %{timeout}. Ð”Ð»Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸ÐµÐ², которые импортируютÑÑ Ð·Ð° большее времÑ, иÑпользуйте комбинацию команд clone/push."
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 maximum file size allowed is 200KB."
-msgstr ""
+msgstr "МакÑимально допуÑтимый размер файла ÑоÑтавлÑет 200 Кб."
msgid "The number of attempts GitLab will make to access a storage."
msgstr "КоличеÑтво попыток, которые GitLab будет предпринимать Ð´Ð»Ñ Ð´Ð¾Ñтупа к хранилищу."
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr "КоличеÑтво Ñбоев, поÑле которого Gitlab полноÑтью прекратит доÑтуп к хранилищу. Изменение Ñчётчика \"количеÑтво Ñбоев\" может быть произведено через админиÑтративный интерфейÑ: %{link_to_health_page} или при помощи API %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "Фаза жизненного цикла разработки."
@@ -4194,14 +6270,17 @@ msgstr "ДоÑтуп к проекту возможен любым зарегиÑ
msgid "The project can be accessed without any authentication."
msgstr "ДоÑтуп к проекту возможен без какой-либо проверки подлинноÑти."
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "Репозиторий Ð´Ð»Ñ Ñтого проекта не ÑущеÑтвует."
msgid "The repository for this project is empty"
-msgstr ""
+msgstr "Репозиторий Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ проекта пуÑтой"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
-msgstr ""
+msgstr "Репозиторий должен быть доÑтупен через протоколы <code>http: //</code>, <code>https: //</code> или <code>git: //</code>."
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 "Этап обзора показывает Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа ÑлиÑÐ½Ð¸Ñ Ð´Ð¾ его выполнениÑ. Данные будут автоматичеÑки добавлены поÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ запроÑа на ÑлиÑние."
@@ -4209,6 +6288,9 @@ msgstr "Этап обзора показывает Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ ÑозданÐ
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "Этап поÑтановки показывает Ð²Ñ€ÐµÐ¼Ñ Ð¼ÐµÐ¶Ð´Ñƒ ÑлиÑнием \"MR\" и развертыванием кода в производÑтвенной Ñреде. Данные будут автоматичеÑки добавлены поÑле Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð² производÑтве первый раз."
@@ -4221,16 +6303,25 @@ msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð² Ñекундах, в течение которого GitLa
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð² Ñекундах в течении которого GitLab будет пытатьÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ доÑтуп к хранилищу. ПоÑле Ñтого времени будет зафикÑирована ошибка Ð¿Ñ€ÐµÐ²Ñ‹ÑˆÐµÐ½Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸ ожиданиÑ."
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 "Среднее значение в Ñ€Ñду. Пример: между 3, 5, 9, Ñреднее 5, между 3, 5, 7, 8, Ñреднее (5+7)/2 = 6."
msgid "There are no issues to show"
+msgstr "Ðет обÑуждений, которые можно показать"
+
+msgid "There are no labels yet"
msgstr ""
msgid "There are no merge requests to show"
@@ -4239,33 +6330,57 @@ msgstr ""
msgid "There are problems accessing Git storage: "
msgstr "Проблемы Ñ Ð´Ð¾Ñтупом к Git хранилищу: "
-msgid "There was an error loading results"
-msgstr ""
-
msgid "There was an error loading users activity calendar."
msgstr ""
msgid "There was an error saving your notification settings."
-msgstr ""
+msgstr "Произошла ошибка при Ñохранении наÑтроек уведомлений."
msgid "There was an error subscribing to this label."
-msgstr ""
+msgstr "Произошла ошибка подпиÑки на Ñту метку."
msgid "There was an error when reseting email token."
msgstr ""
msgid "There was an error when subscribing to this label."
-msgstr ""
+msgstr "При подпиÑке на Ñту метку произошла ошибка."
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
-msgstr "УÑтановлен отбор"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr "Этот ÑкземплÑÑ€ GitLab пока не предоÑтавлÑет никаких общих обработчиков заданий. ÐдминиÑтраторы ÑкземплÑров могут региÑтрировать общие обработчики заданий в Ñекции админиÑтрированиÑ."
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
msgid "This directory"
+msgstr "Этот каталог"
+
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
msgstr ""
+msgid "This group does not provide any group Runners yet."
+msgstr "Эта группа пока не Ñодержит групповых обработчики заданий."
+
msgid "This is a confidential issue."
msgstr "Это конфиденциальное обÑуждение."
@@ -4282,22 +6397,31 @@ msgid "This issue is locked."
msgstr "ОбÑуждение заблокировано."
msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
-msgstr ""
+msgstr "Это задание завиÑит от того как пользователь инициирует Ñвой процеÑÑ Ñборки. Чаще вÑего оно иÑпользуетÑÑ Ð´Ð»Ñ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð´Ð° на продуктивных Ñредах"
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr "Это задание завиÑит от рабочих заданий на верхнем уровне, которые должны завершитьÑÑ ÑƒÑпешно, чтобы Ñто задание было запущено"
+msgid "This job does not have a trace."
+msgstr "Это фоновое задание не Ñодержит траÑÑировку иÑполнениÑ."
+
+msgid "This job has been canceled"
+msgstr "Это фоновое задание отменено"
+
+msgid "This job has been skipped"
+msgstr "Это фоновое задание пропущено"
+
msgid "This job has not been triggered yet"
-msgstr ""
+msgstr "Это фоновое задание еще не запущено"
msgid "This job has not started yet"
-msgstr ""
+msgstr "Эта задача еще не запущена"
msgid "This job is in pending state and is waiting to be picked by a runner"
-msgstr ""
+msgstr "Это фоновое задание находитÑÑ Ð² ÑоÑтоÑнии Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð¸ ожидает запуÑка процеÑÑа иÑполнениÑ"
msgid "This job requires a manual action"
-msgstr ""
+msgstr "Это фоновое задание требует ручных дейÑтвий"
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Это означает, что вы не можете отправить код, пока не Ñоздадите пуÑтой репозиторий или не импортируете ÑущеÑтвующий."
@@ -4305,20 +6429,35 @@ msgstr "Это означает, что вы не можете отправитÑ
msgid "This merge request is locked."
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние заблокирован."
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
-msgid "This project"
+msgid "This page will be removed in a future release."
msgstr ""
+msgid "This project"
+msgstr "Этот проект"
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr "Этот проект не отноÑитÑÑ Ð½Ð¸ к какой группе и поÑтому не может иÑпользовать групповые обработчики заданий."
+
msgid "This repository"
+msgstr "Этот репозиторий"
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
msgstr ""
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr "Эти Ñлектронные пиÑьма автоматичеÑки преобразуютÑÑ Ð² обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ (комментарии раÑÑылаютÑÑ ÐºÐ°Ðº ветвь обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð² почте), перечиÑленные здеÑÑŒ."
+msgstr ""
msgid "Time before an issue gets scheduled"
msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð´Ð¾ начала Ð¿Ð¾Ð¿Ð°Ð´Ð°Ð½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð² планировщик"
@@ -4329,12 +6468,15 @@ msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð´Ð¾ начала работы над обÑуждением"
msgid "Time between merge request creation and merge/close"
msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð¼ÐµÐ¶Ð´Ñƒ Ñозданием запроÑа ÑлиÑÐ½Ð¸Ñ Ð¸ ÑлиÑнием / закрытием"
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
msgstr ""
+msgid "Time spent"
+msgstr "Времени затрачено"
+
msgid "Time tracking"
msgstr ""
@@ -4356,6 +6498,9 @@ msgstr "%s дней назад"
msgid "Timeago|%s days remaining"
msgstr "ОÑталоÑÑŒ %s дней"
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr "ОÑталоÑÑŒ %s чаÑов"
@@ -4371,6 +6516,9 @@ msgstr "%s меÑÑцев назад"
msgid "Timeago|%s months remaining"
msgstr "ОÑталоÑÑŒ %s меÑÑцев"
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr "ОÑталоÑÑŒ %s Ñекунд(Ñ‹)"
@@ -4386,48 +6534,45 @@ msgstr "%s лет назад"
msgid "Timeago|%s years remaining"
msgstr "ОÑталоÑÑŒ %s лет"
+msgid "Timeago|1 day ago"
+msgstr ""
+
msgid "Timeago|1 day remaining"
msgstr "ОÑталÑÑ Ð´ÐµÐ½ÑŒ"
+msgid "Timeago|1 hour ago"
+msgstr ""
+
msgid "Timeago|1 hour remaining"
msgstr "ОÑталÑÑ Ñ‡Ð°Ñ"
+msgid "Timeago|1 minute ago"
+msgstr ""
+
msgid "Timeago|1 minute remaining"
msgstr "ОÑталаÑÑŒ одна минута"
+msgid "Timeago|1 month ago"
+msgstr ""
+
msgid "Timeago|1 month remaining"
msgstr "ОÑталÑÑ Ð¼ÐµÑÑц"
+msgid "Timeago|1 week ago"
+msgstr ""
+
msgid "Timeago|1 week remaining"
msgstr "ОÑталаÑÑŒ неделÑ"
+msgid "Timeago|1 year ago"
+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|a year ago"
-msgstr "год назад"
-
-msgid "Timeago|about %s hours ago"
-msgstr "около %s чаÑов назад"
-
-msgid "Timeago|about a minute ago"
-msgstr "около минуты назад"
-
-msgid "Timeago|about an hour ago"
-msgstr "около чаÑа назад"
-
msgid "Timeago|in %s days"
msgstr "Через %s дней"
@@ -4467,11 +6612,14 @@ msgstr "через неделю"
msgid "Timeago|in 1 year"
msgstr "через год"
-msgid "Timeago|in a while"
-msgstr "через некоторое времÑ"
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
+msgstr ""
-msgid "Timeago|less than a minute ago"
-msgstr "менее чем минуту назад"
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4491,12 +6639,15 @@ msgid "Time|s"
msgstr "Ñ"
msgid "Tip:"
-msgstr ""
+msgstr "Совет:"
msgid "Title"
-msgstr "Заголовок"
+msgstr ""
msgid "To GitLab"
+msgstr "Ð’ GitLab"
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
msgstr ""
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
@@ -4508,6 +6659,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4517,30 +6674,60 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr "Чтобы начать выполнÑÑ‚ÑŒ Ñвои заданиÑ, вы можете добавить обработчик заданий в вашу группу"
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
+msgstr "Дела"
+
+msgid "Todos"
msgstr ""
-msgid "Toggle sidebar"
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
msgstr ""
+msgid "Toggle navigation"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr "Переключить боковую панель"
+
msgid "ToggleButton|Toggle Status: OFF"
msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr "Общее времÑ"
@@ -4548,22 +6735,40 @@ msgid "Total test time for all commits/merges"
msgstr "Общее Ð²Ñ€ÐµÐ¼Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð¸ÐºÑаций/ÑлиÑний"
msgid "Total: %{total}"
-msgstr ""
+msgstr "Ð’Ñего: %{total}"
msgid "Track activity with Contribution Analytics."
-msgstr "ОтÑлеживать активноÑÑ‚ÑŒ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ðналитики УчаÑтников."
+msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
-msgstr "Следите за обÑуждениÑми, Ñгруппированными по темам, Ñразу из неÑкольких проектов и Ñтапов"
+msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
+msgstr "ЗапуÑтить Ñто дейÑтвие вручную"
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
msgstr ""
+msgid "Try again"
+msgstr "Попробовать Ñнова"
+
msgid "Turn on Service Desk"
-msgstr "Включить Службу Поддержки"
+msgstr ""
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr "Ðе удаетÑÑ Ð·Ð°Ð³Ñ€ÑƒÐ·Ð¸Ñ‚ÑŒ отличиÑ. %{button_try_again}"
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
msgid "Unknown"
msgstr ""
@@ -4577,26 +6782,62 @@ msgstr "Разблокировано"
msgid "Unresolve discussion"
msgstr "Переоткрыть диÑкуÑÑию"
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr "СнÑÑ‚ÑŒ отметку"
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr "Ðеподтверждено"
+
msgid "Up to date"
+msgstr "Ðктуальный"
+
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
-msgstr "ПовыÑьте ваш тарифный план, чтобы активировать Улучшенный Глобальный ПоиÑк."
+msgstr ""
msgid "Upgrade your plan to activate Contribution Analytics."
-msgstr "ПовыÑьте ваш тарифный план, чтобы активировать Ðналитики УчаÑтников."
+msgstr ""
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr "ПовыÑьте ваш тарифный план, чтобы активировать Групповые Веб-Обработчики."
+msgstr ""
msgid "Upgrade your plan to activate Issue weight."
-msgstr "ПовыÑьте ваш тарифный план чтобы активировать Ð²ÐµÑ Ð¾Ð±Ñуждений."
+msgstr ""
msgid "Upgrade your plan to improve Issue boards."
-msgstr "ПовыÑьте ваш тарифный план, чтобы улучшить доÑки обÑуждений."
+msgstr ""
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
msgid "Upload New File"
msgstr "Загрузить новый файл"
@@ -4605,19 +6846,28 @@ msgid "Upload file"
msgstr "Загрузить файл"
msgid "Upload new avatar"
-msgstr ""
+msgstr "Загрузить аватар"
msgid "UploadLink|click to upload"
msgstr "кликните Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸"
msgid "Upvotes"
-msgstr ""
+msgstr "ГолоÑа \"за\""
msgid "Usage statistics"
+msgstr "СтатиÑтика иÑпользованиÑ"
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
-msgstr "ИÑпользуйте Службу поддержки Ð´Ð»Ñ ÑвÑзи Ñ Ð²Ð°ÑˆÐ¸Ð¼Ð¸ пользователÑми (например, Ð´Ð»Ñ Ð¾ÑущеÑÑ‚Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ клиентов) через Ñлектронную почту непоÑредÑтвенно в GitLab"
+msgstr ""
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
msgid "Use the following registration token during setup:"
msgstr "ИÑпользуйте Ñледующий токен региÑтрации в процеÑÑе уÑтановки:"
@@ -4628,24 +6878,39 @@ msgstr "ИÑпользуютÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ‹Ð¹ наÑтройки увеÐ
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
-msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgid "User map"
msgstr ""
+msgid "Users"
+msgstr "Пользователи"
+
+msgid "Variables"
+msgstr "Переменные"
+
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr "Переменные применÑÑŽÑ‚ÑÑ Ðº Ñреде через обработчики заданий. Их можно защитить, Ð´ÐµÐ»Ð°Ñ Ð¸Ñ… доÑтупными только защищенным ветвÑм или меткам. Ð’Ñ‹ можете иÑпользовать переменные Ð´Ð»Ñ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¹, Ñекретных ключей и прочего."
+
msgid "Various container registry settings."
msgstr ""
msgid "Various email settings."
-msgstr ""
+msgstr "Различные наÑтройки Ñлектронной почты."
msgid "Various settings that affect GitLab performance."
-msgstr ""
+msgstr "Различные наÑтройки, которые влиÑÑŽÑ‚ на производительноÑÑ‚ÑŒ GitLab."
-msgid "View and edit lines"
+msgid "Verification information"
msgstr ""
+msgid "Verified"
+msgstr "Проверено"
+
msgid "View epics list"
msgstr ""
@@ -4653,16 +6918,28 @@ msgid "View file @ "
msgstr "ПроÑмотр файла @ "
msgid "View group labels"
+msgstr "ПроÑмотр меток группы"
+
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
msgstr ""
msgid "View labels"
+msgstr "ПроÑмотр меток"
+
+msgid "View log"
msgstr ""
msgid "View open merge request"
msgstr "ПроÑмотреть открытый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
msgid "View project labels"
-msgstr ""
+msgstr "ПроÑмотр меток проекта"
msgid "View replaced file @ "
msgstr "ПроÑмотр заменённого файла @ "
@@ -4670,6 +6947,12 @@ msgstr "ПроÑмотр заменённого файла @ "
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "Ограниченный"
@@ -4685,7 +6968,7 @@ msgstr "Ðе определен"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Хотите увидеть данные? ОбратитеÑÑŒ к админиÑтратору за доÑтупом."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4695,18 +6978,30 @@ msgid "We want to be sure it is you, please confirm you are not a robot."
msgstr "Мы хотим быть уверены, что Ñто вы, пожалуйÑта, подтвердите, что вы не робот."
msgid "Web IDE"
-msgstr ""
+msgstr "Web IDE"
msgid "Web terminal"
-msgstr ""
+msgstr "Web терминал"
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr "Веб-обработчики позволÑÑŽÑ‚ вам вызывать Ð°Ð´Ñ€ÐµÑ URL еÑли, например, отправлен новый код или Ñоздано новое обÑуждение. Ð’Ñ‹ можете наÑтроить веб-обработчики так, чтобы они реагировали на определённые ÑобытиÑ, такие как отправки кода, обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ запроÑÑ‹ на ÑлиÑние. Групповые веб-обработчики применÑÑŽÑ‚ÑÑ ÐºÐ¾ вÑем проектам в группе и позволÑÑŽÑ‚ вам Ñтандартизовать функциональноÑÑ‚ÑŒ веб-обработчиков Ð´Ð»Ñ Ð²Ñей вашей группы."
+msgstr ""
+
+msgid "Weeks"
+msgstr ""
msgid "Weight"
-msgstr "ВеÑ"
+msgstr ""
+
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr "Когда процеÑÑ Runner заблокирован, он не может быть назначен другим проектам"
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4733,8 +7028,32 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
-msgstr "Ð’Ñ‹ не можете Ñоздавать вики-Ñтраницы"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr "Создать вашу первую Ñтраницу"
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr "Предложить улучшение Wiki"
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "Это уÑÑ‚Ð°Ñ€ÐµÐ²ÑˆÐ°Ñ Ð²ÐµÑ€ÑÐ¸Ñ Ñтой Ñтраницы."
@@ -4769,6 +7088,12 @@ msgstr "ÐÐ¾Ð²Ð°Ñ Ð’Ð¸ÐºÐ¸ Страница"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr "Ð’Ñ‹ уверены что хотите удалить Ñту Ñтраницу?"
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr "Кто-то редактирует Ñтраницу одновременно Ñ Ð²Ð°Ð¼Ð¸. ПожалуйÑта проверьте %{page_link} и убедитеÑÑŒ, что внеÑенные Вами Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ затрут чужие."
@@ -4784,8 +7109,8 @@ msgstr "Обновить %{page_title}"
msgid "WikiPage|Page slug"
msgstr "СемантичеÑкий Url Ð´Ð»Ñ Ñтраницы"
-msgid "WikiPage|Write your content or drag files here..."
-msgstr "Ðапишите ваше Ñодержимое или перетащите Ñюда файлы..."
+msgid "WikiPage|Write your content or drag files here…"
+msgstr ""
msgid "Wiki|Create Page"
msgstr "Создать Страницу"
@@ -4796,9 +7121,6 @@ msgstr "Создать Ñтраницу"
msgid "Wiki|Edit Page"
msgstr "Редактировать Ñтраницу"
-msgid "Wiki|Empty page"
-msgstr "ПуÑÑ‚Ð°Ñ Ñтраница"
-
msgid "Wiki|More Pages"
msgstr "Ещё Ñтраницы"
@@ -4818,43 +7140,61 @@ msgid "Wiki|Wiki Pages"
msgstr "Вики Страницы"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr "С аналитикой учаÑтников вы можете изучать активноÑÑ‚ÑŒ в обÑуждениÑÑ…, запроÑах на ÑлиÑние и Ñобытий отправки кода Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ организации и её учаÑтников."
+msgstr ""
msgid "Withdraw Access Request"
msgstr "Отменить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr "Да"
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ удалить %{group_name}. Удаленные группы ÐЕ МОГУТ быть воÑÑтановлены! Ð’Ñ‹ ÐБСОЛЮТÐО уверены?"
msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
-msgstr ""
+msgstr "Ð’Ñ‹ хотите удалить %{project_full_name}. Удаленный проект ÐЕ МОЖЕТ быть воÑÑтановлен! Ð’Ñ‹ ÐБСОЛЮТÐО уверены?"
msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ удалить ÑвÑзь Ð¾Ñ‚Ð²ÐµÑ‚Ð²Ð»ÐµÐ½Ð¸Ñ Ñ Ð¸Ñходным проектом %{forked_from_project}. Ð’Ñ‹ ÐБСОЛЮТÐО уверены?"
msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
-msgstr ""
+msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ передать проект %{project_full_name} другому владельцу. Ð’Ñ‹ ÐБСОЛЮТÐО уверены?"
msgid "You are on a read-only GitLab instance."
+msgstr "Ð’Ñ‹ иÑпользуете GitLab в режиме только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ."
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
-msgstr ""
+msgstr "Ð’Ñ‹ также можете Ñоздать проект из командной Ñтроки."
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
msgid "You can move around the graph by using the arrow keys."
-msgstr ""
+msgstr "Ð’Ñ‹ можете перемещатьÑÑ Ð¿Ð¾ диаграмме Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ клавиш Ñо Ñтрелками."
msgid "You can only add files when you are on a branch"
msgstr "Ð’Ñ‹ можете добавлÑÑ‚ÑŒ только файлы, когда находитеÑÑŒ в ветке"
@@ -4862,22 +7202,40 @@ msgstr "Ð’Ñ‹ можете добавлÑÑ‚ÑŒ только файлы, когда
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
-msgstr "Ð’Ñ‹ не можете запиÑывать на подчиненные ÑкземплÑры \"только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ\" клаÑтера GitLab Geo. ИÑпользуйте вмеÑто Ñтого %{link_to_primary_node}."
+msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr "Ð’Ñ‹ не можете запиÑывать на Ñтот ÑкземплÑÑ€ \"только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ\" клаÑтера GitLab."
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Ð’Ñ‹ доÑтигли Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð² вашем проекте"
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4886,6 +7244,9 @@ msgstr "Ðеобходимо войти, чтобы оценить проект"
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "Вам нужно разрешение."
@@ -4914,31 +7275,46 @@ msgid "You won't be able to pull or push project code via SSH until you add an S
msgstr "Ð’Ñ‹ не Ñможете работать Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð¾Ð¼ через SSH, пока не добавите в Ñвой профиль SSH ключ"
msgid "You'll need to use different branch names to get a valid comparison."
+msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð³Ð¾ ÑÑ€Ð°Ð²Ð½ÐµÐ½Ð¸Ñ Ð²Ð°Ð¼ нужно иÑпользовать разные имена веток."
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
msgstr ""
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr "Ð’Ñ‹ получили Ñто Ñлектронное пиÑьмо из Ñвоей учетной запиÑи %{host}. %{manage_notifications_link} &middot; %{help_link}"
+
+msgid "YouTube"
msgstr ""
msgid "Your Groups"
-msgstr ""
+msgstr "Ваши Группы"
msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
-msgstr ""
+msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ Вашем Kubernetes клаÑтере на Ñтой Ñтранице по-прежнему доÑтупна Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ, но мы рекомендуем вам отключить его перед переконфигурированием"
msgid "Your Projects (default)"
-msgstr ""
+msgstr "Ваши проекты (по умолчанию)"
msgid "Your Projects' Activity"
-msgstr ""
+msgstr "ÐктивноÑÑ‚ÑŒ в ваших проектов"
msgid "Your Todos"
+msgstr "Ваши дела"
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
msgstr ""
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
-msgstr ""
+msgstr "Ваши Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð±Ñ‹Ð»Ð¸ зафикÑированы. Коммит %{commitId} %{commitStats}"
msgid "Your comment will not be visible to the public."
msgstr "Ваш комментарий не будет виден вÑем."
@@ -4952,10 +7328,13 @@ msgstr "Ваше имÑ"
msgid "Your projects"
msgstr "Ваши проекты"
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4963,13 +7342,43 @@ msgstr[2] ""
msgstr[3] ""
msgid "assign yourself"
-msgstr ""
+msgstr "назначить ÑебÑ"
msgid "branch name"
msgstr "Ð¸Ð¼Ñ Ð²ÐµÑ‚Ð²Ð¸"
msgid "by"
-msgstr "по"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
@@ -4977,39 +7386,102 @@ msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
+msgstr ""
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Description"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -5019,19 +7491,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -5040,15 +7509,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5058,12 +7566,18 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "день"
+msgstr[1] "дней"
+msgstr[2] "дней"
+msgstr[3] "дней"
+
+msgid "deploy token"
+msgstr ""
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
@@ -5082,16 +7596,28 @@ msgstr[3] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr "отключено"
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr "включено"
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5103,15 +7629,18 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
msgid "merge request"
msgid_plural "merge requests"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgstr[1] "запроÑов на ÑлиÑние"
+msgstr[2] "запроÑов на ÑлиÑние"
+msgstr[3] "запроÑов на ÑлиÑние"
msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
msgstr ""
@@ -5128,24 +7657,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5173,6 +7696,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr "Создать обÑуждение Ð´Ð»Ñ ÐµÐ³Ð¾ поÑледующего решениÑ"
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5206,9 +7732,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5216,10 +7757,10 @@ msgid "mrWidget|Refresh"
msgstr ""
msgid "mrWidget|Refresh now"
-msgstr ""
+msgstr "Обновить ÑейчаÑ"
msgid "mrWidget|Refreshing now"
-msgstr ""
+msgstr "ОбновлÑетÑÑ ÑейчаÑ"
msgid "mrWidget|Remove Source Branch"
msgstr ""
@@ -5269,6 +7810,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5278,9 +7822,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5291,7 +7832,7 @@ msgid "mrWidget|branch does not exist."
msgstr ""
msgid "mrWidget|command line"
-msgstr ""
+msgstr "ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð°Ñ Ñтрока"
msgid "mrWidget|into"
msgstr ""
@@ -5306,14 +7847,14 @@ msgid "notification emails"
msgstr "email Ð´Ð»Ñ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ð¹"
msgid "or"
-msgstr ""
+msgstr "или"
msgid "parent"
msgid_plural "parents"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "владелец"
+msgstr[1] "владельцы"
+msgstr[2] "владельцы"
+msgstr[3] "владельцы"
msgid "password"
msgstr "пароль"
@@ -5324,7 +7865,13 @@ msgstr "токен Ð´Ð»Ñ Ð¿ÐµÑ€Ñонального доÑтупа"
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr "оÑталоÑÑŒ"
+
msgid "remove due date"
+msgstr "убрать плановый Ñрок"
+
+msgid "remove weight"
msgstr ""
msgid "source"
@@ -5333,11 +7880,14 @@ msgstr "иÑходный текÑÑ‚"
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
-msgid "this document"
+msgid "started"
msgstr ""
+msgid "this document"
+msgstr "Ñтот документ"
+
msgid "to help your contributors communicate effectively!"
-msgstr "чтобы помочь учаÑтникам взаимодейÑтвовать Ñффективнее!"
+msgstr ""
msgid "username"
msgstr "Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
@@ -5345,6 +7895,16 @@ msgstr "Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
diff --git a/locale/tr_TR/gitlab.po b/locale/tr_TR/gitlab.po
index 6a025dfbae5..e145780c68f 100644
--- a/locale/tr_TR/gitlab.po
+++ b/locale/tr_TR/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:35-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Turkish\n"
"Language: tr_TR\n"
@@ -15,9 +13,25 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: tr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:39\n"
msgid " and"
-msgstr " ve"
+msgstr ""
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+msgstr[1] ""
msgid "%d commit"
msgid_plural "%d commits"
@@ -54,6 +68,26 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+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] ""
@@ -65,17 +99,32 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} katılımcı"
msgstr[1] "%{count} katılımcı"
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
@@ -88,23 +137,80 @@ msgstr ""
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+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 "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} daha fazla"
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr "- daha az göster"
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] ""
@@ -116,9 +222,51 @@ msgstr "İlk katkı!"
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -128,38 +276,68 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr ""
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr "Kötüye Kullanım Raporları"
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr "Erişim anahtarları"
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr "Hesap"
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "Etkin"
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr "Etkinlik"
msgid "Add"
-msgstr "Ekle"
+msgstr ""
msgid "Add Changelog"
msgstr "DeÄŸiÅŸiklik Bilgisi Ekle"
@@ -179,12 +357,39 @@ msgstr "Lisans Ekle"
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "Yeni dizin ekle"
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr "Yapılacaklara Ekle"
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "Tüm işleri durdur"
@@ -237,7 +442,7 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
msgid "Advanced"
-msgstr "GeliÅŸmiÅŸ"
+msgstr ""
msgid "Advanced settings"
msgstr "GeliÅŸmiÅŸ ayarlar"
@@ -251,7 +456,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -275,6 +483,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -290,6 +540,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -305,13 +558,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr "Projeler yüklenirken bir hata oluştu"
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -347,18 +600,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr "Kullanıcı adı doğrulanırken bir hata oluştu"
msgid "An error occurred. Please try again."
msgstr "Bir hata oluştu. Lütfen tekrar deneyin."
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr "Görünüm"
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr "Uygulamalar"
@@ -368,12 +645,21 @@ msgstr "Nis"
msgid "April"
msgstr "Nisan"
-msgid "Archived project! Repository is read-only"
-msgstr "Arşivlenmiş proje! Sadece okuma yapılabilir"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -389,6 +675,12 @@ msgstr "Emin misiniz?"
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -413,12 +705,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Sürükleyip bırakarak bir dosya ekle veya %{upload_link}"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr "AÄŸustos"
@@ -428,12 +735,36 @@ msgstr "AÄŸustos"
msgid "Authentication Log"
msgstr "Kimlik Doğrulama Günlüğü"
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr "Yazar"
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr "Yazarlar: %{authors}"
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -449,7 +780,10 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
@@ -470,12 +804,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr "Kullanılabilir"
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr "Avatar kaldırılacak. Emin misiniz?"
@@ -485,12 +825,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -509,6 +930,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -533,6 +957,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -545,6 +978,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -622,7 +1067,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -703,7 +1148,7 @@ msgstr ""
msgid "Browse files"
msgstr ""
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -712,6 +1157,9 @@ msgstr ""
msgid "CI / CD"
msgstr ""
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -721,12 +1169,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr ""
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -784,15 +1292,30 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -898,9 +1421,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -913,6 +1457,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -922,6 +1469,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -931,10 +1481,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -949,9 +1502,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -967,6 +1517,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -976,22 +1529,25 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching projects"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -1000,7 +1556,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1012,6 +1568,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1039,6 +1601,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1075,7 +1643,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1102,6 +1676,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -1114,9 +1700,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1144,19 +1727,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select zone"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1189,6 +1790,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1219,13 +1823,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1292,6 +1905,9 @@ msgstr ""
msgid "Committed by"
msgstr ""
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr ""
@@ -1340,6 +1956,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1406,15 +2025,30 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr ""
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr ""
@@ -1430,12 +2064,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1451,9 +2094,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr ""
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1466,12 +2121,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr ""
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr ""
@@ -1484,9 +2145,15 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1505,6 +2172,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1523,10 +2193,16 @@ msgstr ""
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr ""
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1541,6 +2217,15 @@ msgstr ""
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr ""
@@ -1550,6 +2235,12 @@ msgstr ""
msgid "Customize colors"
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 "Cycle Analytics"
msgstr ""
@@ -1574,6 +2265,9 @@ msgstr ""
msgid "CycleAnalyticsStage|Test"
msgstr ""
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1586,15 +2280,36 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr ""
msgid "Delete"
msgstr ""
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] ""
@@ -1603,39 +2318,195 @@ msgstr[1] ""
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr ""
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr ""
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr ""
@@ -1678,16 +2549,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr ""
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1699,15 +2585,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1723,6 +2618,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1735,15 +2642,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1756,33 +2678,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1798,12 +2741,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1822,6 +2759,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1834,6 +2786,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1861,9 +2816,30 @@ msgstr ""
msgid "Every week (Sundays at 4:00am)"
msgstr ""
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1891,6 +2867,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1900,6 +2879,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr ""
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1909,6 +2891,12 @@ msgstr ""
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1918,9 +2906,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr ""
@@ -1930,6 +2915,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr ""
@@ -1939,6 +2927,12 @@ msgstr ""
msgid "Find file"
msgstr ""
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1948,12 +2942,39 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] ""
@@ -1971,9 +2992,24 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
@@ -1986,6 +3022,12 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -2004,7 +3046,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2019,31 +3064,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2064,19 +3121,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Replication slots"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr ""
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2085,16 +3148,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2112,13 +3178,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr ""
+
+msgid "GeoNodes|Wikis"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2148,6 +3220,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2157,6 +3235,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2169,34 +3250,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
msgid "GoToYourFork|Fork"
msgstr ""
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2208,7 +3355,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2238,6 +3406,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2315,15 +3510,54 @@ msgid_plural "Hide values"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr ""
msgid "Housekeeping successfully started"
msgstr ""
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2336,15 +3570,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2363,10 +3636,22 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance"
@@ -2380,6 +3665,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2395,6 +3683,9 @@ msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2413,12 +3704,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2437,6 +3737,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2461,6 +3764,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr ""
@@ -2470,12 +3776,21 @@ msgstr ""
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2485,6 +3800,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2520,6 +3838,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2544,18 +3865,36 @@ msgstr ""
msgid "Leave project"
msgstr ""
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2565,24 +3904,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2595,13 +3955,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2619,24 +4000,60 @@ msgstr ""
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2646,18 +4063,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2670,6 +4096,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2682,9 +4111,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2715,6 +4162,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2748,6 +4198,15 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2757,6 +4216,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2766,21 +4228,60 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] ""
msgstr[1] ""
-msgid "New Kubernetes Cluster"
+msgid "New Label"
msgstr ""
-msgid "New Kubernetes cluster"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
msgstr ""
msgid "New branch"
@@ -2801,6 +4302,9 @@ msgstr ""
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr ""
@@ -2810,6 +4314,9 @@ msgstr ""
msgid "New merge request"
msgstr ""
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2825,6 +4332,12 @@ msgstr ""
msgid "New tag"
msgstr ""
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2846,7 +4359,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2855,6 +4398,9 @@ msgstr ""
msgid "No schedules"
msgstr ""
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -2891,6 +4437,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -2978,27 +4527,72 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr ""
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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 ""
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3032,12 +4626,30 @@ msgstr ""
msgid "Password"
msgstr ""
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3056,6 +4668,9 @@ msgstr ""
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr ""
@@ -3152,10 +4767,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
+msgstr ""
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3164,7 +4791,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3182,18 +4809,42 @@ msgstr ""
msgid "Pipeline|with stages"
msgstr ""
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3203,9 +4854,24 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3218,9 +4884,21 @@ msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3239,9 +4917,27 @@ msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3251,6 +4947,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3260,6 +4959,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -3272,6 +4977,9 @@ msgstr ""
msgid "Project '%{project_name}' was successfully updated."
msgstr ""
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr ""
@@ -3296,6 +5004,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -3305,24 +5016,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr ""
-
-msgid "ProjectFeature|Everyone with access"
-msgstr ""
-
-msgid "ProjectFeature|Only team members"
-msgstr ""
-
msgid "ProjectFileTree|Name"
msgstr ""
@@ -3332,12 +5034,18 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
-msgid "ProjectNetworkGraph|Graph"
+msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3356,6 +5064,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3374,7 +5085,37 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3455,21 +5196,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3485,28 +5250,43 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr ""
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr ""
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
+msgid "Reference:"
msgstr ""
-msgid "RefSwitcher|Tags"
+msgid "Refresh"
msgstr ""
-msgid "Reference:"
+msgid "Register / Sign In"
msgstr ""
-msgid "Register / Sign In"
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
msgstr ""
msgid "Registry"
@@ -3539,36 +5319,60 @@ msgstr ""
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr ""
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr ""
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr ""
@@ -3578,10 +5382,28 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3595,6 +5417,9 @@ msgstr ""
msgid "Revert this merge request"
msgstr ""
+msgid "Review"
+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 ""
@@ -3604,18 +5429,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3628,6 +5471,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3649,15 +5501,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr ""
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr ""
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3673,15 +5549,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr ""
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr ""
@@ -3694,9 +5585,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3712,6 +5621,9 @@ msgstr ""
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3754,9 +5666,15 @@ msgstr ""
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3766,32 +5684,65 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3804,6 +5755,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3813,13 +5767,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3828,9 +5788,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -3948,9 +5917,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr ""
@@ -3972,33 +5968,66 @@ msgstr ""
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr ""
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -4007,6 +6036,9 @@ msgstr[1] ""
msgid "Tags"
msgstr ""
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4070,7 +6102,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4085,6 +6117,15 @@ msgstr ""
msgid "Team"
msgstr ""
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4124,12 +6165,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr ""
@@ -4148,6 +6192,9 @@ msgstr ""
msgid "The project can be accessed without any authentication."
msgstr ""
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr ""
@@ -4163,6 +6210,9 @@ msgstr ""
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 ""
@@ -4175,25 +6225,31 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 no issues to show"
msgstr ""
-msgid "There are no merge requests to show"
+msgid "There are no labels yet"
msgstr ""
-msgid "There are problems accessing Git storage: "
+msgid "There are no merge requests to show"
msgstr ""
-msgid "There was an error loading results"
+msgid "There are problems accessing Git storage: "
msgstr ""
msgid "There was an error loading users activity calendar."
@@ -4214,12 +6270,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4241,6 +6324,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4259,15 +6351,30 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4283,10 +6390,13 @@ msgstr ""
msgid "Time between merge request creation and merge/close"
msgstr ""
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time remaining"
+msgstr ""
+
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4310,6 +6420,9 @@ msgstr ""
msgid "Timeago|%s days remaining"
msgstr ""
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr ""
@@ -4325,6 +6438,9 @@ msgstr ""
msgid "Timeago|%s months remaining"
msgstr ""
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr ""
@@ -4340,46 +6456,43 @@ msgstr ""
msgid "Timeago|%s years remaining"
msgstr ""
-msgid "Timeago|1 day remaining"
-msgstr ""
-
-msgid "Timeago|1 hour remaining"
+msgid "Timeago|1 day ago"
msgstr ""
-msgid "Timeago|1 minute remaining"
+msgid "Timeago|1 day remaining"
msgstr ""
-msgid "Timeago|1 month remaining"
+msgid "Timeago|1 hour ago"
msgstr ""
-msgid "Timeago|1 week remaining"
+msgid "Timeago|1 hour remaining"
msgstr ""
-msgid "Timeago|1 year remaining"
+msgid "Timeago|1 minute ago"
msgstr ""
-msgid "Timeago|Past due"
+msgid "Timeago|1 minute remaining"
msgstr ""
-msgid "Timeago|a day ago"
+msgid "Timeago|1 month ago"
msgstr ""
-msgid "Timeago|a month ago"
+msgid "Timeago|1 month remaining"
msgstr ""
-msgid "Timeago|a week ago"
+msgid "Timeago|1 week ago"
msgstr ""
-msgid "Timeago|a year ago"
+msgid "Timeago|1 week remaining"
msgstr ""
-msgid "Timeago|about %s hours ago"
+msgid "Timeago|1 year ago"
msgstr ""
-msgid "Timeago|about a minute ago"
+msgid "Timeago|1 year remaining"
msgstr ""
-msgid "Timeago|about an hour ago"
+msgid "Timeago|Past due"
msgstr ""
msgid "Timeago|in %s days"
@@ -4421,10 +6534,13 @@ msgstr ""
msgid "Timeago|in 1 year"
msgstr ""
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
msgstr ""
-msgid "Timeago|less than a minute ago"
+msgid "Timeout"
msgstr ""
msgid "Time|hr"
@@ -4449,6 +6565,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4458,6 +6577,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4467,21 +6592,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4491,6 +6640,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr ""
@@ -4509,12 +6664,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4527,12 +6700,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr ""
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4548,6 +6754,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr ""
@@ -4566,9 +6775,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4578,9 +6796,21 @@ msgstr ""
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4593,7 +6823,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4605,9 +6838,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr ""
@@ -4620,6 +6865,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr ""
@@ -4635,7 +6886,7 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4653,10 +6904,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4683,7 +6946,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4719,6 +7006,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4734,7 +7027,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4746,9 +7039,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4773,7 +7063,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4791,7 +7090,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4800,6 +7102,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4812,22 +7120,40 @@ msgstr ""
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4836,6 +7162,9 @@ msgstr ""
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr ""
@@ -4866,9 +7195,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4884,6 +7222,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4902,10 +7246,13 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
msgstr[1] ""
@@ -4919,45 +7266,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4967,19 +7407,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST detected no security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4988,15 +7425,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr ""
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -5006,11 +7482,17 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -5024,16 +7506,28 @@ msgstr[1] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5045,6 +7539,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5068,24 +7565,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5113,6 +7604,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5146,9 +7640,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5209,6 +7718,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5218,9 +7730,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5262,15 +7771,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5283,6 +7801,14 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
+
diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po
index 2c7d3751531..d7a4fb0d6b7 100644
--- a/locale/uk/gitlab.po
+++ b/locale/uk/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:35-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Ukrainian\n"
"Language: uk_UA\n"
@@ -15,10 +13,32 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: uk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:41\n"
msgid " and"
msgstr " Ñ–"
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] " погіршилоÑÑ Ð½Ð° %d одиницю"
+msgstr[1] " погіршилоÑÑ Ð½Ð° %d одиниць"
+msgstr[2] " погіршилоÑÑ Ð½Ð° %d одиниць"
+msgstr[3] " погіршилоÑÑ Ð½Ð° %d одиниць"
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] " покращилоÑÑ Ð½Ð° %d одиницю"
+msgstr[1] " покращилоÑÑ Ð½Ð° %d одиниць"
+msgstr[2] " покращилоÑÑ Ð½Ð° %d одиниць"
+msgstr[3] " покращилоÑÑ Ð½Ð° %d одиниць"
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] "%d змінений файл"
+msgstr[1] "%d змінених файли"
+msgstr[2] "%d змінених файлів"
+msgstr[3] "%d змінених файлів"
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d коміт"
@@ -68,6 +88,34 @@ msgstr[1] "%d метрики"
msgstr[2] "%d метрик"
msgstr[3] "%d метрик"
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] "%d нова ліцензіÑ"
+msgstr[1] "%d нових ліцензій"
+msgstr[2] "%d нових ліцензій"
+msgstr[3] "%d нових ліцензій"
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] "%d проіндекÑована зміна"
+msgstr[1] "%d проіндекÑовані зміни"
+msgstr[2] "%d проіндекÑованих змін"
+msgstr[3] "%d проіндекÑованих змін"
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] "%d неіндекÑована зміна"
+msgstr[1] "%d неіндекÑовані зміни"
+msgstr[2] "%d неіндекÑованих змін"
+msgstr[3] "%d неіндекÑованих змін"
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] "%d вразливіÑÑ‚ÑŒ"
+msgstr[1] "%d вразливоÑтей"
+msgstr[2] "%d вразливоÑтей"
+msgstr[3] "%d вразливоÑтей"
+
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] "%s доданий коміт був виключений Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð±Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ із продуктивніÑÑ‚ÑŽ."
@@ -81,6 +129,9 @@ msgstr "%{actionText} Ñ– %{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr "%{commit_author_link} закомітив %{commit_timeago}"
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} учаÑтник"
@@ -88,12 +139,24 @@ msgstr[1] "%{count} учаÑтника"
msgstr[2] "%{count} учаÑтників"
msgstr[3] "%{count} учаÑтників"
+msgid "%{filePath} deleted"
+msgstr "%{filePath} видалено"
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr "%{group_docs_link_start}Групи%{group_docs_link_end} дозволÑÑŽÑ‚ÑŒ вам керувати Ñ– взаємодіÑти між кількома проектами. Члени групи мають доÑтуп до уÑÑ–Ñ… Ñ—Ñ— проектів."
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} Початок"
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr "%{lock_path} заблоковано кориÑтувачем GitLab %{lock_user_id}"
+msgid "%{name}'s avatar"
+msgstr "Ðватар %{name}"
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr "%{nip_domain} може бути викориÑтана Ñк альтернатива влаÑному домену."
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr "на %{number_commits_behind} комітів позаду %{default_branch}, на %{number_commits_ahead} комітів попереду"
@@ -106,6 +169,9 @@ msgstr "%{number_of_failures} від %{maximum_failures} невдач. GitLab а
msgid "%{openOrClose} %{noteable}"
msgstr "%{openOrClose} %{noteable}"
+msgid "%{percent}%% complete"
+msgstr "%{percent}%% завершено"
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] "%{storage_name}: Ñпроба невдалого доÑтупу до Ñховища на хоÑÑ‚Ñ–:"
@@ -113,18 +179,90 @@ msgstr[1] "%{storage_name}: %{failed_attempts} невдалі Ñпроби доÑ
msgstr[2] "%{storage_name}: %{failed_attempts} невдалих Ñпроб доÑтупу до Ñховища:"
msgstr[3] "%{storage_name}: %{failed_attempts} невдалих Ñпроб доÑтупу до Ñховища:"
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{text} is available"
msgstr "%{text} доÑтупний"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(перейдіть за поÑиланнÑм %{link} Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— ÑтоÑовно вÑтановленнÑ)."
+msgid "%{title} changes"
+msgstr "%{title} зміни"
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] "%{type} виÑвило 1 вразливіÑÑ‚ÑŒ"
+msgstr[1] "%{type} виÑвило %{vulnerabilityCount} вразливоÑтей"
+msgstr[2] "%{type} виÑвило %{vulnerabilityCount} вразливоÑтей"
+msgstr[3] "%{type} виÑвило %{vulnerabilityCount} вразливоÑтей"
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr "%{unstaged} неіндекÑованих та %{staged} проіндекÑованих змін"
msgid "+ %{moreCount} more"
msgstr "+ ще %{moreCount}"
+msgid "- Runner is active and can process any new jobs"
+msgstr "- Runner активний Ñ– може виконувати нові завданнÑ"
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr "- Runner призупинено Ñ– він не зможе виконувати нові завданнÑ"
+
msgid "- show less"
msgstr "- показати менше"
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] "1 %{type} доповненнÑ"
+msgstr[1] "%{count} %{type} доповненнÑ"
+msgstr[2] "%{count} %{type} доповнень"
+msgstr[3] "%{count} %{type} доповнень"
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] "1 %{type} зміна"
+msgstr[1] "%{count} %{type} зміни"
+msgstr[2] "%{count} %{type} змін"
+msgstr[3] "%{count} %{type} змін"
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] "1 закрита проблема"
+msgstr[1] "%d закриті проблеми"
+msgstr[2] "%d закритих проблем"
+msgstr[3] "%d закритих проблем"
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] "1 закритий запит на злиттÑ"
+msgstr[1] "%d закритих запити на злиттÑ"
+msgstr[2] "%d закритих запитів на злиттÑ"
+msgstr[3] "%d закритих запитів на злиттÑ"
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] "1 заÑтоÑований запит на злиттÑ"
+msgstr[1] "%d заÑтоÑованих запити на злиттÑ"
+msgstr[2] "%d заÑтоÑованих запитів на злиттÑ"
+msgstr[3] "%d заÑтоÑованих запитів на злиттÑ"
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] "1 відкрита проблема"
+msgstr[1] "%d відкриті проблеми"
+msgstr[2] "%d відкритих проблем"
+msgstr[3] "%d відкритих проблем"
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] "1 відкритий запит на злиттÑ"
+msgstr[1] "%d відкритих запити на злиттÑ"
+msgstr[2] "%d відкритих запитів на злиттÑ"
+msgstr[3] "%d відкритих запитів на злиттÑ"
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "1 конвеєр"
@@ -138,9 +276,51 @@ msgstr "Перший внеÑок!"
msgid "2FA enabled"
msgstr "Двоетапна Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð°"
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr "Будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора GitLab, щоб отримати дозвіл."
+
+msgid "403|You don't have the permission to access this page."
+msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтупу до цієї Ñторінки."
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr "Перевірте, що адреÑа правильна Ñ– те, що Ñторінка не переміщувалаÑÑ."
+
+msgid "404|Page Not Found"
+msgstr "Сторінка не знайдена"
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr "Будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора GitLab, Ñкщо ви вважаєте, що це помилка."
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr "<strong>%{created_count}</strong> Ñтворено, <strong>%{accepted_count}</strong> прийнÑто."
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr "<strong>%{created_count}</strong> Ñтворено, <strong>%{closed_count}</strong> закрито."
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>ВидалÑÑ”</strong> гілку-джерело"
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr "'Runner' — це процеÑ, Ñкий виконує завданнÑ. Ви можете Ñтворити потрібну кількіÑÑ‚ÑŒ Runner'ів."
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Ðабір графіків відноÑно безперервної інтеграції"
@@ -150,33 +330,63 @@ 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 "Проект — це міÑце де ви можете розміщувати Ñвої файли (репозиторій), планувати роботу (проблеми) Ñ– публікувати документацію (wiki), %{among_other_things_link}."
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr "КориÑтувач із правом запиÑу в гілку-джерело вибрав цей варіант"
+msgid "About GitLab"
+msgstr "Про GitLab"
+
+msgid "About GitLab CE"
+msgstr "Про GitLab CE"
+
msgid "About auto deploy"
msgstr "Про авто розгортаннÑ"
+msgid "About this feature"
+msgstr "Про цю функцію"
+
msgid "Abuse Reports"
msgstr "Звіти про зловживаннÑ"
msgid "Abuse reports"
-msgstr ""
+msgstr "Звіти про зловживаннÑ"
+
+msgid "Accept terms"
+msgstr "ПрийнÑти умови"
+
+msgid "Accepted MR"
+msgstr "ЗаÑтоÑований запит на злиттÑ"
msgid "Access Tokens"
msgstr "Токени доÑтупу"
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr "ДоÑтуп до \"%{classification_label}\" заборонено"
+
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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr "Обліковий запиÑ"
-msgid "Account and limit settings"
-msgstr ""
+msgid "Account and limit"
+msgstr "Обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ‚Ð° ліміт"
msgid "Active"
msgstr "Ðктивний"
+msgid "Active Sessions"
+msgstr "Ðктивні ÑеÑÑ–Ñ—"
+
msgid "Activity"
msgstr "ÐктивніÑÑ‚ÑŒ"
@@ -201,12 +411,39 @@ msgstr "Додати ліцензію"
msgid "Add Readme"
msgstr "Додати інÑтрукцію"
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr "Створіть додатковий текÑÑ‚, Ñкий буде приÑутній у вÑÑ–Ñ… повідомленнÑÑ… електронної пошти. МакÑимальна кількіÑÑ‚ÑŒ Ñимволів — %{character_limit}"
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "Додати новий каталог"
+msgid "Add reaction"
+msgstr "Додати реакцію"
+
msgid "Add todo"
msgstr "Додати задачу"
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr "Додати кориÑтувача до групи"
+
+msgid "Additional text"
+msgstr "Додатковий текÑÑ‚"
+
+msgid "Admin Area"
+msgstr "ОблаÑÑ‚ÑŒ адмініÑтратора"
+
+msgid "Admin Overview"
+msgstr "ОглÑд адмініÑтратора"
+
+msgid "Admin area"
+msgstr "ОблаÑÑ‚ÑŒ адмініÑтратора"
+
msgid "AdminArea|Stop all jobs"
msgstr "Зупинити вÑÑ– завданнÑ"
@@ -273,29 +510,74 @@ msgstr "Ð’ÑÑ– зміни закомічені"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "Ð’ÑÑ– функції Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… проектів берутьÑÑ Ñ–Ð· шаблонів або під Ñ‡Ð°Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ, але ви можете вимикати Ñ—Ñ… пізніше в налаштуваннÑÑ… проекту."
-msgid "Allow edits from maintainers."
-msgstr "Дозволити Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¾ÑŽ проекту."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr "Дозволити коміти від учаÑників, Ñкі можуть зливати в цільову гілку."
-msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
+msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
+msgstr "Дозволити Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ñ–Ð°Ð³Ñ€Ð°Ð¼ PlantUML в документах Asciidoc."
+
msgid "Allow requests to the local network from hooks and services."
-msgstr ""
+msgstr "Дозволити запити до локальної мережі із гуків та ÑервіÑів."
msgid "Allows you to add and manage Kubernetes clusters."
msgstr "ДозволÑÑ” додавати та керувати клаÑтерами Kubernetes."
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
-msgstr ""
+msgstr "Також відомий Ñк «Емітент» або «Ідентифікатор довіри перевірÑючої Ñторони»"
msgid "Also called \"Relying party service URL\" or \"Reply URL\""
-msgstr ""
+msgstr "Також називаєтьÑÑ \"URL-адреÑа Ñлужби перевірÑючої Ñторони\" або \"URL-адреÑа відповіді\""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr ""
+msgstr "Крім того, ви можете викориÑтовувати %{personal_access_token_link}. Коли ви Ñтворюватимете Ñвій перÑональний токен доÑтупу, вам потрібно буде вибрати облаÑÑ‚ÑŒ дії <code>repo</code>, щоб ми могли відобразити ÑпиÑок ваших публічних та приватних репозиторіїв, доÑтупних Ð´Ð»Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ."
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
-msgstr "Крім того, ви можете викориÑтовувати %{personal_access_token_link}. Коли ви Ñтворюватимете Ñвій перÑональний токен доÑтупу, вам потрібно буде вибрати облаÑÑ‚ÑŒ <code>репозиторіÑ</code>, щоб ми могли відобразити ÑпиÑок ваших публічних та приватних репозиторіїв, доÑтупних Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ."
+msgstr "Крім того, ви можете викориÑтовувати %{personal_access_token_link}. Коли ви Ñтворюватимете Ñвій перÑональний токен доÑтупу, вам потрібно буде вибрати облаÑÑ‚ÑŒ <code>repo</code>, щоб ми могли відобразити ÑпиÑок ваших публічних та приватних репозиторіїв, доÑтупних Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ."
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr "Помилка при Ñтворенні нової гілки."
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при завантаженні вÑÑ–Ñ… файлів."
+
+msgid "An error occured whilst loading the file content."
+msgstr "Помилка при завантаженні вміÑту файлу."
+
+msgid "An error occured whilst loading the file."
+msgstr "Помилка при завантаженні файлу."
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
msgid "An error occurred previewing the blob"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾Ð³Ð¾ переглÑду об'єкта"
@@ -312,8 +594,11 @@ msgstr "Помилка при додаванні учаÑника Ð´Ð»Ñ Ð·Ð°Ñ‚Ð
msgid "An error occurred while detecting host keys"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при виÑвленні ключів хоÑта"
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr "Помилка при відхиленні попередженнÑ. Оновіть Ñторінку та Ñпробуйте знову."
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
-msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при вимкненні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ функцію. Оновіть Ñторінку Ñ– Ñпробуйте знову."
+msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при відхиленні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ функцію. Оновіть Ñторінку Ñ– Ñпробуйте знову."
msgid "An error occurred while fetching markdown preview"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при попередньому переглÑді markdown"
@@ -327,14 +612,14 @@ msgstr "Помилка при отриманні данних конвеєра."
msgid "An error occurred while getting projects"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при отриманні проектів"
-msgid "An error occurred while importing project"
-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 commits"
-msgstr "Помилка при завантажені комітів"
+msgid "An error occurred while loading commit signatures"
+msgstr ""
msgid "An error occurred while loading diff"
msgstr "Помилка при завантаженні разниці (diff)"
@@ -369,18 +654,42 @@ msgstr "Помилка при збереженні ÑтатуÑу перевиз
msgid "An error occurred while saving assignees"
msgstr "Помилка при збереженні виконавців"
+msgid "An error occurred while subscribing to notifications."
+msgstr "Помилка при підпиÑці на ÑповіщеннÑ."
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr "Помилка при відпиÑці від Ñповіщень."
+
msgid "An error occurred while validating username"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ імені кориÑтувача"
msgid "An error occurred. Please try again."
msgstr "СталаÑÑŒ помилка. Спробуйте ще раз."
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr "Будь-Ñка мітка"
msgid "Appearance"
msgstr "Зовнішній виглÑд"
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr "ЗаÑтоÑунки"
@@ -390,12 +699,21 @@ msgstr "квіт."
msgid "April"
msgstr "квітень"
-msgid "Archived project! Repository is read-only"
-msgstr "Заархівований проект! Репозиторій доÑтупний лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr "Ðрхівований проект! Репозиторій та інші реÑурÑи проекту доÑтупні лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ви впевнені, що хочете видалити цей розклад Ð´Ð»Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ð°?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Ви впевнені, що бажаєте перегенерувати реєÑтраційний токен?"
@@ -411,8 +729,14 @@ msgstr "Ви впевнені?"
msgid "Artifacts"
msgstr "Ðртефакти"
+msgid "Ascending"
+msgstr "За зроÑтаннÑм"
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr "ПопроÑÑ–Ñ‚ÑŒ керівника групи, щоб налаштувати груповий Runner."
+
msgid "Assertion consumer service URL"
-msgstr ""
+msgstr "URL-адреÑа Ñлужби обробника тверджень"
msgid "Assign custom color like #FF0000"
msgstr "Призначити влаÑний колір типу #FF0000"
@@ -427,20 +751,35 @@ msgid "Assign to"
msgstr "Призначити"
msgid "Assigned Issues"
-msgstr ""
+msgstr "Призначені проблеми"
msgid "Assigned Merge Requests"
-msgstr ""
+msgstr "Призначені запити на злиттÑ"
msgid "Assigned to :name"
-msgstr ""
+msgstr "Призначено :ім'Ñ"
+
+msgid "Assigned to me"
+msgstr "Призначено мені"
msgid "Assignee"
msgstr "Виконавець"
+msgid "Assignee boards not available with your current license"
+msgstr "Дошки виконавців не доÑтупні з вашою поточною ліцензією"
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr "СпиÑки виконавців показують уÑÑ– проблеми, призначені вибраному кориÑтувачу."
+
+msgid "Assignee(s)"
+msgstr "Виконавець(ці)"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Прикріпити файл за допомогою перетÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ %{upload_link}"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr "Ñерп."
@@ -450,17 +789,41 @@ msgstr "Ñерпень"
msgid "Authentication Log"
msgstr "Журнал автентифікації"
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr "Ðвтор"
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr "Ðвтори: %{authors}"
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr "Auto DevOps увімкнено"
msgid "Auto DevOps, runners and job artifacts"
-msgstr ""
+msgstr "Auto DevOps, runner'и і артефакти завдань"
msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr "Ð”Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Auto Review Apps та Auto Deploy необхіден %{kubernetes}."
@@ -471,8 +834,11 @@ msgstr "Ð”Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Auto Review Apps та Auto Deploy
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Ð”Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Auto Review Apps та Auto Deploy необхідно вказати доменне ім’Ñ."
-msgid "AutoDevOps|Auto DevOps (Beta)"
-msgstr "Auto DevOps (бета)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr "Auto DevOps"
msgid "AutoDevOps|Auto DevOps documentation"
msgstr "Auto DevOps документації"
@@ -492,12 +858,18 @@ msgstr "Ви можете автоматично збирати й теÑтувÐ
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr "додати Kubernetes-клаÑтер"
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
-msgstr "Увімкнути Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr "Увімкнути Auto DevOps"
msgid "Available"
msgstr "ДоÑтупно"
+msgid "Available group Runners : %{runners}"
+msgstr "ДоÑтупні групові Runner'и: %{runners}"
+
+msgid "Available group Runners : %{runners}."
+msgstr "ДоÑтупні групові Runner'и: %{runners}."
+
msgid "Avatar will be removed. Are you sure?"
msgstr "Ðватар буде видалено. Ви впевнені?"
@@ -505,14 +877,95 @@ msgid "Average per day: %{average}"
msgstr "Ð’ Ñередньому за день: %{average}"
msgid "Background Color"
+msgstr "Колір фону"
+
+msgid "Background Jobs"
msgstr ""
+msgid "Background color"
+msgstr "Колір фону"
+
msgid "Background jobs"
-msgstr ""
+msgstr "Фонові завданнÑ"
+
+msgid "Badges"
+msgstr "Значки"
+
+msgid "Badges|A new badge was added."
+msgstr "Ðовий значок був доданий."
+
+msgid "Badges|Add badge"
+msgstr "Додати значок"
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐºÐ° не вдалоÑÑ, будь лаÑка перевірте введений URL Ñ– Ñпробуйте знову."
+
+msgid "Badges|Badge image URL"
+msgstr "URL-адреÑа Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐºÐ°"
+
+msgid "Badges|Badge image preview"
+msgstr "Попередній переглÑд значка"
+
+msgid "Badges|Delete badge"
+msgstr "Видалити значок"
+
+msgid "Badges|Delete badge?"
+msgstr "Видалити значок?"
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐºÐ° не вдалоÑÑ, будь лаÑка Ñпробуйте ще раз."
+
+msgid "Badges|Group Badge"
+msgstr "Значок групи"
+
+msgid "Badges|Link"
+msgstr "ПоÑиланнÑ"
+
+msgid "Badges|No badge image"
+msgstr "Значок Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ñутній"
+
+msgid "Badges|No image to preview"
+msgstr "Ðемає Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾Ð³Ð¾ переглÑду"
+
+msgid "Badges|Project Badge"
+msgstr "Значок проекту"
+
+msgid "Badges|Reload badge image"
+msgstr "Оновити Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐºÐ°"
+
+msgid "Badges|Save changes"
+msgstr "Зберегти зміни"
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr "Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐºÐ° не вдалоÑÑ, будь лаÑка перевірте введений URL Ñ– Ñпробуйте знову."
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr "%{docsLinkStart}Змінні%{docsLinkEnd}, Ñкі підтримує GitLab: %{placeholders}"
+
+msgid "Badges|The badge was deleted."
+msgstr "Значок був видалений."
+
+msgid "Badges|The badge was saved."
+msgstr "Значок було збережено."
+
+msgid "Badges|This group has no badges"
+msgstr "Ð¦Ñ Ð³Ñ€ÑƒÐ¿Ð° не має значків"
+
+msgid "Badges|This project has no badges"
+msgstr "У цього проекту немає значків"
+
+msgid "Badges|Your badges"
+msgstr "Ваші значки"
msgid "Begin with the selected commit"
msgstr "Почати із виділеного коміту"
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr "Білінг"
@@ -531,6 +984,9 @@ msgstr "Служба підтримки"
msgid "BillingPlans|Downgrade"
msgstr "Понизити"
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про кожен план, читаючи наш %{faq_link}."
@@ -544,10 +1000,10 @@ msgid "BillingPlans|See all %{plan_name} features"
msgstr "ПодивітьÑÑ Ð²ÑÑ– можливоÑÑ‚Ñ– %{plan_name}"
msgid "BillingPlans|This group uses the plan associated with its parent group."
-msgstr "Ð¦Ñ Ð³Ñ€ÑƒÐ¿Ð° викориÑтовує план, пов'Ñзаний із батьківÑькою групою."
+msgstr "Ð¦Ñ Ð³Ñ€ÑƒÐ¿Ð° викориÑтовує план, пов'Ñзаний з батьківÑькою групою."
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
-msgstr "Щоб керувати планом цієї групи, відвідайте розділ білінгу на %{parent_billing_page_link}."
+msgstr "Ð”Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð¿Ð»Ð°Ð½Ð¾Ð¼ цієї групи відвідайте Ñекцію оплати %{parent_billing_page_link}."
msgid "BillingPlans|Upgrade"
msgstr "Підвищити"
@@ -555,6 +1011,15 @@ msgstr "Підвищити"
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr "Зараз ви викориÑтовуєте план %{plan_link}."
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr "ЧаÑÑ‚Ñ– питаннÑ"
@@ -565,7 +1030,19 @@ msgid "BillingPlans|paid annually at %{price_per_year}"
msgstr "ОплачуєтьÑÑ Ñ‰Ð¾Ñ€Ñ–Ñ‡Ð½Ð¾ %{price_per_year}"
msgid "BillingPlans|per user"
-msgstr "за кориÑтувача"
+msgstr "За кориÑтувача"
+
+msgid "Bitbucket import"
+msgstr "Імпорт з Bitbucket"
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr "Дошки"
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
@@ -646,7 +1123,7 @@ msgstr "Ðемає гілок Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr "Як тільки ви підтвердите Ñ– натиÑнете %{delete_protected_branch}, дані будуть втрачені, Ñ– Ñ—Ñ… не можливо буде відновити."
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr "Тільки керівник або влаÑник проекту може видалити захищену гілку"
msgid "Branches|Overview"
@@ -727,8 +1204,8 @@ msgstr "ПереглÑд файлів"
msgid "Browse files"
msgstr "ПереглÑд файлів"
-msgid "Business"
-msgstr "БізнеÑ"
+msgid "Business metrics (Custom)"
+msgstr "Ð‘Ñ–Ð·Ð½ÐµÑ Ð¼ÐµÑ‚Ñ€Ð¸ÐºÐ¸ (ВлаÑні)"
msgid "ByAuthor|by"
msgstr "від"
@@ -736,6 +1213,9 @@ msgstr "від"
msgid "CI / CD"
msgstr "CI / CD"
+msgid "CI / CD Settings"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
+
msgid "CI/CD"
msgstr "CI/CD"
@@ -745,26 +1225,86 @@ msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
msgid "CI/CD for external repo"
msgstr "CI/CD Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ репозиторіÑ"
+msgid "CI/CD settings"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr "Ðеобхідно вказати %{ci_file} перед тим, Ñк ви зможете викориÑтовувати безперервну інтреграцію та розгортаннÑ."
+
+msgid "CICD|Auto DevOps"
+msgstr "Auto DevOps"
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr "Auto DevOps буду автоматично збирати, теÑтувати та розгортати ваш заÑтоÑунок на оÑнові Ñтандартної конфігурації неперервної інтеграції та розгортаннÑ."
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr "Ðвтоматичне Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° staging, ручне Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
+
+msgid "CICD|Continuous deployment to production"
+msgstr "Безперервне Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
+
+msgid "CICD|Deployment strategy"
+msgstr "Ð¡Ñ‚Ñ€Ð°Ñ‚ÐµÐ³Ñ–Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr "Ðеобхідно вказати доменне Ñ–Ð¼â€™Ñ Ð´Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Ñтратегії розгортаннÑ."
+
+msgid "CICD|Disable Auto DevOps"
+msgstr "Вимкнути Auto DevOps"
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr "Ðе вказуйте тут домен, Ñкщо ви налаштовуєте декілька клаÑтерів Kubernetes за допомогою Auto DevOps."
+
+msgid "CICD|Enable Auto DevOps"
+msgstr "Увімкнути Auto DevOps"
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr "ВикориÑтовувати Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½ÑтанÑу за замовчаннÑм Ð´Ð»Ñ ÑƒÐ²Ñ–Ð¼ÐºÐµÐ½Ð½Ñ Ð°Ð±Ð¾ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Auto DevOps, Ñкщо у проекті відÑутній %{ci_file}."
+
+msgid "CICD|Instance default (%{state})"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½ÑтанÑу за замовчуваннÑм (%{state})"
+
msgid "CICD|Jobs"
msgstr "ЗавданнÑ"
+msgid "CICD|Learn more about Auto DevOps"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Auto DevOps"
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr "ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñƒ Auto DevOps буде заÑтоÑовуватиÑÑ, коли в проекті немає %{ci_file}."
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr "Вам потрібно вказати домен, Ñкщо ви хочете викориÑтовувати Auto Review Apps та Auto Deploy."
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "СкаÑувати"
+msgid "Cancel this job"
+msgstr "СкаÑувати це завданнÑ"
+
msgid "Cannot be merged automatically"
-msgstr ""
+msgstr "Ðеможливо злити автоматично"
msgid "Cannot modify managed Kubernetes cluster"
msgstr "Ðеможливо змінити керований клаÑтер Kubernetes"
msgid "Certificate fingerprint"
-msgstr ""
+msgstr "Відбиток Ñертифіката"
msgid "Change Weight"
-msgstr "Вага зміни"
+msgstr ""
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
-msgstr ""
+msgstr "Змініть це значеннÑ, щоб вплинути на чаÑтоту Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñу GitLab."
msgid "ChangeTypeActionLabel|Pick into branch"
msgstr "Вибрати в гілці"
@@ -808,15 +1348,30 @@ msgstr "Вибрати (cherry-pick) цей коміт"
msgid "Cherry-pick this merge request"
msgstr "Вибрати (cherry-pick) цей запит на злиттÑ"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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}) Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду змін або Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на злиттÑ."
+msgid "Choose any color."
+msgstr "Вибрати будь-Ñкий колір."
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr "Виберіть файл..."
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr "Виберіть групи Ð´Ð»Ñ Ñинхронізації на цей вторинний вузол."
@@ -922,20 +1477,44 @@ msgstr "Перевірка невдала"
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "circuitbreaker api"
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr "не доÑтупно: %{reason}"
+
+msgid "Clear search input"
+msgstr "ОчиÑтити поле вводу"
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr "Клікніть по будь-Ñкому <strong>імені проекту</strong> зі ÑпиÑку нижче Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб перейти до етапу проекту."
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr "ÐатиÑніть кнопку <strong>ПеренеÑти</strong> у правому верхному куті щоб перенеÑти етап на рівень групи."
+
+msgid "Click the <strong>Select none</strong> 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 "ÐатиÑніть кнопку нижче, щоб розпочати Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð²ÑтановленнÑ, перейшовши на Ñторінку Kubernetes"
+msgid "Click to expand it."
+msgstr "ÐатиÑніть, щоб розгорнути його."
+
msgid "Click to expand text"
msgstr "ÐатиÑніть, щоб розгорнути текÑÑ‚"
msgid "Client authentication certificate"
-msgstr ""
+msgstr "Сертифікат автентифікації клієнта"
msgid "Client authentication key"
-msgstr ""
+msgstr "Ключ автентифікації клієнта"
msgid "Client authentication key password"
-msgstr ""
+msgstr "Пароль ключа аутентифікації клієнта"
+
+msgid "Clients"
+msgstr "Клієнти"
msgid "Clone repository"
msgstr "Клонувати репозиторій"
@@ -946,6 +1525,9 @@ msgstr "Закрити"
msgid "Closed"
msgstr "Закрито"
+msgid "Closed issues"
+msgstr "Закриті проблеми"
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} були уÑпішно вÑтановлені на ваш Kubernetes-клаÑтер"
@@ -955,12 +1537,15 @@ msgstr "API URL"
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr "Додати Kubernetes клаÑтер"
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
-msgstr "Додати Ñ–Ñнуючий Kubernetes-клаÑтер"
-
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Детальні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— із цим Kubernetes-клаÑтером"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr "Помилка при отриманні зон проекту: %{error}"
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr "Помилка при отриманні ваших проектів: %{error}"
+
msgid "ClusterIntegration|Applications"
msgstr "ЗаÑтоÑунки"
@@ -973,9 +1558,6 @@ msgstr "Сертифікат центру Ñертифікації"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Ðабір Ñертифікатів (формат PEM)"
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr "Виберіть ÑпоÑіб Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— із Kubernetes-клаÑтером"
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr "Виберіть Ñкі з Ñередовищ вашого проекту викориÑтовуватимуть цей Kubernetes-клаÑтер."
@@ -991,6 +1573,9 @@ msgstr "Скопіювати Ñертифікат центру ÑертифікÐ
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr "Копіювати IP-адреÑу Ingress в буфер обміну"
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr "Скопіювати Ñ–Ð¼â€™Ñ Ñ…Ð¾Ñта Jupyter в буфер обміну"
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr "Скопіювати Ñ–Ð¼â€™Ñ Kubernetes-клаÑтера"
@@ -1000,44 +1585,53 @@ msgstr "Скопіювати Токен"
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr "Створити Kubernetes-клаÑтер"
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
-msgstr "Створити Kubernetes-клаÑтер на Google Kubernetes Engine"
-
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr "Створити Kubernetes-клаÑтер на Google Kubernetes Engine прÑмо із GitLab"
-
-msgid "ClusterIntegration|Create on GKE"
-msgstr "Створити в GKE"
-
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr "Вкажіть параметри Ñ–Ñнуючого клаÑтера Kubernetes"
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr "Введіть параметри вашого Kubernetes-клаÑтера"
msgid "ClusterIntegration|Environment scope"
+msgstr "Межі Ñередовищ"
+
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
+msgid "ClusterIntegration|Fetching machine types"
+msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ‚Ð¸Ð¿Ñ–Ð² машин"
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ñ–Ð²"
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð·Ð¾Ð½"
+
msgid "ClusterIntegration|GitLab Integration"
msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ð· GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
-msgstr "Ідентифікатор проекту в Google Cloud Platform"
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr "Проект Google Cloud Platform"
msgid "ClusterIntegration|Google Kubernetes Engine"
msgstr "Google Kubernetes Engine"
msgid "ClusterIntegration|Google Kubernetes Engine project"
-msgstr "Проект Google Kubernetes Engine"
+msgstr "проекті Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
-msgstr "Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб показувати Ñтан клаÑтера, нам потрібно буде вÑтановити Prometheus на ваш клаÑтер Ð´Ð»Ñ Ð·Ð±Ð¾Ñ€Ñƒ необхідних даних."
+msgstr "Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб показати працездатніÑÑ‚ÑŒ клаÑтера, нам потрібно буде забезпечити ваш клаÑтер з Prometheus Ð´Ð»Ñ Ð·Ð±Ð¾Ñ€Ñƒ необхідних даних."
msgid "ClusterIntegration|Ingress"
msgstr "Ingress"
@@ -1063,6 +1657,12 @@ msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ ÐºÐ»Ð°Ñтерної автоматизації Kub
msgid "ClusterIntegration|Integration status"
msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ—"
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr "Ð†Ð¼â€™Ñ Ñ…Ð¾Ñта Jupyter"
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr "JupyterHub"
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "Kubernetes-клаÑтер"
@@ -1099,8 +1699,14 @@ msgstr "Kubernetes-клаÑтери дозволÑÑŽÑ‚ÑŒ вам викориÑÑ‚
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr "Kubernetes-клаÑтери можуть бути викориÑтані Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð·Ð°ÑтоÑунків Ñ– викориÑÑ‚Ð°Ð½Ð½Ñ Review Apps Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
-msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про %{help_link_start_machine_type}типи машин%{help_link_end} та %{help_link_start_pricing}ціни%{help_link_end}."
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про %{help_link_start}Kubernetes%{help_link_end}."
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про %{help_link_start}зони%{help_link_end}."
msgid "ClusterIntegration|Learn more about environments"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñередовища"
@@ -1112,7 +1718,7 @@ msgid "ClusterIntegration|Machine type"
msgstr "Тип машини"
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
-msgstr "ВпевнітьÑÑ, що ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð´Ð¾Ð²Ñ–Ð»ÑŒÐ½ÑÑ” %{link_to_requirements} Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Kubernetes-клаÑтерів"
+msgstr "ВпевнітьÑÑ, що ваш обліковий Ð·Ð°Ð¿Ð¸Ñ %{link_to_requirements} Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Kubernetes-клаÑтерів"
msgid "ClusterIntegration|Manage"
msgstr "УправліннÑ"
@@ -1126,6 +1732,18 @@ msgstr "Додаткова інформаціÑ"
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr "Декілька Kubernetes-клаÑтерів доÑтупні в GitLab Enterprise Edition Premium та Ultimate"
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr "Жоден тип машин не відповідає вашому пошуку"
+
+msgid "ClusterIntegration|No projects found"
+msgstr "Проектів не знайдено"
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr "Жоден проект не відповідає вашому пошуку"
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr "Жодна зона не відповідає вашому пошуку"
+
msgid "ClusterIntegration|Note:"
msgstr "Примітка:"
@@ -1138,9 +1756,6 @@ msgstr "Введіть інформацію про доÑтуп до Ñвого
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Будь-лаÑка впевнітьÑÑ, що ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Google задовольнÑÑ” наÑтупним вимогам:"
-msgid "ClusterIntegration|Project ID"
-msgstr "Ідентифікатор проекту"
-
msgid "ClusterIntegration|Project namespace"
msgstr "ПроÑÑ‚Ñ–Ñ€ імен проекту"
@@ -1168,20 +1783,38 @@ msgstr "Запит про початок вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ викоÐ
msgid "ClusterIntegration|Save changes"
msgstr "Зберегти зміни"
+msgid "ClusterIntegration|Search machine types"
+msgstr "Пошук по типам машин"
+
+msgid "ClusterIntegration|Search projects"
+msgstr "Пошук проектів"
+
+msgid "ClusterIntegration|Search zones"
+msgstr "Пошук зон"
+
msgid "ClusterIntegration|Security"
msgstr "Безпека"
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "ПереглÑнути та редагувати параметри вашого Kubernetes-клаÑтера"
-msgid "ClusterIntegration|See machine types"
-msgstr "ПереглÑнути типи машин"
+msgid "ClusterIntegration|Select machine type"
+msgstr "Вибрати тип машин"
+
+msgid "ClusterIntegration|Select project"
+msgstr "Вибрати проект"
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr "Виберіть проект та зону Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, що вибрати тип машини"
-msgid "ClusterIntegration|See your projects"
-msgstr "ПереглÑнути ваші проекти"
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr "Виберіть проект, щоб вибрати зону"
-msgid "ClusterIntegration|See zones"
-msgstr "ПереглÑнути зони"
+msgid "ClusterIntegration|Select zone"
+msgstr "Вибрати зону"
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr "Виберіть зону, щоб вибрати тип машин"
msgid "ClusterIntegration|Service token"
msgstr "Токен СервіÑа"
@@ -1213,6 +1846,9 @@ msgstr "Увімкнути/вимкнути Kubernetes-клаÑтер"
msgid "ClusterIntegration|Token"
msgstr "Токен"
+msgid "ClusterIntegration|Validating project billing status"
+msgstr "Перевірка Ñтану білінгу проекта"
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "За допомогою підключеного до цього проекту Kubernetes-клаÑтера, ви можете викориÑтовувати Review Apps, розгортати ваші проекти, запуÑкати конвеєри збірки тощо."
@@ -1243,13 +1879,22 @@ msgstr "задовольнÑÑ” вимогам"
msgid "ClusterIntegration|properly configured"
msgstr "правильно налаштований"
+msgid "ClusterIntegration|sign up"
+msgstr "зареєÑтрувати"
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr "Згорнути"
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr "Згорнути бокову панель"
+
+msgid "Comment & resolve discussion"
msgstr "Залишити коментар Ñ– завершити обговореннÑ"
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr "Залишити коментар Ñ– повторно відкрити обговореннÑ"
msgid "Comments"
@@ -1320,6 +1965,9 @@ msgstr "Ðе знайдено пов'Ñзаних запитів на злитт
msgid "Committed by"
msgstr "Коміт від"
+msgid "Commit…"
+msgstr "Коміт…"
+
msgid "Compare"
msgstr "ПорівнÑти"
@@ -1330,10 +1978,10 @@ msgid "Compare Revisions"
msgstr "ПорівнÑÐ½Ð½Ñ Ñ€ÐµÐ´Ð°ÐºÑ†Ñ–Ð¹"
msgid "Compare changes with the last commit"
-msgstr ""
+msgstr "ПорівнÑти зміни з оÑтаннім комітом"
msgid "Compare changes with the merge request target branch"
-msgstr ""
+msgstr "ПорівнÑти зміни із ціловою гілкою запиту на злиттÑ"
msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
msgstr "%{source_branch} і %{target_branch} однакові."
@@ -1348,31 +1996,34 @@ msgid "CompareBranches|Target"
msgstr "Ціль"
msgid "CompareBranches|There isn't anything to compare."
-msgstr ""
+msgstr "Ðема чого порівнювати."
msgid "Confidential"
-msgstr ""
+msgstr "Конфіденційний"
msgid "Confidentiality"
msgstr "КонфіденційніÑÑ‚ÑŒ"
msgid "Configure Gitaly timeouts."
-msgstr ""
+msgstr "Ðалаштувати таймаути Gitaly."
msgid "Configure Sidekiq job throttling."
-msgstr ""
+msgstr "Ðалаштувати чаÑтоту завдань Sidekiq."
msgid "Configure automatic git checks and housekeeping on repositories."
-msgstr ""
+msgstr "Ðалаштувати автоматичні перевірки git Ñ– Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð² репозиторіÑÑ…."
msgid "Configure limits for web and API requests."
+msgstr "Ðалаштуйте Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð²ÐµÐ± та API запитів."
+
+msgid "Configure push and pull mirrors."
msgstr ""
msgid "Configure storage path and circuit breaker settings."
-msgstr ""
+msgstr "Ðалаштуйте шлÑÑ… до Ñховищ та circuit breaker."
msgid "Configure the way a user creates a new account."
-msgstr ""
+msgstr "Ðалаштувати ÑпоÑіб ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувачем нового облікового запиÑу."
msgid "Connect"
msgstr "Підключити"
@@ -1384,7 +2035,7 @@ msgid "Connect repositories from GitHub"
msgstr "Підключити репозиторії з GitHub"
msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
-msgstr "Підключіть ваші зовнішні репозиторії, Ñ– CI/CD конвеєри будуть запуÑкатиÑÑ Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… комітів. Створений GitLab-проект буде мати лише CI/CD фунції."
+msgstr ""
msgid "Connecting..."
msgstr "З'єднаннÑ..."
@@ -1434,7 +2085,19 @@ msgstr "ВикориÑтовуйте різні імена образів"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr "За допомогою вбудованого в GitLab реєÑтру Docker контейнерів кожен проект може мати влаÑне міÑце Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Docker образів."
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr "Ви також можете викориÑтовувати %{deploy_token} Ð´Ð»Ñ Ð´Ð¾Ñтупу тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð´Ð¾ образів у реєÑтрі."
+
+msgid "Continue"
+msgstr "Продовжити"
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
+msgstr "Безперервна Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ‚Ð° розгортаннÑ"
+
+msgid "Contribute to GitLab"
msgstr ""
msgid "Contribution"
@@ -1443,6 +2106,9 @@ msgstr "ВнеÑок"
msgid "Contribution guide"
msgstr "ІнÑÑ‚Ñ€ÑƒÐºÑ†Ñ–Ñ Ð´Ð»Ñ ÑƒÑ‡Ð°Ñників"
+msgid "Contributions per group member"
+msgstr "КількіÑÑ‚ÑŒ внеÑків на кожного учаÑника групи"
+
msgid "Contributors"
msgstr "УчаÑники"
@@ -1458,12 +2124,21 @@ msgstr "Коміти в %{branch_name}, за винÑтком комітів зÐ
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr "Будь лаÑка, зачекайте, Ñ†Ñ Ñторінка автоматично оновитьÑÑ, коли буде готова."
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr "Задати макÑимальну кількіÑÑ‚ÑŒ потоків Ð´Ð»Ñ Ñ„Ð¾Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ LFS/вкладень Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ вторинного вузла"
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr "Задати макÑимальну кількіÑÑ‚ÑŒ потоків Ð´Ð»Ñ Ñ„Ð¾Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð² Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ вторинного вузла"
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr "Ð’Ñтановіть макÑимальну кількіÑÑ‚ÑŒ паралельних операцій перевірки Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Geo-вузла"
+
+msgid "ConvDev Index"
+msgstr "Ð†Ð½Ð´ÐµÐºÑ ConvDev"
+
msgid "Copy SSH public key to clipboard"
msgstr "Скопіюйте відкритий SSH-ключ в буфер обміну"
@@ -1479,9 +2154,21 @@ msgstr "Скопіювати команду в буфер обміну"
msgid "Copy commit SHA to clipboard"
msgstr "Скопіювати ідентифікатор в буфер обміну"
+msgid "Copy file path to clipboard"
+msgstr "Скопіювати шлÑÑ… до файлу в буфер обміну"
+
+msgid "Copy incoming email address to clipboard"
+msgstr "Копіювати адреÑу електронної пошти у буфер обміну"
+
msgid "Copy reference to clipboard"
msgstr "Скопіювати поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð² буфер обміну"
+msgid "Copy to clipboard"
+msgstr "Копіювати в буфер обміну"
+
+msgid "Copy token to clipboard"
+msgstr "Скопіювати токен в буфер обміну"
+
msgid "Create"
msgstr "Створити"
@@ -1494,17 +2181,23 @@ msgstr "Створити нову гілку"
msgid "Create a new branch and merge request"
msgstr "Створити нову гілку Ñ– запит на злиттÑ"
+msgid "Create a new issue"
+msgstr "Створити нову проблему"
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Створіть токен доÑтупу Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ аккаунта, щоб відправлÑти та отримувати через %{protocol}."
msgid "Create branch"
msgstr "Створити гілку"
+msgid "Create commit"
+msgstr "Створити коміт"
+
msgid "Create directory"
msgstr "Створити каталог"
msgid "Create empty repository"
-msgstr ""
+msgstr "Створити порожній репозиторій"
msgid "Create epic"
msgstr "Створити епік"
@@ -1512,11 +2205,17 @@ msgstr "Створити епік"
msgid "Create file"
msgstr "Створити файл"
+msgid "Create group"
+msgstr "Створити групу"
+
msgid "Create group label"
msgstr "Створити мітку групи"
+msgid "Create issue"
+msgstr "Створити проблему"
+
msgid "Create lists from labels. Issues with that label appear in that list."
-msgstr "Створити ÑпиÑок на оÑтнові міток. Ð’ ньому будуть проблеми з такими мітками."
+msgstr "Створити ÑпиÑок на оÑнові міток. Ð’ ньому будуть проблеми з такими мітками."
msgid "Create merge request"
msgstr "Створити запит на злиттÑ"
@@ -1533,6 +2232,9 @@ msgstr "Створити новий каталог"
msgid "Create new file"
msgstr "Створити новий файл"
+msgid "Create new file or directory"
+msgstr "Створити новий файл чи папку"
+
msgid "Create new label"
msgstr "Створити нову мітку"
@@ -1551,11 +2253,17 @@ msgstr "Тег"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "Створити токен Ð´Ð»Ñ Ð¾ÑобиÑтого доÑтупу"
-msgid "Creates a new branch from %{branchName}"
-msgstr "Створює нову гілку із %{branchName}"
+msgid "Created"
+msgstr "Створено"
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
-msgstr "Створює нову гілку із %{branchName} Ñ– перенаправлÑÑ” Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ запиту на злиттÑ"
+msgid "Created At"
+msgstr ""
+
+msgid "Created by me"
+msgstr "Створено мною"
+
+msgid "Created on:"
+msgstr ""
msgid "Creating epic"
msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐµÐ¿Ñ–ÐºÑƒ"
@@ -1569,6 +2277,15 @@ msgstr "СинтакÑÐ¸Ñ Cron"
msgid "Current node"
msgstr "Поточний вузол"
+msgid "CurrentUser|Profile"
+msgstr "Профіль"
+
+msgid "CurrentUser|Settings"
+msgstr "ÐалаштуваннÑ"
+
+msgid "Custom CI config path"
+msgstr "КориÑтувацький шлÑÑ… до CI config"
+
msgid "Custom notification events"
msgstr "КориÑтувацькі Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ про події"
@@ -1576,6 +2293,12 @@ msgid "Custom notification levels are the same as participating levels. With cus
msgstr "Спеціальні рівні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñпівпадають з рівнем учаÑÑ‚Ñ–. За допомогою Ñпеціальних рівнів Ñповіщень ви також отримуватимете ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ вибрані події. Щоб дізнатиÑÑŒ більше, переглÑньте %{notification_link}."
msgid "Customize colors"
+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 "Cycle Analytics"
@@ -1602,6 +2325,9 @@ msgstr "Staging"
msgid "CycleAnalyticsStage|Test"
msgstr "ТеÑтуваннÑ"
+msgid "Dashboard"
+msgstr "Панель керуваннÑ"
+
msgid "DashboardProjects|All"
msgstr "Ð’ÑÑ–"
@@ -1614,15 +2340,36 @@ msgstr "груд."
msgid "December"
msgstr "грудень"
+msgid "Decline and sign out"
+msgstr "Відхити та вийти"
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr "Визначте влаÑний шаблон за допомогою ÑинтакÑиÑу cron"
msgid "Delete"
msgstr "Видалити"
+msgid "Delete Snippet"
+msgstr "Видалити Ñніпет"
+
+msgid "Delete list"
+msgstr "Видалити ÑпиÑок"
+
+msgid "Deleted"
+msgstr "Видалено"
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "РозгортаннÑ"
@@ -1633,11 +2380,143 @@ msgstr[3] "Розгортань"
msgid "Deploy Keys"
msgstr "Ключі Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ"
+msgid "DeployKeys|+%{count} others"
+msgstr "+%{count} інших"
+
+msgid "DeployKeys|Current project"
+msgstr "Поточний проект"
+
+msgid "DeployKeys|Deploy key"
+msgstr "Ключ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ"
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr "Увімкнені ключі розгортаннÑ"
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr "Помилка при увімкненні ключа розгортаннÑ"
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr "Помилка при отриманні колючів розгортаннÑ"
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr "Помилка при видаленні ключа розгортаннÑ"
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr "Розгорнути %{count} інших проектів"
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÐºÐ¾Ð»ÑŽÑ‡Ñ–Ð² розгортаннÑ"
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr "Ðе знайдено ключів розгортаннÑ. Створюйте Ñ—Ñ… за допомогою форми вище."
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr "Приватні ключі розгортаннÑ"
+
+msgid "DeployKeys|Project usage"
+msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñƒ проектах"
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr "Публічні колючі розгортаннÑ"
+
+msgid "DeployKeys|Read access only"
+msgstr "ДоÑтуп тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ"
+
+msgid "DeployKeys|Write access allowed"
+msgstr "Дозволено доÑтуп Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу"
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr "Ви видалÑєте ключ розгортаннÑ. Ви впевнені?"
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr "Ðктивні токени Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ (%{active_tokens})"
+
+msgid "DeployTokens|Add a deploy token"
+msgstr "Додати токен розгортаннÑ"
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr "ДозволÑÑ” доÑтуп лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð´Ð¾ реєÑтру образів"
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr "ДозволÑÑ” доÑтуп лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð´Ð¾ репозиторіÑ"
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr "Скопіюйте токен Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð² буфер обміну"
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr "Скопіюйте ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача в буфер обміну"
+
+msgid "DeployTokens|Create deploy token"
+msgstr "Створити токен розгортаннÑ"
+
+msgid "DeployTokens|Created"
+msgstr "Створено"
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr "Токени РозгортаннÑ"
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr "Токени Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð°Ð´Ð°ÑŽÑ‚ÑŒ доÑтуп лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð´Ð¾ вашого репозиторію Ñ– образів у реєÑтрі."
+
+msgid "DeployTokens|Expires"
+msgstr "Термін дії"
+
+msgid "DeployTokens|Name"
+msgstr "Ðазва"
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr "Виберіть назву заÑтоÑунку Ñ– ми надамо вам унікальний токен розгортаннÑ."
+
+msgid "DeployTokens|Revoke"
+msgstr "Відкликати"
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr "Відкликати %{name}"
+
+msgid "DeployTokens|Scopes"
+msgstr "Межі"
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr "Ð¦Ñ Ð´Ñ–Ñ Ð½Ðµ може бути ÑкаÑована."
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr "Цей проект не має активних токенів розгортаннÑ."
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr "ВикориÑтовуйте цей токен Ñк пароль. ПереконайтеÑÑ, що ви його зберегли: ви більше не зможете отримати доÑтуп до нього."
+
+msgid "DeployTokens|Use this username as a login."
+msgstr "ВикориÑтовувати це ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Ñк логін."
+
+msgid "DeployTokens|Username"
+msgstr "Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
+
+msgid "DeployTokens|You are about to revoke"
+msgstr "Зараз ви відкличете"
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr "Ваш новий токер розгортаннÑ"
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr "Створено ваш новий токен Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ñƒ."
+
+msgid "Deprioritize label"
+msgstr "Зменшити пріоритет мітки"
+
+msgid "Descending"
+msgstr "За ÑпаданнÑм"
+
msgid "Description"
msgstr "ОпиÑ"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
-msgstr "Шаблони опиÑу дозволÑÑŽÑ‚ÑŒ визначити конкретні шаблони обговорень проблем та запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проекту."
+msgstr "Шаблони опиÑу дозволÑÑŽÑ‚ÑŒ визначити конкретні шаблони обговорень та запитів на Ð·Ð»Ð¸Ð²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проекту."
+
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
msgid "Details"
msgstr "Деталі"
@@ -1645,27 +2524,51 @@ msgstr "Деталі"
msgid "Diffs|No file name available"
msgstr "Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ не доÑтупне"
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr "Ім'Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ñƒ"
msgid "Disable"
msgstr "Вимкнути"
+msgid "Disable for this project"
+msgstr "Вимкнути Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
+
+msgid "Disable group Runners"
+msgstr "Вимкнути групові Runner'и"
+
+msgid "Discard changes"
+msgstr "Відхилити зміни"
+
msgid "Discard draft"
msgstr "Видалити чернетку"
msgid "Discover GitLab Geo."
msgstr "Відкрийте GitLab Geo."
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
-msgstr "Відмінити блок вÑтупу до Ðналитики Циклу"
+msgstr "Відхилити блок вÑтупу до Ðналитики Циклу"
msgid "Dismiss Merge Request promotion"
-msgstr "Вимкнути рекламу Ð´Ð»Ñ Ð—Ð°Ð¿Ð¸Ñ‚Ñ–Ð² на злиттÑ"
+msgstr ""
-msgid "Documentation for popular identity providers"
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
msgstr ""
+msgid "Documentation for popular identity providers"
+msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ Ð´Ð»Ñ Ð¿Ð¾ÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ… провайдерів ідентифікації"
+
+msgid "Domain"
+msgstr "Домен"
+
msgid "Don't show again"
msgstr "Ðе показувати знову"
@@ -1700,70 +2603,112 @@ msgid "DownloadSource|Download"
msgstr "Завантажити"
msgid "Downvotes"
-msgstr ""
+msgstr "Дизлайки"
msgid "Due date"
msgstr "Запланована дата завершеннÑ"
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
-msgstr ""
+msgstr "Під Ñ‡Ð°Ñ Ñ†ÑŒÐ¾Ð³Ð¾ процеÑу вам буде запропоновано вказати URL-адреÑи з боку GitLab. ВикориÑтовуйте URL-адреÑи, показані нижче."
+
+msgid "Each Runner can be in one of the following states:"
+msgstr "Кожен Runner може бути в одному із таких Ñтанів:"
msgid "Edit"
msgstr "Редагувати"
+msgid "Edit Label"
+msgstr "Редагувати мітку"
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "Редагувати Розклад Конвеєра %{id}"
+msgid "Edit Snippet"
+msgstr "Редагувати Ñніпет"
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr "Редагуйте файли в редакторі і закомітьте зміни тут"
-msgid "Editing"
+msgid "Edit group: %{group_name}"
msgstr ""
-msgid "Elasticsearch"
+msgid "Edit identity for %{user_name}"
msgstr ""
+msgid "Elasticsearch"
+msgstr "Elasticsearch"
+
msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
-msgstr ""
+msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ð· Elasticsearch. Elasticsearch AWS IAM."
msgid "Email"
+msgstr "Електронна пошта"
+
+msgid "Email patch"
msgstr ""
msgid "Emails"
msgstr "ÐдреÑи електронної пошти"
+msgid "Embed"
+msgstr "Вбудувати"
+
msgid "Enable"
msgstr "Увімкнути"
msgid "Enable Auto DevOps"
msgstr "Увімкнути Auto DevOps"
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
msgid "Enable Sentry for error reporting and logging."
-msgstr ""
+msgstr "Увімкнути Sentry Ð´Ð»Ñ Ð·Ð²Ñ–Ñ‚Ñ–Ð² про помилки та логуваннÑ."
msgid "Enable and configure InfluxDB metrics."
-msgstr ""
+msgstr "Включити і налаштувати метрики InfluxDB."
msgid "Enable and configure Prometheus metrics."
-msgstr ""
+msgstr "Включити і налаштувати метрики Prometheus."
msgid "Enable classification control using an external service"
msgstr ""
-msgid "Enable or disable version check and usage ping."
+msgid "Enable for this project"
+msgstr "Увімкнути Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
+
+msgid "Enable group Runners"
+msgstr "Увімкнути групові Runner'и"
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr "Увімкніть або вимкніть певні функції групи та виберіть рівні доÑтупу."
+
+msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
+msgid "Enable or disable version check and usage ping."
+msgstr "Увімкнути чи вимкнути перевірку верÑÑ–Ñ— та надÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… про викориÑтаннÑ."
+
msgid "Enable reCAPTCHA or Akismet and set IP limits."
-msgstr ""
+msgstr "Увімкнути reCAPTCHA або Akismet та вÑтановити Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð¾ IP."
msgid "Enable the Performance Bar for a given group."
-msgstr ""
+msgstr "Увімкнути панель продуктивноÑÑ‚Ñ– Ð´Ð»Ñ Ð´Ð°Ð½Ð¾Ñ— групи."
msgid "Enabled"
-msgstr ""
+msgstr "Увімкнено"
+
+msgid "Ends at (UTC)"
+msgstr "ЗавершуєтьÑÑ Ð¾ (за Грінвічем)"
+
+msgid "Environments"
+msgstr "Середовища"
msgid "Environments|An error occurred while fetching the environments."
msgstr "Виникла помилка при завантаженні Ñередовищ."
@@ -1771,9 +2716,18 @@ msgstr "Виникла помилка при завантаженні Ñеред
msgid "Environments|An error occurred while making the request."
msgstr "Під Ñ‡Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°."
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr "Коміт"
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr "РозгортаннÑ"
@@ -1786,33 +2740,54 @@ msgstr "Середовища"
msgid "Environments|Job"
msgstr "ЗавданнÑ"
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr "Ðове Ñередовище"
msgid "Environments|No deployments yet"
msgstr "Ще немає розгортань"
-msgid "Environments|Open"
-msgstr "Відкрити"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr ""
-msgid "Environments|Re-deploy"
-msgstr "Повторно розгорнути"
+msgid "Environments|Re-deploy to environment"
+msgstr ""
msgid "Environments|Read more about environments"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñередовища"
-msgid "Environments|Rollback"
-msgstr "Відкотити"
+msgid "Environments|Rollback environment"
+msgstr ""
msgid "Environments|Show all"
msgstr "Показати вÑÑ–"
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Оновлено"
msgid "Environments|You don't have any environments right now."
msgstr "Ви поки не налаштували жодного Ñередовища."
+msgid "Epic"
+msgstr "Епік"
+
msgid "Epic will be removed! Are you sure?"
msgstr "Епік буде видалено! Ви впевнені?"
@@ -1826,16 +2801,10 @@ msgid "Epics let you manage your portfolio of projects more efficiently and with
msgstr "Епіки дозволÑÑŽÑ‚ÑŒ керувати вашим портфелем проектів ефективніше та з меншими зуÑиллÑми"
msgid "Error Reporting and Logging"
-msgstr ""
-
-msgid "Error checking branch data. Please try again."
-msgstr "Помилка при отриманні даних гілки. Будь лаÑка, Ñпробуйте пізніше."
-
-msgid "Error committing changes. Please try again."
-msgstr "Помилка при коміті змін. Будь лаÑка, Ñпробуйте пізніше."
+msgstr "Звіти про помилки та логуваннÑ"
msgid "Error creating epic"
-msgstr "Помилка при Ñтворенні епіку"
+msgstr ""
msgid "Error fetching contributors data."
msgstr "Помилка Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… учаÑників."
@@ -1850,7 +2819,22 @@ msgid "Error fetching refs"
msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ refs"
msgid "Error fetching usage ping data."
-msgstr "Помилка при отриманні данних по перевірці з’єднаннÑ."
+msgstr "Помилка при отриманні данних про викориÑтаннÑ."
+
+msgid "Error loading branch data. Please try again."
+msgstr "Помилка при завантаженні даних про гілку. Будь лаÑка, Ñпробуйте знову."
+
+msgid "Error loading last commit."
+msgstr "Помилка при завантаженні оÑтаннього коміту."
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr "Помилка при завантаженні запитів на злиттÑ."
+
+msgid "Error loading project data. Please try again."
+msgstr "Помилка при завантаженні даних проекту. Будь лаÑка, Ñпробуйте знову."
msgid "Error occurred when toggling the notification subscription"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñки на ÑповіщеннÑ"
@@ -1864,6 +2848,9 @@ msgstr "Помилка Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑтатуÑу Ð´Ð»Ñ Ð²ÑÑ–Ñ… задÐ
msgid "Error updating todo status."
msgstr "Помилка при оновленні ÑтатуÑу задачі."
+msgid "Estimated"
+msgstr "За оцінками"
+
msgid "EventFilterBy|Filter by all"
msgstr "Фільтрувати по вÑім"
@@ -1891,9 +2878,30 @@ msgstr "Кожен міÑÑць (1-го чиÑла о 4:00 ранку)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Ð©Ð¾Ñ‚Ð¸Ð¶Ð½Ñ (в неділю о 4:00 ранку)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr "Розгорнути"
+msgid "Expand all"
+msgstr "Розгорнути вÑе"
+
+msgid "Expand sidebar"
+msgstr "Розгорніть бічну панель"
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr "ОглÑд груп"
+
msgid "Explore projects"
msgstr "ОглÑд проектів"
@@ -1901,16 +2909,16 @@ msgid "Explore public groups"
msgstr "ПереглÑнути публічні групи"
msgid "External Classification Policy Authorization"
-msgstr "ÐÐ²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð½Ð¾ до зовнішньої політики"
+msgstr ""
msgid "External authentication"
-msgstr ""
+msgstr "Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ"
msgid "External authorization denied access to this project"
msgstr "Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ Ð·Ð°Ð±Ð¾Ñ€Ð¾Ð½Ð¸Ð»Ð° доÑтуп до цього проекту"
msgid "External authorization request timeout"
-msgstr ""
+msgstr "Ð§Ð°Ñ Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на зовнішню авторизацію"
msgid "ExternalAuthorizationService|Classification Label"
msgstr "Мітка клаÑифікації"
@@ -1921,15 +2929,21 @@ msgstr "Мітка клаÑифікації"
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr "Якщо клаÑифікаційну мітку не вÑтановлено, викориÑтовуватиметьÑÑ Ñтандартна мітка `%{default_label}`."
+msgid "Facebook"
+msgstr "Facebook"
+
msgid "Failed"
msgstr "Ðевдало"
msgid "Failed Jobs"
-msgstr ""
+msgstr "Провалені завданнÑ"
msgid "Failed to change the owner"
msgstr "Ðе вдалоÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ влаÑника"
+msgid "Failed to check related branches."
+msgstr "Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ пов’Ñзані гілки."
+
msgid "Failed to remove issue from board, please try again."
msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ проблему з дошки, будь лаÑка, Ñпробуйте ще раз."
@@ -1939,6 +2953,12 @@ msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ розклад конвеєра"
msgid "Failed to update issues, please try again."
msgstr "Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ проблеми. Будь лаÑка, Ñпробуйте ще раз."
+msgid "Failure"
+msgstr "Помилка"
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr "лют."
@@ -1948,9 +2968,6 @@ msgstr "лютий"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "ÐŸÐ¾Ð»Ñ Ð½Ð° цій Ñторінці зараз недоÑтупні Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ, ви можете налаштувати"
-msgid "File name"
-msgstr "Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ"
-
msgid "Files"
msgstr "Файли"
@@ -1958,6 +2975,9 @@ msgid "Files (%{human_size})"
msgstr "Файли (%{human_size})"
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr "Заповніть Ð¿Ð¾Ð»Ñ Ð½Ð¸Ð¶Ñ‡Ðµ, увімкніть <strong>%{enable_label}</strong> та натиÑніть <strong>%{save_changes}</strong>"
+
+msgid "Filter"
msgstr ""
msgid "Filter by commit message"
@@ -1969,6 +2989,12 @@ msgstr "Пошук по шлÑху"
msgid "Find file"
msgstr "Знайти файл"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr "Завершено"
@@ -1978,10 +3004,37 @@ msgstr "Перший"
msgid "FirstPushedBy|pushed by"
msgstr "відправлено"
-msgid "Font Color"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr "Імпорт з FogBugz"
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr "Імпорт з FogBugz"
+
+msgid "Follow the steps below to export your Google Code project data."
msgstr ""
+msgid "Font Color"
+msgstr "Колір шрифту"
+
msgid "Footer message"
+msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² футері"
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
msgid "Fork"
@@ -2003,9 +3056,24 @@ msgstr "ВідбуваєтьÑÑ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ„Ð¾Ñ€ÐºÑƒ"
msgid "Format"
msgstr "Формат"
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr "Знайдено помилки у вашому .gitlab-ci.yml:"
+
msgid "From %{provider_title}"
msgstr "З %{provider_title}"
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr "З моменту ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ до Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
@@ -2018,14 +3086,20 @@ msgstr "Із Ñторінки деталей Kubernetes-клаÑтера, вÑÑ‚
msgid "GPG Keys"
msgstr "GPG ключі"
+msgid "General"
+msgstr "Загальні"
+
+msgid "General pipelines"
+msgstr "Загальні конвеєри"
+
msgid "Generate a default set of labels"
-msgstr "Створити Ñтандартний набір міткок"
+msgstr "Створити Ñтандартний набір міток"
msgid "Geo Nodes"
msgstr "Гео-Вузли"
msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
-msgstr ""
+msgstr "Geo дозволÑÑ” вам реплікувати ваш інÑÑ‚Ð°Ð½Ñ GitLab у інших географічних локаціÑÑ…."
msgid "GeoNodeSyncStatus|Node is failing or broken."
msgstr "Вузол не працює або зламаний."
@@ -2036,8 +3110,11 @@ msgstr "Вузол працює повільно, перевантажений Ð
msgid "GeoNodes|Checksummed"
msgstr "Із контрольною Ñумою"
-msgid "GeoNodes|Database replication lag:"
-msgstr "Затримка реплікації бази даних:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr "Дані заÑтаріли на %{timeago}"
+
+msgid "GeoNodes|Data replication lag"
+msgstr "Затримка реплікації даних"
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
msgstr "Ð’Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð²ÑƒÐ·Ð»Ð° зупинÑÑ” Ð¿Ñ€Ð¾Ñ†ÐµÑ Ñинхронізації. Ви впевнені?"
@@ -2051,32 +3128,44 @@ msgstr "Ðевдало"
msgid "GeoNodes|Full"
msgstr "Повний"
+msgid "GeoNodes|GitLab version"
+msgstr "ВерÑÑ–Ñ GitLab"
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr "ВерÑÑ–Ñ GitLab не відповідає верÑÑ–Ñ— оÑновного вузла"
-msgid "GeoNodes|GitLab version:"
-msgstr "ВерÑÑ–Ñ GitLab:"
+msgid "GeoNodes|Health status"
+msgstr "Стан працездатноÑÑ‚Ñ–"
-msgid "GeoNodes|Health status:"
-msgstr "Стан працездатноÑÑ‚Ñ–:"
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr "ОÑтанній ідентифікатор події, оброблений курÑором"
-msgid "GeoNodes|Last event ID processed by cursor:"
-msgstr "Ідентифікатор оÑтанньої події, обробленої курÑором:"
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr "ОÑтанній ідентифікатор події, видимий з оÑновного"
-msgid "GeoNodes|Last event ID seen from primary:"
-msgstr "Ідентифікатор оÑтанньої події видимої із оÑновного:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñтан обчиÑÐ»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ñ— Ñуми репозиторію"
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про перевірку репозиторію"
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñтан обчиÑÐ»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ñ— Ñуми wiki"
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про перевірку wiki"
msgid "GeoNodes|Loading nodes"
msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð²ÑƒÐ·Ð»Ñ–Ð²"
-msgid "GeoNodes|Local Attachments:"
-msgstr "Локальні Ð²ÐºÐ»Ð°Ð´ÐµÐ½Ð½Ñ (attachments):"
+msgid "GeoNodes|Local LFS objects"
+msgstr "Локальні LFS-об’єкти"
-msgid "GeoNodes|Local LFS objects:"
-msgstr "Локальні LFS-об’єкти:"
+msgid "GeoNodes|Local attachments"
+msgstr "Локальні додатки"
-msgid "GeoNodes|Local job artifacts:"
-msgstr "Ðртефакти локальних завдань:"
+msgid "GeoNodes|Local job artifacts"
+msgstr "Ðртефакти локальних завдань"
msgid "GeoNodes|New node"
msgstr "Ðовий вузол"
@@ -2096,20 +3185,26 @@ msgstr "РозÑинхронізовані"
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²ÑƒÐ·Ð»Ð° зупинÑÑ” Ð¿Ñ€Ð¾Ñ†ÐµÑ Ñинхронізації. Ви впевнені?"
-msgid "GeoNodes|Replication slot WAL:"
-msgstr "Слот реплікації WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr "Слот реплікації WAL"
+
+msgid "GeoNodes|Replication slots"
+msgstr "Слоти реплікації"
+
+msgid "GeoNodes|Repositories"
+msgstr "Репозиторії"
-msgid "GeoNodes|Replication slots:"
-msgstr "Слоти реплікації:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr "Контрольні Ñуми репозиторіїв обчиÑлено Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ Ñ—Ñ… копій на вторинних вузлах"
-msgid "GeoNodes|Repositories checksummed:"
-msgstr "Репозиторії із контрольними Ñумами:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr "Репозиторії перевірено із їхніми копіÑми на первинному вузлі"
-msgid "GeoNodes|Repositories:"
-msgstr "Репозиторії:"
+msgid "GeoNodes|Repository checksum progress"
+msgstr "Стан обчиÑÐ»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ñ— Ñуми репозиторію"
-msgid "GeoNodes|Repository checksums verified:"
-msgstr "Репозиторії із підтвердженою контрольною Ñумою:"
+msgid "GeoNodes|Repository verification progress"
+msgstr "Стан перевірки репозиторію"
msgid "GeoNodes|Selective"
msgstr "Вибіркові"
@@ -2117,17 +3212,20 @@ msgstr "Вибіркові"
msgid "GeoNodes|Something went wrong while changing node status"
msgstr "Проблема при зміні ÑтатуÑа вузла"
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr "Проблема при отриманні вузлів"
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr "Проблема при видаленні вузла"
msgid "GeoNodes|Something went wrong while repairing node"
msgstr "Проблема при полагодженні вузла"
-msgid "GeoNodes|Storage config:"
-msgstr "ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ñховища:"
+msgid "GeoNodes|Storage config"
+msgstr "ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ñховища"
-msgid "GeoNodes|Sync settings:"
-msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñинхронізації:"
+msgid "GeoNodes|Sync settings"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñинхронізації"
msgid "GeoNodes|Synced"
msgstr "Синхронізовано"
@@ -2144,14 +3242,20 @@ msgstr "ВикориÑтані Ñлоти"
msgid "GeoNodes|Verified"
msgstr "Підтверджені"
-msgid "GeoNodes|Wiki checksums verified:"
-msgstr "Wiki репозиторії із підтвердженою контрольною Ñумою:"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr "Стан обчиÑÐ»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ñ— Ñуми wiki"
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr "Стан перевірки wiki"
+
+msgid "GeoNodes|Wikis"
+msgstr "Wiki"
-msgid "GeoNodes|Wikis checksummed:"
-msgstr "Wiki із контрольною Ñумою:"
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr "Контрольні Ñуми wiki обчиÑлено Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ Ñ—Ñ… копій на вторинних вузлах"
-msgid "GeoNodes|Wikis:"
-msgstr "Wiki:"
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr "Wiki перевірено із їхніми копіÑми на первинному вузлі"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr "Ви налаштували Geo-вузли через незахищене HTTP-з’єднаннÑ. Ми рекомендуємо викориÑтовувати HTTPS."
@@ -2180,6 +3284,12 @@ msgstr "Виберіть групи Ð´Ð»Ñ Ñ€ÐµÐ¿Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ—."
msgid "Geo|Shards to synchronize"
msgstr "Сегменти Ð´Ð»Ñ Ñинхронізації"
+msgid "Geo|Verification capacity"
+msgstr "ПропуÑкна здатніÑÑ‚ÑŒ перевірки"
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr "URL Git-репозиторіÑ"
@@ -2189,6 +3299,9 @@ msgstr "Git-редакціÑ"
msgid "Git storage health information has been reset"
msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ÑÑ‚Ð°Ñ‚ÑƒÑ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Git була Ñкинута"
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr "Git-верÑÑ–Ñ"
@@ -2196,40 +3309,106 @@ msgid "GitHub import"
msgstr "GitHub-імпорт"
msgid "GitLab CI Linter has been moved"
-msgstr ""
+msgstr "GitLab CI Linter був перенеÑений"
msgid "GitLab Geo"
+msgstr "GitLab Geo"
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr "Групові Runner'и Gitlab можуть виконувати код Ð´Ð»Ñ ÑƒÑÑ–Ñ… проектів у цій групі."
+
+msgid "GitLab Import"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "Розділ GitLab Runner"
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
+msgstr "URL єдиного входу GitLab"
+
+msgid "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."
msgstr ""
-msgid "Gitaly"
+msgid "GitLab.com import"
msgstr ""
+msgid "Gitaly"
+msgstr "Gitaly"
+
msgid "Gitaly Servers"
msgstr "Сервери Gitaly"
+msgid "Gitaly|Address"
+msgstr "ÐдреÑа"
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr "ПовернутиÑÑ"
+
msgid "Go back"
msgstr "ПовернутиÑÑ"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "Перейти до вашого форку"
msgid "GoToYourFork|Fork"
msgstr "Форк"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "ÐÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Google не %{link_to_documentation}. ПопроÑÑ–Ñ‚ÑŒ Ñвого адмініÑтратора GitLab, Ñкщо ви хочете ÑкориÑтатиÑÑ Ñ†Ð¸Ð¼ ÑервіÑом."
msgid "Got it!"
msgstr "Зрозуміло!"
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr "Епіки дозволють вам керувати портфоліо ваших проектів більш ефективно Ñ– з меншими зуÑиллÑми"
+msgid "Graph"
+msgstr "Графік"
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD групи"
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr "Ідентифікатор групи"
+
+msgid "Group Runners"
+msgstr "Групові Runner'и"
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr "Керівники групи можуть зареєÑтрувати групові runner'и через %{link}"
+
+msgid "Group: %{group_name}"
+msgstr "Група: %{group_name}"
msgid "GroupRoadmap|From %{dateWord}"
msgstr "Від %{dateWord}"
@@ -2240,8 +3419,29 @@ msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ð»Ð°Ð½Ñƒ-графіку"
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "Проблема при завантаженні епіків"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду плану-графіку додайте заплановані дати початку та Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ одного з ваших епіків у цій групі або Ñ—Ñ— підгрупах. ВідображаютьÑÑ Ð»Ð¸ÑˆÐµ епіки за попередні та наÑтупні 3 міÑÑці: від %{startDate} до %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr "Вибачте, жоден епік не задовольнÑÑ” критеріÑм вашого пошуку"
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr "План-графік епіків відображає Ñтан ваших епіків у чаÑÑ–"
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду плану-графіку додайте заплановані дати початку та Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ одного з ваших епіків у цій групі або Ñ—Ñ— підгрупах. При поміÑÑчному переглÑді показуютьÑÑ Ð»Ð¸ÑˆÐµ епіки за попередній, поточний та наÑтупні 5 міÑÑців: від %{startDate} до %{endDate}."
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду плану-графіку додайте заплановані дати початку та Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ одного з ваших епіків у цій групі або Ñ—Ñ— підгрупах. При поквартальному переглÑді показуютьÑÑ Ð»Ð¸ÑˆÐµ епіки за попередній, поточний та наÑтупні 4 квартали: від %{startDate} до %{endDate}."
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
msgstr "До %{dateWord}"
@@ -2270,6 +3470,33 @@ msgstr "не може бути ÑкаÑовано поки \"БлокуваннÑ
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr "Видалити Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñпільного доÑтупу з іншими групами з %{ancestor_group_name}"
+msgid "Groups"
+msgstr "Групи"
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr "Група — набір із декількох проектів."
@@ -2313,7 +3540,7 @@ msgid "Have your users email"
msgstr "Електронна пошта Ð´Ð»Ñ Ð·Ð²ÐµÑ€Ñ‚Ð°Ð½ÑŒ кориÑтувачів"
msgid "Header message"
-msgstr ""
+msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°"
msgid "Health Check"
msgstr "Перевірка ПрацездатноÑÑ‚Ñ–"
@@ -2334,13 +3561,13 @@ msgid "HealthCheck|Unhealthy"
msgstr "Ðездоровий"
msgid "Help"
-msgstr ""
+msgstr "Допомога"
msgid "Help page"
-msgstr ""
+msgstr "Сторінка довідки"
msgid "Help page text and support page url."
-msgstr ""
+msgstr "ТекÑÑ‚ Ñторінки довідки та url-адреÑа Ñторінки підтримки."
msgid "Hide value"
msgid_plural "Hide values"
@@ -2349,18 +3576,57 @@ msgstr[1] "Сховати значеннÑ"
msgstr[2] "Сховати значень"
msgstr[3] "Сховати значень"
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr "ІÑторіÑ"
msgid "Housekeeping successfully started"
msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ ÑƒÑпішно розпочато"
+msgid "I accept the %{terms_link}"
+msgstr "Я приймаю %{terms_link}"
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr "Я погоджуюÑÑŒ з Правилами кориÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑервіÑом Ñ– політикою конфіденційноÑÑ‚Ñ–"
+
+msgid "ID"
+msgstr "ID"
+
+msgid "IDE|Commit"
+msgstr "Коміт"
+
+msgid "IDE|Edit"
+msgstr "Редагувати"
+
+msgid "IDE|Go back"
+msgstr "ПовернутиÑÑ Ð½Ð°Ð·Ð°Ð´"
+
+msgid "IDE|Open in file view"
+msgstr "Відкрити Ñк файл"
+
+msgid "IDE|Review"
+msgstr "ОглÑд"
+
+msgid "Identifier"
+msgstr "Ідентифікатор"
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
+msgstr "URL єдиного входу провайдера ідентифікації"
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
-msgid "If enabled, access to projects will be validated on an external service using their classification label."
+msgid "If enabled"
msgstr ""
+msgid "If enabled, access to projects will be validated on an external service using their classification label."
+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}"
@@ -2370,15 +3636,54 @@ msgstr "Якщо у Ð²Ð°Ñ ÑƒÐ¶Ðµ Ñ” файли, ви можете відпраÐ
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr "Якщо ваш HTTP-репозиторій не Ñ” публічним, додайте дані Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— до URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgid "ImageDiffViewer|2-up"
+msgstr "2 поруч"
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr "Ðакладені (прозоріÑÑ‚ÑŒ)"
+
+msgid "ImageDiffViewer|Swipe"
+msgstr "Ðакладені (проведеннÑ)"
+
msgid "Import"
msgstr "Імпорт"
+msgid "Import Projects from Gitea"
+msgstr "Імпортувати проекти з Gitea"
+
+msgid "Import all compatible projects"
+msgstr "Імпорт вÑÑ–Ñ… ÑуміÑних проектів"
+
+msgid "Import all projects"
+msgstr "Імпортувати вÑÑ– проекти"
+
msgid "Import all repositories"
msgstr "Імпорт вÑÑ–Ñ… репозиторіїв"
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr "Імпорт триває"
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr "Імпорт репозиторіїв з GitHub"
@@ -2389,7 +3694,7 @@ msgid "ImportButtons|Connect repositories from"
msgstr "Підключити репозиторії із"
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr "Покращити дошки обговорень проблем за допомогою верÑÑ–Ñ— GitLab Enterprise Edition."
+msgstr "Покращити дошки обговорень за допомогою верÑÑ–Ñ— GitLab Enterprise Edition."
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
msgstr "Покращити ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð°Ð¼Ð¸ з можливіÑÑ‚ÑŽ Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð°Ð³Ð¸ проблеми за допомогою GitLab Enterprise Edition."
@@ -2397,16 +3702,28 @@ msgstr "Покращити ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð°Ð¼Ð¸ з можл
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr "Покращити пошук за допомогою розширеного глобального пошук в верÑÑ–Ñ— GitLab Enterprise Edition."
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr "Включити угоду про Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг та правила конфіденційноÑÑ‚Ñ–, Ñкі повинні прийнÑти вÑÑ– кориÑтувачі."
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr "Вбудований"
+
+msgid "Install GitLab Runner"
+msgstr ""
+
msgid "Install Runner on Kubernetes"
msgstr "Ð’Ñтановити Runner на Kubernetes"
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "Ð’Ñтановіть Runner, ÑуміÑний з GitLab CI"
-
msgid "Instance"
msgid_plural "Instances"
msgstr[0] "ІнÑтанÑ"
-msgstr[1] "IнÑтанÑи"
+msgstr[1] "ІнÑтанÑів"
msgstr[2] "ІнÑтанÑів"
msgstr[3] "ІнÑтанÑів"
@@ -2416,6 +3733,9 @@ msgstr "Цей інÑÑ‚Ð°Ð½Ñ Ð½Ðµ підтримує декілька Kubernete
msgid "Integrations"
msgstr "Інтеграції"
+msgid "Integrations Settings"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ—"
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "Зацікавлені Ñторони за бажаннÑм можуть навіть робити внеÑки шлÑхом Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð²."
@@ -2431,6 +3751,9 @@ msgstr "Шаблон інтервалу"
msgid "Introducing Cycle Analytics"
msgstr "ПредÑтавлÑємо аналітику циклу"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr "Режим фокуÑÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð´Ð¾ÑˆÐºÐ¸ обговорень"
@@ -2449,12 +3772,21 @@ msgstr "Проблеми"
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr "Проблеми можуть бути помилками, завданнÑми чи ідеÑми Ð´Ð»Ñ Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ. Крім того, проблеми доÑтупні Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ та фільтруваннÑ."
+msgid "Issues closed"
+msgstr "Проблеми закриті"
+
msgid "Jan"
msgstr "Ñіч."
msgid "January"
msgstr "Ñічень"
+msgid "Job"
+msgstr "ЗавданнÑ"
+
+msgid "Job has been erased"
+msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ Ñтерте"
+
msgid "Jobs"
msgstr "ЗавданнÑ"
@@ -2471,6 +3803,9 @@ msgid "June"
msgstr "червень"
msgid "Koding"
+msgstr "Koding"
+
+msgid "Koding Dashboard"
msgstr ""
msgid "Kubernetes"
@@ -2497,6 +3832,9 @@ msgstr "Kubernetes налаштовано"
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr "СпоÑіб Інтеграції Kubernetes Ñк ÑервіÑа заÑтарів. %{deprecated_message_content} ваші Kubernetes-клаÑтери за допомогою нової Ñторінки <a href=\"%{url}\"/>КлаÑтери Kubernetes</a>"
+msgid "LFS"
+msgstr "LFS"
+
msgid "LFSStatus|Disabled"
msgstr "Вимкнено"
@@ -2506,12 +3844,21 @@ msgstr "Увімкнено"
msgid "Label"
msgstr "Мітка"
+msgid "Label actions dropdown"
+msgstr "Випадаючий ÑпиÑок дій із мітками"
+
+msgid "Label lists show all issues with the selected label."
+msgstr "Ð’ ÑпиÑках на оÑнові міток відображаютьÑÑ Ð»Ð¸ÑˆÐµ Ñ‚Ñ– проблеми, Ñкі мають вибрану мітку."
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr "%{firstLabelName} + %{remainingLabelCount} ще"
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr "%{labelsString} і %{remainingLabelCount} ще"
+msgid "LabelSelect|Labels"
+msgstr "Мітки"
+
msgid "Labels"
msgstr "Мітки"
@@ -2521,11 +3868,14 @@ msgstr "Мітки можуть бути заÑтоÑовані до %{features}
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "Мітки можуть бути заÑтоÑовані до проблем та запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ñ—Ñ… категоризації."
+msgid "Labels can be applied to issues and merge requests."
+msgstr "Мітки можуть бути заÑтоÑовані до проблем та запитів на злиттÑ."
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
-msgstr ""
+msgstr "<span>ПеренеÑти мітку</span> %{labelTitle} <span>на рівень групи?</span>"
msgid "Labels|Promote Label"
-msgstr "Підвищити мітку"
+msgstr "ПеренеÑти мітку"
msgid "Last %d day"
msgid_plural "Last %d days"
@@ -2558,6 +3908,9 @@ msgstr "Ви відправили зміни до"
msgid "LastPushEvent|at"
msgstr "в"
+msgid "Latest changes"
+msgstr "ОÑтанні зміни"
+
msgid "Learn more"
msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ"
@@ -2582,18 +3935,36 @@ msgstr "Залишити групу"
msgid "Leave project"
msgstr "Залишити проект"
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
msgstr "ЛіцензіÑ"
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr "СпиÑок"
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr "СпиÑок ваших репозиторіїв GitHub"
+msgid "Loading contribution stats for group members"
+msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÑтатиÑтики учаÑників групи"
+
msgid "Loading the GitLab IDE..."
msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ IDE GitLab..."
+msgid "Loading..."
+msgstr "ЗавантаженнÑ..."
+
msgid "Lock"
msgstr "БлокуваннÑ"
@@ -2603,22 +3974,43 @@ msgstr "Заблокувати %{issuableDisplayName}"
msgid "Lock not found"
msgstr "Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ðµ знайдено"
+msgid "Lock to current projects"
+msgstr "Закріпити за поточними проектами"
+
msgid "Locked"
msgstr "Заблоковано"
msgid "Locked Files"
msgstr "Заблоковані файли"
+msgid "Locked to current projects"
+msgstr "Закріплено за поточними проектами"
+
msgid "Locks give the ability to lock specific file or folder."
msgstr "Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ð¾Ð¶Ðµ бути заÑтоÑоване до конкретного файлу або директорії."
-msgid "Login"
-msgstr "Вхід"
+msgid "Logs"
+msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr "Зробіть кожного учаÑника команди більш продуктивним незалежно від його міÑцезнаходженнÑ. GitLab Geo Ñтворює копії \"тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ\" вашого GitLab Ñервера, щоб Ñкоротити Ñ‡Ð°Ñ Ð´Ð»Ñ ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ– Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ з великих репозиторіїв."
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
+msgstr "Керувати вÑіма ÑповіщеннÑми"
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
msgstr ""
msgid "Manage group labels"
@@ -2631,6 +4023,24 @@ msgid "Manage project labels"
msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ñ–Ñ‚ÐºÐ°Ð¼Ð¸ проекту"
msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr "Керуйте членÑтвом у вашій групі додаючи ще один рівень безпеки із SAML."
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
msgstr ""
msgid "Mar"
@@ -2639,8 +4049,11 @@ msgstr "бер."
msgid "March"
msgstr "березень"
-msgid "Mark done"
-msgstr "Позначити виконаним"
+msgid "Mark todo as done"
+msgstr "Відмітити Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¸Ð¼"
+
+msgid "Markdown enabled"
+msgstr "Markdown увімкнено"
msgid "Maximum git storage failures"
msgstr "МакÑимальна кількіÑÑ‚ÑŒ невдач в Ñховищі даних git"
@@ -2655,47 +4068,92 @@ msgid "Members"
msgstr "КориÑтувачі"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
-msgstr ""
+msgstr "УчаÑники будуть перенаправлені Ñюди, коли будуть заходити до вашої групи. Отримайте його від Ñвого провайдера ідентифікації, де вона також може називатиÑÑ \"SSO Service Location\", \"SAML Token Issuance Endpoint\", або \"SAML 2.0/W-Federation URL\"."
+
+msgid "Merge Request"
+msgstr "Запит на злиттÑ"
+
+msgid "Merge Request:"
+msgstr "Запит на злиттÑ:"
msgid "Merge Requests"
msgstr "Запити на злиттÑ"
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr "Події злиттÑ"
msgid "Merge request"
msgstr "Запит на злиттÑ"
+msgid "Merge request approvals"
+msgstr "Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² на злиттÑ"
+
+msgid "Merge requests"
+msgstr "Запити на злиттÑ"
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ â€” це ÑпоÑіб запропонувати Ñвої зміни до проекту Ñ– обговорити Ñ—Ñ… із іншими"
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr "ПереглÑнути файл @ %{commitId}"
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr "Злито"
msgid "Messages"
msgstr "ПовідомленнÑ"
+msgid "Metrics"
+msgstr "Метрики"
+
msgid "Metrics - Influx"
-msgstr ""
+msgstr "Метрики - Influx"
msgid "Metrics - Prometheus"
-msgstr ""
+msgstr "Метрики - Prometheus"
msgid "Metrics|Business"
msgstr "БізнеÑ"
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr "Створити метрику"
msgid "Metrics|Edit metric"
msgstr "Редагувати метрику"
+msgid "Metrics|Environment"
+msgstr "Середовище"
+
msgid "Metrics|For grouping similar metrics"
msgstr "Ð”Ð»Ñ Ð³Ñ€ÑƒÐ¿ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð´Ñ–Ð±Ð½Ð¸Ñ… метрик"
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr "Ðазва вертикальної оÑÑ– графіка. Зазвичай це — одиниці вимірюваннÑ. Горизонтальна віÑÑŒ (віÑÑŒ X) завжди відображає чаÑ."
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr "Заголовок легенди (необов’Ñзковий)"
@@ -2708,6 +4166,9 @@ msgstr "Ім'Ñ"
msgid "Metrics|New metric"
msgstr "Ðова метрика"
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ Ð¿Ð¾ запитам Prometheus"
@@ -2720,9 +4181,27 @@ msgstr "Відповідь"
msgid "Metrics|System"
msgstr "СиÑтема"
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr "Тип"
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr "Одиниці вимірюваннÑ"
@@ -2753,6 +4232,9 @@ msgstr "напр. зап/Ñек"
msgid "Milestone"
msgstr "Етап"
+msgid "Milestones"
+msgstr "Етапи"
+
msgid "Milestones|Delete milestone"
msgstr "Видалити етап"
@@ -2766,13 +4248,13 @@ msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "Етап %{milestoneTitle} не знайдено"
msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
-msgstr "Підвищити %{milestoneTitle} до групового етапу?"
+msgstr "ПеренеÑти %{milestoneTitle} на рівень групи?"
msgid "Milestones|Promote Milestone"
-msgstr "Підвищити Етап"
+msgstr "ПеренеÑти етап"
msgid "Milestones|This action cannot be reversed."
-msgstr ""
+msgstr "Цю дію не можна ÑкаÑувати."
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "не додаÑте SSH ключ"
@@ -2786,6 +4268,15 @@ msgstr "Закрити"
msgid "Monitoring"
msgstr "Моніторинг"
+msgid "Months"
+msgstr "МіÑÑці"
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr "Додаткові дії"
+
msgid "More info"
msgstr "Детальніше"
@@ -2795,6 +4286,9 @@ msgstr "Детальніше"
msgid "More information is available|here"
msgstr "тут"
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr "ПереміÑтити"
@@ -2804,9 +4298,45 @@ msgstr "ПереміÑтити проблему"
msgid "Multiple issue boards"
msgstr "Кілька дошок обговореннÑ"
+msgid "Name"
+msgstr "Ім’Ñ"
+
msgid "Name new label"
msgstr "Ðазвіть нову мітку"
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr "Допомога"
+
+msgid "Nav|Home"
+msgstr "Додому"
+
+msgid "Nav|Sign In / Register"
+msgstr "Увійти / зареєÑтруватиÑÑ"
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr "Вийти із зайти під інший обліковим запиÑом"
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr "Ðовий"
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr "Ðова ідентичніÑÑ‚ÑŒ"
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Ðова проблема"
@@ -2814,15 +4344,18 @@ msgstr[1] "Ðові проблеми"
msgstr[2] "Ðових проблем"
msgstr[3] "Ðових проблем"
-msgid "New Kubernetes Cluster"
-msgstr "Ðовий Kubernetes-клаÑтер"
-
-msgid "New Kubernetes cluster"
-msgstr "Ðовий Kubernetes-клаÑтер"
+msgid "New Label"
+msgstr "Ðова мітка"
msgid "New Pipeline Schedule"
msgstr "Ðовий розклад Конвеєра"
+msgid "New Snippet"
+msgstr "Ðовий Ñніпет"
+
+msgid "New Snippets"
+msgstr "Ðові Ñніпети"
+
msgid "New branch"
msgstr "Ðова гілка"
@@ -2841,6 +4374,9 @@ msgstr "Ðовий файл"
msgid "New group"
msgstr "Ðова група"
+msgid "New identity"
+msgstr "Ðова ідентичніÑÑ‚ÑŒ"
+
msgid "New issue"
msgstr "Ðова проблема"
@@ -2850,6 +4386,9 @@ msgstr "Ðова мітка"
msgid "New merge request"
msgstr "Ðовий запит на злиттÑ"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr "Ðовий проект"
@@ -2865,6 +4404,12 @@ msgstr "Ðова підгрупа"
msgid "New tag"
msgstr "Ðовий тег"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr "ÐÑ–"
+
msgid "No Label"
msgstr "Без Мітки"
@@ -2886,14 +4431,47 @@ msgstr "Ðемає запланованого або витраченого ча
msgid "No file chosen"
msgstr "Файл не вибрано"
-msgid "No labels created yet."
-msgstr "Мітки ще не Ñтворені."
+msgid "No files found"
+msgstr "Ðе знайдено жодного файлу"
+
+msgid "No files found."
+msgstr "Ðе знайдено жодного файлу."
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr "Ðе знайдено жодного запиту на злиттÑ"
+
+msgid "No messages were logged"
+msgstr "Ðемає повідомлень у журналі"
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr ""
msgid "No repository"
msgstr "Ðемає репозиторію"
msgid "No schedules"
-msgstr "немає Розкладів"
+msgstr "Ðемає розкладів"
+
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
msgid "None"
msgstr "Ðемає"
@@ -2920,17 +4498,20 @@ msgid "Note that the master branch is automatically protected. %{link_to_protect
msgstr "Майте на увазі, що гілка master захищена автоматично. %{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr "Примітка: Ñк адмініÑтратор ви можете налаштувати %{github_integration_link}, що дозволить входити через GitHub Ñ– підключати репозиторії без ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾ÑобиÑтого токену доÑтупу."
+msgstr ""
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr "Примітка: Ñк адмініÑтратор ви можете налаштувати %{github_integration_link}, що дозволить входити через GitHub Ñ– імпортувати репозиторії без ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾ÑобиÑтого токену доÑтупу."
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr "Примітка: звернітьÑÑ Ð´Ð¾ вашого адмініÑтратора GitLab, щоб налаштувати %{github_integration_link}, Ñкий дозволить входити за допомогою GitHub Ñ– приєднувати репозиторії без генерації перÑонального токена доÑтупу."
+msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr "Примітка: звернітьÑÑ Ð´Ð¾ вашого адмініÑтратора GitLab, щоб налаштувати %{github_integration_link}, Ñкий дозволить входити за допомогою GitHub та імпортувати репозиторії без генерації перÑонального токена доÑтупу."
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ події"
@@ -3016,34 +4597,79 @@ msgid "OfSearchInADropdown|Filter"
msgstr "Фільтр"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
-msgstr "ПіÑÐ»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ репозиторії можуть бути віддзеркалені через SSH. ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ %{ssh_link}"
+msgstr ""
-msgid "Online IDE integration settings."
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— із онлайн IDE."
+
+msgid "Only comments from the following commit are shown below"
+msgstr "Ðижче наведено лише коментарі з наÑтупного коміту"
+
msgid "Only project members can comment."
msgstr "Тільки учаÑники проекту можуть залишати коментарі."
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr "Відкрити"
+msgid "Open in Xcode"
+msgstr "Відкрити в Xcode"
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr "Відкрито"
+msgid "Opened MR"
+msgstr "Відкритий запит на злиттÑ"
+
+msgid "Opened issues"
+msgstr "Відкриті проблеми"
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Відкрито"
msgid "Opens in a new window"
msgstr "ВідкриваєтьÑÑ Ñƒ новому вікні"
+msgid "Operations"
+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 "Параметри"
+msgid "Or you can choose one of the suggested colors below"
+msgstr "Ðбо ви можете вибрати один із запропонованих нижче кольорів"
+
+msgid "Other Labels"
+msgstr "Інші мітки"
+
+msgid "Other information"
+msgstr "Інша інформаціÑ"
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr "Ð’ іншому разі рекомендуєтьÑÑ Ð¿Ð¾Ñ‡Ð°Ñ‚Ð¸ з одного з наведених нижче варіантів."
msgid "Outbound requests"
-msgstr ""
+msgstr "Вихідні запити"
msgid "Overview"
msgstr "ОглÑд"
@@ -3052,7 +4678,7 @@ msgid "Owner"
msgstr "ВлаÑник"
msgid "Pages"
-msgstr ""
+msgstr "Сторінки"
msgid "Pagination|Last »"
msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ Â»"
@@ -3067,17 +4693,35 @@ msgid "Pagination|« First"
msgstr "« Перша"
msgid "Part of merge request changes"
-msgstr ""
+msgstr "ЧаÑтина змін у запиті на злиттÑ"
msgid "Password"
msgstr "Пароль"
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr "Призупинити"
+
msgid "Pending"
msgstr "В очікуванні"
-msgid "Performance optimization"
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr "Виконуйте такі розширені операції, Ñк зміна шлÑху, перенеÑÐµÐ½Ð½Ñ Ñ‡Ð¸ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð³Ñ€ÑƒÐ¿Ð¸."
+
+msgid "Performance optimization"
+msgstr "ÐžÐ¿Ñ‚Ð¸Ð¼Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾ÑÑ‚Ñ–"
+
+msgid "Permissions"
+msgstr "Права доÑтупу"
+
msgid "Personal Access Token"
msgstr "Токену перÑонального доÑтупу"
@@ -3096,6 +4740,9 @@ msgstr "Розклади Конвеєрів"
msgid "Pipeline quota"
msgstr "Квота на конвеєри"
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr "Ðевдалі:"
@@ -3192,11 +4839,23 @@ msgstr "Ð’ даний Ñ‡Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” конвеєрів."
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "Цей проект в даний Ñ‡Ð°Ñ Ð½Ðµ налаштований Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑку конвеєрів."
-msgid "Pipeline|Retry pipeline"
-msgstr "ПерезапуÑтити конвеєр"
+msgid "Pipeline|Create for"
+msgstr "Створити длÑ"
+
+msgid "Pipeline|Create pipeline"
+msgstr "Створити конвеєр"
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
-msgstr "ПерезапуÑтити конвеєр #%{pipelineId}?"
+msgid "Pipeline|Existing branch name or tag"
+msgstr "ІÑнуюче Ñ–Ð¼â€™Ñ Ð³Ñ–Ð»ÐºÐ¸ або тег"
+
+msgid "Pipeline|Run Pipeline"
+msgstr "ЗапуÑтити Конвеєр"
+
+msgid "Pipeline|Search branches"
+msgstr "Пошук гілки"
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr "Вкажіть Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¸Ñ…, Ñкі будуть викориÑтані в цьому запуÑку. Інакше будуть викориÑтані значеннÑ, вказані в %{settings_link}."
msgid "Pipeline|Stop pipeline"
msgstr "Зупинити конвеєр"
@@ -3204,8 +4863,8 @@ msgstr "Зупинити конвеєр"
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr "Зупинити конвеєр #%{pipelineId}?"
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
-msgstr "Зараз ви перезапуÑтите конвеєр %{pipelineId}."
+msgid "Pipeline|Variables"
+msgstr "Змінні"
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
msgstr "Зараз ви зупинете конвеєр %{pipelineId}."
@@ -3222,18 +4881,42 @@ msgstr "зі Ñтадією"
msgid "Pipeline|with stages"
msgstr "зі ÑтадіÑми"
-msgid "PlantUML"
+msgid "Plain diff"
msgstr ""
+msgid "Planned finish date"
+msgstr "Запланована дата завершеннÑ"
+
+msgid "Planned start date"
+msgstr "Запланована дата початку"
+
+msgid "PlantUML"
+msgstr "PlantUML"
+
msgid "Play"
msgstr "Відтворити"
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
-msgstr "Будь лаÑка, <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">увімкніть білінга Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ з ваших проектів, щоб мати можливіÑÑ‚ÑŒ Ñтворити Kubernetes-клаÑтер</a>, Ñ– повторіть Ñпробу."
+msgid "Please accept the Terms of Service before continuing."
+msgstr "Будь лаÑка, Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð¹Ð¼Ñ–Ñ‚ÑŒ умови Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг."
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr "Будь лаÑка виберіть хоча б один фільтр, щоб побачити результати"
msgid "Please solve the reCAPTCHA"
msgstr "Будь лаÑка, пройдіть reCAPTCHA"
+msgid "Please try again"
+msgstr "Будь лаÑка, Ñпробуйте ще раз"
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr "Будь лаÑка, почекайте поки ми з’єднуємоÑÑ Ñ–Ð· вашим репозиторієм. Оновлюйте Ñторінку за бажаннÑм."
@@ -3243,9 +4926,24 @@ msgstr "Будь лаÑка, почекайте поки ми імпортуєм
msgid "Preferences"
msgstr "ÐалаштуваннÑ"
+msgid "Preferences|Navigation theme"
+msgstr "Тема навігації"
+
msgid "Primary"
msgstr "Головний"
+msgid "Prioritize"
+msgstr "Пріоритизувати"
+
+msgid "Prioritize label"
+msgstr "Пріоритизувати мітку"
+
+msgid "Prioritized Labels"
+msgstr "Пріоритетні мітки"
+
+msgid "Prioritized label"
+msgstr "Пріоритетні мітки"
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Приватний — доÑтуп до проекту повинен надаватиÑÑ ÐºÐ¾Ð¶Ð½Ð¾Ð¼Ñƒ кориÑтувачеві."
@@ -3258,9 +4956,21 @@ msgstr "Приватні проекти можуть бути Ñтворені Ñ
msgid "Profile"
msgstr "Профіль"
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr "Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð¿Ð»Ð°Ð½Ð¾Ð²Ð°Ð½Ð¸Ð¹ Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ."
+msgid "Profiles|Add key"
+msgstr "Додати ключ"
+
+msgid "Profiles|Change username"
+msgstr "Змінити ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
+
+msgid "Profiles|Current path: %{path}"
+msgstr "Поточний шлÑÑ…: %{path}"
+
msgid "Profiles|Delete Account"
msgstr "Видалити обліковий запиÑ"
@@ -3279,9 +4989,27 @@ msgstr "Ðеправильний пароль"
msgid "Profiles|Invalid username"
msgstr "Ðеправильне ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
+msgid "Profiles|Path"
+msgstr "ШлÑÑ…"
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Введіть ваш %{confirmationValue} Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ:"
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr "Оновити ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr "Помилка при збереженні імені кориÑтувача - %{message}"
+
+msgid "Profiles|Username successfully changed"
+msgstr "Ð†Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача уÑпішно збережено"
+
msgid "Profiles|You don't have access to delete this user."
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтупу Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ кориÑтувача."
@@ -3291,15 +5019,24 @@ msgstr "Вам необхідно змінити влаÑника або видÐ
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ” влаÑником в цих групах:"
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr "ваш обліковий запиÑ"
msgid "Profiling - Performance bar"
-msgstr ""
+msgstr "ÐŸÑ€Ð¾Ñ„Ñ–Ð»ÑŽÐ²Ð°Ð½Ð½Ñ - панель продуктивноÑÑ‚Ñ–"
msgid "Programming languages used in this repository"
msgstr "Мови програмуваннÑ, що викориÑтовуєтьÑÑ Ð² цьому репозиторії"
+msgid "Progress"
+msgstr "ПрогреÑ"
+
+msgid "Project"
+msgstr "Проект"
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Проект '%{project_name}' перебуває в процеÑÑ– видаленнÑ."
@@ -3312,6 +5049,9 @@ msgstr "Проект '%{project_name}' уÑпішно Ñтворений."
msgid "Project '%{project_name}' was successfully updated."
msgstr "Проект '%{project_name}' уÑпішно оновлено."
+msgid "Project Badges"
+msgstr "Значки проекту"
+
msgid "Project access must be granted explicitly to each user."
msgstr "ДоÑтуп до проекту повинен надаватиÑÑ ÐºÐ¾Ð¶Ð½Ð¾Ð¼Ñƒ кориÑтувачеві."
@@ -3336,6 +5076,9 @@ msgstr "ЗакінчивÑÑ Ñ‚ÐµÑ€Ð¼Ñ–Ð½ дії поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° проÐ
msgid "Project export started. A download link will be sent by email."
msgstr "Розпочато екÑпорт проекту. ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð»Ñ ÑÐºÐ°Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ надіÑлана електронною поштою."
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "ПідпиÑатиÑÑ"
@@ -3343,26 +5086,17 @@ msgid "ProjectCreationLevel|Allowed to create projects"
msgstr "Дозволено Ñтворювати проекти"
msgid "ProjectCreationLevel|Default project creation protection"
-msgstr ""
+msgstr "ЗахиÑÑ‚ Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ñ–Ð² за замовчуваннÑм"
-msgid "ProjectCreationLevel|Developers + Masters"
-msgstr "Розробники + Керівники"
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr "Розробники + керівники"
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr "Керівники"
msgid "ProjectCreationLevel|No one"
msgstr "Ðіхто"
-msgid "ProjectFeature|Disabled"
-msgstr "Вимкнено"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "Ð’ÑÑ– із доÑтупом"
-
-msgid "ProjectFeature|Only team members"
-msgstr "Тільки члени команди"
-
msgid "ProjectFileTree|Name"
msgstr "Ім'Ñ"
@@ -3372,12 +5106,18 @@ msgstr "Ðіколи"
msgid "ProjectLifecycle|Stage"
msgstr "СтадіÑ"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "ІÑторіÑ"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "ЗвернітьÑÑ Ð´Ð¾ адмініÑтратора, щоб змінити це налаштуваннÑ."
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr "Помилка при захиÑÑ‚Ñ– тегу"
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr "Помилка при оновленні тегу!"
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr "Тільки підпиÑані коміти можуть бути надіÑлані в цей репозиторій."
@@ -3396,6 +5136,9 @@ msgstr "КориÑтувачі можуть відправлÑти в цей Ñ€Ð
msgid "Projects"
msgstr "Проекти"
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr "ЧаÑто відвідувані"
@@ -3414,8 +5157,38 @@ msgstr "ЩоÑÑŒ пішло не так з нашого боку."
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "Ðа жаль, по вашоу запиту проектів не знайдено"
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±ÑƒÑ” підтримки localStorage вашим браузером"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr "ЧаÑ"
msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr "було знайдено %{exporters} з %{metrics}"
@@ -3493,25 +5266,49 @@ msgid "PrometheusService|Waiting for your first deployment to an environment to
msgstr "ÐžÑ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ñƒ Ñеридовищі Ð´Ð»Ñ Ð·Ð±Ð¾Ñ€Ñƒ загальних метрик"
msgid "Promote"
-msgstr ""
+msgstr "ПеренеÑти"
-msgid "Promote to Group Label"
-msgstr "ПеренеÑти мітку на рівень групи"
+msgid "Promote these project milestones into a group milestone."
+msgstr "ПеренеÑти ці проектні етапи на рівень групи."
msgid "Promote to Group Milestone"
-msgstr ""
+msgstr "ПеренеÑти Етап на рівень групи"
+
+msgid "Promote to group label"
+msgstr "ПеренеÑти мітку на рівень групи"
+
+msgid "Promotions|Don't show me this again"
+msgstr "Більше не показувати це"
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr "Епіки дозволÑÑŽÑ‚ÑŒ вам більш ефективно та з меншими зуÑиллÑми керувати портфелÑми проектів, відÑлідковуючи групи Ñпоріднених питань у етапах та проектах."
+
+msgid "Promotions|This feature is locked."
+msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð°."
+
+msgid "Promotions|Upgrade plan"
+msgstr "Перейти на вищий тарифний план"
msgid "Protip:"
msgstr "Підказка:"
+msgid "Provider"
+msgstr "ПоÑтачальник"
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Публічна — група та вÑÑ– публічні проекти можуть переглÑдатиÑÑ Ð±ÐµÐ· автентифікації."
msgid "Public - The project can be accessed without any authentication."
msgstr "Публічний — проект может переглÑдатиÑÑ Ð±ÐµÐ· автентифікації."
+msgid "Public pipelines"
+msgstr "Публічні конвеєри"
+
msgid "Push Rules"
-msgstr "Правила відправленнÑ"
+msgstr "Push-правила"
msgid "Push events"
msgstr "Події Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ (push)"
@@ -3525,30 +5322,45 @@ msgstr "ÐатиÑніть, щоб Ñтворити проект"
msgid "PushRule|Committer restriction"
msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ‚ÐµÑ€Ð°"
+msgid "Pushed"
+msgstr "Відправлено"
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr "Квартали"
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "Швидкі дії можна викориÑтовувати в опиÑах проблем Ñ– коментарÑÑ…."
msgid "Read more"
msgstr "Докладніше"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "ІнÑтрукціÑ"
msgid "Real-time features"
-msgstr ""
-
-msgid "RefSwitcher|Branches"
-msgstr "Гілки"
-
-msgid "RefSwitcher|Tags"
-msgstr "Теги"
+msgstr "Фунції реального чаÑу"
msgid "Reference:"
msgstr "ПоÑиланнÑ:"
+msgid "Refresh"
+msgstr "Оновити"
+
msgid "Register / Sign In"
msgstr "ЗареєÑтруватиÑÑ / Увійти"
+msgid "Register and see your runners for this group."
+msgstr "ЗареєÑтруйте Ñ– переглÑдайте ваші Runner’и Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи."
+
+msgid "Register and see your runners for this project."
+msgstr "ЗареєÑтруйте Ñ– переглÑдайте ваші Runner’и Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
+
msgid "Registry"
msgstr "РеєÑÑ‚Ñ€"
@@ -3571,7 +5383,7 @@ msgid "Related Merged Requests"
msgstr "Пов'Ñзані виконані запити"
msgid "Related merge requests"
-msgstr ""
+msgstr "Пов'Ñзані запити на злиттÑ"
msgid "Remind later"
msgstr "Ðагадати пізніше"
@@ -3579,36 +5391,60 @@ msgstr "Ðагадати пізніше"
msgid "Remove"
msgstr "Видалити"
+msgid "Remove Runner"
+msgstr "Видалити Runner"
+
msgid "Remove avatar"
msgstr "Видалити аватар"
+msgid "Remove priority"
+msgstr "Видалити пріоритет"
+
msgid "Remove project"
msgstr "Видалити проект"
msgid "Repair authentication"
msgstr "Відновити аутентифікацію"
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr "Репозиторії по URL"
msgid "Repository"
msgstr "Репозиторій"
+msgid "Repository Settings"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ"
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr "Репозиторій не має блокувань."
msgid "Repository maintenance"
-msgstr ""
+msgstr "ОбÑÐ»ÑƒÐ³Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ"
-msgid "Repository mirror settings"
-msgstr ""
+msgid "Repository mirror"
+msgstr "Дзеркало репозиторію"
msgid "Repository storage"
-msgstr ""
+msgstr "Сховище репозиторію"
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr "Вибрати"
msgid "Request Access"
msgstr "Запит доÑтупу"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr "Вимагати від уÑÑ–Ñ… кориÑтувачів приймати умови Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг та політику конфіденційноÑÑ‚Ñ–, коли вони отримують доÑтуп до GitLab."
+
msgid "Reset git storage health information"
msgstr "Скиньте інформацію про працездатніÑÑ‚ÑŒ Ñховища git"
@@ -3618,11 +5454,29 @@ msgstr "Оновити токен доÑтупу Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ прÐ
msgid "Reset runners registration token"
msgstr "Перегенерувати реєÑтраційний токен runner-ів"
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr "Вирішити конфлікти у гілці-джерелі"
+
msgid "Resolve discussion"
msgstr "Завершити обговореннÑ"
-msgid "Response"
-msgstr "Відповідь"
+msgid "Response metrics (Custom)"
+msgstr "Метрики відповідей (ВлаÑні)"
+
+msgid "Resume"
+msgstr "Продовжити"
+
+msgid "Retry"
+msgstr "Спробувати знову"
+
+msgid "Retry this job"
+msgstr "Повторити це завданнÑ"
+
+msgid "Retry verification"
+msgstr "Повторити перевірку"
msgid "Reveal value"
msgid_plural "Reveal values"
@@ -3637,13 +5491,19 @@ msgstr "Ðнулювати цей коміт"
msgid "Revert this merge request"
msgstr "Ðнулювати цей запит на злиттÑ"
+msgid "Review"
+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 ""
+msgstr "ПереглÑте Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð²Ð°Ð¹Ð´ÐµÑ€Ñ–Ð² поÑлуг у вашому провайдері ідентифікації — в такому разі GitLab Ñ” \"провайдером поÑлуг\" або \"довірÑючою Ñтороною\"."
msgid "Reviewing"
-msgstr ""
+msgstr "ЗатвердженнÑ"
msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr "Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ (запиту на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ !%{mergeRequestId})"
+
+msgid "Revoke"
msgstr ""
msgid "Roadmap"
@@ -3652,24 +5512,48 @@ msgstr "План-графік"
msgid "Run CI/CD pipelines for external repositories"
msgstr "ЗапуÑтити CI/CD конвеєри Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ–Ñ… репозиторіїв"
+msgid "Runner token"
+msgstr "Токен Runner'а"
+
msgid "Runners"
-msgstr ""
+msgstr "Runner'и"
+
+msgid "Runners API"
+msgstr "API Runner’ів"
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr "Runner’и можуть розміщуватиÑÑ Ñƒ різних кориÑтувачів, на Ñерверах Ñ– навіть на вашій локальній машині."
msgid "Running"
msgstr "ВиконуєтьÑÑ"
+msgid "SAML SSO"
+msgstr "Єдиний вхід SAML"
+
+msgid "SAML SSO for %{group_name}"
+msgstr "Єдиний вхід SAML Ð´Ð»Ñ %{group_name}"
+
msgid "SAML Single Sign On"
-msgstr ""
+msgstr "Єдиний вхід SAML"
msgid "SAML Single Sign On Settings"
-msgstr ""
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ”Ð´Ð¸Ð½Ð¾Ð³Ð¾ входу SAML"
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
-msgstr ""
+msgstr "Відбиток SHA1 Ñертифікату Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸ÑÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð¾ÐºÐµÐ½Ñ–Ð² SAML. Отримайте його від провайдера ідентифікації, де він також може називатиÑÑ \"Thumbprint\"."
msgid "SSH Keys"
msgstr "Ключі SSH"
+msgid "SSL Verification"
+msgstr "Перевірка SSL"
+
+msgid "Save"
+msgstr "Зберегти"
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr "Зберегти зміни"
@@ -3691,15 +5575,39 @@ msgstr "Розклади"
msgid "Scheduling Pipelines"
msgstr "ÐŸÐ»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñ–Ð²"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr "Тематичні дошки проблем"
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr "Прокрутити вниз"
+
+msgid "Scroll to top"
+msgstr "Прокрутити вгору"
+
msgid "Search"
msgstr "Пошук"
+msgid "Search branches"
+msgstr "Пошук у гілках"
+
msgid "Search branches and tags"
msgstr "Пошук гілок та тегів"
+msgid "Search files"
+msgstr "Пошук файлів"
+
+msgid "Search for projects, issues, etc."
+msgstr "Пошук в проектах, проблемах і т. д."
+
+msgid "Search merge requests"
+msgstr "Пошук у запитах на злиттÑ"
+
msgid "Search milestones"
msgstr "Пошук етапів"
@@ -3715,15 +5623,30 @@ msgstr "КількіÑÑ‚ÑŒ Ñекунд до ÑÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–
msgid "Seconds to wait for a storage access attempt"
msgstr "КількіÑÑ‚ÑŒ Ñекунд Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ´ повторною Ñпробою доÑтупу до Ñховища даних"
-msgid "Secret variables"
-msgstr "Секретні змінні"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr "Панель безпеки"
msgid "Security report"
-msgstr "Звіт по безпеці"
+msgstr "Звіт про безпеку"
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr "Вибрати"
msgid "Select Archive Format"
msgstr "Виберіть формат архіву"
+msgid "Select a namespace to fork the project"
+msgstr "Виберіть проÑÑ‚Ñ–Ñ€ імен Ð´Ð»Ñ Ñ„Ð¾Ñ€ÐºÑƒ проекту"
+
msgid "Select a timezone"
msgstr "Вибрати чаÑовий поÑÑ"
@@ -3736,9 +5659,27 @@ msgstr "Виберіть виконавцÑ"
msgid "Select branch/tag"
msgstr "Виберіть гілку або тег"
+msgid "Select project"
+msgstr "Вибрати проект"
+
+msgid "Select project and zone to choose machine type"
+msgstr "Вибрати проект та зону Ð´Ð»Ñ Ð²Ð¸Ð±Ð¾Ñ€Ñƒ типу машини"
+
+msgid "Select project to choose zone"
+msgstr "Вибрати проект Ð´Ð»Ñ Ð²Ð¸Ð±Ð¾Ñ€Ñƒ зони"
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr "Виберіть гілку-джерело"
+
msgid "Select target branch"
msgstr "Вибір цільової гілки"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr "Вибіркова ÑинхронізаціÑ"
@@ -3754,29 +5695,32 @@ msgstr "вереÑень"
msgid "Server version"
msgstr "ВерÑÑ–Ñ Ñервера"
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr "Шаблони ÑервіÑів"
msgid "Service URL"
-msgstr "Ð¡ÐµÑ€Ð²Ñ–Ñ URL"
+msgstr "URL ÑервіÑу"
msgid "Session expiration, projects limit and attachment size."
-msgstr ""
+msgstr "Термін дії ÑеÑÑ–Ñ—, проектні ліміти та розміри вкладень."
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Ð’Ñтановіть пароль Ð´Ð»Ñ Ñвого облікового запиÑу, щоб мати можливіÑÑ‚ÑŒ відправлÑти та отримувати через %{protocol}."
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
-msgstr ""
+msgstr "Ð’Ñтановіть Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð° замовчуваннÑм Ñ– обмежте рівні видимоÑÑ‚Ñ–. Ðалаштуйте джерела імпорту Ñ– протокол доÑтупу git."
msgid "Set max session time for web terminal."
-msgstr ""
+msgstr "МакÑимальний термін дії ÑеÑÑ–Ñ— Ð´Ð»Ñ Ð²ÐµÐ±-терміналу."
msgid "Set notification email for abuse reports."
-msgstr ""
+msgstr "Ðалаштувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ð¾ електронній пошті Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ про зловживаннÑ."
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
-msgstr ""
+msgstr "Ð’Ñтановіть вимоги Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ кориÑтувачів. Увімкніть обов’Ñзкову двофакторну автентифікацію."
msgid "Set up CI/CD"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
@@ -3785,7 +5729,7 @@ msgid "Set up Koding"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Koding"
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
-msgstr ""
+msgstr "Ðалаштуйте твердженнÑ/атрибути (email, ім'Ñ, прізвище) Ñ– NameID відповідно до %{docsLinkStart} документації %{icon}%{docsLinkEnd}"
msgid "SetPasswordToCloneLink|set a password"
msgstr "вÑтановити пароль"
@@ -3794,29 +5738,50 @@ msgid "Settings"
msgstr "ÐалаштуваннÑ"
msgid "Setup a specific Runner automatically"
-msgstr ""
+msgstr "Ðвтоматично налаштувати Ñпецифічний runner"
+
+msgid "Share"
+msgstr "ПоділитиÑÑ"
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
-msgstr ""
+msgstr "ПоділітьÑÑ <strong>%{sso_label}</strong> із учаÑниками Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб вони могли увійти до вашої групи через провайдера ідентифікації"
+
+msgid "Shared Runners"
+msgstr "Загальні Runner'и"
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr "При обнуленні хвилин конвеєрів Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проÑтору імен, кількіÑÑ‚ÑŒ вже викориÑтаних хвилин буде дорівнювати 0."
msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
-msgstr "Обнулити хвилини в конвеєрі"
+msgstr "Скинути хвилини в конвеєрі"
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr "Обнулити викориÑтані хвилини в конвеєрі"
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr "Показати команду"
+msgid "Show complete raw log"
+msgstr "Показати повний неформатований журнал"
+
+msgid "Show latest version"
+msgstr "Показати оÑтанню верÑÑ–ÑŽ"
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr "Показати батьківÑькі Ñторінки"
msgid "Show parent subgroups"
msgstr "Показати батьківÑькі підгрупи"
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "Показано %d подію"
@@ -3824,28 +5789,49 @@ msgstr[1] "Показано %d події"
msgstr[2] "Показано %d подій"
msgstr[3] "Показано %d подій"
+msgid "Side-by-side"
+msgstr ""
+
msgid "Sidebar|Change weight"
msgstr "Змінити вагу"
-msgid "Sidebar|No"
-msgstr "ÐÑ–"
-
msgid "Sidebar|None"
msgstr "Ðемає"
+msgid "Sidebar|Only numeral characters allowed"
+msgstr "Дозволені лише цифри"
+
msgid "Sidebar|Weight"
msgstr "Вага"
-msgid "Sign-in restrictions"
+msgid "Sign in"
msgstr ""
-msgid "Sign-up restrictions"
+msgid "Sign in / Register"
msgstr ""
+msgid "Sign in to %{group_name}"
+msgstr "Увійти в %{group_name}"
+
+msgid "Sign in with Single Sign-On"
+msgstr "Увійти за допомогою єдиного входу"
+
+msgid "Sign out"
+msgstr "Вийти"
+
+msgid "Sign-in restrictions"
+msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ"
+
+msgid "Sign-up restrictions"
+msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації"
+
msgid "Size and domain settings for static websites"
-msgstr ""
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾Ð·Ð¼Ñ–Ñ€Ñƒ та домену Ð´Ð»Ñ Ñтатичних веб-Ñайтів"
msgid "Slack application"
+msgstr "заÑтоÑунок Slack"
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
msgstr ""
msgid "Snippets"
@@ -3855,16 +5841,22 @@ msgid "Something went wrong on our end"
msgstr "ЩоÑÑŒ пішло не так з нашого боку"
msgid "Something went wrong on our end."
+msgstr "ЩоÑÑŒ пішло не так з нашого боку."
+
+msgid "Something went wrong on our end. Please try again!"
msgstr ""
msgid "Something went wrong when toggling the button"
msgstr "Помилка при перемиканні кнопки"
-msgid "Something went wrong while fetching Dependency Scanning."
-msgstr ""
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr "Помилка при закритті %{issuable}. Будь лаÑка, Ñпробуйте пізніше"
+
+msgid "Something went wrong while fetching assignees list"
+msgstr "Помилка при отриманні ÑпиÑку виконавців"
-msgid "Something went wrong while fetching SAST."
-msgstr "Помилка при отриманні SAST."
+msgid "Something went wrong while fetching group member contributions"
+msgstr "Помилка при завантаженні внеÑків учаÑників групи"
msgid "Something went wrong while fetching the projects."
msgstr "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ñ–Ð²"
@@ -3872,9 +5864,18 @@ msgstr "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð
msgid "Something went wrong while fetching the registry list."
msgstr "ЩоÑÑŒ пішло не так при отриманні ÑпиÑку із реєÑтру."
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr "Помилка при повторному відкритті %{issuable}. Будь лаÑка, Ñпробуйте пізніше"
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr "Помилка при завершенні обговореннÑ. Будь лаÑка, Ñпробуйте пізніше."
+
msgid "Something went wrong. Please try again."
msgstr "ЩоÑÑŒ пішло не так. Будь лаÑка Ñпробуйте ще раз."
+msgid "Sorry, no epics matched your search"
+msgstr "Вибачте, жоден епік не задовольнÑÑ” критеріÑм вашого пошуку"
+
msgid "Sort by"
msgstr "Сортувати за"
@@ -3990,19 +5991,46 @@ msgid "Spam Logs"
msgstr "Спам-журнал"
msgid "Spam and Anti-bot Protection"
-msgstr ""
+msgstr "ЗахиÑÑ‚ від Ñпаму Ñ– ботів"
+
+msgid "Specific Runners"
+msgstr "Спеціальні Runner’и"
msgid "Specify the following URL during the Runner setup:"
msgstr "Зазначте наÑтупний URL під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Runner-а:"
+msgid "Squash commits"
+msgstr "Виконати об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ (squash) комітів"
+
+msgid "Stage"
+msgstr "СтадіÑ"
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr "ПроіндекÑувати вÑÑ– зміни"
+
+msgid "Stage changes"
+msgstr "ПроіндекÑувати зміни"
+
+msgid "Staged"
+msgstr "ПроіндекÑовано"
+
+msgid "Staged %{type}"
+msgstr "ПроіндекÑовано %{type}"
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr "Позначте мітку, щоб зробити Ñ—Ñ— пріоритетною. ПеретÑгуйте пріоритетні мітки Ð´Ð»Ñ Ð·Ð¼Ñ–Ð½Ð¸ Ñ—Ñ… відноÑного пріоритету."
+
msgid "StarProject|Star"
msgstr "В обрані"
msgid "Starred Projects"
-msgstr ""
+msgstr "Обрані Проекти"
msgid "Starred Projects' Activity"
-msgstr ""
+msgstr "ÐктивніÑÑ‚ÑŒ в обраних проектах"
msgid "Starred projects"
msgstr "Обрані проекти"
@@ -4016,33 +6044,66 @@ msgstr "ЗапуÑÑ‚Ñ–Ñ‚ÑŒ Runner!"
msgid "Started"
msgstr "Запущений"
+msgid "Starts at (UTC)"
+msgstr "ПочинаєтьÑÑ Ð¾ (за Грінвічем)"
+
msgid "State your message to activate"
-msgstr ""
+msgstr "Залиште Ñвоє Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ—"
msgid "Status"
msgstr "СтатуÑ"
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr "Зупинити це Ñередовище"
+
msgid "Stopped"
msgstr "Зупинено"
msgid "Storage"
msgstr "Сховище"
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr "Підгрупи"
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr "ПідпиÑатиÑÑ"
+
+msgid "Subscribe at group level"
+msgstr "ПідпиÑатиÑÑ Ð½Ð° рівні групи"
+
+msgid "Subscribe at project level"
+msgstr "ПідпиÑатиÑÑ Ð½Ð° рівні проекту"
+
msgid "Switch branch/tag"
msgstr "Перейти в гілку/тег"
-msgid "System"
-msgstr "СиÑтема"
+msgid "Sync information"
+msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ñинхронізацію"
msgid "System Hooks"
msgstr "СиÑтемні гуки"
-msgid "System header and footer:"
+msgid "System Info"
msgstr ""
+msgid "System header and footer:"
+msgstr "Заголовок Ñ– футер ÑиÑтеми:"
+
+msgid "System metrics (Custom)"
+msgstr "СиÑтемні метрики (ВлаÑні)"
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] "Тег (%{tag_count})"
@@ -4053,6 +6114,9 @@ msgstr[3] "Тегів (%{tag_count})"
msgid "Tags"
msgstr "Теги"
+msgid "Tags:"
+msgstr "Теги:"
+
msgid "TagsPage|Browse commits"
msgstr "ПереглÑнути коміти"
@@ -4116,8 +6180,8 @@ msgstr "Цей тег не міÑтить опиÑу релізу."
msgid "TagsPage|Use git tag command to add a new one:"
msgstr "ВикориÑтовуйте команду git tag, щоб додати новий тег:"
-msgid "TagsPage|Write your release notes or drag files here..."
-msgstr "Ðапишіть Ñвій Ð¾Ð¿Ð¸Ñ Ñ€ÐµÐ»Ñ–Ð·Ñƒ або перетÑгніть файли Ñюди..."
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr "Створіть Ð¾Ð¿Ð¸Ñ Ñ€ÐµÐ»Ñ–Ð·Ñƒ або перетÑгніть файли Ñюди…"
msgid "TagsPage|protected"
msgstr "захищений"
@@ -4126,22 +6190,31 @@ msgid "Target Branch"
msgstr "Цільова гілка"
msgid "Target branch"
-msgstr ""
+msgstr "Цільова гілка"
msgid "Team"
msgstr "Команда"
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr "Угода про Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг Ñ– політика конфіденційноÑÑ‚Ñ–"
+
+msgid "Terms of Service and Privacy Policy"
+msgstr "Правилами кориÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑервіÑом Ñ– політика конфіденційноÑÑ‚Ñ–"
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr "ДÑкую! Більше не показувати це повідомленнÑ"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
-msgstr "Розширений глобальний пошук в GitLab - це потужний інÑтрумент Ñкий заощаджує ваш чаÑ. ЗаміÑÑ‚ÑŒ Ð´ÑƒÐ±Ð»ÑŽÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ Ñ– витрати чаÑу, ви можете шукати код інших команд, Ñкий може допомогти у вашому проекті."
+msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
-msgstr ""
+msgstr "Трекер проблем — це міÑце, де можна додати речі, Ñкі потрібно покращити або розв’Ñзати в проекті"
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
-msgstr ""
+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 ""
@@ -4153,7 +6226,7 @@ msgid "The collection of events added to the data gathered for that stage."
msgstr "ÐšÐ¾Ð»ÐµÐºÑ†Ñ–Ñ Ð¿Ð¾Ð´Ñ–Ð¹ додана до даних, зібраних Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
-msgstr "Ð—â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ припинено піÑÐ»Ñ %{timeout}. Ð”Ð»Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð², Ñким потрібно більше чаÑу, викориÑтовуйте комбінацію clone/push."
+msgstr ""
msgid "The fork relationship has been removed."
msgstr "Зв'Ñзок форку видалено."
@@ -4170,12 +6243,15 @@ msgstr "МакÑимальний розмір файлу — 200 Кб."
msgid "The number of attempts GitLab will make to access a storage."
msgstr "КількіÑÑ‚ÑŒ Ñпроб, Ñкі зробить GitLab Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу до Ñховища даних."
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr "КількіÑÑ‚ÑŒ збоїв піÑÐ»Ñ Ñкої Gitlab повніÑÑ‚ÑŽ заблокує доÑтуп до Ñховища данних. Лічильник кількоÑÑ‚Ñ– збоїв можна буде обнулити в інтерфейÑÑ– адмініÑтратора (%{link_to_health_page}), або через %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "Фаза життєвого циклу розробки."
@@ -4194,6 +6270,9 @@ msgstr "ДоÑтуп до проекту можливий будь-Ñким за
msgid "The project can be accessed without any authentication."
msgstr "ДоÑтуп до проекту можливий без будь-Ñкої перевірки автентичноÑÑ‚Ñ–."
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "Репозиторій Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту не Ñ–Ñнує."
@@ -4209,6 +6288,9 @@ msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ð¾ÐºÐ°Ð·ÑƒÑ” Ñ‡Ð°Ñ Ð²Ñ–Ð´ ÑÑ‚Ð
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr "План-графік показує Ñтан ваших епіків у чаÑÑ–"
+msgid "The secure token used by the Runner to checkout the project"
+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 "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Staging показує Ñ‡Ð°Ñ Ð¼Ñ–Ð¶ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° розгортаннÑм коду у production. Дані автоматично додаютьÑÑ Ð¿Ñ–ÑÐ»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ñƒ production вперше."
@@ -4221,27 +6303,33 @@ msgstr "КількіÑÑ‚ÑŒ Ñекунд, протÑгом Ñкої GitLab збе
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "КількіÑÑ‚ÑŒ Ñекунд, протÑгом Ñкої GitLab намагатиметьÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ доÑтуп до Ñховища даних. По завершенню цього періоду буде згенерована помилка про Ð¿ÐµÑ€ÐµÐ²Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð»Ñ–Ð¼Ñ–Ñ‚Ñƒ чаÑу."
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
-msgstr "Ð§Ð°Ñ Ñƒ Ñекундах між перевірками Ñховища. Якщо Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ° іще на завершена, GitLab пропуÑтить наÑтупну."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 "Середнє Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð² Ñ€Ñдку. Приклад: між 3, 5, 9, Ñередніми 5, між 3, 5, 7, 8, Ñередніми (5 + 7) / 2 = 6."
msgid "There are no issues to show"
msgstr "Ðемає проблем Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
+msgid "There are no labels yet"
+msgstr ""
+
msgid "There are no merge requests to show"
msgstr "Ðемає запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
msgid "There are problems accessing Git storage: "
msgstr "Є проблеми з доÑтупом до Ñховища git: "
-msgid "There was an error loading results"
-msgstr ""
-
msgid "There was an error loading users activity calendar."
msgstr "Помилка при завантаженні ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾ÑÑ‚Ñ– кориÑтувачів."
@@ -4260,12 +6348,39 @@ msgstr "Помилка при підпиÑці на цю мітку."
msgid "There was an error when unsubscribing from this label."
msgstr "Помилка при відпиÑці від цієї мітки."
-msgid "This board\\'s scope is reduced"
-msgstr "ВидиміÑÑ‚ÑŒ цієї дошки обмежена"
+msgid "They can be managed using the %{link}."
+msgstr "Ðими можна керувати за допомогою %{link}."
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr "Цей інÑÑ‚Ð°Ð½Ñ GitLab ще немає загальних Runner'ів. ÐдмініÑтратори можуть Ñ—Ñ… зареєÑтрувати у Ñпеціальному розділі конфігурації."
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr ""
msgid "This directory"
msgstr "Цей каталог"
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr "Ð¦Ñ Ð³Ñ€ÑƒÐ¿Ð° ще не має жодного групового Runner’а."
+
msgid "This is a confidential issue."
msgstr "Це конфіденційна проблема."
@@ -4287,6 +6402,15 @@ msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð°Ð¿ÑƒÑкаєтьÑÑ ÐºÐ¾Ñ€Ð¸Ñтувачем.
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð¸Ñ‚ÑŒ від попередніх, Ñкі повинні завершитиÑÑ ÑƒÑпішно Ð´Ð»Ñ Ð¹Ð¾Ð³Ð¾ запуÑку"
+msgid "This job does not have a trace."
+msgstr "Лог Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ñутній."
+
+msgid "This job has been canceled"
+msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ відмінене"
+
+msgid "This job has been skipped"
+msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ пропущене"
+
msgid "This job has not been triggered yet"
msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ñ‰Ðµ не було запущене"
@@ -4294,7 +6418,7 @@ msgid "This job has not started yet"
msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ñ‰Ðµ не запуÑтилоÑÑ"
msgid "This job is in pending state and is waiting to be picked by a runner"
-msgstr ""
+msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ±ÑƒÐ²Ð°Ñ” в Ñтані Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñ– чекає на запуÑк Runner"
msgid "This job requires a manual action"
msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼Ð°Ð³Ð°Ñ” ручних дій"
@@ -4305,20 +6429,35 @@ msgstr "Це означає, що ви не можете відправлÑти
msgid "This merge request is locked."
msgstr "Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾."
+msgid "This option is disabled while you still have unstaged changes"
+msgstr "Ð¦Ñ Ð¾Ð¿Ñ†Ñ–Ñ Ð½ÐµÐ´Ð¾Ñтупна, поки у Ð²Ð°Ñ Ñ” неіндекÑовані зміни"
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr "Ð¦Ñ Ñторінка недоÑтупна, тому що ви не можете переглÑдати інформацію по кількох проектах."
+msgid "This page will be removed in a future release."
+msgstr "Цю Ñторінку буде видалено у майбутній верÑÑ–Ñ—."
+
msgid "This project"
msgstr "Цей проект"
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr "Цей проект не входить до жодної групи Ñ– тому не може викориÑтовувати групові Runner’и."
+
msgid "This repository"
msgstr "Цей репозиторій"
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
-msgstr "Це призведе до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ð»Ð°Ñної метрики, ви впевнені?"
+msgstr ""
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr "Ці Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти автоматично Ñтануть обговореннÑми проблем, Ñкі відображатимутьÑÑ Ñ‚ÑƒÑ‚ (причому коментарі Ñтануть чаÑтиною перепиÑки)."
+msgstr ""
msgid "Time before an issue gets scheduled"
msgstr "Ð§Ð°Ñ Ð´Ð¾ початку потраплÑÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ в планувальник"
@@ -4329,12 +6468,15 @@ msgstr "Ð§Ð°Ñ Ð´Ð¾ початку роботи над проблемою"
msgid "Time between merge request creation and merge/close"
msgstr "Ð§Ð°Ñ Ð¼Ñ–Ð¶ ÑтвореннÑм запиту Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– його виконаннÑм або закриттÑм"
-msgid "Time between updates and capacity settings."
-msgstr ""
-
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr ""
+msgid "Time remaining"
+msgstr "ЗалишилоÑÑ Ñ‡Ð°Ñу"
+
+msgid "Time spent"
+msgstr "Витрачено чаÑу"
+
msgid "Time tracking"
msgstr "ВідÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу"
@@ -4356,6 +6498,9 @@ msgstr "%s днів тому"
msgid "Timeago|%s days remaining"
msgstr "залишилоÑÑ %s днів"
+msgid "Timeago|%s hours ago"
+msgstr "%s годин тому"
+
msgid "Timeago|%s hours remaining"
msgstr "залишилоÑÑ %s годин"
@@ -4371,6 +6516,9 @@ msgstr "%s міÑÑці(в) тому"
msgid "Timeago|%s months remaining"
msgstr "залишилоÑÑ %s міÑÑці(в)"
+msgid "Timeago|%s seconds ago"
+msgstr "%s Ñекунд тому"
+
msgid "Timeago|%s seconds remaining"
msgstr "%s Ñекунд, що залишаютьÑÑ"
@@ -4386,48 +6534,45 @@ msgstr "%s років тому"
msgid "Timeago|%s years remaining"
msgstr "залишилоÑÑ %s роки"
+msgid "Timeago|1 day ago"
+msgstr "1 день тому"
+
msgid "Timeago|1 day remaining"
msgstr "ЗалишивÑÑ 1 день"
+msgid "Timeago|1 hour ago"
+msgstr "1 година тому"
+
msgid "Timeago|1 hour remaining"
msgstr "ЗалишилаÑÑŒ 1 година"
+msgid "Timeago|1 minute ago"
+msgstr "1 хвилина тому"
+
msgid "Timeago|1 minute remaining"
msgstr "ЗалишилаÑÑŒ 1 хвилина"
+msgid "Timeago|1 month ago"
+msgstr "1 міÑÑць тому"
+
msgid "Timeago|1 month remaining"
msgstr "ЗалишивÑÑ 1 міÑÑць"
+msgid "Timeago|1 week ago"
+msgstr "1 тиждень тому"
+
msgid "Timeago|1 week remaining"
msgstr "ЗалишивÑÑ 1 тиждень"
+msgid "Timeago|1 year ago"
+msgstr "1 рік тому"
+
msgid "Timeago|1 year remaining"
msgstr "ЗалишивÑÑ 1 рік"
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|a year ago"
-msgstr "рік тому"
-
-msgid "Timeago|about %s hours ago"
-msgstr "Близько %s годин(и) тому"
-
-msgid "Timeago|about a minute ago"
-msgstr "Близько хвилини тому"
-
-msgid "Timeago|about an hour ago"
-msgstr "Близько години тому"
-
msgid "Timeago|in %s days"
msgstr "через %s дні(в)"
@@ -4467,11 +6612,14 @@ msgstr "через тиждень"
msgid "Timeago|in 1 year"
msgstr "через рік"
-msgid "Timeago|in a while"
-msgstr "невдовзі"
+msgid "Timeago|just now"
+msgstr "щойно"
-msgid "Timeago|less than a minute ago"
-msgstr "менше хвилини тому"
+msgid "Timeago|right now"
+msgstr "зараз"
+
+msgid "Timeout"
+msgstr "Ð§Ð°Ñ Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ"
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4494,44 +6642,77 @@ msgid "Tip:"
msgstr "Порада:"
msgid "Title"
-msgstr "Ðазва"
+msgstr ""
msgid "To GitLab"
msgstr "Ð’ GitLab"
-msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
msgstr ""
+msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
+msgstr "Ð”Ð»Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð² з GitHub, ви можете викориÑтовувати %{personal_access_token_link}. Коли ви Ñтворюватимете ваш перÑональний токен доÑтупу, вам потрібно буде вибрати облаÑÑ‚ÑŒ дії <code>repo</code>, щоб ми могли відобразити ÑпиÑок ваших публічних та приватних репозиторіїв, доÑтупних Ð´Ð»Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ."
+
msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
msgstr "Ð”Ð»Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð² з GitHub, ви Ñпочатку повинні дозволити Gitlab доÑтуп до ÑпиÑку ваших репозиторіїв на GitHub:"
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr "Ð”Ð»Ñ Ð¿Ñ€Ð¸Ñ”Ð´Ð½Ð°Ð½Ð½Ñ SVN-репозиторію, переглÑньте %{svn_link}."
-msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr "Ð”Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð² з GitHub, ви можете викориÑтовувати %{personal_access_token_link}. Коли ви Ñтворюватимете ваш перÑональний токен доÑтупу, вам потрібно буде вибрати облаÑÑ‚ÑŒ дії <code>repo</code>, щоб ми могли відобразити ÑпиÑок ваших публічних та приватних репозиторіїв, доÑтупних Ð´Ð»Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ."
+
msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
msgstr "Ð”Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ репозиторіїв з GitHub, ви Ñпочатку повинні дозволити Gitlab доÑтуп до ÑпиÑку ваших репозиторіїв на GitHub:"
msgid "To import an SVN repository, check out %{svn_link}."
msgstr "Ð”Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ SVN-репозиторію, переглÑньте %{svn_link}."
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr "Щоб викориÑтовувати лише функції CI/CD Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ репозиторію, виберіть <strong>CI/CD Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ репозиторію</strong>."
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr "Щоб налаштувати аутентифікацію SAML Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— групи через провайдера ідентифікації такої Ñк Azure, Okta, Onelogin, Ping Identity або вашого влаÑного поÑтачальника SAML 2.0:"
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr "Ð”Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¸Ñ… завдань ви можете додати Runner’и до вашої групи"
+
+msgid "To this GitLab instance"
msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
-msgstr ""
+msgstr "Щоб перевірити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ GitLab CI, перейдіть до \"CI / CD → Конвеєри\" у вашому проекті та натиÑніть кнопку \"Перевірка конфігурації (CI Lint)\"."
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr "Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду плану-графіку додайте заплановані дати початку та Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ одного з ваших епіків у цій групі або Ñ—Ñ— підгрупах. ВідображаютьÑÑ Ð»Ð¸ÑˆÐµ епіки за попередні та наÑтупні 3 міÑÑці."
+msgid "To widen your search, change or remove filters."
+msgstr "Щоб розширити пошук, змініть або видаліть фільтри."
+
msgid "Todo"
msgstr "Задача"
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr "Перемикач бічної панелі"
+
+msgid "Toggle discussion"
+msgstr "Перемикач диÑкуÑÑ–Ñ—"
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr "Перемикач бічної панелі"
@@ -4541,6 +6722,12 @@ msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ñ‡Ð°: ВИМКÐЕÐО"
msgid "ToggleButton|Toggle Status: ON"
msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ñ‡Ð°: УВІМКÐЕÐО"
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr "Загальна кількіÑÑ‚ÑŒ внеÑків"
+
msgid "Total Time"
msgstr "Загальний чаÑ"
@@ -4551,7 +6738,7 @@ msgid "Total: %{total}"
msgstr "Ð’Ñього: %{total}"
msgid "Track activity with Contribution Analytics."
-msgstr "ВідÑтежувати активніÑÑ‚ÑŒ за допомогою Ðналітики учаÑників."
+msgstr "ВідÑтежувати активніÑÑ‚ÑŒ за допомогою аналітики учаÑників."
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr "ВідÑтежуйте групи проблем зі Ñпільною темою з різних проектів та етапів"
@@ -4559,12 +6746,30 @@ msgstr "ВідÑтежуйте групи проблем зі Ñпільною Ñ
msgid "Track time with quick actions"
msgstr "ВідÑтежуйте Ñ‡Ð°Ñ Ð·Ð° допомогою швидких дій"
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr "ЗапуÑтити цю ручну дію"
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr "Спробуйте ще раз"
+
msgid "Turn on Service Desk"
msgstr "Ввімкнути Service Desk"
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr "Ðеможливо завантажити порівнÑÐ½Ð½Ñ (diff). %{button_try_again}"
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr "Ðеможливо увійти до групи за допомогою SAML через \"%{reason}\""
+
msgid "Unknown"
msgstr "Ðевідомо"
@@ -4577,12 +6782,45 @@ msgstr "Розблоковано"
msgid "Unresolve discussion"
msgstr "Повторно відкрити обговореннÑ"
+msgid "Unstage all changes"
+msgstr "Ðе індекÑувати вÑÑ– зміни"
+
+msgid "Unstage changes"
+msgstr "Ðе індекÑувати зміни"
+
+msgid "Unstaged"
+msgstr "ÐеіндекÑовано"
+
+msgid "Unstaged %{type}"
+msgstr "ÐеіндекÑовано %{type}"
+
+msgid "Unstaged and staged %{type}"
+msgstr "ÐеіндекÑовано та проіндекÑовано %{type}"
+
msgid "Unstar"
msgstr "Видалити із обраних"
+msgid "Unsubscribe"
+msgstr "ВідпиÑатиÑÑ"
+
+msgid "Unsubscribe at group level"
+msgstr "ВідпиÑатиÑÑ Ð½Ð° рівні групи"
+
+msgid "Unsubscribe at project level"
+msgstr "ВідпиÑатиÑÑ Ð½Ð° рівні проекту"
+
+msgid "Unverified"
+msgstr "Ðепідтверджено"
+
msgid "Up to date"
msgstr "Ðктуальний"
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr "Оновіть Ñ–Ð¼â€™Ñ Ð³Ñ€ÑƒÐ¿Ð¸, опиÑ, аватар та інші загальні налаштуваннÑ."
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "Перейдіть на вищий тарифний план щоб активувати Покращений Глобальний Пошук."
@@ -4598,6 +6836,9 @@ msgstr "Перейдіть на вищий тарифний план щоб ак
msgid "Upgrade your plan to improve Issue boards."
msgstr "Перейдіть на вищий тарифний план щоб покращити дошки обговорень."
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr "Завантажити новий файл"
@@ -4611,14 +6852,23 @@ msgid "UploadLink|click to upload"
msgstr "ÐатиÑніть, щоб завантажити"
msgid "Upvotes"
-msgstr ""
+msgstr "Лайки"
msgid "Usage statistics"
+msgstr "СтатиÑтика викориÑтаннÑ"
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr "ВикориÑтовуйте Service Desk Ð´Ð»Ñ Ð·Ð²â€™Ñзку з вашими кориÑтувачами (наприклад, щоб запропонувати клієнтÑьку підтримку) через електронну пошту безпоÑередньо із GitLab"
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr "ВикориÑтовуйте групові етапи, щоб керувати у одному етапі проблеми з різних проектів."
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "ВикориÑтовувати токен під Ñ‡Ð°Ñ ÑƒÑтановки:"
@@ -4626,25 +6876,40 @@ msgid "Use your global notification setting"
msgstr "ВикориÑтовуютьÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ– Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ"
msgid "Used by members to sign in to your group in GitLab"
-msgstr ""
+msgstr "ВикориÑтовуєтьÑÑ ÑƒÑ‡Ð°Ñниками Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ у вашу групу в GitLab"
+
+msgid "User Settings"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
msgid "User and IP Rate Limits"
+msgstr "Ліміти чаÑтоти Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів та IP"
+
+msgid "User map"
msgstr ""
+msgid "Users"
+msgstr "КориÑтувачі"
+
+msgid "Variables"
+msgstr "Змінні"
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr "Змінні заÑтоÑовуютьÑÑ Ð´Ð¾ Ñередовищ через runner. Вони можуть бути захищені, в такому випадку вони доÑтупні тільки Ð´Ð»Ñ Ð·Ð°Ñ…Ð¸Ñ‰ÐµÐ½Ð¸Ñ… гілок та тегів. Ви можете викориÑтовувати змінні Ð´Ð»Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ–Ð², Ñекретний ключів тощо."
msgid "Various container registry settings."
-msgstr ""
+msgstr "Різноманітні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑ”Ñтру контейнерів."
msgid "Various email settings."
-msgstr ""
+msgstr "Різноманітні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ÑˆÑ‚Ð¸."
msgid "Various settings that affect GitLab performance."
-msgstr ""
+msgstr "Різноманітні налаштуваннÑ, що впливають на продуктивніÑÑ‚ÑŒ GitLab."
+
+msgid "Verification information"
+msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ перевірку"
-msgid "View and edit lines"
-msgstr "ПереглÑнути та відредагувати Ñ€Ñдки"
+msgid "Verified"
+msgstr "Підтверджено"
msgid "View epics list"
msgstr "ПереглÑнути ÑпиÑок епіків"
@@ -4655,9 +6920,21 @@ msgstr "ПереглÑд файла @ "
msgid "View group labels"
msgstr "ПереглÑнути мітки групи"
+msgid "View issue"
+msgstr "ПереглÑнути проблему"
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr "ПереглÑнути завданнÑ"
+
msgid "View labels"
msgstr "ПереглÑнути мітки"
+msgid "View log"
+msgstr "ПереглÑнути журнал"
+
msgid "View open merge request"
msgstr "ПереглÑнути відкритий запит на злиттÑ"
@@ -4668,6 +6945,12 @@ msgid "View replaced file @ "
msgstr "ПереглÑд заміненого файлу @ "
msgid "Visibility and access controls"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ð¸Ð¼Ð¾ÑÑ‚Ñ– та доÑтупу"
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
msgstr ""
msgid "VisibilityLevel|Internal"
@@ -4685,8 +6968,8 @@ msgstr "Ðевідомий"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Хочете побачити дані? Будь лаÑка, попроÑить у адмініÑтратора доÑтуп."
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
-msgstr "Ми не змогли перевірити, що один із ваших проектів в GCP має ввімкнений білінг. Будь лаÑка, Ñпробуйте ще раз."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "Ми не маємо доÑтатньо даних Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
@@ -4698,16 +6981,28 @@ msgid "Web IDE"
msgstr "Веб-IDE"
msgid "Web terminal"
-msgstr ""
+msgstr "Веб-термінал"
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr "Веб-гук дозволÑÑ” вам викликати URL Ñкщо, наприклад, був відправлений новий код або Ñтворено нову проблему. Ви можете налаштувати його так, щоб він реагував на певні події (відправки коду, проблеми або запити на злиттÑ). Групові веб-гуки заÑтоÑовуютьÑÑ Ð´Ð¾ вÑÑ–Ñ… проектів в групі Ñ– дозволÑÑŽÑ‚ÑŒ вам Ñтандартизувати Ñ—Ñ… Ð´Ð»Ñ Ð²Ñієї вашої групи."
+msgid "Weeks"
+msgstr "Тижні"
+
msgid "Weight"
msgstr "Вага"
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
-msgstr ""
+msgid "Weight %{weight}"
+msgstr "Вага %{weight}"
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr "Коли runner закріплений (за проектами), його не можна викориÑтовувати в інших проектах"
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr "Якщо увімкнено, кориÑтувачі не можуть викориÑтовувати GitLab, поки вони не приймуть умови."
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr "Якщо залишити URL порожнім, можна вÑтановлювати мітки клаÑифікації без Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ð¹ проекту та Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ñ— авторизації."
msgid "Wiki"
msgstr "Wiki"
@@ -4733,8 +7028,32 @@ msgstr "Порада: Ви можете переміÑтити цю Ñторін
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr "Вже Ñ–Ñнує Ñторінка з таким шлÑхом Ñ– заголовком."
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
-msgstr "Ви не можете Ñтворювати wiki-Ñторінки"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr "Запропонувати Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ wiki"
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr "Ви маєте бути учаÑником проекту Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб додавати wiki Ñторінки. Якщо у Ð²Ð°Ñ Ñ” пропозиції ÑтоÑовно Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ wiki цього проекту, відкрийте проблему в %{issues_link}."
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr "РеєÑÑ‚Ñ€ проблем"
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr "Wiki дозволÑÑ” зберігати інформацію про ваш проект. Ðаприклад причину ÑтвореннÑ, принципи, Ñк його викориÑтовувати Ñ– Ñ‚. д."
+
+msgid "WikiEmpty|Create your first page"
+msgstr "Створіть вашу першу Ñторінку"
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr "Запропонувати Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ wiki"
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr "Wiki дозволÑÑŽÑ‚ÑŒ пиÑати документацію Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проекту"
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr "Цей проект не має wiki Ñторінок"
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr "Ви повинні бути учаÑником проекту Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб додавати wiki Ñторінки."
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "Це — Ñтара верÑÑ–Ñ Ñторінки."
@@ -4769,6 +7088,12 @@ msgstr "Ðова wiki-Ñторінка"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr "Ви дійÑно бажаєте видалити цю Ñторінку?"
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr "Видалити Ñторінку"
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr "Видалити Ñторінку %{pageTitle}?"
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr "ХтоÑÑŒ редагував Ñторінку в той же чаÑ, що Ñ– ви. Будь лаÑка, ознайомтеÑÑ Ð· %{page_link} Ñ– переконайтеÑÑ, ваші зміни не затруть зміни інших."
@@ -4784,8 +7109,8 @@ msgstr "Оновити %{page_title}"
msgid "WikiPage|Page slug"
msgstr "ШлÑÑ… Ñторінки"
-msgid "WikiPage|Write your content or drag files here..."
-msgstr "Ðапишіть текÑÑ‚ або перетÑгніть файли Ñюди..."
+msgid "WikiPage|Write your content or drag files here…"
+msgstr "Ðапишіть текÑÑ‚ або перетÑгніть файли Ñюди…"
msgid "Wiki|Create Page"
msgstr "Створити Ñторінку"
@@ -4796,9 +7121,6 @@ msgstr "Створити Ñторінку"
msgid "Wiki|Edit Page"
msgstr "Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ cторінки"
-msgid "Wiki|Empty page"
-msgstr "ÐŸÐ¾Ñ€Ð¾Ð¶Ð½Ñ Ñторінка"
-
msgid "Wiki|More Pages"
msgstr "Більше Ñторінок"
@@ -4818,13 +7140,22 @@ msgid "Wiki|Wiki Pages"
msgstr "Wiki-Ñторінки"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr "З аналітикою учаÑників ви може вивчати активніÑÑ‚ÑŒ в обговореннÑÑ…, запитах на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– змінах у коді Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— організації та Ñ—Ñ— учаÑників."
+msgstr "З аналітикою контриб’юторів ви може вивчати активніÑÑ‚ÑŒ в обговореннÑÑ…, запитах на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– подій відправки коду Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— організації Ñ– Ñ—Ñ— учаÑників."
msgid "Withdraw Access Request"
msgstr "СкаÑувати запит доÑтупу"
-msgid "Write a commit message..."
-msgstr "Ðапишіть коміт-повідомленнÑ..."
+msgid "Yes"
+msgstr "Так"
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "Ви хочете видалити %{group_name}. Видалені групи ÐЕ МОЖÐРбуду відновити! Ви ÐБСОЛЮТÐО впевнені?"
@@ -4839,9 +7170,12 @@ msgid "You are going to transfer %{project_full_name} to another owner. Are you
msgstr "Ви збираєтеÑÑ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‚Ð¸ проект %{project_full_name} іншому влаÑнику. Ви ÐБСОЛЮТÐО впевнені?"
msgid "You are on a read-only GitLab instance."
-msgstr ""
+msgstr "Ви знаходитеÑÑ Ð½Ð° інÑтанÑÑ– Gitlab \"тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ\"."
+
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr "Ви знаходитеÑÑŒ на вторинному <b>лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ</b> Geo-вузлі. Якщо ви хочете внеÑти будь-Ñкі зміни, ви повинні відвідати %{primary_node}."
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4850,6 +7184,12 @@ msgstr "Ви також можете Ñтворити проект із кома
msgid "You can also star a label to make it a priority label."
msgstr "Ви можете додати мітку в обрані, щоб зробити її пріоритетною."
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr "Ви також можете перевірити Ñвій .gitlab-ci.yml за допомогою %{linkStart}Lint%{linkEnd}"
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr "Ви можете легко вÑтановити Runner на клаÑтері Kubernetes. %{link_to_help_page}"
@@ -4862,23 +7202,41 @@ msgstr "Ви можете додавати файли тільки коли пе
msgid "You can only edit files when you are on a branch"
msgstr "Ви можете редагувати файли, лише перебуваючи у ÑкійÑÑŒ гілці"
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr "Ви можете розв’Ñзати цей конфлікт Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð° допомогою інтерактивного режиму (викориÑтовуючи кнопки %{use_ours} та %{use_theirs}), або безпоÑередньо редагуючи файли. Закомітити зміни у %{branch_name}"
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr "Ви не можете запиÑувати на вторинні інÑтанÑи \"тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ\" GitLab Geo. Будь лаÑка викориÑтовуйте %{link_to_primary_node}."
msgid "You cannot write to this read-only GitLab instance."
msgstr "Ви не можете запиÑувати на цей \"тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ\" інÑÑ‚Ð°Ð½Ñ GitLab."
+msgid "You do not have any assigned merge requests"
+msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” призначених запитів на злиттÑ"
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” необхідних прав доÑтупу, щоб перевизначити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñинхронізації LDAP-груп."
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” прав доÑтупу"
+msgid "You have not created any merge requests"
+msgstr "Ви ще не Ñтворювали запитів на злиттÑ"
+
msgid "You have reached your project limit"
msgstr "Ви доÑÑгли Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð² вашому проекті"
-msgid "You must have master access to force delete a lock"
-msgstr "У Ð²Ð°Ñ Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ бути доÑтуп на рівні керівника, Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÑƒÑового Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr "Ви повинні прийнÑти правила кориÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑервіÑом Ñ– політику конфіденційноÑÑ‚Ñ– Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб Ñтворити обліковий запиÑ"
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr "Ви повинні мати доÑтуп керівника Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÑƒÑового Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ "
msgid "You must sign in to star a project"
msgstr "Ðеобхідно увійти, щоб оцінити проект"
@@ -4886,6 +7244,9 @@ msgstr "Ðеобхідно увійти, щоб оцінити проект"
msgid "You need a different license to enable FileLocks feature"
msgstr "Ð”Ð»Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ— функції Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¤Ð°Ð¹Ð»Ñ–Ð² вам потрібна інша ліцензіÑ"
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "Вам потрібен дозвіл"
@@ -4916,22 +7277,37 @@ msgstr "Ви не зможете відправлÑти та отримуватÐ
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "Вам необхідно викориÑтовувати різні імена гілок Ð´Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ð³Ð¾ порівнÑннÑ."
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr "Ви отримали це Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð° %{host}. %{manage_notifications_link} &middot; %{help_link}"
+
+msgid "YouTube"
msgstr ""
msgid "Your Groups"
-msgstr ""
+msgstr "Ваші групи"
msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ваш Kubernetes-клаÑтер вÑе ще доÑтупна Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð° цій Ñторінці, але ми радимо вимкнути Ñ– повторно налаштувати"
msgid "Your Projects (default)"
-msgstr ""
+msgstr "Ваші проекти (за замовчуваннÑм)"
msgid "Your Projects' Activity"
-msgstr ""
+msgstr "ÐктивніÑÑ‚ÑŒ ваших проектів"
msgid "Your Todos"
+msgstr "Ваші Задачі"
+
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
msgstr ""
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
@@ -4952,15 +7328,18 @@ msgstr "Ваше ім'Ñ"
msgid "Your projects"
msgstr "Ваші проекти"
+msgid "ago"
+msgstr "тому"
+
msgid "among other things"
-msgstr "між іншим"
+msgstr "тощо"
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "та 1 виправлена вразливіÑÑ‚ÑŒ"
+msgstr[1] "Ñ– %d виправлених вразливоÑтей"
+msgstr[2] "Ñ– %d виправлених вразливоÑтей"
+msgstr[3] "Ñ– %d виправлених вразливоÑтей"
msgid "assign yourself"
msgstr "призначити Ñебе"
@@ -4971,45 +7350,138 @@ msgstr "ім'Ñ Ð³Ñ–Ð»ÐºÐ¸"
msgid "by"
msgstr "від"
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr "%{linkStartTag}ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² %{linkEndTag}"
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr "%{linkStartTag}ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про DAST%{linkEndTag}"
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr "%{linkStartTag}ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей%{linkEndTag}"
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr "%{linkStartTag}ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про SAST%{linkEndTag}"
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr "%{vulnerability} впливає на %{namespace}."
+
+msgid "ciReport|%{packagesString} and "
+msgstr "%{packagesString} та "
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr "%{packagesString} та %{lastPackage}"
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr "%{remainingPackagesCount} більше"
+
+msgid "ciReport|%{reportName} is loading"
+msgstr "%{reportName} завантажуєтьÑÑ"
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr "%{reportName} видав помилку при завантаженні результатів"
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
-msgstr ""
+msgstr "%{type} не виÑвило нових вразливоÑтей"
msgid "ciReport|%{type} detected no security vulnerabilities"
-msgstr ""
+msgstr "%{type} не виÑвило вразливоÑтей"
+
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr "%{type} не виÑвив вразливоÑтей"
+
+msgid "ciReport|Class"
+msgstr "КлаÑ"
msgid "ciReport|Code quality"
msgstr "ЯкіÑÑ‚ÑŒ коду"
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
-msgstr "DAST не виÑвив попереджень при аналізі цього review app"
+msgid "ciReport|Confidence"
+msgstr "ВпевненіÑÑ‚ÑŒ"
-msgid "ciReport|Dependency scanning"
-msgstr ""
+msgid "ciReport|Container scanning detected"
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² виÑвило"
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² виÑвлÑÑ” відомі вразливоÑÑ‚Ñ– у ваших Docker образах."
+
+msgid "ciReport|Container scanning is loading"
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² завантажуєтьÑÑ"
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² видало помилку при завантаженні результатів"
+
+msgid "ciReport|DAST detected"
+msgstr "DAST виÑвив"
+
+msgid "ciReport|DAST is loading"
+msgstr "DAST завантажуєтьÑÑ"
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr "DAST видав помилку при завантаженні результатів"
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей виÑвлÑÑ” відомі вразливоÑÑ‚Ñ– у залежноÑÑ‚ÑÑ… вашого коду."
msgid "ciReport|Dependency scanning detected"
-msgstr ""
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей виÑвило"
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
-msgstr ""
+msgid "ciReport|Dependency scanning is loading"
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей завантажуєтьÑÑ"
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
-msgstr ""
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей видало помилку при завантаженні результатів"
+
+msgid "ciReport|Description"
+msgstr "ОпиÑ"
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr "Відхилити вразливіÑÑ‚ÑŒ"
+
+msgid "ciReport|Dismissed by"
+msgstr "Відхилено"
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr "Динамічне теÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ заÑтоÑунків (DAST) виÑвлÑÑ” відомі вразливоÑÑ‚Ñ– у вашому веб-заÑтоÑунку."
msgid "ciReport|Failed to load %{reportName} report"
msgstr "Помилка при завантаженні звіту %{reportName}"
+msgid "ciReport|File"
+msgstr "Файл"
+
msgid "ciReport|Fixed:"
msgstr "Виправлено:"
+msgid "ciReport|Identifiers"
+msgstr "Ідентифікатори"
+
msgid "ciReport|Instances"
msgstr "ІнÑтанÑи"
-msgid "ciReport|Learn more about whitelisting"
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr ""
+msgid "ciReport|Learn more about whitelisting"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про білий ÑпиÑок"
+
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñми виÑвило %{licenseInfo}"
+
+msgid "ciReport|License management detected no new licenses"
+msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñми не виÑвило нових ліцензій"
+
+msgid "ciReport|Links"
+msgstr "ПоÑиланнÑ"
+
msgid "ciReport|Loading %{reportName} report"
msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð²Ñ–Ñ‚Ñƒ %{reportName}"
+msgid "ciReport|Method"
+msgstr "СпоÑіб"
+
+msgid "ciReport|Namespace"
+msgstr "ПроÑÑ‚Ñ–Ñ€ імен"
+
msgid "ciReport|No changes to code quality"
msgstr "Ðемає змін у ÑкоÑÑ‚Ñ– коду"
@@ -5019,36 +7491,72 @@ msgstr "Ðемає змін у показниках продуктивноÑÑ‚Ñ–
msgid "ciReport|Performance metrics"
msgstr "Показники продуктивноÑÑ‚Ñ–"
-msgid "ciReport|SAST"
-msgstr "SAST"
+msgid "ciReport|Revert dismissal"
+msgstr "Відмінити відхиленнÑ"
msgid "ciReport|SAST detected"
msgstr "SAST виÑвив"
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr "SAST не виÑвив нових вразливоÑтей"
-
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr "SAST не виÑвив жодних вразливоÑтей"
+msgid "ciReport|SAST is loading"
+msgstr "SAST завантажуєтьÑÑ"
-msgid "ciReport|SAST:container no vulnerabilities were found"
-msgstr ""
+msgid "ciReport|SAST resulted in error while loading results"
+msgstr "SAST видав помилку при завантаженні результатів"
msgid "ciReport|Security scanning"
-msgstr ""
+msgstr "Перевірка безпеки"
msgid "ciReport|Security scanning failed loading any results"
+msgstr "Помилка при завантаженні результатів перевірки безпеки"
+
+msgid "ciReport|Security scanning is loading"
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ завантажуєтьÑÑ"
+
+msgid "ciReport|Severity"
+msgstr "СерйозніÑÑ‚ÑŒ"
+
+msgid "ciReport|Solution"
+msgstr "РішеннÑ"
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr "Статичне теÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ заÑтоÑунків (SAST) виÑвлÑÑ” відомі вразливоÑÑ‚Ñ– у вашому коді."
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr "Помилка при Ñтворенні проблеми. Будь лаÑка Ñпробуйте знову."
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr "Помилка при відхиленні вразливоÑÑ‚Ñ–. Будь лаÑка, Ñпробуйте знову."
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr "Помилка при завантаженні звіту DAST"
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr "Помилка при завантаженні звіту SAST"
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr "Помилка при завантаженні звіту по Ñкануванню контейнерів"
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr "Помилка при завантаженні звіту по Ñкануванню залежноÑтей"
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
-msgstr "Показати повний звіт про вразливоÑÑ‚Ñ– в коді"
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr "Ðезатверджені вразливоÑÑ‚Ñ– (червоні) можуть бути відмічені Ñк затверджені."
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
-msgstr "Ðезатверджені вразливоÑÑ‚Ñ– (червоні) можуть бути відмічені Ñк затверджені. %{helpLink}"
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr "Оновити %{name} з %{version} до %{fixed}."
-msgid "ciReport|no vulnerabilities"
+msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|no vulnerabilities"
+msgstr "немає вразливоÑтей"
+
+msgid "ciReport|on pipeline"
+msgstr "в конвеєрі"
+
msgid "command line instructions"
msgstr "інÑтрукції Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð³Ð¾ Ñ€Ñдка"
@@ -5056,6 +7564,9 @@ msgid "connecting"
msgstr "з'єднаннÑ"
msgid "could not read private key, is the passphrase correct?"
+msgstr "неможливо зчитати приватний ключ, чи є пароль правильним?"
+
+msgid "customize"
msgstr ""
msgid "day"
@@ -5065,35 +7576,50 @@ msgstr[1] "дні"
msgstr[2] "днів"
msgstr[3] "днів"
+msgid "deploy token"
+msgstr "токен Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "виÑвлено %d виправлену вразливіÑÑ‚ÑŒ"
+msgstr[1] "виÑвлено %d виправлених вразливоÑтей"
+msgstr[2] "виÑвлено %d виправлених вразливоÑтей"
+msgstr[3] "виÑвлено %d виправлених вразливоÑтей"
msgid "detected %d new vulnerability"
msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "виÑвлено %d нову вразливіÑÑ‚ÑŒ"
+msgstr[1] "виÑвлено %d нових вразливоÑтей"
+msgstr[2] "виÑвлено %d нових вразливоÑтей"
+msgstr[3] "виÑвлено %d нових вразливоÑтей"
msgid "detected no vulnerabilities"
+msgstr "не виÑвило вразливоÑтей"
+
+msgid "disabled"
+msgstr "вимкнено"
+
+msgid "done"
msgstr ""
+msgid "enabled"
+msgstr "увімкнено"
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr "%{slash_command} перезезапиÑує запланований Ñ‡Ð°Ñ Ð¾Ñтаннім значеннÑм."
+msgid "for this project"
+msgstr "Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
+
msgid "here"
msgstr "тут"
+msgid "import flow"
+msgstr ""
+
msgid "importing"
msgstr "імпорт"
-msgid "in progress"
-msgstr ""
-
msgid "is invalid because there is downstream lock"
msgstr "неправильний через наÑвніÑÑ‚ÑŒ блокувань на нижчих рівнÑÑ…"
@@ -5101,7 +7627,10 @@ msgid "is invalid because there is upstream lock"
msgstr "неправильний через наÑвніÑÑ‚ÑŒ блокувань на вищих рівнÑÑ…"
msgid "is not a valid X509 certificate."
-msgstr ""
+msgstr "не відповідний Ñертифікат X509."
+
+msgid "latest version"
+msgstr "оÑÑ‚Ð°Ð½Ð½Ñ Ð²ÐµÑ€ÑÑ–Ñ"
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr "заблоковано %{path_lock_user_name} %{created_at}"
@@ -5117,37 +7646,31 @@ msgid "mrWidget| Please restore it or use a different %{missingBranchName} branc
msgstr "Будь лаÑка відновіть Ñ—Ñ— або викориÑтовуйте іншу %{missingBranchName} гілку"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ %{metricsLinkStart} пам’ÑÑ‚Ñ– %{metricsLinkEnd} %{emphasisStart} впало %{emphasisEnd} з %{memoryFrom}Мб до %{memoryTo}Мб"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ %{metricsLinkStart} пам’ÑÑ‚Ñ– %{metricsLinkEnd} %{emphasisStart} зроÑло %{emphasisEnd} з %{memoryFrom}Мб до %{memoryTo}Мб"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
-msgstr ""
+msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ %{metricsLinkStart} пам’ÑÑ‚Ñ– %{metricsLinkEnd} %{emphasisStart} не змінилоÑÑ %{emphasisEnd} %{memoryFrom}Мб"
msgid "mrWidget|Add approval"
msgstr "Додати затвердженнÑ"
-msgid "mrWidget|Allows edits from maintainers"
-msgstr "Дозволити Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¾ÑŽ проекту"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr "ДозволÑÑ” коміти від учаÑників, Ñкі можуть зливати до цільової гілки"
msgid "mrWidget|An error occured while removing your approval."
msgstr "Під Ñ‡Ð°Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°."
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr "Помилка при отриманні даних про Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиту на злиттÑ."
-
-msgid "mrWidget|An error occured while submitting your approval."
-msgstr "Помилка при обробці вашого затвердженнÑ."
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
msgid "mrWidget|Approve"
msgstr "Затвердити"
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
-msgstr ""
+msgstr "Затверджено"
msgid "mrWidget|Cancel automatic merge"
msgstr "СкаÑувати автоматичне злиттÑ"
@@ -5173,8 +7696,11 @@ msgstr "Закритий"
msgid "mrWidget|Closes"
msgstr "Закриває"
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr "Створіть проблему, щоб вирішити їх пізніше"
+
msgid "mrWidget|Deployment statistics are not available currently"
-msgstr ""
+msgstr "СтатиÑтика Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð°Ñ€Ð°Ð·Ñ– недоÑтупна"
msgid "mrWidget|Did not close"
msgstr "Ðе закрив"
@@ -5183,7 +7709,7 @@ msgid "mrWidget|Email patches"
msgstr "Email-патчі"
msgid "mrWidget|Failed to load deployment statistics"
-msgstr ""
+msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ ÑтатиÑтику розгортаннÑ"
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr "Якщо гілка %{branch} Ñ–Ñнує у вашому локальному репозиторії, то ви можете заÑтоÑувати цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ за допомогою"
@@ -5192,7 +7718,7 @@ msgid "mrWidget|If the %{missingBranchName} branch exists in your local reposito
msgstr "Якщо гілка %{missingBranchName} Ñ–Ñнує у вашому локальному репозиторії, то ви можете заÑтоÑувати цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ за допомогою командного Ñ€Ñдка"
msgid "mrWidget|Loading deployment statistics"
-msgstr ""
+msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÑтатиÑтики розгортаннÑ"
msgid "mrWidget|Mentions"
msgstr "Згадки"
@@ -5206,9 +7732,24 @@ msgstr "Ð—Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¾Ð¹ÑˆÐ»Ð¾ невдало."
msgid "mrWidget|Merge locally"
msgstr "Злити локально"
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr "Злито"
+msgid "mrWidget|No Approval required"
+msgstr "Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð½Ðµ потрібне"
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr "Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð½Ðµ Ñ” обов’Ñзковим; але ви вÑе одно можете це зробити"
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr "ПроÑте порівнÑÐ½Ð½Ñ (diff)"
@@ -5269,6 +7810,9 @@ msgstr "Гілку-джерело не буде видалено"
msgid "mrWidget|There are merge conflicts"
msgstr "Ñ–Ñнують конфлікти при злитті"
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr "ПриÑутні незавершені обговореннÑ. Будь лаÑка завершіть Ñ—Ñ…"
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr "ВідбулаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при автоматичному злитті цього запиту"
@@ -5278,9 +7822,6 @@ msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð² процеÑÑ– виконаннÑ"
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "Цей проект заархівований, доÑтуп до запиÑу було відключено"
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "Ви можете прийнÑти цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ за допомогою"
@@ -5322,22 +7863,31 @@ msgid "personal access token"
msgstr "оÑобиÑтий токен доÑтупу"
msgid "private key does not match certificate."
-msgstr ""
+msgstr "приватний ключ не відповідає Ñертифікату."
+
+msgid "remaining"
+msgstr "залишилоÑÑŒ"
msgid "remove due date"
msgstr "видалити заплановану дату завершеннÑ"
+msgid "remove weight"
+msgstr "видалити вагу"
+
msgid "source"
msgstr "джерело"
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "%{slash_command} оновлює Ñуму витраченого чаÑу."
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr "цей документ"
msgid "to help your contributors communicate effectively!"
-msgstr "щоб допомогти учаÑникам ефективно ÑпілкуватиÑÑ!"
+msgstr "щоб допомогти вашим контриб’юторам ефективно ÑпілкуватиÑÑ!"
msgid "username"
msgstr "ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
@@ -5345,6 +7895,16 @@ msgstr "ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
msgid "uses Kubernetes clusters to deploy your code!"
msgstr "викориÑтовує клаÑтери Kubernetes Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ!"
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr "з %{additions} додаваннÑми Ñ– %{deletions} видаленнÑми."
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] "протÑгом %d хвилини "
+msgstr[1] "протÑгом %d хвилин "
+msgstr[2] "протÑгом %d хвилин "
+msgstr[3] "протÑгом %d хвилин "
+
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index c25e892e568..07537eda598 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:36-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -15,9 +13,22 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: zh-CN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
msgid " and"
-msgstr " 和"
+msgstr "和"
+
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] " ä¸‹é™ %d 点"
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] " æ高 %d 点"
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] "%d 个已更改的文件"
msgid "%d commit"
msgid_plural "%d commits"
@@ -37,7 +48,7 @@ msgstr[0] "%d 个议题"
msgid "%d layer"
msgid_plural "%d layers"
-msgstr[0] "%d 层"
+msgstr[0] "%d 个层"
msgid "%d merge request"
msgid_plural "%d merge requests"
@@ -45,56 +56,135 @@ msgstr[0] "%d 个åˆå¹¶è¯·æ±‚"
msgid "%d metric"
msgid_plural "%d metrics"
-msgstr[0] "%d 指标"
+msgstr[0] "%d 个指标"
+
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] "%d 个新许å¯è¯"
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] "%d 个已暂存的修改"
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] "%d 个未暂存的修改"
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] "%d 个æ¼æ´ž"
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] "为æ高页é¢åŠ è½½é€Ÿåº¦åŠæ€§èƒ½ï¼Œå·²çœç•¥äº† %s 次æ交。"
msgid "%{actionText} & %{openOrClose} %{noteable}"
-msgstr "%{actionText} & %{openOrClose} %{noteable}"
+msgstr "%{actionText} 和 %{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr "ç”± %{commit_author_link} æ交于 %{commit_timeago}"
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} ä½å‚与者"
+msgid "%{filePath} deleted"
+msgstr "%{filePath} 已删除"
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr "%{group_docs_link_start}群组%{group_docs_link_end} å…许您管ç†ã€å作多个项目。群组的æˆå‘˜å¯ä»¥è®¿é—®ç¾¤ç»„下的所有项目。"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} 已开始"
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr "%{lock_path} 被GitLab用户 %{lock_user_id} é”定"
+msgid "%{name}'s avatar"
+msgstr "%{name} 的头åƒ"
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr "%{nip_domain} å¯ä»¥æ›¿ä»£è‡ªå®šä¹‰åŸŸä½¿ç”¨ã€‚"
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
-msgstr "%{number_commits_behind} 个è½åŽ %{default_branch} 分支的æ交, %{number_commits_ahead} 早超å‰çš„æ交"
+msgstr "%{number_commits_behind} 个è½åŽ %{default_branch} 分支的æ交, %{number_commits_ahead} 个超å‰çš„æ交"
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "已失败 %{number_of_failures} 次/最多å…许失败失败 %{maximum_failures} 次,GitLab 将继续é‡è¯•ã€‚"
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
-msgstr "已失败 %{number_of_failures} 次/最多å…许失败 %{maximum_failures} 次,GitLab ä¸ä¼šç»§ç»­è‡ªåŠ¨é‡è¯•ã€‚请在问题解决åŽé‡ç½®å­˜å‚¨å¥åº·ä¿¡æ¯ã€‚"
+msgstr "已失败 %{number_of_failures} 次/最多å…许失败 %{maximum_failures} 次,GitLab å°†ä¸å†è‡ªåŠ¨é‡è¯•ã€‚请在问题解决åŽæ‰‹åŠ¨é‡ç½®å­˜å‚¨ç©ºé—´ä¿¡æ¯ã€‚"
msgid "%{openOrClose} %{noteable}"
msgstr "%{openOrClose} %{noteable}"
+msgid "%{percent}%% complete"
+msgstr "å·²å®Œæˆ %{percent}%%"
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] "%{storage_name}:已 %{failed_attempts} 次å°è¯•è®¿é—®å­˜å‚¨å¤±è´¥ï¼š"
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+
msgid "%{text} is available"
msgstr "%{text}å¯ç”¨"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(如需了解更多的安装信æ¯ï¼Œè¯·æŸ¥çœ‹ %{link})"
+msgid "%{title} changes"
+msgstr "%{title}更改"
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] "%{type}检测到%{vulnerabilityCount}个æ¼æ´ž"
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr "%{unstaged}个未暂存的更改åŠ%{staged}个已暂存的更改"
msgid "+ %{moreCount} more"
-msgstr "+ 还有 %{moreCount} æ¡"
+msgstr "+ 其余 %{moreCount} 项"
+
+msgid "- Runner is active and can process any new jobs"
+msgstr "- Runnerå·²å¯ç”¨ï¼Œéšæ—¶å¯ä»¥å¤„ç†æ–°ä½œä¸š"
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr "- Runner已暂åœï¼Œæš‚æ—¶ä¸ä¼šæŽ¥å—新的作业"
msgid "- show less"
msgstr "- 显示较少"
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] "%{count} 个 %{type} 的添加"
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] "%{count} 个 %{type} 的修改"
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] "%d 个已关闭的议题"
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] "%d 个已关闭的åˆå¹¶è¯·æ±‚"
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] "%d 个已åˆå¹¶çš„åˆå¹¶è¯·æ±‚"
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] "%d 个开å¯ä¸­çš„议题"
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] "%d 个开å¯ä¸­çš„åˆå¹¶è¯·æ±‚"
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "%d æ¡æµæ°´çº¿"
@@ -105,9 +195,51 @@ msgstr "最高贡献"
msgid "2FA enabled"
msgstr "å¯ç”¨ä¸¤æ­¥éªŒè¯"
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr "请è”系您的 GitLab 管ç†å‘˜èŽ·å–访问æƒé™ã€‚"
+
+msgid "403|You don't have the permission to access this page."
+msgstr "您没有æƒé™è®¿é—®æ­¤é¡µé¢ã€‚"
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr "请确认您访问的地å€æ­£ç¡®å¹¶ä¸”页é¢æœªè¢«ç§»åŠ¨ã€‚"
+
+msgid "404|Page Not Found"
+msgstr "页é¢æœªæ‰¾åˆ°"
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr "如果您觉得这是一个错误的æ示信æ¯ï¼Œè¯·è”系您的 GitLab 管ç†å‘˜ã€‚"
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr "已创建 <strong>%{created_count}</strong> 个, 已关闭 <strong>%{accepted_count}</strong> 个。"
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr "已创建 <strong>%{created_count}</strong> 个, 已关闭 <strong>%{closed_count}</strong> 个。"
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr "<strong>%{pushes}</strong> 个推é€ï¼Œè¶…å‰ <strong>%{people}</strong> å贡献者的 <strong>%{commits}</strong> 个æ交。"
+
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>删除</strong>æºåˆ†æ”¯"
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr "Runner是一个执行任务的进程。您å¯ä»¥æ ¹æ®éœ€è¦é…置任æ„æ•°é‡çš„Runner。"
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "æŒç»­é›†æˆæ•°æ®å›¾"
@@ -117,33 +249,63 @@ msgstr "将在派生(fork)项目中中创建一个新的分支, 并开å¯ä¸€ä¸ªæ
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr "项目å¯ä»¥ç”¨äºŽå­˜æ”¾æ–‡ä»¶(仓库),安排计划(议题),并å‘布文档(wiki), %{among_other_things_link}。"
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr "用于在作业跟踪中查找匹é…测试覆盖率输出的正则表达å¼ã€‚留空则ç¦ç”¨"
+
msgid "A user with write access to the source branch selected this option"
msgstr "具有对æºåˆ†æ”¯çš„写入æƒé™çš„用户选择了此选项"
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr "关于自动部署"
+msgid "About this feature"
+msgstr "关于此功能"
+
msgid "Abuse Reports"
msgstr "滥用报告"
msgid "Abuse reports"
-msgstr ""
+msgstr "滥用报告"
+
+msgid "Accept terms"
+msgstr "接å—æ¡æ¬¾"
+
+msgid "Accepted MR"
+msgstr "接å—åˆå¹¶è¯·æ±‚"
msgid "Access Tokens"
msgstr "访问令牌"
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr "ä¸å…许访问%{classification_label}"
+
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 "为方便修å¤æŒ‚载问题,访问故障存储已被暂时ç¦ç”¨ã€‚在问题解决åŽè¯·é‡ç½®å­˜å‚¨å¥åº·ä¿¡æ¯ï¼Œä»¥å…许å†æ¬¡è®¿é—®ã€‚"
+msgstr "为方便修å¤æŒ‚载问题,访问故障存储已被暂时ç¦ç”¨ã€‚在问题解决åŽè¯·é‡ç½®å­˜å‚¨è¿è¡ŒçŠ¶å†µä¿¡æ¯ï¼Œä»¥å…许å†æ¬¡è®¿é—®ã€‚"
+
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr "访问您的 runner 令牌,自定义æµæ°´çº¿é…置,以åŠæŸ¥çœ‹æµæ°´çº¿çŠ¶æ€å’Œè¦†ç›–率报告。"
msgid "Account"
msgstr "å¸å·"
-msgid "Account and limit settings"
-msgstr "å¸æˆ·å’Œé™åˆ¶è®¾ç½®"
+msgid "Account and limit"
+msgstr "å¸æˆ·å’Œé™åˆ¶"
msgid "Active"
msgstr "å¯ç”¨"
+msgid "Active Sessions"
+msgstr "活动会è¯"
+
msgid "Activity"
msgstr "活动"
@@ -157,7 +319,7 @@ msgid "Add Contribution guide"
msgstr "添加贡献指å—"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr "添加组Webhookså’ŒGitLabä¼ä¸šç‰ˆã€‚"
+msgstr "添加组 Webhooks å’Œ GitLab ä¼ä¸šç‰ˆã€‚"
msgid "Add Kubernetes cluster"
msgstr "添加 Kubernetes 集群"
@@ -168,12 +330,39 @@ msgstr "添加许å¯è¯"
msgid "Add Readme"
msgstr "添加自述文件"
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr "添加包å«åœ¨æ‰€æœ‰ç”µå­é‚®ä»¶ä¸­çš„附加文本。 长度ä¸è¶…过%{character_limit} 字符"
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "添加目录"
+msgid "Add reaction"
+msgstr "添加回应"
+
msgid "Add todo"
msgstr "添加待办事项"
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr "附加文本"
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr "åœæ­¢æ‰€æœ‰ä½œä¸š"
@@ -190,7 +379,7 @@ msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs
msgstr "您å³å°†åœæ­¢æ‰€æœ‰ä½œä¸šã€‚所有正在è¿è¡Œçš„都会被åœæ­¢ã€‚"
msgid "AdminHealthPageLink|health page"
-msgstr "å¥åº·é¡µé¢"
+msgstr "è¿è¡ŒçŠ¶å†µé¡µé¢"
msgid "AdminProjects|Delete"
msgstr "删除"
@@ -240,30 +429,75 @@ msgstr "所有更改å‡å·²æ交"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "从模æ¿æˆ–导入时为空白项目将å¯ç”¨æ‰€æœ‰åŠŸèƒ½ï¼Œä½†å¯ä»¥åœ¨é¡¹ç›®è®¾ç½®ä¸­å°†å…¶ç¦ç”¨ã€‚"
-msgid "Allow edits from maintainers."
-msgstr "å…许上游项目维护人员进行编辑。"
+msgid "Allow commits from members who can merge to the target branch."
+msgstr "具有åˆå¹¶åˆ°ç›®æ ‡åˆ†æ”¯æƒé™çš„æˆå‘˜å…许æ交"
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr "å…许所有人访问æµæ°´çº¿å’Œä½œä¸šè¯¦æƒ…,包括输出日志和工件"
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
-msgstr ""
+msgstr "å…许在Asciidoc文档中渲染PlantUML图。"
msgid "Allow requests to the local network from hooks and services."
-msgstr ""
+msgstr "å…许æ¥è‡ªé’©å­å’ŒæœåŠ¡çš„对本地网络的请求。"
msgid "Allows you to add and manage Kubernetes clusters."
msgstr "这里å¯ä»¥æ·»åŠ å’Œç®¡ç† Kubernetes 集群。"
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
-msgstr ""
+msgstr "也称为“签å‘者â€æˆ–“ä¾èµ–方信任标识符â€"
msgid "Also called \"Relying party service URL\" or \"Reply URL\""
-msgstr ""
+msgstr "也称为“ä¾èµ–æ–¹æœåŠ¡URLâ€æˆ–“回å¤URLâ€"
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr "此外,也å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。创建Personal Access Token时,在范围中需选择 <code>repo</code> ,以便显示å¯ä¾›è¿žæŽ¥çš„公开和ç§æœ‰çš„仓库列表。"
+msgstr "此外,也å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。创建 Personal Access Token 时,在范围中需选择 <code>repo</code> ,以便显示å¯ä¾›è¿žæŽ¥çš„公开和ç§æœ‰çš„仓库列表。"
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "此外,也å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。创建Personal Access Token时,在范围中需选择 <code>repo</code> ,以便显示å¯ä¾›å¯¼å…¥å…¬å¼€å’Œç§æœ‰çš„仓库列表"
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr "æ交更改时å‘生错误。"
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr "创建新分支时å‘生错误。"
+
+msgid "An error occured whilst fetching the job trace."
+msgstr "获å–作业日志时å‘生错误。"
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr "获å–æµæ°´çº¿æ—¶å‘生错误。"
+
+msgid "An error occured whilst loading all the files."
+msgstr "所有文件都无法加载。"
+
+msgid "An error occured whilst loading the file content."
+msgstr "加载文件评论时å‘生错误。"
+
+msgid "An error occured whilst loading the file."
+msgstr "加载文件时å‘生错误。"
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr "加载åˆå¹¶è¯·æ±‚å˜æ›´å†…容时å‘生错误。"
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr "加载åˆå¹¶è¯·æ±‚的版本数æ®æ—¶å‘生错误。"
+
+msgid "An error occured whilst loading the merge request."
+msgstr "加载åˆå¹¶è¯·æ±‚æ—¶å‘生错误。"
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr "加载æµæ°´çº¿ä½œä¸šæ—¶å‘生错误。"
+
msgid "An error occurred previewing the blob"
msgstr "预览 blob 时出错"
@@ -274,11 +508,14 @@ msgid "An error occurred when updating the issue weight"
msgstr "更新议题æƒé‡æ—¶å‘生错误"
msgid "An error occurred while adding approver"
-msgstr "添加批准人时å‘生错误"
+msgstr "添加审批者时出错"
msgid "An error occurred while detecting host keys"
msgstr "检测主机密钥时å‘生错误"
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr "消除警告时å‘生错误。请刷新页é¢å¹¶å†æ¬¡å°è¯•ã€‚"
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr "关闭功能çªå‡ºæ˜¾ç¤ºæ—¶å‘生错误。请刷新页é¢å¹¶å†æ¬¡å°è¯•ã€‚"
@@ -294,14 +531,14 @@ msgstr "获å–æµæ°´çº¿æ—¶å‡ºé”™"
msgid "An error occurred while getting projects"
msgstr "获å–项目时å‘生错误"
-msgid "An error occurred while importing project"
-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 commits"
-msgstr "加载æ交时å‘生错误"
+msgid "An error occurred while loading commit signatures"
+msgstr ""
msgid "An error occurred while loading diff"
msgstr "加载差异时å‘生错误"
@@ -336,18 +573,42 @@ msgstr "ä¿å­˜LDAP覆盖状æ€æ—¶å‘生错误。请å†è¯•ä¸€æ¬¡ã€‚"
msgid "An error occurred while saving assignees"
msgstr "ä¿å­˜è¢«æŒ‡æ´¾äººæ—¶å‡ºçŽ°é”™è¯¯ã€‚"
+msgid "An error occurred while subscribing to notifications."
+msgstr "订阅通知时å‘生错误。"
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr "å–消订阅通知时å‘生错误。"
+
msgid "An error occurred while validating username"
msgstr "验è¯ç”¨æˆ·åæ—¶å‘生错误"
msgid "An error occurred. Please try again."
msgstr "å‘生了错误,请å†è¯•ä¸€æ¬¡ã€‚"
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr "任何标记"
msgid "Appearance"
msgstr "外观"
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr "应用程åº"
@@ -357,20 +618,29 @@ msgstr "å››"
msgid "April"
msgstr "四月"
-msgid "Archived project! Repository is read-only"
-msgstr "项目已归档ï¼ä»“库为åªè¯»çŠ¶æ€"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr "已归档项目ï¼ä»“库和其他项目资æºå‡ä¸ºåªè¯»"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "确定è¦åˆ é™¤æ­¤æµæ°´çº¿è®¡åˆ’å—?"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+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 "确定è¦é‡ç½®å¥åº·æ£€æŸ¥ä»¤ç‰Œå—?"
+msgstr "确定è¦é‡ç½®è¿è¡ŒçŠ¶å†µæ£€æŸ¥ä»¤ç‰Œå—?"
msgid "Are you sure you want to unlock %{path_lock_path}?"
-msgstr "确定è¦è§£é” %{path_lock_path} å—?"
+msgstr "你确定è¦è§£é” %{path_lock_path} å—?"
msgid "Are you sure?"
msgstr "确定å—?"
@@ -378,8 +648,14 @@ msgstr "确定å—?"
msgid "Artifacts"
msgstr "产物"
+msgid "Ascending"
+msgstr "å‡åºæŽ’列"
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr "请群组维护者é…置一个群组级 Runner。"
+
msgid "Assertion consumer service URL"
-msgstr ""
+msgstr "断言消费者æœåŠ¡ URL"
msgid "Assign custom color like #FF0000"
msgstr "分é…自定义颜色,如FF0000"
@@ -402,12 +678,27 @@ msgstr "已分é…åˆå¹¶è¯·æ±‚"
msgid "Assigned to :name"
msgstr "已分é…至 :name"
+msgid "Assigned to me"
+msgstr "已分派给我"
+
msgid "Assignee"
msgstr "指派人"
+msgid "Assignee boards not available with your current license"
+msgstr "当å‰è®¸å¯è¯æ— æ³•ä½¿ç”¨æŒ‡æ´¾çœ‹æ¿"
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr "指派列表显示分é…给选定用户的所有议题。"
+
+msgid "Assignee(s)"
+msgstr "指派"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "拖放文件到此处或者 %{upload_link}"
+msgid "Audit Events"
+msgstr "审计事件"
+
msgid "Aug"
msgstr "å…«"
@@ -417,17 +708,41 @@ msgstr "八月"
msgid "Authentication Log"
msgstr "认è¯æ—¥å¿—"
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr "作者"
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr "作者:%{authors}"
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr "å¯ç”¨Auto DevOps"
msgid "Auto DevOps, runners and job artifacts"
-msgstr ""
+msgstr "Auto DevOps, runnersåŠä½œä¸šäº§ç‰©"
msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr "自动审阅程åºå’Œè‡ªåŠ¨éƒ¨ç½²ç¨‹åºéœ€è¦ %{kubernetes} æ‰èƒ½æ­£å¸¸å·¥ä½œã€‚"
@@ -438,8 +753,11 @@ msgstr "自动审阅程åºå’Œè‡ªåŠ¨éƒ¨ç½²ç¨‹åºéœ€è¦ä¸€ä¸ªåŸŸåå’Œ %{kubernete
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "自动审阅程åºå’Œè‡ªåŠ¨éƒ¨ç½²ç¨‹åºéœ€è¦ä¸€ä¸ªåŸŸåæ‰èƒ½æ­£å¸¸å·¥ä½œã€‚"
-msgid "AutoDevOps|Auto DevOps (Beta)"
-msgstr "DevOps 自动化(测试版)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr "自动å–消多余的和挂起的æµæ°´çº¿"
+
+msgid "AutoDevOps|Auto DevOps"
+msgstr "Auto DevOps"
msgid "AutoDevOps|Auto DevOps documentation"
msgstr "DevOps 自动化文档"
@@ -457,14 +775,20 @@ msgid "AutoDevOps|You can automatically build and test your application if you %
msgstr "如果当å‰é¡¹ç›®%{link_to_auto_devops_settings}, å¯ä»¥è‡ªåŠ¨çš„构建和测试应用。如果已%{link_to_add_kubernetes_cluster},则也å¯ä»¥å®žçŽ°è‡ªåŠ¨éƒ¨ç½²ã€‚"
msgid "AutoDevOps|add a Kubernetes cluster"
-msgstr "添加Kubernetes群集"
+msgstr "添加Kubernetes集群"
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
-msgstr "å¯ç”¨Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr "å¯ç”¨Auto DevOps"
msgid "Available"
msgstr "å¯ç”¨çš„"
+msgid "Available group Runners : %{runners}"
+msgstr "å¯ç”¨çš„群组Runner: %{runners}"
+
+msgid "Available group Runners : %{runners}."
+msgstr "å¯ç”¨çš„群组Runner: %{runners}."
+
msgid "Avatar will be removed. Are you sure?"
msgstr "å³å°†åˆ é™¤å¤´åƒã€‚确定继续å—?"
@@ -472,25 +796,106 @@ msgid "Average per day: %{average}"
msgstr "å¹³å‡æ¯å¤©: %{average}"
msgid "Background Color"
+msgstr "背景é¡è‰²"
+
+msgid "Background Jobs"
msgstr ""
+msgid "Background color"
+msgstr "背景颜色"
+
msgid "Background jobs"
-msgstr ""
+msgstr "åŽå°ä½œä¸š"
+
+msgid "Badges"
+msgstr "徽章"
+
+msgid "Badges|A new badge was added."
+msgstr "新徽章已添加。"
+
+msgid "Badges|Add badge"
+msgstr "添加徽章"
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr "添加徽章失败,请检查输入的网å€å¹¶é‡è¯•ã€‚"
+
+msgid "Badges|Badge image URL"
+msgstr "徽章图åƒç½‘å€"
+
+msgid "Badges|Badge image preview"
+msgstr "徽章图åƒé¢„览"
+
+msgid "Badges|Delete badge"
+msgstr "删除徽章"
+
+msgid "Badges|Delete badge?"
+msgstr "删除徽章�"
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr "删除徽章失败,请é‡è¯•ã€‚"
+
+msgid "Badges|Group Badge"
+msgstr "群组徽章"
+
+msgid "Badges|Link"
+msgstr "链接"
+
+msgid "Badges|No badge image"
+msgstr "无徽章图åƒ"
+
+msgid "Badges|No image to preview"
+msgstr "无图åƒå¯é¢„览"
+
+msgid "Badges|Project Badge"
+msgstr "项目徽章"
+
+msgid "Badges|Reload badge image"
+msgstr "é‡æ–°åŠ è½½å¾½ç« å›¾åƒ"
+
+msgid "Badges|Save changes"
+msgstr "ä¿å­˜æ›´æ”¹"
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr "ä¿å­˜å¾½ç« å¤±è´¥ï¼Œè¯·æ£€æŸ¥è¾“入的网å€å¹¶é‡è¯•ã€‚"
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr "%{docsLinkStart}å˜é‡%{docsLinkEnd} GitLab支æŒï¼š %{placeholders}"
+
+msgid "Badges|The badge was deleted."
+msgstr "徽章已删除。"
+
+msgid "Badges|The badge was saved."
+msgstr "徽章已ä¿å­˜ã€‚"
+
+msgid "Badges|This group has no badges"
+msgstr "当å‰ç¾¤ç»„无徽章"
+
+msgid "Badges|This project has no badges"
+msgstr "当å‰é¡¹ç›®æ— å¾½ç« "
+
+msgid "Badges|Your badges"
+msgstr "您的徽章"
msgid "Begin with the selected commit"
msgstr "从选定的æ交开始"
+msgid "Below are examples of regex for existing tools:"
+msgstr "以下是现有工具的正则表达å¼ç¤ºä¾‹ï¼š"
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
-msgstr "è´¦å•"
+msgstr "计费"
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
-msgstr "%{group_name}ç›®å‰ä½äºŽ%{plan_link}计划中。"
+msgstr "%{group_name} 正在使用 %{plan_link} 方案。"
msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
-msgstr "自动é™çº§ã€å‡çº§åˆ°æŸäº›è®¡åˆ’ç›®å‰ä¸å¯ç”¨ã€‚"
+msgstr "ç›®å‰æ— æ³•è‡ªåŠ¨é™çº§æˆ–å‡çº§åˆ°æŸäº›è®¡åˆ’。"
msgid "BillingPlans|Current plan"
-msgstr "当å‰è®¡åˆ’"
+msgstr "当å‰æ–¹æ¡ˆ"
msgid "BillingPlans|Customer Support"
msgstr "客户支æŒ"
@@ -498,29 +903,41 @@ msgstr "客户支æŒ"
msgid "BillingPlans|Downgrade"
msgstr "é™çº§"
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr "请查阅%{faq_link} 进一步了解æ¯ä¸ªè®¡åˆ’的相关信æ¯ã€‚"
msgid "BillingPlans|Manage plan"
-msgstr "管ç†è®¡åˆ’"
+msgstr "管ç†æ–¹æ¡ˆ"
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
msgstr "在这ç§æƒ…况下请è”ç³»%{customer_support_link}。"
msgid "BillingPlans|See all %{plan_name} features"
-msgstr "查看所有%{plan_name}功能"
+msgstr "查看 %{plan_name} 计划中的功能"
msgid "BillingPlans|This group uses the plan associated with its parent group."
-msgstr "该群组使用其父群组相关è”的计划。"
+msgstr "使用与其父项目一致的方案"
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
-msgstr "è¦ç®¡ç†æ­¤ç¾¤ç»„的计划,请访问%{parent_billing_page_link}çš„è´¦å•éƒ¨åˆ†ã€‚"
+msgstr "访问 %{parent_billing_page_link} 以管ç†è¯¥é¡¹ç›®çš„计费方案。"
msgid "BillingPlans|Upgrade"
msgstr "å‡çº§"
msgid "BillingPlans|You are currently on the %{plan_link} plan."
-msgstr "您目å‰æ­£åœ¨ä½¿ç”¨%{plan_link}计划。"
+msgstr "您目å‰æ­£åœ¨ä½¿ç”¨ %{plan_link} 方案。"
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
msgid "BillingPlans|frequently asked questions"
msgstr "常问问题"
@@ -534,6 +951,18 @@ msgstr "æ¯å¹´æ”¯ä»˜ %{price_per_year}"
msgid "BillingPlans|per user"
msgstr "æ¯ç”¨æˆ·"
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr "看æ¿"
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr "未在此项目的仓库中找到 %{branchName} 分支。"
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] "分支(%{branch_count})"
@@ -610,8 +1039,8 @@ msgstr "找ä¸åˆ°åˆ†æ”¯"
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr "确认执行 %{delete_protected_branch} åŽå°†æ— æ³•æ’¤é”€æˆ–æ¢å¤ã€‚"
-msgid "Branches|Only a project master or owner can delete a protected branch"
-msgstr "åªæœ‰é¡¹ç›®ç®¡ç†è€…或所有者æ‰èƒ½åˆ é™¤å—ä¿æŠ¤çš„分支ï¼"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr "åªæœ‰é¡¹ç›®ç»´æŠ¤è€…或所有者æ‰èƒ½åˆ é™¤å—ä¿æŠ¤çš„分支"
msgid "Branches|Overview"
msgstr "概览"
@@ -647,7 +1076,7 @@ msgid "Branches|Stale branches"
msgstr "éžæ´»è·ƒåˆ†æ”¯"
msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
-msgstr "分支ä¸èƒ½è‡ªåŠ¨æ›´æ–°ï¼Œå› ä¸ºå®ƒä¸Žä¸Šæ¸¸åˆ†æ”¯ä¸ä¸€è‡´ã€‚"
+msgstr "分支无法自动æ交,因为与上游分支冲çªã€‚"
msgid "Branches|The default branch cannot be deleted"
msgstr "无法删除默认分支"
@@ -668,7 +1097,7 @@ msgid "Branches|You’re about to permanently delete the protected branch %{bran
msgstr "å°†è¦æ°¸ä¹…删除å—ä¿æŠ¤çš„ %{branch_name} 分支。"
msgid "Branches|diverged from upstream"
-msgstr "上游分支"
+msgstr "与上游存在差异"
msgid "Branches|merged"
msgstr "å·²åˆå¹¶"
@@ -691,8 +1120,8 @@ msgstr "æµè§ˆæ–‡ä»¶"
msgid "Browse files"
msgstr "æµè§ˆæ–‡ä»¶"
-msgid "Business"
-msgstr "业务"
+msgid "Business metrics (Custom)"
+msgstr "业务指标(自定义)"
msgid "ByAuthor|by"
msgstr "作者:"
@@ -700,35 +1129,98 @@ msgstr "作者:"
msgid "CI / CD"
msgstr "CI / CD"
+msgid "CI / CD Settings"
+msgstr "CI/CD 设置"
+
msgid "CI/CD"
-msgstr "CI/CD"
+msgstr "æŒç»­é›†æˆ/æŒç»­éƒ¨ç½²"
msgid "CI/CD configuration"
msgstr "CI/CD é…ç½®"
msgid "CI/CD for external repo"
-msgstr "外部仓库的 CI/CD"
+msgstr "为外部仓库设置的æŒç»­é›†æˆ/æŒç»­éƒ¨ç½²"
+
+msgid "CI/CD settings"
+msgstr "CI/CD 设置"
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr "在开始使用æŒç»­é›†æˆå’ŒæŒç»­äº¤ä»˜ä¹‹å‰ï¼Œéœ€è¦æ˜Žç¡®æŒ‡å®š %{ci_file}。"
+
+msgid "CICD|Auto DevOps"
+msgstr "Auto DevOps"
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr "Auto DevOps 将根æ®é¢„定义的æŒç»­é›†æˆå’ŒæŒç»­äº¤ä»˜é…置自动化地构建ã€æµ‹è¯•å’Œéƒ¨ç½²åº”用程åºã€‚"
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr "自动部署到预å‘布环境,手动部署到生产环境"
+
+msgid "CICD|Continuous deployment to production"
+msgstr "æŒç»­éƒ¨ç½²åˆ°ç”Ÿäº§çŽ¯å¢ƒ"
+
+msgid "CICD|Deployment strategy"
+msgstr "部署策略"
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr "部署策略需è¦ä¸€ä¸ªåŸŸåæ‰èƒ½æ­£å¸¸å·¥ä½œã€‚"
+
+msgid "CICD|Disable Auto DevOps"
+msgstr "ç¦ç”¨ Auto DevOps"
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr "如果您使用Auto DevOps设置多个Kubernetes群集,请ä¸è¦åœ¨æ­¤å¤„设置域。"
+
+msgid "CICD|Enable Auto DevOps"
+msgstr "å¯ç”¨Auto DevOps"
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr "项目ä¸å­˜åœ¨ %{ci_file} 时,将会使用系统默认设置å¯ç”¨æˆ–ç¦ç”¨Auto DevOps。"
+
+msgid "CICD|Instance default (%{state})"
+msgstr "系统默认 (%{state})"
msgid "CICD|Jobs"
msgstr "作业"
+msgid "CICD|Learn more about Auto DevOps"
+msgstr "了解更多Auto DevOps的相关信æ¯"
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr "当项目中没有 %{ci_file} 时,将使用 Auto DevOpsæµæ°´çº¿é…置。"
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr "如需使用自动化应用程åºè¯„审和自动部署,请指定域å。"
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr "无法找到此分支的 HEAD æ交"
+
msgid "Cancel"
msgstr "å–消"
+msgid "Cancel this job"
+msgstr "å–消此作业"
+
msgid "Cannot be merged automatically"
msgstr "无法自动åˆå¹¶"
msgid "Cannot modify managed Kubernetes cluster"
-msgstr "无法修改托管的 Kubernetes 群集"
+msgstr "无法修改托管的 Kubernetes 集群"
msgid "Certificate fingerprint"
-msgstr ""
+msgstr "è¯ä¹¦æŒ‡çº¹"
msgid "Change Weight"
-msgstr "改å˜æƒé‡"
+msgstr "更改æƒé‡"
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
-msgstr ""
+msgstr "更改此值以影å“GitLab UI拉å–更新的频率。"
msgid "ChangeTypeActionLabel|Pick into branch"
msgstr "选择分支"
@@ -767,10 +1259,16 @@ msgid "Checking branch availability..."
msgstr "正在检查分支的å¯ç”¨æ€§..."
msgid "Cherry-pick this commit"
-msgstr "优选此æ交"
+msgstr "拣选此æ交"
msgid "Cherry-pick this merge request"
-msgstr "优选此åˆå¹¶è¯·æ±‚"
+msgstr "拣选此åˆå¹¶è¯·æ±‚"
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
msgid "Choose File ..."
msgstr "选择文件 ……"
@@ -778,14 +1276,23 @@ 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})以查看更改内容或创建åˆå¹¶è¯·æ±‚。"
+msgid "Choose any color."
+msgstr "选择任何颜色。"
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr "选择 <code>克隆</code> 或 <code>拉å–</code> 以获å–最近的应用程åºä»£ç "
+
msgid "Choose file..."
msgstr "选择文件..."
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
-msgstr "选择è¦åŒæ­¥åˆ°æ­¤æ¬¡èŠ‚点的群组。"
+msgstr "选择您希望与此次è¦èŠ‚点åŒæ­¥çš„群组。"
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
-msgstr "清选择è¦è¿žæŽ¥å¹¶è¿è¡Œ CI/CD æµæ°´çº¿çš„代ç ä»“库。"
+msgstr "请选择è¦è¿žæŽ¥å¹¶è¿è¡Œ CI/CD æµæ°´çº¿çš„代ç ä»“库。"
msgid "Choose which repositories you want to import."
msgstr "选择è¦å¯¼å…¥çš„仓库"
@@ -886,9 +1393,30 @@ msgstr "验è¯å¤±è´¥"
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "断路器 API"
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr "ä¸å¯ç”¨: %{reason}"
+
+msgid "Clear search input"
+msgstr "清除æœç´¢è¾“å…¥"
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr "å•å‡»ä¸‹é¢é¡¹ç›®åˆ—表中的任何 <strong>项目å称</strong> 跳转到项目里程碑。"
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr "点击å³ä¸Šè§’çš„ <strong>å‡çº§</strong> 按钮以å‡çº§åˆ°åˆ°ç¾¤ç»„里程碑。"
+
+msgid "Click the <strong>Select none</strong> 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 "点击下é¢çš„按钮转到Kubernetes页é¢å¼€å§‹å®‰è£…过程"
+msgid "Click to expand it."
+msgstr "点击以展开。"
+
msgid "Click to expand text"
msgstr "点击展开文本"
@@ -901,6 +1429,9 @@ msgstr "客户端认è¯å¯†é’¥"
msgid "Client authentication key password"
msgstr "客户端认è¯å¯†é’¥å¯†ç "
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr "克隆仓库"
@@ -910,26 +1441,32 @@ msgstr "关闭"
msgid "Closed"
msgstr "已关闭"
+msgid "Closed issues"
+msgstr "已关闭议题"
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
-msgstr "%{appList} å·²æˆåŠŸå®‰è£…到Kubernetes群集上"
+msgstr "%{appList} å·²æˆåŠŸå®‰è£…到Kubernetes集群上"
msgid "ClusterIntegration|API URL"
msgstr "API地å€"
msgid "ClusterIntegration|Add Kubernetes cluster"
-msgstr "添加 Kubernetes 群集"
-
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
-msgstr "添加现有的Kubernetes群集"
+msgstr "添加 Kubernetes 集群"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Kubernetes集群集æˆçš„高级选项"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr "å°è¯•èŽ·å–项目地域时å‘生错误:%{error}"
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr "å°è¯•èŽ·å–您的项目时å‘生错误:%{error}"
+
msgid "ClusterIntegration|Applications"
msgstr "应用程åº"
msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
-msgstr "确定è¦åˆ é™¤æ­¤Kubernetes集群的集æˆå—?注æ„这并ä¸ä¼šåˆ é™¤å®žé™…çš„Kubernetes群集本身。"
+msgstr "确定è¦åˆ é™¤æ­¤Kubernetes集群的集æˆå—?注æ„这并ä¸ä¼šåˆ é™¤å®žé™…çš„Kubernetes集群本身。"
msgid "ClusterIntegration|CA Certificate"
msgstr "CAè¯ä¹¦"
@@ -937,11 +1474,8 @@ msgstr "CAè¯ä¹¦"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "è¯ä¹¦æŽˆæƒåŒ…(PEMæ ¼å¼)"
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr "选择如何设置Kubernetes集群集æˆ"
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr "请选择使用此Kubernetes群集的环境。"
+msgstr "请选择使用此Kubernetes集群的环境。"
msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr "控制Kubernetes集群与GitLab集æˆæ–¹å¼"
@@ -955,6 +1489,9 @@ msgstr "å¤åˆ¶CAè¯ä¹¦"
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr "å¤åˆ¶Ingress IP地å€åˆ°å‰ªè´´æ¿"
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr "å°†Jupyter主机åå¤åˆ¶åˆ°å‰ªè´´æ¿"
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr "å¤åˆ¶Kubernetes集群å称"
@@ -962,34 +1499,37 @@ msgid "ClusterIntegration|Copy Token"
msgstr "å¤åˆ¶ä»¤ç‰Œ"
msgid "ClusterIntegration|Create Kubernetes cluster"
-msgstr "创建Kubernetes群集"
+msgstr "创建Kubernetes集群"
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
-msgstr "在Google Kubernetes引擎上创建Kubernetes集群"
-
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr "通过GitLab在Google Kubernetes引擎上创建Kubernetes集群"
-
-msgid "ClusterIntegration|Create on GKE"
-msgstr "在GKE中创建"
-
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr "输入现有的 Kubernetes 集群详细信æ¯"
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
-msgstr "输入Kubernetes群集的详细信æ¯"
+msgstr "输入Kubernetes集群的详细信æ¯"
msgid "ClusterIntegration|Environment scope"
msgstr "环境范围"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr "æ¯ä¸ªæ–°çš„ Google äº‘å¹³å° (GCP) å¸æˆ·ä½¿ç”¨æ­¤é“¾æŽ¥ %{sign_up_link} å¯ä»¥æ”¶åˆ° 300 美元的赠金。因为与 Google çš„åˆä½œå…³ç³»ï¼ŒGitLab 能够为新的和现有的 GCP å¸æˆ·æä¾›é¢å¤– 200 美元的赠金,以便于开始将 GitLab 集æˆåˆ° Google Kubernetes 引擎。"
+
+msgid "ClusterIntegration|Fetching machine types"
+msgstr "正在获å–实例类型"
+
+msgid "ClusterIntegration|Fetching projects"
+msgstr "正在获å–项目"
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr "正在获å–地域"
+
msgid "ClusterIntegration|GitLab Integration"
msgstr "GitLab集æˆ"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
-msgstr "Google 云平å°é¡¹ç›®ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr "Google 云平å°é¡¹ç›®"
msgid "ClusterIntegration|Google Kubernetes Engine"
msgstr "Google Kubernetes Engine"
@@ -1000,11 +1540,17 @@ msgstr "Google Kubernetes Engine 项目"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr "如果您正在设置多个群集并使用Auto DevOps,请%{help_link_start}先查阅此处%{help_link_end}。"
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr "为了显示集群的å¥åº·çŠ¶å†µï¼Œæ‚¨çš„集群需è¦é…ç½®Prometheus以收集所需的数æ®ã€‚"
msgid "ClusterIntegration|Ingress"
-msgstr "å…¥å£"
+msgstr "Ingress"
msgid "ClusterIntegration|Ingress IP Address"
msgstr "Ingress IP地å€"
@@ -1027,44 +1573,56 @@ msgstr "集æˆKubernetes集群自动化"
msgid "ClusterIntegration|Integration status"
msgstr "集æˆçŠ¶æ€"
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr "Jupyter主机å"
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr "JupyterHub"
+
msgid "ClusterIntegration|Kubernetes cluster"
-msgstr "Kubernetes 群集"
+msgstr "Kubernetes 集群"
msgid "ClusterIntegration|Kubernetes cluster details"
-msgstr "Kubernetes群集详细信æ¯"
+msgstr "Kubernetes集群详细信æ¯"
msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "Kubernetes集群å¥åº·åº¦"
+msgstr "Kubernetes集群è¿è¡ŒçŠ¶å†µ"
msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr "Kubernetes集群集æˆ"
msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr "此项目已ç¦ç”¨ Kubernetes 群集集æˆã€‚"
+msgstr "此项目已ç¦ç”¨ Kubernetes 集群集æˆã€‚"
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr "此项目已å¯ç”¨ Kubernetes 群集集æˆã€‚"
+msgstr "此项目已å¯ç”¨ Kubernetes 集群集æˆã€‚"
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "此项目已å¯ç”¨ Kubernetes 群集集æˆã€‚ç¦ç”¨æ­¤é›†æˆä¸ä¼šå½±å“ Kubernetes 群集本身, åªä¼šæš‚时关闭 GitLab 与其连接。"
+msgstr "此项目已å¯ç”¨ Kubernetes 集群集æˆã€‚ç¦ç”¨æ­¤é›†æˆä¸ä¼šå½±å“ Kubernetes 集群本身, åªä¼šæš‚时关闭 GitLab 与其连接。"
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
-msgstr "正在Google Kubernetes Engine上创建Kubernetes群集..."
+msgstr "正在Google Kubernetes Engine上创建Kubernetes集群..."
msgid "ClusterIntegration|Kubernetes cluster name"
-msgstr "Kubernetes 群集å称"
+msgstr "Kubernetes 集群å称"
msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
-msgstr "Kubernetes集群已在Google Kubernetes Engine上æˆåŠŸåˆ›å»ºã€‚刷新页é¢ä»¥æŸ¥çœ‹Kubernetes群集的详细信æ¯"
+msgstr "Kubernetes集群已在Google Kubernetes Engine上æˆåŠŸåˆ›å»ºã€‚刷新页é¢ä»¥æŸ¥çœ‹Kubernetes集群的详细信æ¯"
msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr "通过Kubernetes 群集集æˆï¼Œå¯ä»¥æ–¹ä¾¿åœ°ä½¿ç”¨å®¡é˜…应用ã€éƒ¨ç½²åº”用程åºã€è¿è¡Œæµæ°´çº¿ç­‰ç­‰ã€‚%{link_to_help_page}"
+msgstr "通过Kubernetes 集群集æˆï¼Œå¯ä»¥æ–¹ä¾¿åœ°ä½¿ç”¨å®¡é˜…应用ã€éƒ¨ç½²åº”用程åºã€è¿è¡Œæµæ°´çº¿ç­‰ç­‰ã€‚%{link_to_help_page}"
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
-msgstr "Kubernetes 群集å¯ç”¨äºŽéƒ¨ç½²åº”用程åºå’Œæ供此项目的审阅应用"
+msgstr "Kubernetes 集群å¯ç”¨äºŽéƒ¨ç½²åº”用程åºå’Œæ供此项目的审阅应用"
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr "进一步了解 %{help_link_start_machine_type}实例类型%{help_link_end} å’Œ %{help_link_start_pricing}定价信æ¯%{help_link_end}。"
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr "进一步了解 %{help_link_start}Kubernetes%{help_link_end}。"
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
-msgstr "进一步了解%{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr "进一步了解 %{help_link_start}地域%{help_link_end}。"
msgid "ClusterIntegration|Learn more about environments"
msgstr "进一步了解有关环境的信æ¯"
@@ -1076,13 +1634,13 @@ msgid "ClusterIntegration|Machine type"
msgstr "机器类型"
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
-msgstr "请确ä¿æ‚¨çš„å¸æˆ· %{link_to_requirements} å¯ä»¥åˆ›å»º Kubernetes 群集"
+msgstr "请确ä¿æ‚¨çš„å¸æˆ· %{link_to_requirements} å¯ä»¥åˆ›å»º Kubernetes 集群"
msgid "ClusterIntegration|Manage"
msgstr "管ç†"
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
-msgstr "通过访问 %{link_gke} ç®¡ç† Kubernetes 群集"
+msgstr "通过访问 %{link_gke} ç®¡ç† Kubernetes 集群"
msgid "ClusterIntegration|More information"
msgstr "更多信æ¯"
@@ -1090,6 +1648,18 @@ msgstr "更多信æ¯"
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr "在GitLabä¼ä¸šé«˜çº§å’Œæ——舰版中å¯ä»¥ä½¿ç”¨å¤šä¸ªKubernetes集群"
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr "未找到您æœç´¢çš„实例类型"
+
+msgid "ClusterIntegration|No projects found"
+msgstr "未找到项目"
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr "未找到您æœç´¢çš„项目"
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr "未找到您æœç´¢çš„地域"
+
msgid "ClusterIntegration|Note:"
msgstr "注æ„:"
@@ -1102,9 +1672,6 @@ msgstr "请输入Kubernetes集群的访问信æ¯ã€‚如需帮助,å¯ä»¥é˜…读Ku
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "请确ä¿æ‚¨çš„ Google å¸æˆ·ç¬¦åˆä»¥ä¸‹è¦æ±‚:"
-msgid "ClusterIntegration|Project ID"
-msgstr "项目 ID"
-
msgid "ClusterIntegration|Project namespace"
msgstr "项目命å空间"
@@ -1124,7 +1691,7 @@ msgid "ClusterIntegration|Remove integration"
msgstr "删除集æˆ"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
-msgstr "从当å‰é¡¹ç›®ä¸­åˆ é™¤æ­¤Kubernetes集群的é…置。该æ“作并ä¸ä¼šåˆ é™¤å®žé™…Kubernetes群集。"
+msgstr "从当å‰é¡¹ç›®ä¸­åˆ é™¤æ­¤Kubernetes集群的é…置。该æ“作并ä¸ä¼šåˆ é™¤å®žé™…Kubernetes集群。"
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "请求安装失败"
@@ -1132,20 +1699,38 @@ msgstr "请求安装失败"
msgid "ClusterIntegration|Save changes"
msgstr "ä¿å­˜æ›´æ”¹"
+msgid "ClusterIntegration|Search machine types"
+msgstr "æœç´¢å®žä¾‹ç±»åž‹"
+
+msgid "ClusterIntegration|Search projects"
+msgstr "æœç´¢é¡¹ç›®"
+
+msgid "ClusterIntegration|Search zones"
+msgstr "æœç´¢åœ°åŸŸ"
+
msgid "ClusterIntegration|Security"
msgstr "安全"
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr "查看并编辑Kubernetes集群的详细信æ¯"
-msgid "ClusterIntegration|See machine types"
-msgstr "å‚è§æœºå™¨ç±»åž‹"
+msgid "ClusterIntegration|Select machine type"
+msgstr "选择实例类型"
+
+msgid "ClusterIntegration|Select project"
+msgstr "选择项目"
-msgid "ClusterIntegration|See your projects"
-msgstr "看到您的项目"
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr "按项目和地域选择实例类型"
-msgid "ClusterIntegration|See zones"
-msgstr "查看区域"
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr "按项目选择地域"
+
+msgid "ClusterIntegration|Select zone"
+msgstr "选择地域"
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr "按地域选择实例类型"
msgid "ClusterIntegration|Service token"
msgstr "æœåŠ¡ä»¤ç‰Œ"
@@ -1169,14 +1754,17 @@ msgid "ClusterIntegration|This account must have permissions to create a Kuberne
msgstr "该å¸æˆ·éœ€å…·å¤‡åœ¨ä¸‹é¢æŒ‡å®šçš„%{link_to_container_project}中创建 Kubernetes集群的æƒé™"
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
-msgstr "开关Kubernetes 群集"
+msgstr "开关Kubernetes 集群"
msgid "ClusterIntegration|Toggle Kubernetes cluster"
-msgstr "开关Kubernetes 群集"
+msgstr "开关Kubernetes 集群"
msgid "ClusterIntegration|Token"
msgstr "令牌"
+msgid "ClusterIntegration|Validating project billing status"
+msgstr "验è¯é¡¹ç›®è´¦å•çŠ¶æ€"
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr "使用与此项目关è”çš„Kubernetes集群,å¯ä»¥æ–¹ä¾¿åœ°ä½¿ç”¨å®¡é˜…应用,部署应用程åºï¼Œè¿è¡Œæµæ°´çº¿ç­‰ç­‰ã€‚"
@@ -1184,7 +1772,7 @@ msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "您的å¸æˆ·å¿…须拥有%{link_to_kubernetes_engine}"
msgid "ClusterIntegration|Zone"
-msgstr "区域"
+msgstr "地域"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr "访问 Google Kubernetes Engine"
@@ -1207,13 +1795,22 @@ msgstr "符åˆè¦æ±‚"
msgid "ClusterIntegration|properly configured"
msgstr "正确é…ç½®"
+msgid "ClusterIntegration|sign up"
+msgstr "注册"
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr "收起"
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr "折å ä¾§è¾¹æ "
+
+msgid "Comment & resolve discussion"
msgstr "评论并解决讨论"
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr "评论并将讨论å˜ä¸ºæœªå†³"
msgid "Comments"
@@ -1278,6 +1875,9 @@ msgstr "无相关åˆå¹¶è¯·æ±‚"
msgid "Committed by"
msgstr "æ交者:"
+msgid "Commit…"
+msgstr "æ交..."
+
msgid "Compare"
msgstr "比较"
@@ -1291,7 +1891,7 @@ msgid "Compare changes with the last commit"
msgstr "与上个æ交比较å˜æ›´å†…容"
msgid "Compare changes with the merge request target branch"
-msgstr ""
+msgstr "与åˆå¹¶è¯·æ±‚的目标分支比较å˜æ›´å†…容"
msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
msgstr "%{source_branch} å’Œ %{target_branch} 是相åŒçš„"
@@ -1315,19 +1915,22 @@ msgid "Confidentiality"
msgstr "ç§å¯†æ€§"
msgid "Configure Gitaly timeouts."
-msgstr ""
+msgstr "é…ç½®Gitaly超时时间。"
msgid "Configure Sidekiq job throttling."
-msgstr ""
+msgstr "é…ç½® Sidekiq 作业é™åˆ¶ã€‚"
msgid "Configure automatic git checks and housekeeping on repositories."
-msgstr ""
+msgstr "在仓库上é…置自动git检查和仓库整ç†ã€‚"
msgid "Configure limits for web and API requests."
-msgstr ""
+msgstr "é…ç½® web å’Œ API 请求é™åˆ¶ã€‚"
+
+msgid "Configure push and pull mirrors."
+msgstr "é…ç½®PushåŠPullé•œåƒ"
msgid "Configure storage path and circuit breaker settings."
-msgstr ""
+msgstr "é…置存储路径åŠæ–­è·¯å™¨è®¾ç½®ã€‚"
msgid "Configure the way a user creates a new account."
msgstr "é…置用户创建新å¸æˆ·çš„æ–¹å¼ã€‚"
@@ -1348,7 +1951,7 @@ msgid "Connecting..."
msgstr "正在连接..."
msgid "Container Registry"
-msgstr "容器注册"
+msgstr "容器注册表"
msgid "ContainerRegistry|Created"
msgstr "已创建"
@@ -1392,15 +1995,30 @@ msgstr "使用ä¸åŒçš„é•œåƒå称"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr "å°† Docker 容器注册表集æˆåˆ° GitLab 中,æ¯ä¸ªé¡¹ç›®éƒ½å¯ä»¥æœ‰å„自的空间æ¥å­˜å‚¨ Docker çš„é•œåƒã€‚"
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr "您也å¯ä»¥ä½¿ç”¨ %{deploy_token} 以åªè¯»æ–¹å¼è®¿é—®é•œåƒåº“çš„é•œåƒã€‚"
+
+msgid "Continue"
+msgstr "继续"
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr "æŒç»­é›†æˆå’Œéƒ¨ç½²"
+msgid "Contribute to GitLab"
+msgstr "为 GitLab æ交贡献"
+
msgid "Contribution"
msgstr "贡献"
msgid "Contribution guide"
msgstr "贡献指å—"
+msgid "Contributions per group member"
+msgstr "æ¯å群组æˆå‘˜çš„贡献"
+
msgid "Contributors"
msgstr "贡献者"
@@ -1416,14 +2034,23 @@ msgstr "%{branch_name} 分支上的æ交,ä¸å«åˆå¹¶æ交。é™6000次。"
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr "请ç¨å€™ï¼Œå›¾è¡¨æž„建完æˆåŽé¡µé¢ä¼šè‡ªåŠ¨åˆ·æ–°ã€‚"
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
-msgstr "控制此次è¦èŠ‚点的 LFS/attachment 的最大并å‘"
+msgstr "控制此次è¦èŠ‚点的 åŒæ­¥LFS/附件的最大并å‘"
msgid "Control the maximum concurrency of repository backfill for this secondary node"
-msgstr "控制此次è¦èŠ‚点的仓库最大并å‘"
+msgstr "控制此次è¦èŠ‚点的åŒæ­¥å­˜å‚¨åº“的最大并å‘"
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr "控制此Geo节点的校验æ“作的最大并å‘性"
+
+msgid "ConvDev Index"
+msgstr ""
msgid "Copy SSH public key to clipboard"
-msgstr "å¤åˆ¶ SSH 公钥到剪贴æ¿"
+msgstr "å°† SSH 公钥å¤åˆ¶åˆ°å‰ªè´´æ¿"
msgid "Copy URL to clipboard"
msgstr "å¤åˆ¶ URL 到剪贴æ¿"
@@ -1437,9 +2064,21 @@ msgstr "将命令å¤åˆ¶åˆ°å‰ªè´´æ¿"
msgid "Copy commit SHA to clipboard"
msgstr "å¤åˆ¶æ交 SHA 的值到剪贴æ¿"
+msgid "Copy file path to clipboard"
+msgstr "将文件路径å¤åˆ¶åˆ°å‰ªè´´æ¿"
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr "将索引å¤åˆ¶åˆ°å‰ªè´´æ¿"
+msgid "Copy to clipboard"
+msgstr "å¤åˆ¶åˆ°å‰ªè´´æ¿"
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr "创建"
@@ -1452,12 +2091,18 @@ msgstr "创建一个新分支"
msgid "Create a new branch and merge request"
msgstr "创建一个新的分支åŠåˆå¹¶è¯·æ±‚"
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "在å¸æˆ·ä¸Šåˆ›å»ºä¸ªäººè®¿é—®ä»¤ç‰Œï¼Œä»¥é€šè¿‡ %{protocol} æ¥æ‹‰å–或推é€ã€‚"
msgid "Create branch"
msgstr "创建分支"
+msgid "Create commit"
+msgstr "创建æ交"
+
msgid "Create directory"
msgstr "创建目录"
@@ -1470,9 +2115,15 @@ msgstr "创建å²è¯—故事"
msgid "Create file"
msgstr "创建文件"
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr "创建群组标记"
+msgid "Create issue"
+msgstr "创建议题"
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr "从标记创建列表,å«æœ‰è¯¥æ ‡è®°çš„议题将出现在相应的列中。"
@@ -1491,6 +2142,9 @@ msgstr "创建新目录"
msgid "Create new file"
msgstr "创建新文件"
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr "创建新标记"
@@ -1509,11 +2163,17 @@ msgstr "标签"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "创建个人访问令牌"
-msgid "Creates a new branch from %{branchName}"
-msgstr "自%{branchName} 创建一个新分支"
+msgid "Created"
+msgstr "已创建"
+
+msgid "Created At"
+msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
-msgstr "自%{branchName} 创建一个新分支,并转到创建一个新的åˆå¹¶è¯·æ±‚"
+msgid "Created by me"
+msgstr "由我创建"
+
+msgid "Created on:"
+msgstr ""
msgid "Creating epic"
msgstr "创建å²è¯—故事中"
@@ -1527,6 +2187,15 @@ msgstr "Cron 语法"
msgid "Current node"
msgstr "当å‰èŠ‚点"
+msgid "CurrentUser|Profile"
+msgstr "用户资料"
+
+msgid "CurrentUser|Settings"
+msgstr "设置"
+
+msgid "Custom CI config path"
+msgstr "自定义CIé…置路径"
+
msgid "Custom notification events"
msgstr "自定义通知事件"
@@ -1534,6 +2203,12 @@ msgid "Custom notification levels are the same as participating levels. With cus
msgstr "自定义通知级别继承自å‚与级别。使用自定义通知级别,您会收到å‚与级别åŠé€‰å®šäº‹ä»¶çš„通知。想了解更多信æ¯ï¼Œè¯·æŸ¥çœ‹ %{notification_link}."
msgid "Customize colors"
+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 "Cycle Analytics"
@@ -1560,6 +2235,9 @@ msgstr "预å‘布"
msgid "CycleAnalyticsStage|Test"
msgstr "测试"
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr "所有"
@@ -1572,8 +2250,17 @@ msgstr "å二"
msgid "December"
msgstr "å二月"
+msgid "Decline and sign out"
+msgstr "æ‹’ç»å¹¶é€€å‡º"
+
msgid "Default classification label"
-msgstr "默认分类标签"
+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 ""
msgid "Define a custom pattern with cron syntax"
msgstr "使用 Cron 语法定义自定义模å¼"
@@ -1581,6 +2268,18 @@ msgstr "使用 Cron 语法定义自定义模å¼"
msgid "Delete"
msgstr "删除"
+msgid "Delete Snippet"
+msgstr "删除代ç ç‰‡æ®µ"
+
+msgid "Delete list"
+msgstr "删除清å•"
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "部署"
@@ -1588,39 +2287,195 @@ msgstr[0] "部署"
msgid "Deploy Keys"
msgstr "部署密钥"
+msgid "DeployKeys|+%{count} others"
+msgstr "+%{count} 其他"
+
+msgid "DeployKeys|Current project"
+msgstr "DeployKeys |当å‰é¡¹ç›®"
+
+msgid "DeployKeys|Deploy key"
+msgstr "部署密钥"
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr "å¯ç”¨éƒ¨ç½²å¯†é’¥"
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr "å¯ç”¨éƒ¨ç½²å¯†é’¥å‡ºé”™"
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr "获得部署密钥出错"
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr "移除部署密钥出错"
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr "展开 %{count} 个其他项目"
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr "加载部署密钥"
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr "没有找到部署密钥。请使用上é¢çš„表å•åˆ›å»ºéƒ¨ç½²å¯†é’¥ã€‚"
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr "ç§äººè®¿é—®çš„部署密钥"
+
+msgid "DeployKeys|Project usage"
+msgstr "项目使用情况"
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr "公开访问的部署密钥"
+
+msgid "DeployKeys|Read access only"
+msgstr "åªè¯»æƒé™"
+
+msgid "DeployKeys|Write access allowed"
+msgstr "写入æƒé™"
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr "å³å°†å°†åˆ é™¤è¯¥éƒ¨ç½²å¯†é’¥ã€‚确定继续å—?"
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr "å¯ç”¨éƒ¨ç½²ä»¤ç‰Œï¼ˆ%{active_tokens})"
+
+msgid "DeployTokens|Add a deploy token"
+msgstr "添加一个部署令牌"
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr "å…许对容器注册表镜åƒè¿›è¡Œåªè¯»è®¿é—®"
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr "å…许对仓库进行åªè¯»è®¿é—®"
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr "将部署令牌å¤åˆ¶åˆ°å‰ªè´´æ¿"
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr "将用户åå¤åˆ¶åˆ°å‰ªè´´æ¿"
+
+msgid "DeployTokens|Create deploy token"
+msgstr "创建部署令牌"
+
+msgid "DeployTokens|Created"
+msgstr "已创建"
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr "部署令牌"
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr "部署令牌å¯ä»¥å¯¹ä»“库和容器注册表中的镜åƒè¿›è¡Œåªè¯»è®¿é—®ã€‚"
+
+msgid "DeployTokens|Expires"
+msgstr "到期"
+
+msgid "DeployTokens|Name"
+msgstr "å称"
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr "请为应用程åºé€‰æ‹©ä¸€ä¸ªå称,以便生æˆå”¯ä¸€çš„部署令牌。"
+
+msgid "DeployTokens|Revoke"
+msgstr "撤销"
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr "撤销 %{name}"
+
+msgid "DeployTokens|Scopes"
+msgstr "有效范围"
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr "æ­¤æ“作无法撤消。"
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr "该项目没有å¯ç”¨çš„部署令牌。"
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr "此令牌和密ç ä½œç”¨ä¸€æ ·ã€‚当å‰çª—å£å…³é—­åŽå°†æ— æ³•å†æ¬¡æŸ¥çœ‹ä»¤ç‰Œå†…容,请立å³å¦¥å–„ä¿å­˜ã€‚"
+
+msgid "DeployTokens|Use this username as a login."
+msgstr "将此用户å用作登录å。"
+
+msgid "DeployTokens|Username"
+msgstr "用户å"
+
+msgid "DeployTokens|You are about to revoke"
+msgstr "å³å°†æ’¤é”€"
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr "新部署令牌"
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr "新项目部署令牌已创建。"
+
+msgid "Deprioritize label"
+msgstr "å–消优先标记"
+
+msgid "Descending"
+msgstr "é™åº"
+
msgid "Description"
msgstr "æè¿°"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr "æ述模æ¿å…许您为项目的问题和åˆå¹¶è¯·æ±‚定义æ述字段的特定模æ¿ã€‚"
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr "详情"
msgid "Diffs|No file name available"
msgstr "没有å¯ç”¨çš„文件å"
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr "获å–差异线时å‘生错误。"
+
msgid "Directory name"
msgstr "目录å称"
msgid "Disable"
msgstr "ç¦ç”¨"
+msgid "Disable for this project"
+msgstr "在此项目中ç¦ç”¨"
+
+msgid "Disable group Runners"
+msgstr "ç¦ç”¨ç¾¤ç»„Runner"
+
+msgid "Discard changes"
+msgstr "放弃更改"
+
msgid "Discard draft"
-msgstr "èˆå¼ƒè‰ç¨¿"
+msgstr "å–消"
msgid "Discover GitLab Geo."
msgstr "å‘现GitLab Geo。"
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "关闭循环分æžä»‹ç»æ¡†"
msgid "Dismiss Merge Request promotion"
-msgstr "关闭åˆå¹¶è¯·æ±‚中的促销广告"
+msgstr "关闭åˆå¹¶è¯·æ±‚推广"
-msgid "Documentation for popular identity providers"
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
msgstr ""
+msgid "Documentation for popular identity providers"
+msgstr "常è§çš„身份验è¯æ供商的相关文档"
+
+msgid "Domain"
+msgstr "域å"
+
msgid "Don't show again"
msgstr "ä¸å†æ˜¾ç¤º"
@@ -1661,43 +2516,67 @@ msgid "Due date"
msgstr "截止日期"
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
-msgstr ""
+msgstr "在此过程中,我们会è¦æ±‚您æä¾›æ¥è‡ª GitLab çš„ URL 。请使用下é¢çš„网å€ã€‚"
+
+msgid "Each Runner can be in one of the following states:"
+msgstr "æ¯ä¸ªRunnerå¯ä»¥å¤„于以下状æ€ä¸­çš„其中一ç§ï¼š"
msgid "Edit"
msgstr "编辑"
+msgid "Edit Label"
+msgstr "编辑标签"
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "编辑 %{id} æµæ°´çº¿è®¡åˆ’"
+msgid "Edit Snippet"
+msgstr "编辑代ç ç‰‡æ®µ"
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr "在编辑器中编辑文件并在这里​​æ交å˜æ›´å†…容"
-msgid "Editing"
-msgstr "编辑"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
+msgstr "编辑 %{user_name} 的身份信æ¯"
msgid "Elasticsearch"
-msgstr ""
+msgstr "Elasticsearch"
msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
-msgstr ""
+msgstr "Elasticsearch集æˆåŠElasticsearch AWS IAM。"
msgid "Email"
-msgstr ""
+msgstr "电å­é‚®ä»¶"
+
+msgid "Email patch"
+msgstr "电å­é‚®ä»¶è¡¥ä¸"
msgid "Emails"
msgstr "电å­é‚®ä»¶"
+msgid "Embed"
+msgstr "嵌入"
+
msgid "Enable"
msgstr "å¯ç”¨"
msgid "Enable Auto DevOps"
msgstr "å¯ç”¨Auto DevOps"
+msgid "Enable Pseudonymizer data collection"
+msgstr "å¯ç”¨åŒ¿å化的数æ®æ”¶é›†"
+
msgid "Enable SAML authentication for this group"
-msgstr ""
+msgstr "为此群组å¯ç”¨ SAML 身份验è¯"
msgid "Enable Sentry for error reporting and logging."
-msgstr ""
+msgstr "å¯ç”¨S​​entry进行错误报告和日志记录。"
msgid "Enable and configure InfluxDB metrics."
msgstr "å¯ç”¨å¹¶é…ç½®InfluxDB指标。"
@@ -1708,17 +2587,35 @@ msgstr "å¯ç”¨å¹¶é…ç½®Prometheus指标。"
msgid "Enable classification control using an external service"
msgstr "使用外部æœåŠ¡å¯ç”¨åˆ†ç±»æŽ§åˆ¶"
+msgid "Enable for this project"
+msgstr "在此项目中å¯ç”¨"
+
+msgid "Enable group Runners"
+msgstr "å¯ç”¨ç¾¤ç»„Runner"
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr "å¯ç”¨æˆ–ç¦ç”¨éƒ¨åˆ†ç¾¤ç»„功能并选择访问等级。"
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr "å¯ç”¨æˆ–ç¦ç”¨åŒ¿å化数æ®æ”¶é›†."
+
msgid "Enable or disable version check and usage ping."
-msgstr ""
+msgstr "å¯ç”¨æˆ–ç¦ç”¨ç‰ˆæœ¬æ£€æŸ¥åŠä½¿ç”¨ping。"
msgid "Enable reCAPTCHA or Akismet and set IP limits."
-msgstr ""
+msgstr "å¯ç”¨reCAPTCHA或Akismet并设置IPé™åˆ¶ã€‚"
msgid "Enable the Performance Bar for a given group."
-msgstr ""
+msgstr "对指定群组å¯ç”¨æ€§èƒ½æ ã€‚"
msgid "Enabled"
-msgstr ""
+msgstr "å·²å¯ç”¨"
+
+msgid "Ends at (UTC)"
+msgstr "结æŸäºŽ(UTC)"
+
+msgid "Environments"
+msgstr "环境"
msgid "Environments|An error occurred while fetching the environments."
msgstr "获å–环境时å‘生错误。"
@@ -1726,9 +2623,18 @@ msgstr "获å–环境时å‘生错误。"
msgid "Environments|An error occurred while making the request."
msgstr "å‘é€è¯·æ±‚æ—¶å‘生错误。"
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr "æ交"
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr "部署"
@@ -1741,38 +2647,59 @@ msgstr "环境"
msgid "Environments|Job"
msgstr "作业"
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr "新建环境"
msgid "Environments|No deployments yet"
msgstr "未部署"
-msgid "Environments|Open"
-msgstr "打开"
+msgid "Environments|No pod name has been specified"
+msgstr "未指定podå称"
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
-msgid "Environments|Re-deploy"
-msgstr "é‡æ–°éƒ¨ç½²"
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
+msgstr "Pod日志æ¥è‡ªäºŽ"
+
+msgid "Environments|Re-deploy to environment"
+msgstr ""
msgid "Environments|Read more about environments"
msgstr "了解有关环境的更多信æ¯"
-msgid "Environments|Rollback"
-msgstr "还原"
+msgid "Environments|Rollback environment"
+msgstr ""
msgid "Environments|Show all"
msgstr "显示全部"
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "已更新"
msgid "Environments|You don't have any environments right now."
msgstr "当å‰æœªè®¾ç½®çŽ¯å¢ƒ"
+msgid "Epic"
+msgstr "å²è¯—故事"
+
msgid "Epic will be removed! Are you sure?"
-msgstr "EPIC将被删除!是å¦ç¡®å®šï¼Ÿ"
+msgstr "å²è¯—故事将被删除! 是å¦ç¡®å®šï¼Ÿ"
msgid "Epics"
-msgstr "EPIC"
+msgstr "å²è¯—故事"
msgid "Epics Roadmap"
msgstr "å²è¯—故事路线图"
@@ -1781,13 +2708,7 @@ msgid "Epics let you manage your portfolio of projects more efficiently and with
msgstr "利用å²è¯—故事(Epics),产å“线管ç†ä¼šå˜å¾—æ›´è½»æ¾ä¸”更高效"
msgid "Error Reporting and Logging"
-msgstr ""
-
-msgid "Error checking branch data. Please try again."
-msgstr "检查分支数æ®æ—¶å‡ºé”™ã€‚请å†è¯•ä¸€æ¬¡ã€‚"
-
-msgid "Error committing changes. Please try again."
-msgstr "æ交更改时出错。请å†è¯•ä¸€æ¬¡ã€‚"
+msgstr "错误报告和日志记录"
msgid "Error creating epic"
msgstr "创建å²è¯—故事时出错"
@@ -1807,6 +2728,21 @@ msgstr "获å–refs时出错。"
msgid "Error fetching usage ping data."
msgstr "获å–使用情况(usage ping) æ•°æ®æ—¶å‡ºé”™ã€‚"
+msgid "Error loading branch data. Please try again."
+msgstr "加载分支数æ®å¤±è´¥ï¼Œè¯·é‡è¯•ã€‚"
+
+msgid "Error loading last commit."
+msgstr "加载最åŽä¸€æ¬¡æ交失败。"
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr "加载åˆå¹¶è¯·æ±‚时出错。"
+
+msgid "Error loading project data. Please try again."
+msgstr "加载项目数æ®å¤±è´¥ï¼Œè¯·é‡è¯•ã€‚"
+
msgid "Error occurred when toggling the notification subscription"
msgstr "切æ¢é€šçŸ¥è®¢é˜…æ—¶å‘生错误"
@@ -1819,6 +2755,9 @@ msgstr "更新所有待办事项的状æ€æ—¶å‡ºé”™ã€‚"
msgid "Error updating todo status."
msgstr "更新待办事项状æ€æ—¶å‡ºé”™ã€‚"
+msgid "Estimated"
+msgstr "预计"
+
msgid "EventFilterBy|Filter by all"
msgstr "全部"
@@ -1846,9 +2785,30 @@ msgstr "æ¯æœˆæ‰§è¡Œï¼ˆæ¯æœˆ 1 日凌晨 4 点)"
msgid "Every week (Sundays at 4:00am)"
msgstr "æ¯å‘¨æ‰§è¡Œï¼ˆå‘¨æ—¥å‡Œæ™¨ 4 点)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr "展开"
+msgid "Expand all"
+msgstr "展开全部"
+
+msgid "Expand sidebar"
+msgstr "展开侧边æ "
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr "æµè§ˆç¾¤ç»„"
+
msgid "Explore projects"
msgstr "查看项目"
@@ -1859,7 +2819,7 @@ msgid "External Classification Policy Authorization"
msgstr "外部分类政策授æƒ"
msgid "External authentication"
-msgstr ""
+msgstr "外部身份验è¯"
msgid "External authorization denied access to this project"
msgstr "外部授æƒæ‹’ç»è®¿é—®æ­¤é¡¹ç›®"
@@ -1874,7 +2834,10 @@ msgid "ExternalAuthorizationService|Classification label"
msgstr "分类标签"
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
-msgstr "未设置分类标签时,将使用默认的分类标签`%{default_label}`。"
+msgstr "未设置分类标签的时候,将使用默认的分类标签`%{default_label}`。"
+
+msgid "Facebook"
+msgstr ""
msgid "Failed"
msgstr "已失败"
@@ -1885,8 +2848,11 @@ msgstr "失败的作业"
msgid "Failed to change the owner"
msgstr "无法å˜æ›´æ‰€æœ‰è€…"
+msgid "Failed to check related branches."
+msgstr "无法检查相关分支。"
+
msgid "Failed to remove issue from board, please try again."
-msgstr "无法从看æ¿ç§»é™¤é—®é¢˜ï¼Œè¯·é‡è¯•ã€‚"
+msgstr "无法从看æ¿ç§»é™¤è®®é¢˜ï¼Œè¯·é‡è¯•ã€‚"
msgid "Failed to remove the pipeline schedule"
msgstr "无法删除æµæ°´çº¿è®¡åˆ’"
@@ -1894,6 +2860,12 @@ msgstr "无法删除æµæ°´çº¿è®¡åˆ’"
msgid "Failed to update issues, please try again."
msgstr "更新议题失败, 请é‡è¯•"
+msgid "Failure"
+msgstr "失败"
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr "速度更快,因其é‡ç”¨äº†é¡¹ç›®çš„工作空间(如果它ä¸å­˜åœ¨ï¼Œå°†å›žé€€åˆ°å…‹éš†ï¼‰"
+
msgid "Feb"
msgstr "二"
@@ -1903,9 +2875,6 @@ msgstr "二月"
msgid "Fields on this page are now uneditable, you can configure"
msgstr "当å‰é¡µé¢ä¸Šçš„字段ä¸å¯ç¼–辑,å¯ä»¥é…ç½®"
-msgid "File name"
-msgstr "文件å"
-
msgid "Files"
msgstr "文件"
@@ -1913,6 +2882,9 @@ msgid "Files (%{human_size})"
msgstr "文件(%{human_size})"
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr "填写下é¢çš„字段,å¯ç”¨<strong>%{enable_label}</strong>,然åŽç‚¹å‡»<strong>%{save_changes}</strong>"
+
+msgid "Filter"
msgstr ""
msgid "Filter by commit message"
@@ -1924,6 +2896,12 @@ msgstr "按路径查找"
msgid "Find file"
msgstr "查找文件"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr "已完æˆ"
@@ -1933,12 +2911,39 @@ msgstr "首次推é€"
msgid "FirstPushedBy|pushed by"
msgstr "推é€è€…:"
-msgid "Font Color"
+msgid "FogBugz Email"
msgstr ""
-msgid "Footer message"
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
msgstr ""
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
+msgid "Font Color"
+msgstr "字体颜色"
+
+msgid "Footer message"
+msgstr "页脚消æ¯"
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr "对于内部项目,任何已登录的用户都å¯ä»¥æŸ¥çœ‹æµæ°´çº¿å¹¶è®¿é—®ä½œä¸šè¯¦æƒ…(输出日志和工件)"
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr "对于ç§æœ‰é¡¹ç›®ï¼Œä»»ä½•æˆå‘˜ï¼ˆè®¿å®¢æˆ–更高级别)都å¯ä»¥æŸ¥çœ‹æµæ°´çº¿å¹¶è®¿é—®ä½œä¸šè¯¦æƒ…(输出日志和工件)"
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr "对于公共项目,任何人都å¯ä»¥æŸ¥çœ‹æµæ°´çº¿å¹¶è®¿é—®ä½œä¸šè¯¦æƒ…(输出日志和工件)"
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "派生"
@@ -1947,7 +2952,7 @@ msgid "ForkedFromProjectPath|Forked from"
msgstr "派生自"
msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
-msgstr "派生自 %{project_name} (删除)"
+msgstr "派生自 %{project_name} (已删除)"
msgid "Forking in progress"
msgstr "派生(Fork)中"
@@ -1955,9 +2960,24 @@ msgstr "派生(Fork)中"
msgid "Format"
msgstr "æ ¼å¼"
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr "在.gitlab-ci.yml中å‘现错误:"
+
msgid "From %{provider_title}"
msgstr "æ¥è‡ª %{provider_title}"
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr "从创建议题到部署至生产环境"
@@ -1965,11 +2985,17 @@ msgid "From merge request merge until deploy to production"
msgstr "从åˆå¹¶è¯·æ±‚被åˆå¹¶åŽåˆ°éƒ¨ç½²è‡³ç”Ÿäº§çŽ¯å¢ƒ"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
-msgstr "在Kubernetes群集详细信æ¯è§†å›¾ä¸­ï¼Œä»Žåº”用程åºåˆ—表中安装Runner"
+msgstr "在Kubernetes集群详细信æ¯è§†å›¾ä¸­ï¼Œä»Žåº”用程åºåˆ—表中安装Runner"
msgid "GPG Keys"
msgstr "GPG 密钥"
+msgid "General"
+msgstr "通用"
+
+msgid "General pipelines"
+msgstr "一般æµæ°´çº¿"
+
msgid "Generate a default set of labels"
msgstr "生æˆä¸€ç»„默认的标记"
@@ -1977,7 +3003,7 @@ msgid "Geo Nodes"
msgstr "Geo 节点"
msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
-msgstr ""
+msgstr "Geoå…许您将您的GitLab实例å¤åˆ¶åˆ°å…¶ä»–地ç†ä½ç½®ã€‚"
msgid "GeoNodeSyncStatus|Node is failing or broken."
msgstr "节点出现故障或æŸå。"
@@ -1988,11 +3014,14 @@ msgstr "节点è¿è¡Œç¼“æ…¢ã€è¶…è½½, 或者在åœæœºåŽåˆšåˆšæ¢å¤ã€‚"
msgid "GeoNodes|Checksummed"
msgstr "已校验"
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr "æ•°æ®ä»Ž %{timeago} 起过期"
+
+msgid "GeoNodes|Data replication lag"
msgstr "æ•°æ®åº“åŒæ­¥æ»žåŽ"
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
-msgstr "ç¦ç”¨èŠ‚点会中止åŒæ­¥è¿‡ç¨‹ã€‚确定继续å—?"
+msgstr "ç¦ç”¨èŠ‚点会åœæ­¢åŒæ­¥ã€‚确定继续?"
msgid "GeoNodes|Does not match the primary storage configuration"
msgstr "与主存储é…ç½®ä¸ä¸€è‡´"
@@ -2003,41 +3032,53 @@ msgstr "失败"
msgid "GeoNodes|Full"
msgstr "全部"
+msgid "GeoNodes|GitLab version"
+msgstr "GitLab版本"
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr "GitLab版本与主节点版本ä¸ä¸€è‡´"
-msgid "GeoNodes|GitLab version:"
-msgstr "GitLab版本:"
+msgid "GeoNodes|Health status"
+msgstr "è¿è¡ŒçŠ¶å†µçŠ¶æ€"
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr "游标处ç†çš„最åŽäº‹ä»¶ID"
+
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr "主节点中最新的事件ID"
+
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr "了解有关仓库校验和进度的详细信æ¯"
-msgid "GeoNodes|Health status:"
-msgstr "å¥åº·çŠ¶å†µï¼š"
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr "了解更多关于仓库验è¯çš„ä¿¡æ¯"
-msgid "GeoNodes|Last event ID processed by cursor:"
-msgstr "游标处ç†çš„最åŽäº‹ä»¶ID:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr "了解更多关于Wiki验è¯è¿›åº¦çš„ä¿¡æ¯"
-msgid "GeoNodes|Last event ID seen from primary:"
-msgstr "主节点中最åŽäº‹ä»¶ID:"
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr "了解更多关于Wiki验è¯çš„ä¿¡æ¯"
msgid "GeoNodes|Loading nodes"
msgstr "载入节点"
-msgid "GeoNodes|Local Attachments:"
-msgstr "本地附件:"
+msgid "GeoNodes|Local LFS objects"
+msgstr "本地LFS对象"
-msgid "GeoNodes|Local LFS objects:"
-msgstr "本地LFS对象:"
+msgid "GeoNodes|Local attachments"
+msgstr "本地附件"
-msgid "GeoNodes|Local job artifacts:"
-msgstr "本地作业生æˆç‰©:"
+msgid "GeoNodes|Local job artifacts"
+msgstr "本地作业产物"
msgid "GeoNodes|New node"
msgstr "新建节点"
msgid "GeoNodes|Node Authentication was successfully repaired."
-msgstr "节点认è¯å·²æˆåŠŸä¿®å¤ã€‚"
+msgstr "æˆåŠŸä¿®å¤èŠ‚点认è¯ã€‚"
msgid "GeoNodes|Node was successfully removed."
-msgstr "节点已æˆåŠŸåˆ é™¤ã€‚"
+msgstr "æˆåŠŸåˆ é™¤èŠ‚点。"
msgid "GeoNodes|Not checksummed"
msgstr "未校验"
@@ -2048,20 +3089,26 @@ msgstr "ä¸åŒæ­¥"
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr "删除节点会åœæ­¢åŒæ­¥ã€‚确定继续?"
-msgid "GeoNodes|Replication slot WAL:"
-msgstr "å¤åˆ¶æ§½WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr "å¤åˆ¶æ§½ WAL"
-msgid "GeoNodes|Replication slots:"
-msgstr "å¤åˆ¶æ§½ï¼š"
+msgid "GeoNodes|Replication slots"
+msgstr "å¤åˆ¶æ§½"
-msgid "GeoNodes|Repositories checksummed:"
-msgstr "已校验仓库:"
+msgid "GeoNodes|Repositories"
+msgstr "仓库"
+
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr "已计算与主节点对应项验è¯æ ¡éªŒå’Œçš„仓库"
+
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr "已与主节点上对应项验è¯çš„仓库"
-msgid "GeoNodes|Repositories:"
-msgstr "仓库:"
+msgid "GeoNodes|Repository checksum progress"
+msgstr "仓库校验和进度"
-msgid "GeoNodes|Repository checksums verified:"
-msgstr "仓库校验和已验è¯ï¼š"
+msgid "GeoNodes|Repository verification progress"
+msgstr "仓库验è¯è¿›åº¦"
msgid "GeoNodes|Selective"
msgstr "选择性"
@@ -2069,17 +3116,20 @@ msgstr "选择性"
msgid "GeoNodes|Something went wrong while changing node status"
msgstr "更改节点状æ€æ—¶å‘生错误"
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr "读å–节点信æ¯æ—¶å‘生错误"
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr "删除节点时å‘生错误"
msgid "GeoNodes|Something went wrong while repairing node"
msgstr "ä¿®å¤èŠ‚点时å‘生错误"
-msgid "GeoNodes|Storage config:"
-msgstr "存储设置:"
+msgid "GeoNodes|Storage config"
+msgstr "存储设置"
-msgid "GeoNodes|Sync settings:"
-msgstr "åŒæ­¥è®¾ç½®:"
+msgid "GeoNodes|Sync settings"
+msgstr "åŒæ­¥è®¾ç½®"
msgid "GeoNodes|Synced"
msgstr "å·²åŒæ­¥"
@@ -2096,35 +3146,41 @@ msgstr "已使用的槽"
msgid "GeoNodes|Verified"
msgstr "已验è¯"
-msgid "GeoNodes|Wiki checksums verified:"
-msgstr " Wiki校验已验è¯ï¼š"
+msgid "GeoNodes|Wiki checksum progress"
+msgstr "Wiki 校验和进度"
+
+msgid "GeoNodes|Wiki verification progress"
+msgstr "Wiki 验è¯è¿›åº¦"
-msgid "GeoNodes|Wikis checksummed:"
-msgstr "wiki已校验"
+msgid "GeoNodes|Wikis"
+msgstr "Wiki"
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr "已计算与主节点对应项验è¯æ ¡éªŒå’Œçš„Wiki"
-msgid "GeoNodes|Wikis:"
-msgstr "Wiki:"
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr "已与主节点上对应项验è¯çš„Wiki"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
-msgstr "当å‰Geo节点é…置使用éžåŠ å¯†çš„HTTP连接, 建议使用HTTPS。"
+msgstr "当å‰Geo节点é…置使用ä¸å®‰å…¨çš„HTTP连接, 建议使用HTTPS。"
msgid "Geo|All projects"
msgstr "所有项目"
msgid "Geo|File sync capacity"
-msgstr "文件åŒæ­¥é‡"
+msgstr "文件åŒæ­¥å®¹é‡"
msgid "Geo|Groups to synchronize"
msgstr "需åŒæ­¥çš„群组"
msgid "Geo|Projects in certain groups"
-msgstr "特定群组中的项目"
+msgstr "指定群组中的项目"
msgid "Geo|Projects in certain storage shards"
msgstr "特定存储片中的项目"
msgid "Geo|Repository sync capacity"
-msgstr "仓库åŒæ­¥é‡"
+msgstr "仓库åŒæ­¥å®¹é‡"
msgid "Geo|Select groups to replicate."
msgstr "选择è¦å¤åˆ¶çš„群组。"
@@ -2132,6 +3188,12 @@ msgstr "选择è¦å¤åˆ¶çš„群组。"
msgid "Geo|Shards to synchronize"
msgstr "需åŒæ­¥çš„存储片"
+msgid "Geo|Verification capacity"
+msgstr "校验能力"
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr "Git仓库URL"
@@ -2139,7 +3201,10 @@ msgid "Git revision"
msgstr "Gitæ交版本"
msgid "Git storage health information has been reset"
-msgstr "Git 存储å¥åº·ä¿¡æ¯å·²é‡ç½®"
+msgstr "Git 存储è¿è¡ŒçŠ¶å†µä¿¡æ¯å·²é‡ç½®"
+
+msgid "Git strategy for pipelines"
+msgstr "æµæ°´çº¿çš„Gitç­–ç•¥"
msgid "Git version"
msgstr "Git 版本"
@@ -2151,49 +3216,136 @@ msgid "GitLab CI Linter has been moved"
msgstr "GitLab CI Linter已被转移"
msgid "GitLab Geo"
+msgstr "GitLab Geo"
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr "Gitlab群组Runnerå¯ä»¥ç”¨æ¥è¿è¡Œç¾¤ç»„内所有项目的代ç ã€‚"
+
+msgid "GitLab Import"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "GitLab Runner"
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
+msgstr "GitLab SSO 地å€"
+
+msgid "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."
+msgstr "GitLabå°†è¿è¡ŒåŽå°ä»»åŠ¡ï¼Œç”Ÿæˆæ•°æ®åº“的匿å化CSV,并上传到预先设定的对象存储目录。"
+
+msgid "GitLab.com import"
msgstr ""
msgid "Gitaly"
-msgstr ""
+msgstr "Gitaly"
msgid "Gitaly Servers"
msgstr "GitalyæœåŠ¡å™¨"
+msgid "Gitaly|Address"
+msgstr "地å€"
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr "返回"
+
msgid "Go back"
msgstr "返回"
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "跳转到派生项目"
msgid "GoToYourFork|Fork"
msgstr "跳转到派生项目"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "Google 身份验è¯ä¸æ˜¯%{link_to_documentation}。如果您想使用此æœåŠ¡ï¼Œè¯·å’¨è¯¢æ‚¨çš„ GitLab 管ç†å‘˜ã€‚"
msgid "Got it!"
msgstr "了解ï¼"
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr "利用å²è¯—故事(Epics),产å“线管ç†ä¼šå˜å¾—æ›´è½»æ¾ä¸”更高效"
+msgid "Graph"
+msgstr "图表"
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr "群组 CI/CD 设置"
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr "群组 ID"
+
+msgid "Group Runners"
+msgstr "群组Runner"
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr "群组维护者å¯ä»¥åœ¨é€šè¿‡ %{link} 注册群组级 Runner"
+
+msgid "Group: %{group_name}"
+msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
-msgstr "从 %{dateWord}"
+msgstr "从 %{dateWord} 起"
msgid "GroupRoadmap|Loading roadmap"
-msgstr "载入路线图"
+msgstr "载入路线图中"
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "读å–å²è¯—故事时出错"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。åªæ˜¾ç¤ºè¿‡åŽ»3个月和接下æ¥3个月的å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ä»»ä½•ç¬¦åˆæ¡ä»¶çš„å²è¯—故事"
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr "路线图显示了å²è¯—故事沿ç€æ—¶é—´çº¿çš„进展情况"
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在月视图中,åªæ˜¾ç¤ºä¸Šä¸ªæœˆï¼Œæœ¬æœˆä»¥åŠæŽ¥ä¸‹æ¥5个月的å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在季度视图中,åªæ˜¾ç¤ºä¸Šä¸ªå­£åº¦ï¼Œæœ¬å­£åº¦ä»¥åŠæŽ¥ä¸‹æ¥4个季度的å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在周视图中,åªæ˜¾ç¤ºä¸Šå‘¨ï¼Œæœ¬å‘¨ä»¥åŠæŽ¥ä¸‹æ¥å››å‘¨çš„å²è¯—故事&ndash; 从 %{startDate} 至 %{endDate}."
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除过滤器。在月视图中,åªæ˜¾ç¤ºä¸Šæœˆï¼Œæœ¬æœˆå’ŒæŽ¥ä¸‹æ¥çš„四个月的å²è¯—故事 &ndash; 从 %{startDate} 到 %{endDate} 。"
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除过滤器。在季度视图中,åªæ˜¾ç¤ºä¸Šä¸ªå­£åº¦ï¼Œæœ¬å­£åº¦å’ŒæŽ¥ä¸‹æ¥çš„四个季度的å²è¯—故事 &ndash; 从 %{startDate} 到 %{endDate} 。"
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除过滤器。在周视图中,åªæ˜¾ç¤ºä¸Šå‘¨ï¼Œæœ¬å‘¨å’ŒæŽ¥ä¸‹æ¥çš„四周的å²è¯—故事 &ndash; 从 %{startDate} 到 %{endDate} 。"
msgid "GroupRoadmap|Until %{dateWord}"
msgstr "直到 %{dateWord}"
@@ -2222,6 +3374,33 @@ msgstr "无法ç¦ç”¨çˆ¶ç»„的“共享群组é”â€ï¼Œåªæœ‰çˆ¶ç¾¤ç»„的所有者
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr "从 %{ancestor_group_name} 中删除共享群组é”"
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr "也å¯ä»¥é€šè¿‡åˆ›å»º %{subgroup_docs_link_start}å­ç¾¤ç»„æ¥åµŒå¥—群组%{subgroup_docs_link_end}。"
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr "ç»å¸¸è®¿é—®çš„群组"
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr "您ç»å¸¸è®¿é—®çš„群组将出现在这里"
+
+msgid "GroupsDropdown|Loading groups"
+msgstr "加载群组中"
+
+msgid "GroupsDropdown|Search your groups"
+msgstr "æœç´¢æ‚¨çš„群组"
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr "å‘生了内部错误."
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ç¬¦åˆæ¡ä»¶çš„群组"
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr "此功能需è¦æµè§ˆå™¨æœ¬åœ°å­˜å‚¨æ”¯æŒ"
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr "群组是几个项目的集åˆã€‚"
@@ -2262,16 +3441,16 @@ msgid "GroupsTree|Sorry, no groups or projects matched your search"
msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰ä»»ä½•ç¾¤ç»„或项目符åˆæ‚¨çš„æœç´¢"
msgid "Have your users email"
-msgstr "有你的用户邮件"
+msgstr "请让用户å‘é€ç”µå­é‚®ä»¶è‡³"
msgid "Header message"
-msgstr ""
+msgstr "页头消æ¯"
msgid "Health Check"
-msgstr "å¥åº·æ£€æŸ¥"
+msgstr "è¿è¡ŒçŠ¶å†µæ£€æŸ¥"
msgid "Health information can be retrieved from the following endpoints. More information is available"
-msgstr "å¥åº·ä¿¡æ¯å¯ä»¥ä»Žä»¥ä¸‹API路径获å–。如需了解更多信æ¯ï¼Œè¯·æŸ¥çœ‹"
+msgstr "è¿è¡ŒçŠ¶å†µä¿¡æ¯å¯ä»¥ä»Žä»¥ä¸‹API路径获å–。如需了解更多信æ¯ï¼Œè¯·æŸ¥çœ‹"
msgid "HealthCheck|Access token is"
msgstr "访问令牌为"
@@ -2280,7 +3459,7 @@ msgid "HealthCheck|Healthy"
msgstr "å¥åº·"
msgid "HealthCheck|No Health Problems Detected"
-msgstr "没有检测到å¥åº·é—®é¢˜"
+msgstr "没有检测到è¿è¡ŒçŠ¶å†µé—®é¢˜"
msgid "HealthCheck|Unhealthy"
msgstr "éžå¥åº·"
@@ -2298,20 +3477,59 @@ msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "éšè—值"
+msgid "Hide whitespace changes"
+msgstr "éšè—空白å˜æ›´å†…容"
+
msgid "History"
msgstr "历å²"
msgid "Housekeeping successfully started"
msgstr "已开始维护"
+msgid "I accept the %{terms_link}"
+msgstr "æˆ‘æŽ¥å— %{terms_link}"
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr "æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–"
+
+msgid "ID"
+msgstr "ID"
+
+msgid "IDE|Commit"
+msgstr "æ交"
+
+msgid "IDE|Edit"
+msgstr "编辑"
+
+msgid "IDE|Go back"
+msgstr "返回"
+
+msgid "IDE|Open in file view"
+msgstr "在文件视图中打开"
+
+msgid "IDE|Review"
+msgstr "审阅"
+
+msgid "Identifier"
+msgstr "身份标识"
+
+msgid "Identities"
+msgstr "身份标识"
+
msgid "Identity provider single sign on URL"
-msgstr ""
+msgstr "身份验è¯æ供商å•ç‚¹ç™»å½•URL"
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr "如果ç¦ç”¨ï¼Œåˆ™è®¿é—®çº§åˆ«å°†å–决于用户在项目中的æƒé™ã€‚"
+
+msgid "If enabled"
+msgstr "如果已å¯ç”¨"
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr "如果å¯ç”¨ï¼Œåˆ™ä½¿ç”¨å¤–部æœåŠ¡ä¸Šçš„分类标签æ¥éªŒè¯å¯¹é¡¹ç›®çš„访问æƒé™ã€‚"
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
-msgstr "如使用GitHub,GitHub上的æ交和拉å–请求(pull request)将会显示æµæ°´çº¿çŠ¶æ€ã€‚ %{more_info_link}"
+msgstr "如使用 GitHub,GitHubçš„æ交(commits)和拉å–请求(pull request) 页é¢å°†æ˜¾ç¤ºæµæ°´çº¿çŠ¶æ€ã€‚ %{more_info_link}"
msgid "If you already have files you can push them using the %{link_to_cli} below."
msgstr "如果文件已存在,å¯ä»¥ä½¿ç”¨ä¸‹é¢çš„ %{link_to_cli} 推é€å®ƒä»¬ã€‚"
@@ -2319,15 +3537,54 @@ msgstr "如果文件已存在,å¯ä»¥ä½¿ç”¨ä¸‹é¢çš„ %{link_to_cli} 推é€å®ƒä»
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr "如果HTTP仓库ä¸å¯å…¬å¼€è®¿é—®ï¼Œè¯·å°†èº«ä»½éªŒè¯ä¿¡æ¯æ·»åŠ åˆ°URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
+msgid "ImageDiffViewer|2-up"
+msgstr "并列(2-up)"
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr "分帧(Onion skin)"
+
+msgid "ImageDiffViewer|Swipe"
+msgstr "滑动"
+
msgid "Import"
msgstr "导入"
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr "导入所有仓库"
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr "正在导入"
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr "从 GitHub 导入仓库"
@@ -2335,33 +3592,48 @@ msgid "Import repository"
msgstr "导入仓库"
msgid "ImportButtons|Connect repositories from"
-msgstr "用以下方å¼è¿žæŽ¥ä»“库"
+msgstr "用以下方å¼è¿žæŽ¥å‚¨å­˜åº“"
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr "å助改进 GitLab ä¼ä¸šç‰ˆçš„议题看æ¿ã€‚"
+msgstr "使用 GitLab ä¼ä¸šç‰ˆçš„增强议题看æ¿ã€‚"
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
-msgstr "å助改善GitLab ä¼ä¸šç‰ˆçš„议题管ç†ä¸Žæƒé‡ã€‚"
+msgstr "使用GitLab ä¼ä¸šç‰ˆè®®é¢˜æƒé‡å¸¦æ¥çš„增强议题管ç†åŠŸèƒ½ã€‚"
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
-msgstr "å助改进GitLab ä¼ä¸šç‰ˆçš„æœç´¢å’Œé«˜çº§å…¨å±€æœç´¢ 。"
+msgstr "使用GitLabä¼ä¸šç‰ˆå…¨å±€æœç´¢å¸¦æ¥çš„增强æœç´¢åŠŸèƒ½"
+
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr "包括所有用户必须接å—çš„æœåŠ¡æ¡æ¬¾å议和éšç§æ”¿ç­–。"
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr "内è”"
+
+msgid "Install GitLab Runner"
+msgstr ""
msgid "Install Runner on Kubernetes"
msgstr "在Kubernetes上安装Runner"
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "安装一个与 GitLab CI 兼容的 Runner"
-
msgid "Instance"
msgid_plural "Instances"
msgstr[0] "实例"
msgid "Instance does not support multiple Kubernetes clusters"
-msgstr "实例ä¸æ”¯æŒå¤šä¸ªKubernetes群集"
+msgstr "实例ä¸æ”¯æŒå¤šä¸ªKubernetes集群"
msgid "Integrations"
msgstr "导入所有仓库"
+msgid "Integrations Settings"
+msgstr "集æˆè®¾ç½®"
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "相关人员甚至å¯ä»¥é€šè¿‡æŽ¨é€æ交æ¥ä¸ºé¡¹ç›®ä½œå‡ºè´¡çŒ®ã€‚"
@@ -2377,8 +3649,11 @@ msgstr "循环周期"
msgid "Introducing Cycle Analytics"
msgstr "周期分æžç®€ä»‹"
+msgid "Issue Boards"
+msgstr "议题看æ¿"
+
msgid "Issue board focus mode"
-msgstr "议题看æ¿æ¨¡å¼"
+msgstr "议题看æ¿èšç„¦æ¨¡å¼"
msgid "Issue events"
msgstr "议题事件"
@@ -2395,12 +3670,21 @@ msgstr "议题"
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr "议题å¯ä»¥æ˜¯ç¼ºé™·ï¼Œä»»åŠ¡æˆ–è¦è®¨è®ºçš„想法。此外,å¯ä»¥é€šè¿‡æœç´¢å’Œè¿‡æ»¤æ¥æŸ¥æ‰¾è®®é¢˜ã€‚"
+msgid "Issues closed"
+msgstr "议题已关闭"
+
msgid "Jan"
msgstr "一"
msgid "January"
msgstr "一月"
+msgid "Job"
+msgstr "作业"
+
+msgid "Job has been erased"
+msgstr "作业已被删除"
+
msgid "Jobs"
msgstr "作业"
@@ -2417,6 +3701,9 @@ msgid "June"
msgstr "六月"
msgid "Koding"
+msgstr "Koding"
+
+msgid "Koding Dashboard"
msgstr ""
msgid "Kubernetes"
@@ -2435,13 +3722,16 @@ msgid "Kubernetes cluster integration was successfully removed."
msgstr "Kubernetes集群集æˆå·²æˆåŠŸåˆ é™¤ã€‚"
msgid "Kubernetes cluster was successfully updated."
-msgstr "Kubernetes群集已æˆåŠŸæ›´æ–°ã€‚"
+msgstr "Kubernetes集群已æˆåŠŸæ›´æ–°ã€‚"
msgid "Kubernetes configured"
msgstr "Kuberneteså·²é…ç½®"
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
-msgstr "KubernetesæœåŠ¡é›†æˆå³å°†è¢«åœç”¨ã€‚ 请使用新的 <a href=\"%{url}\"/>Kubernetes群集</a> 页é¢%{deprecated_message_content} Kubernetes集群"
+msgstr "KubernetesæœåŠ¡é›†æˆå³å°†è¢«åœç”¨ã€‚ 请使用新的 <a href=\"%{url}\"/>Kubernetes集群</a> 页é¢%{deprecated_message_content} Kubernetes集群"
+
+msgid "LFS"
+msgstr "LFS"
msgid "LFSStatus|Disabled"
msgstr "åœç”¨"
@@ -2452,12 +3742,21 @@ msgstr "å¯ç”¨"
msgid "Label"
msgstr "标记"
+msgid "Label actions dropdown"
+msgstr "标记æ“作下拉èœå•"
+
+msgid "Label lists show all issues with the selected label."
+msgstr "标记列表显示具有所选标记的所有议题。"
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr "%{firstLabelName} +%{remainingLabelCount} 更多"
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr "%{labelsString}和 %{remainingLabelCount} 更多"
+msgid "LabelSelect|Labels"
+msgstr "标记"
+
msgid "Labels"
msgstr "标记"
@@ -2467,6 +3766,9 @@ msgstr "标记å¯ä»¥åº”用于 %{features}。群组标记å¯ç”¨äºŽç¾¤ç»„中的所
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "标记å¯ç”¨äºŽå¯¹è®®é¢˜å’Œåˆå¹¶è¯·æ±‚进行分类。"
+msgid "Labels can be applied to issues and merge requests."
+msgstr "标记å¯ç”¨äºŽè®®é¢˜å’Œåˆå¹¶è¯·æ±‚。"
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr "<span>将标记</span> %{labelTitle} <span>å‡çº§ä¸ºç¾¤ç»„标记?</span>"
@@ -2501,6 +3803,9 @@ msgstr "您推é€äº†"
msgid "LastPushEvent|at"
msgstr "于"
+msgid "Latest changes"
+msgstr "最新更改"
+
msgid "Learn more"
msgstr "进一步了解"
@@ -2525,18 +3830,36 @@ msgstr "退出群组"
msgid "Leave project"
msgstr "退出项目"
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
-msgstr "许å¯åè®®"
+msgstr "许å¯è¯"
+
+msgid "LinkedIn"
+msgstr ""
msgid "List"
msgstr "列表"
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr "列出GitHub仓库"
+msgid "Loading contribution stats for group members"
+msgstr "加载群组æˆå‘˜çš„贡献统计信æ¯"
+
msgid "Loading the GitLab IDE..."
msgstr "加载GitLab IDE..."
+msgid "Loading..."
+msgstr "正在加载..."
+
msgid "Lock"
msgstr "é”定"
@@ -2546,24 +3869,45 @@ msgstr "é”定 %{issuableDisplayName}"
msgid "Lock not found"
msgstr "未找到é”"
+msgid "Lock to current projects"
+msgstr "é”定到当å‰é¡¹ç›®"
+
msgid "Locked"
msgstr "å·²é”定"
msgid "Locked Files"
msgstr "å·²é”定文件"
+msgid "Locked to current projects"
+msgstr "å·²é”定到目å‰çš„项目"
+
msgid "Locks give the ability to lock specific file or folder."
msgstr "加é”å¯ä»¥é”定特定的文件或文件夹。"
-msgid "Login"
-msgstr "登录"
+msgid "Logs"
+msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
-msgstr "GitLab Geo å¯ä»¥åˆ›å»º GitLab 实例的åªè¯»é•œåƒ, 使得从远端克隆和拉å–大型代ç ä»“库的时间大大缩短,æ高团队æˆå‘˜çš„工作效率。"
+msgstr "GitLab Geo å¯ä»¥åˆ›å»º GitLab 实例的åªè¯»é•œåƒ, 使得从远端克隆和拉å–大型代ç ä»“库的时间大大缩短,从而æ高团队æˆå‘˜çš„工作效率。"
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
msgid "Manage all notifications"
msgstr "管ç†å…¨éƒ¨é€šçŸ¥"
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr "管ç†ç¾¤ç»„标记"
@@ -2574,6 +3918,24 @@ msgid "Manage project labels"
msgstr "管ç†é¡¹ç›®æ ‡è®°"
msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr "通过SAML管ç†ç¾¤ç»„æˆå‘˜ï¼Œè¿›ä¸€æ­¥æ高安全性。"
+
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
msgstr ""
msgid "Mar"
@@ -2582,11 +3944,14 @@ msgstr "三"
msgid "March"
msgstr "三月"
-msgid "Mark done"
+msgid "Mark todo as done"
msgstr "标记为已完æˆ"
+msgid "Markdown enabled"
+msgstr "支æŒMarkdownæ ¼å¼"
+
msgid "Maximum git storage failures"
-msgstr "最大 git 存储失败"
+msgstr "最大 git 存储失败次数"
msgid "May"
msgstr "五"
@@ -2598,26 +3963,62 @@ msgid "Members"
msgstr "æˆå‘˜"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
-msgstr ""
+msgstr "群组在登录您的群组时会跳转到此处。请从您的身份认è¯æ供商处获得该信æ¯ã€‚它å¯èƒ½å«åšâ€œSSOæœåŠ¡ä½ç½®ï¼ˆSSO Service Location)â€ï¼Œâ€œSAML令牌é¢å‘点(SAML Token Issuance Endpoint)â€æˆ–“SAML 2.0/W-Federation URLâ€ã€‚"
+
+msgid "Merge Request"
+msgstr "åˆå¹¶è¯·æ±‚"
+
+msgid "Merge Request:"
+msgstr "åˆå¹¶è¯·æ±‚:"
msgid "Merge Requests"
msgstr "åˆå¹¶è¯·æ±‚"
+msgid "Merge Requests created"
+msgstr "已创建åˆå¹¶è¯·æ±‚"
+
msgid "Merge events"
msgstr "åˆå¹¶äº‹ä»¶"
msgid "Merge request"
msgstr "åˆå¹¶è¯·æ±‚"
+msgid "Merge request approvals"
+msgstr "åˆå¹¶è¯·æ±‚批准"
+
+msgid "Merge requests"
+msgstr "åˆå¹¶è¯·æ±‚"
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "åˆå¹¶è¯·æ±‚用于æ出对项目的更改与他人讨论"
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr "在新议题中解决此讨论"
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr "ä¿å­˜è¯„论失败"
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr "切æ¢æ­¤æ–‡ä»¶çš„讨论"
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr "更新讨论失败"
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr "查看文件 @ %{commitId}"
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr "查看已替æ¢æ–‡ä»¶ @ %{commitId}"
+
msgid "Merged"
msgstr "å·²åˆå¹¶"
msgid "Messages"
msgstr "消æ¯"
+msgid "Metrics"
+msgstr "指标"
+
msgid "Metrics - Influx"
msgstr "指标 - Influx"
@@ -2627,18 +4028,27 @@ msgstr "指标 - Prometheus"
msgid "Metrics|Business"
msgstr "业务"
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr "查看有关部署到环境的CI/CD文档"
+
msgid "Metrics|Create metric"
msgstr "创建指标"
msgid "Metrics|Edit metric"
msgstr "编辑指标"
+msgid "Metrics|Environment"
+msgstr "环境"
+
msgid "Metrics|For grouping similar metrics"
msgstr "用于分组类似指标"
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr "图表纵轴的标签。通常表示绘制å•ä½ã€‚水平轴(X轴)一般表示时间。"
+msgid "Metrics|Learn about environments"
+msgstr "了解环境"
+
msgid "Metrics|Legend label (optional)"
msgstr "图例标签(å¯é€‰ï¼‰"
@@ -2651,6 +4061,9 @@ msgstr "å称"
msgid "Metrics|New metric"
msgstr "创建指标"
+msgid "Metrics|No deployed environments"
+msgstr "未部署的环境"
+
msgid "Metrics|Prometheus Query Documentation"
msgstr "Prometheus查询文档"
@@ -2663,9 +4076,27 @@ msgstr "å“应"
msgid "Metrics|System"
msgstr "系统"
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr "获å–环境数æ®æ—¶å‡ºé”™ï¼Œè¯·é‡è¯•"
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr "获å–部署信æ¯æ—¶å‡ºé”™ã€‚"
+
+msgid "Metrics|There was an error getting environments information."
+msgstr "获å–环境信æ¯æ—¶å‡ºé”™ã€‚"
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr "类型"
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr "æ¥è‡ªPrometheus终端节点的æ„外部署数æ®å“应"
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr "æ¥è‡ªPrometheus终端节点的æ„外指标数æ®å“应"
+
msgid "Metrics|Unit label"
msgstr "å•ä½æ ‡ç­¾"
@@ -2679,23 +4110,26 @@ msgid "Metrics|Y-axis label"
msgstr "Y轴标签"
msgid "Metrics|e.g. HTTP requests"
-msgstr "例如HTTP请求"
+msgstr "例如:HTTP 请求"
msgid "Metrics|e.g. Requests/second"
-msgstr "例如æ¯ç§’请求数"
+msgstr "例如:次请求/秒"
msgid "Metrics|e.g. Throughput"
-msgstr "例如åžåé‡"
+msgstr "例如:åžåé‡"
msgid "Metrics|e.g. rate(http_requests_total[5m])"
-msgstr "速率(5分钟内所有http请求)"
+msgstr "例如:速率(5分钟内所有http请求)"
msgid "Metrics|e.g. req/sec"
-msgstr "例如æ¯ç§’请求数"
+msgstr "例如:次请求/秒"
msgid "Milestone"
msgstr "里程碑"
+msgid "Milestones"
+msgstr "里程碑"
+
msgid "Milestones|Delete milestone"
msgstr "删除里程碑"
@@ -2729,6 +4163,15 @@ msgstr "关闭"
msgid "Monitoring"
msgstr "监控"
+msgid "Months"
+msgstr "月"
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr "更多æ“作"
+
msgid "More info"
msgstr "更多信æ¯"
@@ -2738,6 +4181,9 @@ msgstr "更多信æ¯"
msgid "More information is available|here"
msgstr "帮助文档"
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr "移动"
@@ -2745,24 +4191,63 @@ msgid "Move issue"
msgstr "移动议题"
msgid "Multiple issue boards"
-msgstr "多个议题看æ¿"
+msgstr "多é‡è®®é¢˜çœ‹æ¿"
+
+msgid "Name"
+msgstr "姓å"
msgid "Name new label"
msgstr "命å新标记"
+msgid "Name your individual key via a title"
+msgstr "通过标题命å您的个人密钥"
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr "帮助"
+
+msgid "Nav|Home"
+msgstr "首页"
+
+msgid "Nav|Sign In / Register"
+msgstr "注册/登录"
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr "退出并登录到其他账å·"
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr "新增事项"
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr "新建身份标识"
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "新建议题"
-msgid "New Kubernetes Cluster"
-msgstr "新建Kubernetes集群"
-
-msgid "New Kubernetes cluster"
-msgstr "新建Kubernetes集群"
+msgid "New Label"
+msgstr "新标签"
msgid "New Pipeline Schedule"
msgstr "创建æµæ°´çº¿è®¡åˆ’"
+msgid "New Snippet"
+msgstr "新建代ç ç‰‡æ®µ"
+
+msgid "New Snippets"
+msgstr "新建代ç ç‰‡æ®µ"
+
msgid "New branch"
msgstr "新建分支"
@@ -2779,7 +4264,10 @@ msgid "New file"
msgstr "新建文件"
msgid "New group"
-msgstr "新群组"
+msgstr "新建群组"
+
+msgid "New identity"
+msgstr "新身份标识"
msgid "New issue"
msgstr "新建议题"
@@ -2790,6 +4278,9 @@ msgstr "新建标记"
msgid "New merge request"
msgstr "新建åˆå¹¶è¯·æ±‚"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr "æ–°æµæ°´çº¿å°†å–消åŒä¸€åˆ†æ”¯ä¸Šè¾ƒæ—§çš„待处ç†æµæ°´çº¿"
+
msgid "New project"
msgstr "新建项目"
@@ -2805,6 +4296,12 @@ msgstr "新建å­ç¾¤ç»„"
msgid "New tag"
msgstr "新建标签"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr "å¦"
+
msgid "No Label"
msgstr "无标记"
@@ -2821,13 +4318,43 @@ msgid "No due date"
msgstr "无截止日期"
msgid "No estimate or time spent"
-msgstr "无估算或消耗的时间"
+msgstr "无预计或已用时间"
msgid "No file chosen"
msgstr "未选定任何文件"
-msgid "No labels created yet."
-msgstr "尚未创建标记"
+msgid "No files found"
+msgstr "沒有找到文件"
+
+msgid "No files found."
+msgstr "没有找到文件。"
+
+msgid "No issues for the selected time period."
+msgstr "所选时间段没有议题。"
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr "所选时间段没有åˆå¹¶è¯·æ±‚。"
+
+msgid "No merge requests found"
+msgstr "找ä¸åˆ°åˆå¹¶è¯·æ±‚"
+
+msgid "No messages were logged"
+msgstr "未记录任何消æ¯"
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
+msgstr "所选时间段没有推é€ã€‚"
msgid "No repository"
msgstr "没有仓库"
@@ -2835,6 +4362,9 @@ msgstr "没有仓库"
msgid "No schedules"
msgstr "没有计划"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr "æ— "
@@ -2871,6 +4401,9 @@ msgstr "æ示:如GitLab管ç†å‘˜é…ç½® %{github_integration_link},将å…许
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr "æ示:如GitLab管ç†å‘˜é…ç½® %{github_integration_link},将å…许通过GitHub登录并å…许导入Github代ç ä»“库而ä¸éœ€è¦ä¸ªäººè®¿é—®ä»¤ç‰Œã€‚"
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "通知事件"
@@ -2953,37 +4486,82 @@ msgid "October"
msgstr "å月"
msgid "OfSearchInADropdown|Filter"
-msgstr "筛选"
+msgstr "过滤"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr "仓库导入åŽï¼Œå¯ä»¥é€šè¿‡ SSH 拉å–é•œåƒã€‚了解更多 %{ssh_link}"
-msgid "Online IDE integration settings."
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
+msgstr "在线IDE集æˆè®¾ç½®ã€‚"
+
+msgid "Only comments from the following commit are shown below"
+msgstr "下é¢ä»…显示æ¥è‡ªä»¥ä¸‹æ交的评论"
+
msgid "Only project members can comment."
msgstr "åªæœ‰é¡¹ç›®æˆå‘˜å¯ä»¥å‘表评论。"
+msgid "Oops, are you sure?"
+msgstr "å•Š~~, 确定å—?"
+
msgid "Open"
msgstr "打开"
+msgid "Open in Xcode"
+msgstr "用Xcode打开"
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr "已打开"
+msgid "Opened MR"
+msgstr "å¼€å¯çš„åˆå¹¶è¯·æ±‚"
+
+msgid "Opened issues"
+msgstr "å¼€å¯çš„议题"
+
msgid "OpenedNDaysAgo|Opened"
msgstr "创建于"
msgid "Opens in a new window"
msgstr "打开一个新窗å£"
+msgid "Operations"
+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 "æ“作"
+msgid "Or you can choose one of the suggested colors below"
+msgstr "或者您å¯ä»¥é€‰æ‹©ä¸‹é¢çš„建议颜色之一"
+
+msgid "Other Labels"
+msgstr "其他标记"
+
+msgid "Other information"
+msgstr "其他信æ¯"
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr "å¦åˆ™ï¼Œå»ºè®®æ‚¨ä»Žä¸‹é¢çš„一个选项开始。"
msgid "Outbound requests"
-msgstr ""
+msgstr "外å‘请求"
msgid "Overview"
msgstr "概览"
@@ -3007,16 +4585,34 @@ msgid "Pagination|« First"
msgstr "« 首页"
msgid "Part of merge request changes"
-msgstr ""
+msgstr "包å«äºŽåˆå¹¶è¯·æ±‚å˜æ›´ä¸­"
msgid "Password"
msgstr "密ç "
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr "粘贴您的 SSH 公钥,通常包å«åœ¨ '~/.ssh/id_rsa.pub' 文件中,并以 'ssh-rsa' 开头。ä¸è¦ä½¿ç”¨æ‚¨çš„ SSH ç§é’¥ã€‚"
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr "æš‚åœ"
+
msgid "Pending"
-msgstr "等待处ç†"
+msgstr "等待中"
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr "æ¯ä¸ªä½œä¸šã€‚如果作业超过此阈值,则会将其标记为失败"
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr "执行高级选项,例如更改路径,移动或删除群组。"
msgid "Performance optimization"
-msgstr ""
+msgstr "性能优化"
+
+msgid "Permissions"
+msgstr "æƒé™"
msgid "Personal Access Token"
msgstr "个人访问凭è¯"
@@ -3025,7 +4621,7 @@ msgid "Pipeline"
msgstr "æµæ°´çº¿"
msgid "Pipeline Health"
-msgstr "æµæ°´çº¿å¥åº·æŒ‡æ ‡"
+msgstr "æµæ°´çº¿è¿è¡ŒçŠ¶å†µæŒ‡æ ‡"
msgid "Pipeline Schedule"
msgstr "æµæ°´çº¿è®¡åˆ’"
@@ -3036,6 +4632,9 @@ msgstr "æµæ°´çº¿è®¡åˆ’"
msgid "Pipeline quota"
msgstr "æµæ°´çº¿é…é¢"
+msgid "Pipeline triggers"
+msgstr "æµæ°´çº¿è§¦å‘器"
+
msgid "PipelineCharts|Failed:"
msgstr "失败:"
@@ -3132,11 +4731,23 @@ msgstr "当å‰æ²¡æœ‰æµæ°´çº¿ã€‚"
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "此项目当å‰æœªé…ç½®è¿è¡Œæµæ°´çº¿ã€‚"
-msgid "Pipeline|Retry pipeline"
-msgstr "é‡è¯•æµæ°´çº¿"
+msgid "Pipeline|Create for"
+msgstr "创建于"
+
+msgid "Pipeline|Create pipeline"
+msgstr "创建æµæ°´çº¿"
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr "现有分支å称或者标签"
+
+msgid "Pipeline|Run Pipeline"
+msgstr "è¿è¡Œæµæ°´çº¿"
+
+msgid "Pipeline|Search branches"
+msgstr "æœç´¢åˆ†æ”¯"
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
-msgstr "é‡è¯•æµæ°´çº¿ï¼ƒ%{pipelineId}å—?"
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr "指定è¦åœ¨æ­¤æ¬¡è¿è¡Œä¸­ä½¿ç”¨çš„å˜é‡å€¼ã€‚默认情况下将使用 %{settings_link} 中指定的值。"
msgid "Pipeline|Stop pipeline"
msgstr "åœæ­¢æµæ°´çº¿"
@@ -3144,8 +4755,8 @@ msgstr "åœæ­¢æµæ°´çº¿"
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr "åœæ­¢æµæ°´çº¿ï¼ƒ%{pipelineId}å—?"
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
-msgstr "å³å°†é‡è¯•æµæ°´çº¿ %{pipelineId}。"
+msgid "Pipeline|Variables"
+msgstr "å˜é‡"
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
msgstr "å³å°†åœæ­¢æµæ°´çº¿ %{pipelineId}。"
@@ -3162,18 +4773,42 @@ msgstr "于阶段"
msgid "Pipeline|with stages"
msgstr "于阶段"
+msgid "Plain diff"
+msgstr "文本差异"
+
+msgid "Planned finish date"
+msgstr "计划完æˆæ—¥æœŸ"
+
+msgid "Planned start date"
+msgstr "计划开始日期"
+
msgid "PlantUML"
-msgstr ""
+msgstr "PlantUML"
msgid "Play"
msgstr "è¿è¡Œ"
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
-msgstr "请 <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">为æŸä¸ªé¡¹ç›®å¯ç”¨è®¡è´¹åŠŸèƒ½ï¼Œä»¥ä¾¿èƒ½å¤Ÿåˆ›å»ºKubernetes群集</a>,然åŽé‡è¯•ã€‚"
+msgid "Please accept the Terms of Service before continuing."
+msgstr "请接å—æœåŠ¡æ¡æ¬¾ä»¥ç»§ç»­ã€‚"
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
+msgstr "请至少选择一个过滤器æ¥æŸ¥çœ‹ç»“æžœ"
msgid "Please solve the reCAPTCHA"
msgstr "请填写验è¯ç ã€‚"
+msgid "Please try again"
+msgstr "请å†è¯•ä¸€æ¬¡"
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr "连接代ç ä»“库中,请ç¨å€™ã€‚å¯åœ¨ä»»æ„时刻刷新以获å–当å‰çŠ¶æ€ã€‚"
@@ -3183,9 +4818,24 @@ msgstr "导入代ç ä»“库中,请ç¨å€™ã€‚å¯åœ¨ä»»æ„时刻刷新以获å–当
msgid "Preferences"
msgstr "å好设置"
+msgid "Preferences|Navigation theme"
+msgstr "导航主题"
+
msgid "Primary"
msgstr "主è¦"
+msgid "Prioritize"
+msgstr "优先"
+
+msgid "Prioritize label"
+msgstr "优先标记"
+
+msgid "Prioritized Labels"
+msgstr "优先的标记"
+
+msgid "Prioritized label"
+msgstr "优先的标记"
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "ç§äºº - å¿…é¡»å‘æ¯ä¸ªç”¨æˆ·æ˜Žç¡®æŽˆäºˆé¡¹ç›®è®¿é—®æƒé™ã€‚"
@@ -3196,10 +4846,22 @@ msgid "Private projects can be created in your personal namespace with:"
msgstr "ç§æœ‰é¡¹ç›®å¯ä»¥åœ¨ä¸ªäººå称空间中创建:"
msgid "Profile"
-msgstr "用户信æ¯"
+msgstr "用户资料"
+
+msgid "Profile Settings"
+msgstr ""
msgid "Profiles|Account scheduled for removal."
-msgstr "å¸æˆ·åˆ é™¤è®¡åˆ’。"
+msgstr "å¸æˆ·å·²å®‰æŽ’被删除。"
+
+msgid "Profiles|Add key"
+msgstr "添加密钥"
+
+msgid "Profiles|Change username"
+msgstr "更改用户å"
+
+msgid "Profiles|Current path: %{path}"
+msgstr "当å‰è·¯å¾„: %{path}"
msgid "Profiles|Delete Account"
msgstr "删除å¸æˆ·"
@@ -3219,9 +4881,27 @@ msgstr "密ç æ— æ•ˆ"
msgid "Profiles|Invalid username"
msgstr "用户å无效"
+msgid "Profiles|Path"
+msgstr "路径"
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr "这看起æ¥ä¸åƒ SSH 公钥,确定è¦æ·»åŠ å®ƒå—?"
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "键入您的 %{confirmationValue} 以确认:"
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr "通常以“ssh-rsa ...â€å¼€å¤´"
+
+msgid "Profiles|Update username"
+msgstr "更新用户å"
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr "用户å更改失败 - %{message}"
+
+msgid "Profiles|Username successfully changed"
+msgstr "用户å更改æˆåŠŸ"
+
msgid "Profiles|You don't have access to delete this user."
msgstr "您无æƒåˆ é™¤æ­¤ç”¨æˆ·ã€‚"
@@ -3231,15 +4911,24 @@ msgstr "您必须转移所有æƒæˆ–删除这些群组,然åŽæ‰èƒ½åˆ é™¤æ‚¨çš„
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "您的å¸æˆ·ç›®å‰æ˜¯è¿™äº›ç¾¤ç»„的所有者:"
+msgid "Profiles|e.g. My MacBook key"
+msgstr "例如: My MacBook Key"
+
msgid "Profiles|your account"
msgstr "您的å¸æˆ·"
msgid "Profiling - Performance bar"
-msgstr ""
+msgstr "åˆ†æž - 性能æ "
msgid "Programming languages used in this repository"
msgstr "当å‰ä»£ç ä»“库中使用的编程语言"
+msgid "Progress"
+msgstr "进度"
+
+msgid "Project"
+msgstr "项目"
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "项目 “%{project_name}†正在被删除。"
@@ -3252,6 +4941,9 @@ msgstr "项目 '%{project_name}' 已创建æˆåŠŸã€‚"
msgid "Project '%{project_name}' was successfully updated."
msgstr "项目 '%{project_name}' 已更新完æˆã€‚"
+msgid "Project Badges"
+msgstr "项目徽章"
+
msgid "Project access must be granted explicitly to each user."
msgstr "项目访问æƒé™å¿…须明确授æƒç»™æ¯ä¸ªç”¨æˆ·ã€‚"
@@ -3276,6 +4968,9 @@ msgstr "项目导出链接已过期。请从项目设置中é‡æ–°ç”Ÿæˆé¡¹ç›®å¯¼
msgid "Project export started. A download link will be sent by email."
msgstr "项目导出已开始。下载链接将通过电å­é‚®ä»¶å‘é€ã€‚"
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "订阅"
@@ -3283,26 +4978,17 @@ msgid "ProjectCreationLevel|Allowed to create projects"
msgstr "å…许创建项目"
msgid "ProjectCreationLevel|Default project creation protection"
-msgstr "默认项目创建ä¿æŠ¤"
+msgstr "默认的群组项目创建ä¿æŠ¤"
-msgid "ProjectCreationLevel|Developers + Masters"
-msgstr "Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr "å¼€å‘人员 + 维护人员"
-msgid "ProjectCreationLevel|Masters"
-msgstr "Masters"
+msgid "ProjectCreationLevel|Maintainers"
+msgstr "维护人员"
msgid "ProjectCreationLevel|No one"
msgstr "ç¦æ­¢"
-msgid "ProjectFeature|Disabled"
-msgstr "åœç”¨"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "任何对项目有访问æƒçš„人"
-
-msgid "ProjectFeature|Only team members"
-msgstr "åªé™å›¢é˜Ÿæˆå‘˜"
-
msgid "ProjectFileTree|Name"
msgstr "å称"
@@ -3312,17 +4998,23 @@ msgstr "从未"
msgid "ProjectLifecycle|Stage"
msgstr "阶段"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "分支图"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "è”系管ç†å‘˜æ›´æ”¹æ­¤è®¾ç½®ã€‚"
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr "ä¿æŠ¤æ ‡ç­¾å¤±è´¥"
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr "更新标签失败ï¼"
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr "åªæœ‰å·²ç­¾ç½²æ交æ‰å¯ä»¥æŽ¨é€åˆ°æ­¤ä»“库。"
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
-msgstr "此设置已应用于æœåŠ¡å™¨çº§åˆ«ï¼Œå¯ç”±ç®¡ç†å‘˜è¦†ç›–。"
+msgstr "此设置已应用于æœåŠ¡å™¨çº§åˆ«ï¼Œå¯ç”±ç®¡ç†å‘˜ä¿®æ”¹ã€‚"
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
msgstr "此设置应用于æœåŠ¡å™¨çº§åˆ«ï¼Œä½†å·²è¢«è¯¥é¡¹ç›®è¦†ç›–。"
@@ -3331,11 +5023,14 @@ msgid "ProjectSettings|This setting will be applied to all projects unless overr
msgstr "此设置将应用于所有项目,除éžè¢«ç®¡ç†å‘˜è¦†ç›–。"
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
-msgstr "用户åªèƒ½é€šè¿‡è‡ªå·±å·²éªŒè¯çš„电å­é‚®ä»¶åœ°å€å°†æ交到此仓库中。"
+msgstr "用户åªèƒ½é€šè¿‡è‡ªå·±å·²éªŒè¯çš„电å­é‚®ä»¶åœ°å€å°†æ交到此存储库中。"
msgid "Projects"
msgstr "项目"
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr "ç»å¸¸è®¿é—®"
@@ -3354,8 +5049,38 @@ msgstr "å‘生了内部错误"
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ç¬¦åˆæ¡ä»¶çš„项目"
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr "此功能需è¦æµè§ˆå™¨æ”¯æŒ localStorage"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
+msgstr "时间"
msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr "找到%{exporters} åŠ %{metrics}"
@@ -3418,7 +5143,7 @@ msgid "PrometheusService|Prometheus is being automatically managed on your clust
msgstr "Prometheus正在被群集自动管ç†"
msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
-msgstr "在首次部署到æŸçŽ¯å¢ƒä¹‹åŽ, 这些指标æ‰ä¼šè¢«ç›‘控"
+msgstr "在首次部署到环境之åŽ, 这些指标æ‰ä¼šè¢«ç›‘控"
msgid "PrometheusService|Time-series monitoring service"
msgstr "时间åºåˆ—监控æœåŠ¡"
@@ -3435,21 +5160,45 @@ msgstr "等待首次部署到环境以查找常用指标"
msgid "Promote"
msgstr "å‡çº§"
-msgid "Promote to Group Label"
-msgstr "å‡çº§åˆ°ç¾¤ç»„标记"
+msgid "Promote these project milestones into a group milestone."
+msgstr "将这些项目里程碑å‡çº§ä¸ºç¾¤ç»„里程碑。"
msgid "Promote to Group Milestone"
msgstr "å‡çº§åˆ°ç¾¤ç»„里程碑"
+msgid "Promote to group label"
+msgstr "å‡çº§åˆ°ç¾¤ç»„标记"
+
+msgid "Promotions|Don't show me this again"
+msgstr "ä¸å†æ˜¾ç¤º"
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr "å²è¯—故事(Epics) 让您å¯ä»¥é€šè¿‡è¿½è¸ªè·¨é¡¹ç›®å’Œé‡Œç¨‹ç¢‘共享主题的问题组,更有效地管ç†é¡¹ç›®ç»„åˆã€‚"
+
+msgid "Promotions|This feature is locked."
+msgstr "此功能已é”定"
+
+msgid "Promotions|Upgrade plan"
+msgstr "å‡çº§è®¢é˜…计划"
+
msgid "Protip:"
msgstr "专家æ示:"
+msgid "Provider"
+msgstr "æ供者"
+
+msgid "Pseudonymizer data collection"
+msgstr "匿å化数æ®æ”¶é›†"
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "公开 - 群组和任何公共项目å¯ä»¥åœ¨æ²¡æœ‰ä»»ä½•èº«ä»½éªŒè¯çš„情况下查看。"
msgid "Public - The project can be accessed without any authentication."
msgstr "公开 - 无需任何身份验è¯å³å¯è®¿é—®è¯¥é¡¹ç›®ã€‚"
+msgid "Public pipelines"
+msgstr "公共æµæ°´çº¿"
+
msgid "Push Rules"
msgstr "推é€è§„则"
@@ -3463,7 +5212,16 @@ msgid "Push to create a project"
msgstr "通过推é€åˆ›å»ºé¡¹ç›®"
msgid "PushRule|Committer restriction"
-msgstr "æ交é™åˆ¶"
+msgstr "æ交者é™åˆ¶"
+
+msgid "Pushed"
+msgstr "已推é€"
+
+msgid "Pushes"
+msgstr "推é€"
+
+msgid "Quarters"
+msgstr "季度"
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "快速æ“作å¯ç”¨äºŽè®®é¢˜æ述和评论框。"
@@ -3471,24 +5229,30 @@ msgstr "快速æ“作å¯ç”¨äºŽè®®é¢˜æ述和评论框。"
msgid "Read more"
msgstr "进一步了解"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "自述文件"
msgid "Real-time features"
-msgstr ""
-
-msgid "RefSwitcher|Branches"
-msgstr "分支"
-
-msgid "RefSwitcher|Tags"
-msgstr "标签"
+msgstr "实时功能"
msgid "Reference:"
msgstr "引用:"
+msgid "Refresh"
+msgstr "刷新"
+
msgid "Register / Sign In"
msgstr "注册/登录"
+msgid "Register and see your runners for this group."
+msgstr "注册并查看当å‰ç¾¤ç»„çš„Runner。"
+
+msgid "Register and see your runners for this project."
+msgstr "注册和查看这个项目的 Runner"
+
msgid "Registry"
msgstr "注册表"
@@ -3519,50 +5283,92 @@ msgstr "ç¨åŽæ醒"
msgid "Remove"
msgstr "删除"
+msgid "Remove Runner"
+msgstr "移除Runner"
+
msgid "Remove avatar"
msgstr "删除头åƒ"
+msgid "Remove priority"
+msgstr "删除优先级"
+
msgid "Remove project"
msgstr "删除项目"
msgid "Repair authentication"
msgstr "ä¿®å¤è®¤è¯"
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
-msgstr "从URL导入仓库"
+msgstr "从 URL 导入仓库"
msgid "Repository"
msgstr "仓库"
+msgid "Repository Settings"
+msgstr "存储库设置"
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr "当å‰ä»“库无加é”文件。"
msgid "Repository maintenance"
-msgstr ""
+msgstr "仓库维护"
-msgid "Repository mirror settings"
-msgstr ""
+msgid "Repository mirror"
+msgstr "仓库镜åƒ"
msgid "Repository storage"
-msgstr ""
+msgstr "仓库存储"
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr "选择"
msgid "Request Access"
msgstr "申请æƒé™"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr "è¦æ±‚所有用户在访问GitLab时接å—æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–。"
+
msgid "Reset git storage health information"
-msgstr "é‡ç½® Git 存储的å¥åº·ä¿¡æ¯"
+msgstr "é‡ç½® Git 存储的è¿è¡ŒçŠ¶å†µä¿¡æ¯"
msgid "Reset health check access token"
-msgstr "é‡ç½®å¥åº·æ£€æŸ¥è®¿é—®ä»¤ç‰Œ"
+msgstr "é‡ç½®è¿è¡ŒçŠ¶å†µæ£€æŸ¥è®¿é—®ä»¤ç‰Œ"
msgid "Reset runners registration token"
msgstr "é‡ç½® Runner 注册令牌"
+msgid "Resolve all discussions in new issue"
+msgstr "在新议题中解决所有讨论"
+
+msgid "Resolve conflicts on source branch"
+msgstr "在æºåˆ†æ”¯ä¸Šè§£å†³å†²çª"
+
msgid "Resolve discussion"
msgstr "解决讨论"
-msgid "Response"
-msgstr "å“应"
+msgid "Response metrics (Custom)"
+msgstr "å“应指标(自定义)"
+
+msgid "Resume"
+msgstr "æ¢å¤"
+
+msgid "Retry"
+msgstr "é‡è¯•"
+
+msgid "Retry this job"
+msgstr "é‡è¯•å½“å‰ä½œä¸š"
+
+msgid "Retry verification"
+msgstr "é‡è¯•éªŒè¯"
msgid "Reveal value"
msgid_plural "Reveal values"
@@ -3574,13 +5380,19 @@ msgstr "还原此æ交"
msgid "Revert this merge request"
msgstr "还原此åˆå¹¶è¯·æ±‚"
+msgid "Review"
+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 ""
+msgstr "查看在您的身份验è¯æ供商中é…ç½®æœåŠ¡æ供商的æµç¨‹ - 在这里,GitLab是“æœåŠ¡æ供商â€æˆ–“ä¾èµ–æ–¹â€ã€‚"
msgid "Reviewing"
-msgstr "检查"
+msgstr "审阅中"
msgid "Reviewing (merge request !%{mergeRequestId})"
+msgstr "审核(åˆå¹¶è¯·æ±‚ !%{mergeRequestId})"
+
+msgid "Revoke"
msgstr ""
msgid "Roadmap"
@@ -3589,24 +5401,48 @@ msgstr "路线图"
msgid "Run CI/CD pipelines for external repositories"
msgstr "使用外部仓库的CI/CDæµæ°´çº¿"
+msgid "Runner token"
+msgstr "Runner 令牌"
+
msgid "Runners"
msgstr "Runner"
+msgid "Runners API"
+msgstr "Runners API"
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr "Runnerå¯ä»¥æ”¾åœ¨ä¸åŒçš„用户ã€æœåŠ¡å™¨ï¼Œç”šè‡³æœ¬åœ°æœºå™¨ä¸Šã€‚"
+
msgid "Running"
msgstr "è¿è¡Œä¸­"
+msgid "SAML SSO"
+msgstr "SAML SSO(å•ç‚¹ç™»å½•)"
+
+msgid "SAML SSO for %{group_name}"
+msgstr "群组%{group_name} 的 SAML SSO"
+
msgid "SAML Single Sign On"
-msgstr ""
+msgstr "SAML å•ç‚¹ç™»å½•"
msgid "SAML Single Sign On Settings"
-msgstr ""
+msgstr "SAML å•ç‚¹ç™»å½•è®¾ç½®"
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
-msgstr ""
+msgstr "SAML令牌签åè¯ä¹¦çš„SHA1指纹。请从身份验è¯æ供商处获å–(也å¯ä»¥è¢«ç§°ä¸ºâ€œæŒ‡çº¹â€ï¼‰ã€‚"
msgid "SSH Keys"
msgstr "SSH 密钥"
+msgid "SSL Verification"
+msgstr "SSL验è¯"
+
+msgid "Save"
+msgstr "ä¿å­˜"
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr "ä¿å­˜ä¿®æ”¹"
@@ -3628,15 +5464,39 @@ msgstr "日程"
msgid "Scheduling Pipelines"
msgstr "æµæ°´çº¿è®¡åˆ’"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
-msgstr "议题看æ¿èŒƒå›´"
+msgstr "指定范围的议题看æ¿"
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr "滚动到底部"
+
+msgid "Scroll to top"
+msgstr "滚动到顶部"
msgid "Search"
msgstr "æœç´¢"
+msgid "Search branches"
+msgstr "æœç´¢åˆ†æ”¯"
+
msgid "Search branches and tags"
msgstr "æœç´¢åˆ†æ”¯å’Œæ ‡ç­¾"
+msgid "Search files"
+msgstr "æœç´¢æ–‡ä»¶"
+
+msgid "Search for projects, issues, etc."
+msgstr "æœç´¢é¡¹ç›®ã€è®®é¢˜ç­‰ç­‰â€¦"
+
+msgid "Search merge requests"
+msgstr "æœç´¢åˆå¹¶è¯·æ±‚"
+
msgid "Search milestones"
msgstr "æœç´¢é‡Œç¨‹ç¢‘"
@@ -3652,15 +5512,30 @@ msgstr "é‡ç½®å¤±è´¥ä¿¡æ¯ç­‰å¾…时间(秒)"
msgid "Seconds to wait for a storage access attempt"
msgstr "等待存储访问å°è¯•æ—¶é—´(秒)"
-msgid "Secret variables"
-msgstr "加密å˜é‡"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
+msgstr "安全仪表盘"
msgid "Security report"
msgstr "安全报告"
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr "监控代ç ä¸­çš„æ¼æ´ž"
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr "æµæ°´çº¿ %{pipelineLink} 已触å‘"
+
+msgid "Select"
+msgstr "选择"
+
msgid "Select Archive Format"
msgstr "选择下载格å¼"
+msgid "Select a namespace to fork the project"
+msgstr "选择一个命å空间æ¥æ´¾ç”Ÿé¡¹ç›®"
+
msgid "Select a timezone"
msgstr "选择时区"
@@ -3673,9 +5548,27 @@ msgstr "选择指派人"
msgid "Select branch/tag"
msgstr "选择分支/标签"
+msgid "Select project"
+msgstr "选择项目"
+
+msgid "Select project and zone to choose machine type"
+msgstr "按项目和地域选择实例类型"
+
+msgid "Select project to choose zone"
+msgstr "按项目选择地域"
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr "选择æºåˆ†æ”¯"
+
msgid "Select target branch"
msgstr "选择目标分支"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr "选择性åŒæ­¥"
@@ -3691,11 +5584,14 @@ msgstr "ä¹æœˆ"
msgid "Server version"
msgstr "æœåŠ¡å™¨ç‰ˆæœ¬"
+msgid "Service Desk"
+msgstr "æœåŠ¡å°"
+
msgid "Service Templates"
msgstr "æœåŠ¡æ¨¡æ¿"
msgid "Service URL"
-msgstr "æœåŠ¡URL"
+msgstr "æœåŠ¡ URL"
msgid "Session expiration, projects limit and attachment size."
msgstr "会è¯æœ‰æ•ˆæœŸï¼Œé¡¹ç›®é™åˆ¶åŠé™„件大å°ã€‚"
@@ -3707,10 +5603,10 @@ msgid "Set default and restrict visibility levels. Configure import sources and
msgstr "设定缺çœåŠå—é™å¯è§æ€§çº§åˆ«ã€‚é…置导入æ¥æºåŠgit访问å议。"
msgid "Set max session time for web terminal."
-msgstr ""
+msgstr "为Web终端设置最长会è¯æ—¶é—´ã€‚"
msgid "Set notification email for abuse reports."
-msgstr ""
+msgstr "为滥用报告设置通知电å­é‚®ä»¶ã€‚"
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
msgstr "设定用户登录的æ¡ä»¶ã€‚å¯ç”¨å¼ºåˆ¶åŒé‡è®¤è¯ã€‚"
@@ -3722,7 +5618,7 @@ msgid "Set up Koding"
msgstr "设置 Koding"
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
-msgstr ""
+msgstr "æ ¹æ®%{docsLinkStart}文档%{icon}%{docsLinkEnd}设置断言/属性/声明(email,first_name,last_name)和NameID"
msgid "SetPasswordToCloneLink|set a password"
msgstr "设置密ç "
@@ -3731,10 +5627,16 @@ msgid "Settings"
msgstr "设置"
msgid "Setup a specific Runner automatically"
-msgstr "自动创建独享Runner"
+msgstr "自动创建专用Runner"
+
+msgid "Share"
+msgstr "分享"
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
-msgstr ""
+msgstr "分享<strong>%{sso_label}</strong> 给组员,以便他们å¯ä»¥é€šè¿‡æ‚¨çš„身份æ供商登录您的群组"
+
+msgid "Shared Runners"
+msgstr "共享Runner"
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr "通过é‡ç½®æ­¤å‘½å空间的æµæ°´çº¿åˆ†é’Ÿæ•°ï¼Œå½“å‰ä½¿ç”¨çš„分钟数将被归零。"
@@ -3745,8 +5647,20 @@ msgstr "é‡ç½®æµæ°´çº¿åˆ†é’Ÿæ•°"
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr "é‡ç½®å·²ç”¨æµæ°´çº¿åˆ†é’Ÿæ•°"
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
-msgstr "显示命令"
+msgstr "显示相关命令"
+
+msgid "Show complete raw log"
+msgstr "显示完整的原始日志"
+
+msgid "Show latest version"
+msgstr "显示最新版本"
+
+msgid "Show latest version of the diff"
+msgstr "显示最新版本的差异"
msgid "Show parent pages"
msgstr "查看上级页é¢"
@@ -3754,22 +5668,43 @@ msgstr "查看上级页é¢"
msgid "Show parent subgroups"
msgstr "查看上级å­ç¾¤ç»„"
+msgid "Show whitespace changes"
+msgstr "显示空白å˜æ›´å†…容"
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "显示 %d 个事件"
+msgid "Side-by-side"
+msgstr "并排"
+
msgid "Sidebar|Change weight"
msgstr "编辑æƒé‡"
-msgid "Sidebar|No"
-msgstr "æ— "
-
msgid "Sidebar|None"
msgstr "æ— "
+msgid "Sidebar|Only numeral characters allowed"
+msgstr "åªå…许使用数字字符"
+
msgid "Sidebar|Weight"
msgstr "æƒé‡"
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr "登录到 %{group_name}"
+
+msgid "Sign in with Single Sign-On"
+msgstr "使用å•ç‚¹ç™»å½•(SSO)进行登录"
+
+msgid "Sign out"
+msgstr "退出"
+
msgid "Sign-in restrictions"
msgstr "登录é™åˆ¶"
@@ -3780,7 +5715,10 @@ msgid "Size and domain settings for static websites"
msgstr "é™æ€ç½‘站的大å°å’ŒåŸŸè®¾ç½®"
msgid "Slack application"
-msgstr ""
+msgstr "Slack应用"
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr "更慢,但能确ä¿é¡¹ç›®å·¥ä½œç©ºé—´ä¸ŽåŽŸå§‹ç‰ˆæœ¬ä¸€è‡´ï¼›å› å…¶å¯¹æ¯ä¸ªä½œä¸šå‡ä»Žå¤´å¼€å§‹å…‹éš†ä»“库"
msgid "Snippets"
msgstr "代ç ç‰‡æ®µ"
@@ -3791,14 +5729,20 @@ msgstr "出错了,抱歉。"
msgid "Something went wrong on our end."
msgstr "出错了,抱歉。"
+msgid "Something went wrong on our end. Please try again!"
+msgstr "æœåŠ¡å™¨ç«¯å‡ºçŽ°é—®é¢˜ï¼Œè¯·é‡è¯•ã€‚"
+
msgid "Something went wrong when toggling the button"
msgstr "点击按钮时出错"
-msgid "Something went wrong while fetching Dependency Scanning."
-msgstr "读å–ä¾èµ–扫æ结果时å‘生错误。"
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr "关闭 %{issuable} 时出错。请ç¨åŽé‡è¯•"
+
+msgid "Something went wrong while fetching assignees list"
+msgstr "获å–指派人列表时å‘生错误"
-msgid "Something went wrong while fetching SAST."
-msgstr "读å–SAST 时出错。"
+msgid "Something went wrong while fetching group member contributions"
+msgstr "获å–群组æˆå‘˜è´¡çŒ®æ—¶å‡ºé”™"
msgid "Something went wrong while fetching the projects."
msgstr "拉å–项目时å‘生错误。"
@@ -3806,9 +5750,18 @@ msgstr "拉å–项目时å‘生错误。"
msgid "Something went wrong while fetching the registry list."
msgstr "拉å–注册表列表时å‘生错误。"
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr "é‡æ–°å¼€å¯ %{issuable} 时出错。请ç¨åŽå†è¯•"
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr "解决当å‰è®¨è®ºæ—¶å‡ºé”™ï¼Œè¯·é‡è¯•ã€‚"
+
msgid "Something went wrong. Please try again."
msgstr "出现错误。请é‡è¯•ã€‚"
+msgid "Sorry, no epics matched your search"
+msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ä»»ä½•ç¬¦åˆæ¡ä»¶çš„å²è¯—故事"
+
msgid "Sort by"
msgstr "排åº"
@@ -3852,7 +5805,7 @@ msgid "SortOptions|Least popular"
msgstr "最ä¸å—欢迎"
msgid "SortOptions|Less weight"
-msgstr "最低æƒé‡"
+msgstr "é™ä½Žæƒé‡"
msgid "SortOptions|Milestone"
msgstr "里程碑"
@@ -3864,7 +5817,7 @@ msgid "SortOptions|Milestone due soon"
msgstr "å³å°†æˆªæ­¢çš„里程碑"
msgid "SortOptions|More weight"
-msgstr "最高æƒé‡"
+msgstr "增加æƒé‡"
msgid "SortOptions|Most popular"
msgstr "最å—欢迎"
@@ -3924,11 +5877,38 @@ msgid "Spam Logs"
msgstr "垃圾信æ¯æ—¥å¿—"
msgid "Spam and Anti-bot Protection"
-msgstr ""
+msgstr "垃圾邮件åŠé˜²æœºå™¨äººä¿æŠ¤"
+
+msgid "Specific Runners"
+msgstr "专用Runner"
msgid "Specify the following URL during the Runner setup:"
msgstr "在 Runner 设置时指定以下 URL:"
+msgid "Squash commits"
+msgstr "åˆå¹¶æ交"
+
+msgid "Stage"
+msgstr "阶段"
+
+msgid "Stage & Commit"
+msgstr "æš‚å­˜ & æ交"
+
+msgid "Stage all changes"
+msgstr "暂存全部更改"
+
+msgid "Stage changes"
+msgstr "暂存更改"
+
+msgid "Staged"
+msgstr "已暂存"
+
+msgid "Staged %{type}"
+msgstr "已暂存的 %{type}"
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr "把一个标记加上星标,å¯å°†å…¶å˜ä¸ºä¼˜å…ˆæ ‡è®°ã€‚通过拖放æ¥è°ƒæ•´ä¼˜å…ˆæ ‡è®°çš„顺åºï¼Œå¯ä»¥æ”¹å˜ä»–们的相对优先级。"
+
msgid "StarProject|Star"
msgstr "星标"
@@ -3950,33 +5930,66 @@ msgstr "å¯åŠ¨ Runner!"
msgid "Started"
msgstr "å·²å¯åŠ¨"
+msgid "Starts at (UTC)"
+msgstr "开始于(UTC)"
+
msgid "State your message to activate"
-msgstr ""
+msgstr "输入消æ¯ä»¥æ¿€æ´»"
msgid "Status"
msgstr "状æ€"
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr "åœæ­¢å½“å‰çŽ¯å¢ƒ"
+
msgid "Stopped"
msgstr "å·²åœæ­¢"
msgid "Storage"
msgstr "存储"
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr "å­ç¾¤ç»„"
+msgid "Submit as spam"
+msgstr "垃圾信æ¯ä¸¾æŠ¥"
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr "订阅"
+
+msgid "Subscribe at group level"
+msgstr "在群组级别订阅"
+
+msgid "Subscribe at project level"
+msgstr "在项目级别订阅"
+
msgid "Switch branch/tag"
msgstr "切æ¢åˆ†æ”¯/标签"
-msgid "System"
-msgstr "系统"
+msgid "Sync information"
+msgstr "åŒæ­¥ä¿¡æ¯"
msgid "System Hooks"
msgstr "系统钩å­"
-msgid "System header and footer:"
+msgid "System Info"
msgstr ""
+msgid "System header and footer:"
+msgstr "系统页头和页尾:"
+
+msgid "System metrics (Custom)"
+msgstr "系统指标(自定义)"
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] "标签(%{tag_count})"
@@ -3984,6 +5997,9 @@ msgstr[0] "标签(%{tag_count})"
msgid "Tags"
msgstr "标签"
+msgid "Tags:"
+msgstr "标签:"
+
msgid "TagsPage|Browse commits"
msgstr "æµè§ˆæ交"
@@ -4047,8 +6063,8 @@ msgstr "此标签没有å‘行说明。"
msgid "TagsPage|Use git tag command to add a new one:"
msgstr "使用git tag命令添加一个:"
-msgid "TagsPage|Write your release notes or drag files here..."
-msgstr "撰写å‘行说明或拖放文件到这里..."
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr "编写å‘行说明(Release Notes)或将文件拖动到此处..."
msgid "TagsPage|protected"
msgstr "å·²ä¿æŠ¤"
@@ -4062,17 +6078,26 @@ msgstr "目标分支"
msgid "Team"
msgstr "团队"
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr "æœåŠ¡æ¡æ¬¾å议和éšç§æ”¿ç­–"
+
+msgid "Terms of Service and Privacy Policy"
+msgstr "æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–"
+
+msgid "Test coverage parsing"
+msgstr "测试覆盖率解æž"
+
msgid "Thanks! Don't show me this again"
-msgstr "谢谢 ! 请ä¸è¦å†æ˜¾ç¤º"
+msgstr "ä¸å†æ˜¾ç¤º"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
-msgstr "GitLab 中的高级全局æœç´¢æ˜¯ä¸€é¡¹åŠŸèƒ½å¼ºå¤§çš„æœç´¢æœåŠ¡ï¼Œæœ‰åŠ©äºŽèŠ‚约项目开å‘时间。您å¯ä»¥æœç´¢å…¶ä»–团队的代ç ä¸­å¯¹è‡ªå·±é¡¹ç›®æœ‰å¸®åŠ©çš„部分加以利用,从而é¿å…创建é‡å¤ä»£ç å’Œæµªè´¹æ—¶é—´ã€‚"
+msgstr "GitLab 中的高级全局æœç´¢åŠŸèƒ½æ˜¯ä¸€ä¸ªå¼ºå¤§ä¸”节çœæ‚¨çš„时间的æœç´¢æœåŠ¡ã€‚您å¯ä»¥æœç´¢å…¶ä»–团队的代ç ä»¥å¸®åŠ©æ‚¨å®Œå–„自己项目中的代ç ã€‚从而é¿å…创建é‡å¤çš„代ç æˆ–浪费时间。"
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
-msgstr "议题跟踪用于管ç†éœ€è¦æ”¹è¿›æˆ–者解决的问题"
+msgstr "议题跟踪用于管ç†éœ€æ±‚改进或者解决的问题"
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
-msgstr "议题跟踪用于管ç†éœ€è¦æ”¹è¿›æˆ–者解决的问题。请注册或登录åŽä¸ºå½“å‰é¡¹ç›®åˆ›å»ºè®®é¢˜ã€‚"
+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 æ—¶ä»ç„¶éªŒè¯æœåŠ¡å™¨è¯ä¹¦ã€‚"
@@ -4084,7 +6109,7 @@ msgid "The collection of events added to the data gathered for that stage."
msgstr "与该阶段相关的事件集åˆã€‚"
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
-msgstr "该连接将在 %{timeout}åŽè¶…时。对于需è¦é•¿äºŽè¯¥æ—¶é—´æ‰èƒ½å¯¼å…¥çš„仓库,请使用克隆/推é€ç»„åˆã€‚"
+msgstr "该连接将在 %{timeout}åŽè¶…时。如仓库导入耗时超过该时间,请使用克隆/推é€ç»„åˆã€‚"
msgid "The fork relationship has been removed."
msgstr "派生关系已被删除。"
@@ -4101,12 +6126,15 @@ msgstr "文件大å°é™åˆ¶ä¸º 200KB。"
msgid "The number of attempts GitLab will make to access a storage."
msgstr "GitLab 访问存储的次数。"
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr "GitLab 将完全阻止访问存储的故障次数。å¯ä»¥åœ¨ç®¡ç†ç•Œé¢%{link_to_health_page}或使用%{api_documentation_link}é‡ç½®æ•…障次数。"
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr "解密ç§é’¥æ‰€éœ€çš„密ç çŸ­è¯­ã€‚该项为å¯é€‰é¡¹, 并且内容被加密存储。"
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr "CIé…置文件的路径。默认为 <code>.gitlab-ci.yml</code>"
+
msgid "The phase of the development lifecycle."
msgstr "项目生命周期中的å„个阶段。"
@@ -4125,6 +6153,9 @@ msgstr "该项目å…许已登录的用户访问。"
msgid "The project can be accessed without any authentication."
msgstr "该项目å…许任何人访问。"
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr "匿å化数æ®æœé›†å·²ç¦ç”¨ã€‚该功能å¯ç”¨æ—¶ï¼ŒGitLab会è¿è¡ŒåŽå°ä»»åŠ¡ï¼Œç”Ÿæˆæ•°æ®åº“的匿å化CSV,并上传到预先设定的对象存储目录。"
+
msgid "The repository for this project does not exist."
msgstr "此项目的仓库ä¸å­˜åœ¨ã€‚"
@@ -4135,11 +6166,14 @@ msgid "The repository must be accessible over <code>http://</code>, <code>https:
msgstr "该仓库必须å¯é€šè¿‡<code>http://</code>, <code>https://</code> 或 <code>git://</code>进行访问。"
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 "评审阶段概述了从创建åˆå¹¶è¯·æ±‚到被åˆå¹¶çš„时间。当创建第一个åˆå¹¶è¯·æ±‚åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
+msgstr "审阅阶段概述了从创建åˆå¹¶è¯·æ±‚到被åˆå¹¶çš„时间。当创建第一个åˆå¹¶è¯·æ±‚åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr "路线图显示了å²è¯—故事沿ç€æ—¶é—´çº¿çš„进展情况"
+msgid "The secure token used by the Runner to checkout the project"
+msgstr "Runner用于签出项目的安全令牌"
+
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 "预å‘布阶段概述了从åˆå¹¶è¯·æ±‚被åˆå¹¶åˆ°éƒ¨ç½²è‡³ç”Ÿäº§çŽ¯å¢ƒçš„总时间。首次部署到生产环境åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
@@ -4152,27 +6186,33 @@ msgstr "GitLab å°†ä¿æŒå¤±è´¥ä¿¡æ¯çš„时间(秒)。在此期间ä¸å‘生故障
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "GitLab å°†å°è¯•è®¿é—®å­˜å‚¨çš„时间(秒)。在此时间之åŽå°†å¼•å‘超时错误。"
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
-msgstr "存储检查之间的时间间隔(秒)。如上次检查尚未完æˆï¼ŒGitLab将跳过当å‰æ£€æŸ¥ã€‚"
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
+msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 "中ä½æ•°æ˜¯ä¸€ä¸ªæ•°åˆ—中最中间的值。例如在 3ã€5ã€9 之间,中ä½æ•°æ˜¯ 5。在 3ã€5ã€7ã€8 之间,中ä½æ•°æ˜¯ (5 + 7)/ 2 = 6。"
msgid "There are no issues to show"
msgstr "当å‰æ— è®®é¢˜"
+msgid "There are no labels yet"
+msgstr "ç›®å‰è¿˜æ²¡æœ‰æ ‡ç­¾"
+
msgid "There are no merge requests to show"
msgstr "当å‰æ— åˆå¹¶è¯·æ±‚"
msgid "There are problems accessing Git storage: "
msgstr "访问 Git 存储时出现问题:"
-msgid "There was an error loading results"
-msgstr "加载结果时出错"
-
msgid "There was an error loading users activity calendar."
msgstr "加载用户活动日历时出错。"
@@ -4191,12 +6231,39 @@ msgstr "订阅此标记时出错。"
msgid "There was an error when unsubscribing from this label."
msgstr "å–消订阅此标记时出错。"
-msgid "This board\\'s scope is reduced"
-msgstr "这个看æ¿çš„范围缩å°äº†"
+msgid "They can be managed using the %{link}."
+msgstr "他们å¯ä»¥é€šè¿‡ %{link} 进行管ç†ã€‚"
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr "æ­¤GitLab实例尚未æ供任何共享Runner。管ç†å‘˜å¯ä»¥åœ¨ç®¡ç†åŒºåŸŸä¸­æ³¨å†Œå…±äº«Runner。"
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
+msgstr "此差异已折å ã€‚"
msgid "This directory"
msgstr "当å‰ç›®å½•"
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr "此群组å…许您使用%{group_name} å•ç‚¹ç™»å½•å¸æˆ·ç™»å½•ã€‚这将会é‡å®šå‘到外部登录页é¢ã€‚"
+
+msgid "This group does not provide any group Runners yet."
+msgstr "该群组未æ供任何群组Runner。"
+
msgid "This is a confidential issue."
msgstr "这是一个机密议题。"
@@ -4204,7 +6271,7 @@ msgid "This is the author's first Merge Request to this project."
msgstr "这是作者为当å‰é¡¹ç›®è´¡çŒ®çš„第一个åˆå¹¶è¯·æ±‚。"
msgid "This issue is confidential"
-msgstr "当å‰é—®é¢˜ä¸ºç§å¯†é—®é¢˜"
+msgstr "当å‰è®®é¢˜ä¸ºæœºå¯†è®®é¢˜"
msgid "This issue is confidential and locked."
msgstr "这个是机密且已é”定的议题。"
@@ -4218,6 +6285,15 @@ msgstr "当å‰ä½œä¸šéœ€è¦ç”¨æˆ·è§¦å‘其过程。此类作业通常用于将代
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr "当å‰ä½œä¸šéœ€åœ¨ä¸Šçº§ä½œä¸šæˆåŠŸåŽæ‰å¯è¢«å¯åŠ¨ã€‚"
+msgid "This job does not have a trace."
+msgstr "此作业没有输出日志。"
+
+msgid "This job has been canceled"
+msgstr "此作业已å–消"
+
+msgid "This job has been skipped"
+msgstr "此作业已被跳过"
+
msgid "This job has not been triggered yet"
msgstr "作业还未被触å‘"
@@ -4236,20 +6312,35 @@ msgstr "在创建一个空的仓库或导入现有仓库之å‰ï¼Œå°†æ— æ³•æŽ¨é€
msgid "This merge request is locked."
msgstr "æ­¤åˆå¹¶è¯·æ±‚å·²é”定。"
+msgid "This option is disabled while you still have unstaged changes"
+msgstr "有未暂存更改时此选项会被ç¦ç”¨ã€‚"
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr "此页é¢ä¸å¯ç”¨ï¼Œæ‚¨æ— æƒè·¨é¡¹ç›®é˜…读相关信æ¯ã€‚"
+msgid "This page will be removed in a future release."
+msgstr "此页é¢å°†åœ¨å°†æ¥çš„版本中删除。"
+
msgid "This project"
msgstr "当å‰é¡¹ç›®"
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr "该项目ä¸å±žäºŽä»»ä½•ç¾¤ç»„,因此ä¸èƒ½ä½¿ç”¨ç¾¤ç»„Runner。"
+
msgid "This repository"
msgstr "当å‰ä»“库"
+msgid "This source diff could not be displayed because it is too large."
+msgstr "此代ç å·®å¼‚无法显示,因为它太大了。"
+
+msgid "This user has no identities"
+msgstr "该用户没有身份标识"
+
msgid "This will delete the custom metric, Are you sure?"
msgstr "æ­¤æ“作将删除自定义指标,确定继续å—?"
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr "这些电å­é‚®ä»¶è‡ªåŠ¨ç”Ÿæˆä¸ºé—®é¢˜(评论生æˆä¸ºç”µå­é‚®ä»¶å¯¹è¯)在这里列出。"
+msgstr "这些电å­é‚®ä»¶è‡ªåŠ¨ç”Ÿæˆä¸ºè®®é¢˜(评论生æˆä¸ºç”µå­é‚®ä»¶ä¼šè¯)并在此处列出。"
msgid "Time before an issue gets scheduled"
msgstr "议题被列入日程表的时间"
@@ -4260,12 +6351,15 @@ msgstr "开始进行编ç å‰çš„时间"
msgid "Time between merge request creation and merge/close"
msgstr "从创建åˆå¹¶è¯·æ±‚到被åˆå¹¶æˆ–关闭的时间"
-msgid "Time between updates and capacity settings."
-msgstr ""
-
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr "GitLab等待外部æœåŠ¡çš„å“应时间(秒)。当æœåŠ¡æ²¡æœ‰åŠæ—¶å“应时,访问将被拒ç»ã€‚"
+msgid "Time remaining"
+msgstr "剩余时间:"
+
+msgid "Time spent"
+msgstr "花费的时间"
+
msgid "Time tracking"
msgstr "工时统计"
@@ -4287,6 +6381,9 @@ msgstr " %s 天å‰"
msgid "Timeago|%s days remaining"
msgstr "剩余 %s 天"
+msgid "Timeago|%s hours ago"
+msgstr "%s å°æ—¶å‰"
+
msgid "Timeago|%s hours remaining"
msgstr "剩余 %s å°æ—¶"
@@ -4302,6 +6399,9 @@ msgstr " %s 个月å‰"
msgid "Timeago|%s months remaining"
msgstr "剩余 %s 月"
+msgid "Timeago|%s seconds ago"
+msgstr "%s 秒å‰"
+
msgid "Timeago|%s seconds remaining"
msgstr "剩余 %s 秒"
@@ -4317,48 +6417,45 @@ msgstr " %s å¹´å‰"
msgid "Timeago|%s years remaining"
msgstr "剩余 %s 年"
+msgid "Timeago|1 day ago"
+msgstr "1天å‰"
+
msgid "Timeago|1 day remaining"
msgstr "剩余 1 天"
+msgid "Timeago|1 hour ago"
+msgstr "1å°æ—¶å‰"
+
msgid "Timeago|1 hour remaining"
msgstr "剩余 1 å°æ—¶"
+msgid "Timeago|1 minute ago"
+msgstr "1分钟å‰"
+
msgid "Timeago|1 minute remaining"
msgstr "剩余 1 分钟"
+msgid "Timeago|1 month ago"
+msgstr "1个月å‰"
+
msgid "Timeago|1 month remaining"
msgstr "剩余 1 个月"
+msgid "Timeago|1 week ago"
+msgstr "1星期å‰"
+
msgid "Timeago|1 week remaining"
msgstr "剩余 1 星期"
+msgid "Timeago|1 year ago"
+msgstr "1å¹´å‰"
+
msgid "Timeago|1 year remaining"
msgstr "剩余 1 年"
msgid "Timeago|Past due"
msgstr "逾期"
-msgid "Timeago|a day ago"
-msgstr " 1 天å‰"
-
-msgid "Timeago|a month ago"
-msgstr " 1 个月å‰"
-
-msgid "Timeago|a week ago"
-msgstr " 1 星期å‰"
-
-msgid "Timeago|a year ago"
-msgstr " 1 å¹´å‰"
-
-msgid "Timeago|about %s hours ago"
-msgstr "约 %s å°æ—¶å‰"
-
-msgid "Timeago|about a minute ago"
-msgstr "约 1 分钟å‰"
-
-msgid "Timeago|about an hour ago"
-msgstr "约 1 å°æ—¶å‰"
-
msgid "Timeago|in %s days"
msgstr " %s 天åŽ"
@@ -4398,11 +6495,14 @@ msgstr " 1 星期åŽ"
msgid "Timeago|in 1 year"
msgstr " 1 å¹´åŽ"
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
msgstr "刚刚"
-msgid "Timeago|less than a minute ago"
-msgstr "ä¸åˆ° 1 分钟å‰"
+msgid "Timeago|right now"
+msgstr "ç«‹å³"
+
+msgid "Timeout"
+msgstr "超时"
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4424,14 +6524,23 @@ msgstr "标题"
msgid "To GitLab"
msgstr "到GitLab"
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr "è¦æ·»åŠ ä¸€ä¸ª SSH 密钥, æ‚¨éœ€è¦ %{generate_link_start} 生æˆä¸€ä¸ª %{link_end} 或使用一个 %{existing_link_start} 现有的 key%{link_end}。"
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr "å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}连接GitHub仓库。当创建个人访问令牌时,需è¦é€‰æ‹© <code>repo</code> 范围,以显示å¯ä¾›è¿žæŽ¥çš„公共和ç§æœ‰çš„仓库列表。"
+msgstr "å¯ä»¥ä½¿ç”¨ %{personal_access_token_link} 连接GitHub仓库。当创建个人访问令牌时,需è¦é€‰æ‹© <code>repo</code> 范围,以显示å¯ä¾›è¿žæŽ¥çš„公共和ç§æœ‰çš„仓库列表。"
msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
-msgstr "è¦è¿žæŽ¥GitHub仓库,首先需è¦æŽˆæƒGitLab访问列表中的GitHub仓库:"
+msgstr "è¦è¿žæŽ¥GitHub存储库,首先需è¦æŽˆæƒGitLab访问列表中的GitHub仓库:"
msgid "To connect an SVN repository, check out %{svn_link}."
-msgstr "è¦è¿žæŽ¥SVN仓库,请查看 %{svn_link}。"
+msgstr "如è¦è¿žæŽ¥SVN仓库,请查看 %{svn_link}。"
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr "å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}导入GitHub仓库。当创建个人访问令牌时,需è¦é€‰æ‹© <code>repo</code> 范围,以显示å¯å¯¼å…¥çš„公共和ç§æœ‰çš„仓库列表。"
@@ -4442,10 +6551,19 @@ msgstr "è¦å¯¼å…¥GitHub仓库,首先需è¦æŽˆæƒGitLab访问列表中的GitHub
msgid "To import an SVN repository, check out %{svn_link}."
msgstr "è¦å¯¼å…¥SVN仓库,请查看 %{svn_link}。"
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr "è¦ä»…为外部仓库使用CI / CD功能时,请选择</strong>使用外部仓库è¿è¡ŒCI/CD<strong>。"
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr "通过Azure,Okta,Onelogin,Ping Identity或自定义SAML 2.0等身份验è¯ç¨‹åºä¸ºæ‚¨çš„群组设置SAML身份验è¯ï¼š"
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr "è¦å¼€å§‹æ‰§è¡Œä»»åŠ¡ï¼Œè¯·æŠŠRunner加到群组中"
+
+msgid "To this GitLab instance"
msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
@@ -4454,9 +6572,24 @@ msgstr "如需验è¯GitLab CI设置,请访问当å‰é¡¹ç›®çš„'CI/CD → æµæ°´ç
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。åªæ˜¾ç¤ºè¿‡åŽ»3个月和接下æ¥3个月的å²è¯—故事。"
+msgid "To widen your search, change or remove filters."
+msgstr "需è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–移除过滤æ¡ä»¶ã€‚"
+
msgid "Todo"
msgstr "待办事项"
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr "切æ¢ä¾§è¾¹æ "
+
+msgid "Toggle discussion"
+msgstr "开关讨论"
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr "切æ¢è¾¹æ "
@@ -4466,6 +6599,12 @@ msgstr "切æ¢çŠ¶æ€ï¼šå…³é—­"
msgid "ToggleButton|Toggle Status: ON"
msgstr "切æ¢çŠ¶æ€ï¼šå¼€å¯"
+msgid "Too many changes to show."
+msgstr "è¦æ˜¾ç¤ºçš„å˜æ›´å¤ªå¤šã€‚"
+
+msgid "Total Contributions"
+msgstr "贡献总计"
+
msgid "Total Time"
msgstr "总时间"
@@ -4476,7 +6615,7 @@ msgid "Total: %{total}"
msgstr "总计:%{total}"
msgid "Track activity with Contribution Analytics."
-msgstr "跟踪活动与贡献的分æžã€‚"
+msgstr "通过贡献度分æžäº†è§£ç”¨æˆ·æ´»è·ƒåº¦ã€‚"
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr "在ä¸åŒé¡¹ç›®å’Œé‡Œç¨‹ç¢‘中跟踪具有åŒä¸€ä¸»é¢˜çš„议题组"
@@ -4484,11 +6623,29 @@ msgstr "在ä¸åŒé¡¹ç›®å’Œé‡Œç¨‹ç¢‘中跟踪具有åŒä¸€ä¸»é¢˜çš„议题组"
msgid "Track time with quick actions"
msgstr "使用快æ·æ“作æ¥ç»Ÿè®¡å·¥æ—¶"
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr "触å‘此手动æ“作"
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr "触å‘器å¯ä»¥é€šè¿‡ API 调用使特定的分支或标记被é‡æ–°æž„建,这些 token å¯ä»£è¡¨ä¸Žå…¶å…³è”的用户(包括该用户对项目的访问æƒé™ï¼‰"
+
+msgid "Try again"
+msgstr "请é‡è¯•"
+
msgid "Turn on Service Desk"
-msgstr "打开æœåŠ¡å°"
+msgstr "å¯ç”¨æœåŠ¡å°"
+
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr "无法加载差异。 %{button_try_again}"
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr "由于\"%{reason}\"的原因,您暂时ä¸èƒ½è¿›å…¥é…置了SAML 的群组"
msgid "Unknown"
msgstr "未知的"
@@ -4502,26 +6659,62 @@ msgstr "已解é”"
msgid "Unresolve discussion"
msgstr "待解决的讨论"
+msgid "Unstage all changes"
+msgstr "å–消全部暂存更改"
+
+msgid "Unstage changes"
+msgstr "å–消暂存更改"
+
+msgid "Unstaged"
+msgstr "未暂存"
+
+msgid "Unstaged %{type}"
+msgstr "未暂存的 %{type}"
+
+msgid "Unstaged and staged %{type}"
+msgstr "已暂存和未暂存的 %{type}"
+
msgid "Unstar"
msgstr "å–消星标"
+msgid "Unsubscribe"
+msgstr "å–消订阅"
+
+msgid "Unsubscribe at group level"
+msgstr "在群组级别å–消订阅"
+
+msgid "Unsubscribe at project level"
+msgstr "在项目级别å–消订阅"
+
+msgid "Unverified"
+msgstr "未验è¯"
+
msgid "Up to date"
msgstr "已是最新"
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr "更新您的群组å称ã€è¯´æ˜Žã€å¤´åƒä»¥åŠå…¶å®ƒé€šç”¨è®¾ç½®ã€‚"
+
msgid "Upgrade your plan to activate Advanced Global Search."
-msgstr "å‡çº§æ‚¨çš„方案以å¯ç”¨é«˜çº§å…¨å±€æœç´¢ã€‚"
+msgstr "å‡çº§æ‚¨çš„订阅计划以å¯ç”¨é«˜çº§å…¨å±€æœç´¢ã€‚"
msgid "Upgrade your plan to activate Contribution Analytics."
-msgstr "å‡çº§æ‚¨çš„方案以å¯ç”¨è´¡çŒ®åˆ†æžã€‚"
+msgstr "å‡çº§æ‚¨çš„订阅计划以å¯ç”¨è´¡çŒ®åˆ†æžã€‚"
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr "å‡çº§æ‚¨çš„方案以激活 Webhooks 。"
+msgstr "å‡çº§æ‚¨çš„订阅计划以激活Group Webhooks 。"
msgid "Upgrade your plan to activate Issue weight."
-msgstr "å‡çº§æ‚¨çš„方案以激活议题æƒé‡ã€‚"
+msgstr "å‡çº§æ‚¨çš„订阅计划以激活议题æƒé‡ã€‚"
msgid "Upgrade your plan to improve Issue boards."
-msgstr "å‡çº§æ‚¨çš„方案以使用议题看æ¿ã€‚"
+msgstr "å‡çº§æ‚¨çš„订阅计划以使用增强的议题看æ¿ã€‚"
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
msgid "Upload New File"
msgstr "上传新文件"
@@ -4539,10 +6732,19 @@ msgid "Upvotes"
msgstr "顶"
msgid "Usage statistics"
+msgstr "使用情况统计"
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
-msgstr "使用æœåŠ¡å°åœ¨GitLab内部通过电å­é‚®ä»¶ä¸Žç”¨æˆ·è”系(例如æ供客户支æŒï¼‰"
+msgstr "在GitLab内部使用æœåŠ¡å°é€šè¿‡ç”µå­é‚®ä»¶ä¸Žç”¨æˆ·è”系(例如æ供客户支æŒï¼‰"
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr "使用群组里程碑å¯ä»¥ç»Ÿä¸€ç®¡ç†å¤šä¸ªé¡¹ç›®ä¸­åŒä¸€é‡Œç¨‹ç¢‘的议题。"
+
+msgid "Use one line per URI"
+msgstr ""
msgid "Use the following registration token during setup:"
msgstr "在安装过程中使用以下注册令牌:"
@@ -4551,25 +6753,40 @@ msgid "Use your global notification setting"
msgstr "使用全局通知设置"
msgid "Used by members to sign in to your group in GitLab"
+msgstr "ä¾›æˆå‘˜ç™»å½•æ‚¨çš„GitLab群组"
+
+msgid "User Settings"
msgstr ""
msgid "User and IP Rate Limits"
+msgstr "用户和IP频率é™åˆ¶"
+
+msgid "User map"
msgstr ""
+msgid "Users"
+msgstr "用户"
+
+msgid "Variables"
+msgstr "å˜é‡"
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr "å˜é‡é€šè¿‡runner作用于环境中。å¯å°†å˜é‡é™åˆ¶ä¸ºä»…å—ä¿æŠ¤çš„分支或标签å¯ä»¥è®¿é—®ã€‚å¯ä»¥ä½¿ç”¨å˜é‡æ¥ä¿å­˜å¯†ç ã€å¯†é’¥æˆ–任何其他内容。"
msgid "Various container registry settings."
-msgstr ""
+msgstr "容器注册表相关设置。"
msgid "Various email settings."
-msgstr ""
+msgstr "电å­é‚®ä»¶ç›¸å…³è®¾ç½®"
msgid "Various settings that affect GitLab performance."
-msgstr ""
+msgstr "å½±å“GitLab性能相关设置。"
-msgid "View and edit lines"
-msgstr "查看和编辑行"
+msgid "Verification information"
+msgstr "验è¯ä¿¡æ¯"
+
+msgid "Verified"
+msgstr "已验è¯"
msgid "View epics list"
msgstr "查看å²è¯—故事列表"
@@ -4580,9 +6797,21 @@ msgstr "æµè§ˆæ–‡ä»¶ @ "
msgid "View group labels"
msgstr "查看群组标记"
+msgid "View issue"
+msgstr "查看议题"
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr "查看作业"
+
msgid "View labels"
msgstr "查看标记"
+msgid "View log"
+msgstr "查看日志"
+
msgid "View open merge request"
msgstr "查看待处ç†çš„åˆå¹¶è¯·æ±‚"
@@ -4595,6 +6824,12 @@ msgstr "查看替æ¢æ–‡ä»¶ @ "
msgid "Visibility and access controls"
msgstr "å¯è§æ€§ä¸Žè®¿é—®æŽ§åˆ¶"
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "内部"
@@ -4610,8 +6845,8 @@ msgstr "未知"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "æƒé™ä¸è¶³ã€‚如需查看相关数æ®ï¼Œè¯·å‘管ç†å‘˜ç”³è¯·æƒé™ã€‚"
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
-msgstr "无法验è¯æ‚¨åœ¨ GCP 上的æŸä¸ªé¡¹ç›®æ˜¯å¦å¯ç”¨äº†è®¡è´¹ã€‚请é‡è¯•ã€‚"
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "该阶段的数æ®ä¸è¶³ï¼Œæ— æ³•æ˜¾ç¤ºã€‚"
@@ -4623,15 +6858,27 @@ msgid "Web IDE"
msgstr "Web IDE"
msgid "Web terminal"
-msgstr ""
+msgstr "Web终端"
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr "如果有新的推é€æˆ–新的议题,Webhook将自动触å‘您设置URL。 您å¯ä»¥é…ç½® Webhook æ¥ç›‘å¬ç‰¹å®šäº‹ä»¶ï¼Œå¦‚推é€ã€è®®é¢˜æˆ–åˆå¹¶è¯·æ±‚。 群组 Webhook 将适用于团队中的所有项目,并å…许您设置整个团队中的 Webhook 。"
+msgstr "如果有新的推é€æˆ–新的议题,Webhook将自动触å‘您设置URL。 您å¯ä»¥é…ç½® Webhook æ¥ç›‘å¬ç‰¹å®šäº‹ä»¶ï¼Œå¦‚推é€ã€è®®é¢˜æˆ–åˆå¹¶è¯·æ±‚。 群组 Webhook 适用于群组中的所有项目,便于您在整个群组中标准化webhook功能。"
+
+msgid "Weeks"
+msgstr "星期"
msgid "Weight"
msgstr "æƒé‡"
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr "æƒé‡ %{weight}"
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr "当Runner被é”定时,ä¸èƒ½å°†å…¶åˆ†é…给其他项目"
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr "该项å¯ç”¨åŽï¼Œç”¨æˆ·åœ¨æŽ¥å—æ¡æ¬¾è¢«å‰å°†ä¸èƒ½ä½¿ç”¨GitLab。"
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr "å°†URLä¿ç•™ä¸ºç©ºç™½æ—¶ï¼Œä»å¯æŒ‡å®šåˆ†ç±»æ ‡ç­¾ï¼Œè€Œæ— éœ€ç¦ç”¨è·¨é¡¹ç›®åŠŸèƒ½æˆ–执行外部授æƒæ£€æŸ¥ã€‚"
msgid "Wiki"
@@ -4658,8 +6905,32 @@ msgstr "æ示:å¯ä»¥é€šè¿‡å°†è·¯å¾„添加到标题开头æ¥ç§»åŠ¨æ­¤é¡µé¢ã€‚
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr "在该路径中已ç»æœ‰ä¸€ä¸ªå…·æœ‰ç›¸åŒæ ‡é¢˜çš„页é¢ã€‚"
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
-msgstr "您ä¸èƒ½åˆ›å»º wiki 页é¢"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr "Wiki 改进建议"
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr "åªæœ‰è¯¥é¡¹ç›®çš„æˆå‘˜æ‰å¯ä»¥æ·»åŠ  Wiki 页é¢ï¼Œå¦‚果您有任何关于项目 Wiki 的改进建议,请通过 %{issues_link} æ交议题。"
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr "议题跟踪"
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr "您å¯ä»¥ä½¿ç”¨ Wiki æ¥ä¿å­˜é¡¹ç›®çš„详细信æ¯ï¼Œä¾‹å¦‚:创建该项目的原因ã€é¡¹ç›®çš„原则或者项目的使用说明等等。"
+
+msgid "WikiEmpty|Create your first page"
+msgstr "创建您的第一个页é¢"
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr "Wiki 改进建议"
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr "您å¯ä»¥ä½¿ç”¨ Wiki 为您的项目编写文档"
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr "该项目没有任何 Wiki 页é¢"
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr "åªæœ‰é¡¹ç›®æˆå‘˜æ‰å¯ä»¥æ·»åŠ  Wiki 页é¢ã€‚"
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "这是此页é¢çš„过期版本。"
@@ -4694,6 +6965,12 @@ msgstr "æ–° Wiki 页é¢"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr "确定è¦åˆ é™¤æ­¤é¡µé¢å—?"
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr "删除页é¢"
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr "删除页é¢%{pageTitle}?"
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr "有人在åŒä¸€æ—¶é—´ç¼–辑了页é¢ã€‚请检查 %{page_link} 并确ä¿æ‚¨çš„更改ä¸ä¼šæ— æ„中删除。"
@@ -4709,8 +6986,8 @@ msgstr "æ›´æ–° %{page_title}"
msgid "WikiPage|Page slug"
msgstr "页é¢å—"
-msgid "WikiPage|Write your content or drag files here..."
-msgstr "在这里撰写您的内容或拖曳文件到此..."
+msgid "WikiPage|Write your content or drag files here…"
+msgstr "在这里编写您的内容或将文件拖动到此处..."
msgid "Wiki|Create Page"
msgstr "创建页é¢"
@@ -4721,9 +6998,6 @@ msgstr "创建页é¢"
msgid "Wiki|Edit Page"
msgstr "修改页é¢"
-msgid "Wiki|Empty page"
-msgstr "空页é¢"
-
msgid "Wiki|More Pages"
msgstr "更多页é¢"
@@ -4743,13 +7017,22 @@ msgid "Wiki|Wiki Pages"
msgstr "Wiki 页é¢"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr "通过贡献分æžï¼Œæ‚¨å¯ä»¥åˆ†æžæ‚¨çš„组织åŠå…¶æˆå‘˜çš„议题〠åˆå¹¶è¯·æ±‚和推é€æ´»åŠ¨ã€‚"
+msgstr "通过贡献分æžï¼Œæ‚¨å¯ä»¥ä»Žæ€»ä½“上了解您的组织åŠå…¶æˆå‘˜çš„议题〠åˆå¹¶è¯·æ±‚和推é€æ´»åŠ¨çš„情况。"
msgid "Withdraw Access Request"
msgstr "å–消æƒé™ç”³è¯·"
-msgid "Write a commit message..."
-msgstr "填写æ交信æ¯..."
+msgid "Yes"
+msgstr "是"
+
+msgid "Yes, add it"
+msgstr "是的,添加它"
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "å³å°†åˆ é™¤ %{group_name}。已删除的群组无法æ¢å¤ï¼ç¡®å®šç»§ç»­å—?"
@@ -4766,17 +7049,26 @@ msgstr "å°†è¦æŠŠ %{project_full_name} 转移给å¦ä¸€ä¸ªæ‰€æœ‰è€…。确定执è¡
msgid "You are on a read-only GitLab instance."
msgstr "当å‰æ­£åœ¨è®¿é—®åªè¯» GitLab 实例。"
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
-msgstr "当å‰æ­£åœ¨è®¿é—®Geo次(åªè¯»)节点。如需进行任何写入æ“作,必须访问%{primary_node}。"
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr "当å‰æ­£åœ¨è®¿é—®Geo次è¦(åªè¯») 节点。如需进行任何写入æ“作,必须访问%{primary_node}。"
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr "您å¯ä»¥ %{linkStart}查看BLOB%{linkEnd} 代替。"
msgid "You can also create a project from the command line."
-msgstr "å¯ä»¥ä½¿ç”¨å‘½ä»¤è¡Œæ¥åˆ›å»ºé¡¹ç›®ã€‚"
+msgstr "您也å¯ä»¥é€šè¿‡å‘½ä»¤è¡Œæ¥åˆ›å»ºæ–°é¡¹ç›®ã€‚"
msgid "You can also star a label to make it a priority label."
msgstr "å¯ä»¥é€šè¿‡ä¸ºæ ‡è®°è®¾ç½®æ˜Ÿæ ‡æ¥æ高其优先级。"
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr "您也å¯ä»¥é€šè¿‡%{linkStart}Lint%{linkEnd}测试.gitlab-ci.yml"
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
-msgstr "å¯ä»¥è½»æ¾åœ°åœ¨Kubernetes群集上安装Runner。 %{link_to_help_page}"
+msgstr "å¯ä»¥è½»æ¾åœ°åœ¨Kubernetes集群上安装Runner。 %{link_to_help_page}"
msgid "You can move around the graph by using the arrow keys."
msgstr "å¯ä»¥ä½¿ç”¨æ–¹å‘键移动图形。"
@@ -4787,29 +7079,50 @@ msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šæ·»åŠ æ–‡ä»¶"
msgid "You can only edit files when you are on a branch"
msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šç¼–辑文件"
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr "您å¯ä»¥ä½¿ç”¨äº¤äº’模å¼ï¼Œé€šè¿‡é€‰æ‹© %{use_ours} 或 %{use_theirs} 按钮æ¥è§£å†³åˆå¹¶å†²çªã€‚也å¯ä»¥é€šè¿‡ç›´æŽ¥ç¼–辑文件æ¥è§£å†³åˆå¹¶å†²çªã€‚然åŽå°†è¿™äº›æ›´æ”¹æ交到 %{branch_name}"
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
-msgstr "您ä¸èƒ½å†™å…¥åªè¯»çš„辅助 GitLab Geo 实例。请改用%{link_to_primary_node}。"
+msgstr "您ä¸èƒ½å†™å…¥åªè¯»çš„æ¬¡è¦ GitLab Geo 实例。请改用%{link_to_primary_node}。"
msgid "You cannot write to this read-only GitLab instance."
msgstr "您ä¸èƒ½å†™å…¥è¿™ä¸ªåªè¯»çš„ GitLab 实例。"
+msgid "You do not have any assigned merge requests"
+msgstr "没有任何指派给您的åˆå¹¶è¯·æ±‚"
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
-msgstr "没有足够æƒé™æ¥ä¿®æ”¹LDAP组åŒæ­¥ä¸­çš„设置。"
+msgstr "您没有正确的æƒé™æ¥æ›´æ”¹LDAP组åŒæ­¥ä¸­çš„设置。"
+
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
msgid "You have no permissions"
msgstr "没有æƒé™"
+msgid "You have not created any merge requests"
+msgstr "您尚未创建任何åˆå¹¶è¯·æ±‚"
+
msgid "You have reached your project limit"
msgstr "您已达到项目数é‡é™åˆ¶"
-msgid "You must have master access to force delete a lock"
-msgstr "必须拥有 master æƒé™æ‰èƒ½å¼ºåˆ¶è§£é™¤é”定"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr "您必须接å—我们的æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–æ‰èƒ½æ³¨å†Œå¸æˆ·"
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr "必须拥有维护者æƒé™æ‰èƒ½å¼ºåˆ¶åˆ é™¤é”"
msgid "You must sign in to star a project"
msgstr "必须登录æ‰èƒ½å¯¹é¡¹ç›®åŠ æ˜Ÿæ ‡"
msgid "You need a different license to enable FileLocks feature"
-msgstr "需è¦ä½¿ç”¨ä¸Žå½“å‰ä¸åŒçš„许å¯(license)æ‰èƒ½å¯ç”¨FileLocks功能"
+msgstr "需è¦ä½¿ç”¨ä¸Žå½“å‰ä¸åŒçš„许å¯(license) æ‰èƒ½å¯ç”¨FileLocks功能"
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr "您需è¦git-lfs版本 %{min_git_lfs_version} (或更高版本)æ‰èƒ½ç»§ç»­ã€‚请访问https://git-lfs.github.com"
msgid "You need permission."
msgstr "需è¦ç›¸å…³çš„æƒé™ã€‚"
@@ -4841,14 +7154,23 @@ msgstr "在您的个人资料中添加SSH密钥之å‰ï¼Œæ‚¨ä¸èƒ½é€šè¿‡SSHæ¥æ‹‰
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "需è¦ä½¿ç”¨ä¸åŒçš„分支æ‰èƒ½è¿›è¡Œæœ‰æ•ˆçš„比较。"
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr "您收到这å°ç”µå­é‚®ä»¶æ˜¯å› ä¸ºä½ åœ¨ %{host} 拥有å¸æˆ·ã€‚ %{manage_notifications_link} &middot; %{help_link}"
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr "您的群组"
msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
-msgstr "在此页é¢ä¸Šçš„Kubernetes群集信æ¯ä»å¯ç¼–辑,但建议您ç¦ç”¨å¹¶é‡æ–°é…ç½®"
+msgstr "在此页é¢ä¸Šçš„Kubernetes集群信æ¯ä»å¯ç¼–辑,但建议您ç¦ç”¨å¹¶é‡æ–°é…ç½®"
msgid "Your Projects (default)"
msgstr "您的项目 (默认值)"
@@ -4859,6 +7181,12 @@ msgstr "您的项目活动"
msgid "Your Todos"
msgstr "您的待办事项"
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr "åˆå¹¶è¯·æ±‚已开å¯ï¼Œå¯ä»¥æ交å˜æ›´åˆ°%{branch_name}。"
@@ -4877,12 +7205,15 @@ msgstr "您的åå­—"
msgid "Your projects"
msgstr "您的项目"
+msgid "ago"
+msgstr "å‰"
+
msgid "among other things"
-msgstr "除其他事项外"
+msgstr "åŠå…¶ä»–功能"
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] "åŠ%d个修å¤çš„æ¼æ´ž"
+msgstr[0] "åŠ%d个已修å¤çš„æ¼æ´ž"
msgid "assign yourself"
msgstr "分é…给自己"
@@ -4893,45 +7224,138 @@ msgstr "分支å称"
msgid "by"
msgstr "æ¥è‡ª"
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr "%{linkStartTag}了解更多有关容器安全扫æçš„ä¿¡æ¯ %{linkEndTag}"
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr "%{linkStartTag}了解更多有关DAST %{linkEndTag}çš„ä¿¡æ¯"
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr "%{linkStartTag}了解更多有关ä¾èµ–项扫æçš„ä¿¡æ¯ %{linkEndTag}"
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr "%{linkStartTag}了解更多有关SAST %{linkEndTag}çš„ä¿¡æ¯"
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr "ciReport|%{namespace} å— %{vulnerability} å½±å“。"
+
+msgid "ciReport|%{packagesString} and "
+msgstr "%{packagesString} 和"
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr "%{packagesString} 和 %{lastPackage}"
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr "还有%{remainingPackagesCount} 个"
+
+msgid "ciReport|%{reportName} is loading"
+msgstr "%{reportName} 加载中"
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr "加载%{reportName} 时出错"
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr "%{type} 未å‘现新的安全æ¼æ´ž"
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr "%{type} 未å‘现安全æ¼æ´ž"
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr "%{type} 未å‘现安全æ¼æ´ž"
+
+msgid "ciReport|Class"
+msgstr "ç±»"
+
msgid "ciReport|Code quality"
msgstr "代ç è´¨é‡"
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
-msgstr "DAST在审阅应用中未检测到告警"
+msgid "ciReport|Confidence"
+msgstr "置信水平"
+
+msgid "ciReport|Container scanning detected"
+msgstr "容器安全扫æ检测到"
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr "容器扫æå¯ä»¥æ£€æµ‹Dockeré•œåƒä¸­ä¸­å·²çŸ¥çš„安全æ¼æ´žã€‚"
+
+msgid "ciReport|Container scanning is loading"
+msgstr "容器安全扫æ加载中"
-msgid "ciReport|Dependency scanning"
-msgstr "ä¾èµ–关系扫æ"
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr "加载容器扫æ结果时出错"
+
+msgid "ciReport|DAST detected"
+msgstr "DAST检测到"
+
+msgid "ciReport|DAST is loading"
+msgstr "DAST加载中"
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr "加载DAST结果时出错"
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgstr "ä¾èµ–项扫æå¯ä»¥æ£€æµ‹æºä»£ç ä¾èµ–项中已知的æ¼æ´žã€‚"
msgid "ciReport|Dependency scanning detected"
msgstr "ä¾èµ–关系扫æ检测到"
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
-msgstr "ä¾èµ–关系扫æ未检测到新的安全æ¼æ´ž"
+msgid "ciReport|Dependency scanning is loading"
+msgstr "ä¾èµ–项扫æ加载中"
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr "加载ä¾èµ–项扫æ结果时出错"
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
-msgstr "ä¾èµ–关系扫æ未检测到安全æ¼æ´ž"
+msgid "ciReport|Description"
+msgstr "说明"
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr "忽略æ¼æ´ž"
+
+msgid "ciReport|Dismissed by"
+msgstr "忽略æ“作æ¥è‡ª"
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr "动æ€åº”用程åºå®‰å…¨æµ‹è¯•ï¼ˆDAST)å¯æ£€æµ‹Web应用程åºä¸­çš„已知æ¼æ´žã€‚"
msgid "ciReport|Failed to load %{reportName} report"
msgstr "无法加载 %{reportName} 报告"
+msgid "ciReport|File"
+msgstr "文件"
+
msgid "ciReport|Fixed:"
-msgstr "失败:"
+msgstr "已修å¤:"
+
+msgid "ciReport|Identifiers"
+msgstr "标识符"
msgid "ciReport|Instances"
msgstr "实例"
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr "了解有关安全报告(Alpha)的更多信æ¯ã€‚"
+
msgid "ciReport|Learn more about whitelisting"
-msgstr "进一步了解关于白åå•çš„ä¿¡æ¯"
+msgstr "了解更多关于白åå•çš„ä¿¡æ¯"
+
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr "许å¯è¯ç®¡ç†æ£€æµ‹åˆ° %{licenseInfo}"
+
+msgid "ciReport|License management detected no new licenses"
+msgstr "许å¯è¯ç®¡ç†æœªæ£€æµ‹åˆ°æ–°çš„许å¯è¯"
+
+msgid "ciReport|Links"
+msgstr "链接"
msgid "ciReport|Loading %{reportName} report"
msgstr "载入%{reportName} 报告"
+msgid "ciReport|Method"
+msgstr "方法"
+
+msgid "ciReport|Namespace"
+msgstr "命å空间"
+
msgid "ciReport|No changes to code quality"
msgstr "代ç è´¨é‡æ— å˜åŒ–"
@@ -4941,20 +7365,17 @@ msgstr "性能指标无å˜åŒ–"
msgid "ciReport|Performance metrics"
msgstr "性能指标"
-msgid "ciReport|SAST"
-msgstr "SAST"
+msgid "ciReport|Revert dismissal"
+msgstr "å–消忽略"
msgid "ciReport|SAST detected"
msgstr "SAST检测到"
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr "SAST未å‘现新的安全æ¼æ´ž"
-
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr "SAST未å‘现安全æ¼æ´ž"
+msgid "ciReport|SAST is loading"
+msgstr "SAST加载中"
-msgid "ciReport|SAST:container no vulnerabilities were found"
-msgstr "SAST:container未å‘现æ¼æ´ž"
+msgid "ciReport|SAST resulted in error while loading results"
+msgstr "加载SAST结果时出错"
msgid "ciReport|Security scanning"
msgstr "安全扫æ"
@@ -4962,15 +7383,54 @@ msgstr "安全扫æ"
msgid "ciReport|Security scanning failed loading any results"
msgstr "安全扫æ无法加载任何结果"
-msgid "ciReport|Show complete code vulnerabilities report"
-msgstr "显示完整的代ç æ¼æ´žæŠ¥å‘Š"
+msgid "ciReport|Security scanning is loading"
+msgstr "安全扫æ加载中"
+
+msgid "ciReport|Severity"
+msgstr "严é‡æ€§"
+
+msgid "ciReport|Solution"
+msgstr "解决方案"
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
-msgstr "未批准的æ¼æ´ž (红色) å¯ä»¥æ ‡è®°ä¸ºå·²æ‰¹å‡†ã€‚ %{helpLink}"
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr "é™æ€åº”用安全测试(SAST)å¯ä»¥æ£€æµ‹æºä»£ç ä¸­å·²çŸ¥çš„æ¼æ´žã€‚"
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr "创建议题时å‘生错误。请å†è¯•ä¸€æ¬¡ã€‚"
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr "忽略此æ¼æ´žæ—¶å‘生错误。请å†è¯•ä¸€æ¬¡ã€‚"
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr "加载 DAST 报告时出错"
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr "加载SAST报告时出错"
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr "加载容器扫æ报告时出错"
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr "加载ä¾èµ–项扫æ报告时出错"
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr "å–消忽略æ“作时å‘生错误。请å†è¯•ä¸€æ¬¡ã€‚"
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr "未批准的æ¼æ´ž (红色) å¯ä»¥æ ‡è®°ä¸ºå·²æ‰¹å‡†ã€‚"
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr "å°† %{name} 从 %{version} å‡çº§åˆ° %{fixed}。"
+
+msgid "ciReport|View full report"
+msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr "未检测到安全æ¼æ´ž"
+msgid "ciReport|on pipeline"
+msgstr "于æµæ°´çº¿"
+
msgid "command line instructions"
msgstr "命令行指å—"
@@ -4980,10 +7440,16 @@ msgstr "连接中"
msgid "could not read private key, is the passphrase correct?"
msgstr "无法读å–ç§é’¥ï¼Œå¯†ç çŸ­è¯­æ˜¯å¦æ­£ç¡®ï¼Ÿ"
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "天"
+msgid "deploy token"
+msgstr "部署令牌"
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] "检测到%d个安全æ¼æ´žå·²ä¿®å¤"
@@ -4995,18 +7461,30 @@ msgstr[0] "检测到%d个新的安全æ¼æ´ž"
msgid "detected no vulnerabilities"
msgstr "未检测到安全æ¼æ´ž"
+msgid "disabled"
+msgstr "å·²ç¦ç”¨"
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr "å·²å¯ç”¨"
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr "最åŽä¸€æ¬¡%{slash_command} 命令将更新预计时间。"
+msgid "for this project"
+msgstr "对于这个项目"
+
msgid "here"
msgstr "此处"
+msgid "import flow"
+msgstr ""
+
msgid "importing"
msgstr "导入中"
-msgid "in progress"
-msgstr "进行中"
-
msgid "is invalid because there is downstream lock"
msgstr "因下游é”定而无效"
@@ -5016,8 +7494,11 @@ msgstr "因上游é”定而无效"
msgid "is not a valid X509 certificate."
msgstr "ä¸æ˜¯æœ‰æ•ˆçš„X509è¯ä¹¦ã€‚"
+msgid "latest version"
+msgstr "最新版本"
+
msgid "locked by %{path_lock_user_name} %{created_at}"
-msgstr "被 %{path_lock_user_name} 于 %{created_at} é”定"
+msgstr "被 %{path_lock_user_name} 在 %{created_at} é”定"
msgid "merge request"
msgid_plural "merge requests"
@@ -5036,26 +7517,20 @@ msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasis
msgstr "%{metricsLinkStart} 内存 %{metricsLinkEnd} å ç”¨ %{emphasisStart} æ— å˜åŒ– %{emphasisEnd}, ä¿æŒåœ¨ %{memoryFrom}MB"
msgid "mrWidget|Add approval"
-msgstr "增加批准"
+msgstr "添加批准"
-msgid "mrWidget|Allows edits from maintainers"
-msgstr "å…许上游项目维护人员进行编辑。"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr "具有åˆå¹¶åˆ°ç›®æ ‡åˆ†æ”¯æƒé™çš„æˆå‘˜å…许æ交"
msgid "mrWidget|An error occured while removing your approval."
-msgstr "删除批准时å‘生错误。"
-
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr "读å–æ­¤åˆå¹¶è¯·æ±‚的批准数æ®æ—¶å‘生错误。"
+msgstr "删除您的批准时å‘生错误。"
-msgid "mrWidget|An error occured while submitting your approval."
-msgstr "æ交批准时å‘生错误。"
+msgid "mrWidget|An error occurred while submitting your approval."
+msgstr ""
msgid "mrWidget|Approve"
msgstr "批准"
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr "批准人:"
@@ -5069,10 +7544,10 @@ msgid "mrWidget|Checking ability to merge automatically"
msgstr "检查是å¦å¯ä»¥è‡ªåŠ¨åˆå¹¶"
msgid "mrWidget|Cherry-pick"
-msgstr "优选"
+msgstr "拣选"
msgid "mrWidget|Cherry-pick this merge request in a new merge request"
-msgstr "通过新的åˆå¹¶è¯·æ±‚中优选此åˆå¹¶è¯·æ±‚"
+msgstr "通过新的åˆå¹¶è¯·æ±‚中拣选此åˆå¹¶è¯·æ±‚"
msgid "mrWidget|Closed"
msgstr "已关闭"
@@ -5083,6 +7558,9 @@ msgstr "关闭:"
msgid "mrWidget|Closes"
msgstr "关闭"
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr "创建议题以便åŽç»­å¤„ç†"
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr "部署统计信æ¯å½“å‰ä¸å¯ç”¨"
@@ -5116,8 +7594,23 @@ msgstr "åˆå¹¶å¤±è´¥ã€‚"
msgid "mrWidget|Merge locally"
msgstr "本地åˆå¹¶"
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
-msgstr "åˆå¹¶:"
+msgstr "åˆå¹¶è€…:"
+
+msgid "mrWidget|No Approval required"
+msgstr "无需批准"
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr "批准已满足è¦æ±‚; 您ä»ç„¶å¯ä»¥ç»§ç»­æ·»åŠ é¢å¤–批准"
+
+msgid "mrWidget|Open in Web IDE"
+msgstr "在Web IDE中打开"
msgid "mrWidget|Plain diff"
msgstr "文本差异"
@@ -5179,6 +7672,9 @@ msgstr "æºåˆ†æ”¯ä¸ä¼šè¢«åˆ é™¤"
msgid "mrWidget|There are merge conflicts"
msgstr "存在åˆå¹¶å†²çª"
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr "存在尚未解决的讨论。请先解决这些讨论。"
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr "该åˆå¹¶è¯·æ±‚未能自动åˆå¹¶"
@@ -5188,9 +7684,6 @@ msgstr "该åˆå¹¶è¯·æ±‚正在被åˆå¹¶"
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr "该项目已存档,ç¦æ­¢å†™å…¥"
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "å¯ä»¥æ‰‹åŠ¨åˆå¹¶æ­¤åˆå¹¶è¯·æ±‚,使用以下"
@@ -5231,15 +7724,24 @@ msgstr "个人访问令牌"
msgid "private key does not match certificate."
msgstr "ç§é’¥ä¸Žè¯ä¹¦ä¸åŒ¹é…。"
+msgid "remaining"
+msgstr "剩余"
+
msgid "remove due date"
msgstr "删除截止日期"
+msgid "remove weight"
+msgstr "移除æƒé‡"
+
msgid "source"
msgstr "æº"
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "%{slash_command} 将会更新消耗的总时长。"
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr "此文档"
@@ -5252,6 +7754,13 @@ msgstr "用户å"
msgid "uses Kubernetes clusters to deploy your code!"
msgstr "使用 Kubernetes 集群æ¥éƒ¨ç½²ä»£ç ï¼"
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr "å…± %{additions} æ¡æ–°å¢ž, %{deletions} æ¡åˆ é™¤."
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] "在 %d 分钟内 "
+
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index 6bfcae6aa91..b37b7aec35d 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:39-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Chinese Traditional, Hong Kong\n"
"Language: zh_HK\n"
@@ -15,10 +13,23 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: zh-HK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:40\n"
msgid " and"
msgstr ""
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] ""
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] ""
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] " %d 次æ交"
@@ -47,6 +58,22 @@ msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] ""
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] ""
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] ""
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] ""
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] ""
+
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] "為æ高é é¢åŠ è¼‰é€Ÿåº¦åŠæ€§èƒ½ï¼Œå·²çœç•¥äº† %s 次æ交。"
@@ -57,16 +84,31 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
+msgid "%{filePath} deleted"
+msgstr ""
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{name}'s avatar"
+msgstr ""
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr ""
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
@@ -79,22 +121,70 @@ msgstr "已失敗 %{number_of_failures} 次,最大失敗 %{maximum_failures} æ
msgid "%{openOrClose} %{noteable}"
msgstr ""
+msgid "%{percent}%% complete"
+msgstr ""
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] "%{storage_name}:已訪å•æ­¤ä¸»æ©Ÿå¤±æ•— %{failed_attempts} 次"
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+
msgid "%{text} is available"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(想了解更多的安è£è¨Šæ¯è«‹æŸ¥çœ‹ %{link})"
+msgid "%{title} changes"
+msgstr ""
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] ""
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "- Runner is active and can process any new jobs"
+msgstr ""
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr ""
+
msgid "- show less"
msgstr ""
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] "%{count}%{type} 個附加"
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] "%{count}%{type} 個變更"
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] ""
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] ""
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] ""
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] ""
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] ""
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "%d æ¢æµæ°´ç·š"
@@ -105,9 +195,51 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr ""
+
+msgid "403|You don't have the permission to access this page."
+msgstr ""
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr ""
+
+msgid "404|Page Not Found"
+msgstr ""
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr ""
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "相關æŒçºŒé›†æˆçš„圖åƒé›†åˆ"
@@ -117,33 +249,63 @@ 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 regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr ""
+
msgid "A user with write access to the source branch selected this option"
msgstr ""
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
msgid "About auto deploy"
msgstr "關於自動部署"
+msgid "About this feature"
+msgstr ""
+
msgid "Abuse Reports"
msgstr ""
msgid "Abuse reports"
msgstr ""
+msgid "Accept terms"
+msgstr ""
+
+msgid "Accepted MR"
+msgstr ""
+
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
+msgid "Access to '%{classification_label}' not allowed"
+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 "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr ""
+
msgid "Account"
msgstr ""
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
msgstr "啟用"
+msgid "Active Sessions"
+msgstr ""
+
msgid "Activity"
msgstr "活動"
@@ -168,12 +330,39 @@ msgstr "添加許å¯è­‰"
msgid "Add Readme"
msgstr ""
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr ""
+
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr "添加新目錄"
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
+msgid "Additional text"
+msgstr ""
+
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -240,7 +429,10 @@ msgstr ""
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
-msgid "Allow edits from maintainers."
+msgid "Allow commits from members who can merge to the target branch."
+msgstr ""
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
@@ -264,6 +456,48 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
+msgid "An error occured creating the new branch."
+msgstr ""
+
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
+msgid "An error occured whilst loading all the files."
+msgstr ""
+
+msgid "An error occured whilst loading the file content."
+msgstr ""
+
+msgid "An error occured whilst loading the file."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr ""
+
+msgid "An error occured whilst loading the merge request."
+msgstr ""
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -279,6 +513,9 @@ msgstr ""
msgid "An error occurred while detecting host keys"
msgstr ""
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr ""
@@ -294,13 +531,13 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
@@ -336,18 +573,42 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while subscribing to notifications."
+msgstr ""
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Anti-spam verification"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Any Label"
msgstr ""
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr ""
@@ -357,12 +618,21 @@ msgstr ""
msgid "April"
msgstr ""
-msgid "Archived project! Repository is read-only"
-msgstr "歸檔項目ï¼å­˜å„²åº«ç‚ºåªè®€"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "確定è¦åˆªé™¤æ­¤æµæ°´ç·šè¨ˆåŠƒå—Žï¼Ÿ"
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
+msgid "Are you sure you want to remove this identity?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "確定è¦é‡ç½®è¨»å†Šä»¤ç‰Œå—Žï¼Ÿ"
@@ -378,6 +648,12 @@ msgstr "確定嗎?"
msgid "Artifacts"
msgstr ""
+msgid "Ascending"
+msgstr ""
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr ""
+
msgid "Assertion consumer service URL"
msgstr ""
@@ -402,12 +678,27 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
+msgid "Assignee boards not available with your current license"
+msgstr ""
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr ""
+
+msgid "Assignee(s)"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "拖放文件到此處或者 %{upload_link}"
+msgid "Audit Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -417,12 +708,36 @@ msgstr ""
msgid "Authentication Log"
msgstr ""
+msgid "Authentication log"
+msgstr ""
+
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
+msgid "Auto DevOps"
+msgstr ""
+
msgid "Auto DevOps enabled"
msgstr ""
@@ -438,7 +753,10 @@ msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "AutoDevOps|Auto DevOps (Beta)"
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr ""
+
+msgid "AutoDevOps|Auto DevOps"
msgstr ""
msgid "AutoDevOps|Auto DevOps documentation"
@@ -459,12 +777,18 @@ msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
+msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Available"
msgstr ""
+msgid "Available group Runners : %{runners}"
+msgstr ""
+
+msgid "Available group Runners : %{runners}."
+msgstr ""
+
msgid "Avatar will be removed. Are you sure?"
msgstr ""
@@ -474,12 +798,93 @@ msgstr ""
msgid "Background Color"
msgstr ""
+msgid "Background Jobs"
+msgstr ""
+
+msgid "Background color"
+msgstr ""
+
msgid "Background jobs"
msgstr ""
+msgid "Badges"
+msgstr ""
+
+msgid "Badges|A new badge was added."
+msgstr ""
+
+msgid "Badges|Add badge"
+msgstr ""
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|Badge image URL"
+msgstr ""
+
+msgid "Badges|Badge image preview"
+msgstr ""
+
+msgid "Badges|Delete badge"
+msgstr ""
+
+msgid "Badges|Delete badge?"
+msgstr ""
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr ""
+
+msgid "Badges|Group Badge"
+msgstr ""
+
+msgid "Badges|Link"
+msgstr ""
+
+msgid "Badges|No badge image"
+msgstr ""
+
+msgid "Badges|No image to preview"
+msgstr ""
+
+msgid "Badges|Project Badge"
+msgstr ""
+
+msgid "Badges|Reload badge image"
+msgstr ""
+
+msgid "Badges|Save changes"
+msgstr ""
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr ""
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr ""
+
+msgid "Badges|The badge was deleted."
+msgstr ""
+
+msgid "Badges|The badge was saved."
+msgstr ""
+
+msgid "Badges|This group has no badges"
+msgstr ""
+
+msgid "Badges|This project has no badges"
+msgstr ""
+
+msgid "Badges|Your badges"
+msgstr ""
+
msgid "Begin with the selected commit"
msgstr ""
+msgid "Below are examples of regex for existing tools:"
+msgstr ""
+
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -498,6 +903,9 @@ msgstr ""
msgid "BillingPlans|Downgrade"
msgstr ""
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
+msgstr ""
+
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr ""
@@ -522,6 +930,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr ""
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgstr ""
+
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgstr ""
+
+msgid "BillingPlans|features"
+msgstr ""
+
msgid "BillingPlans|frequently asked questions"
msgstr ""
@@ -534,6 +951,18 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
+msgid "Blog"
+msgstr ""
+
+msgid "Boards"
+msgstr ""
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr ""
+
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -610,7 +1039,7 @@ msgstr ""
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr ""
-msgid "Branches|Only a project master or owner can delete a protected branch"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
msgstr ""
msgid "Branches|Overview"
@@ -691,7 +1120,7 @@ msgstr "ç€è¦½æ–‡ä»¶"
msgid "Browse files"
msgstr "ç€è¦½æ–‡ä»¶"
-msgid "Business"
+msgid "Business metrics (Custom)"
msgstr ""
msgid "ByAuthor|by"
@@ -700,6 +1129,9 @@ msgstr "作者:"
msgid "CI / CD"
msgstr ""
+msgid "CI / CD Settings"
+msgstr ""
+
msgid "CI/CD"
msgstr ""
@@ -709,12 +1141,72 @@ msgstr ""
msgid "CI/CD for external repo"
msgstr ""
+msgid "CI/CD settings"
+msgstr ""
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr ""
+
+msgid "CICD|Auto DevOps"
+msgstr ""
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr ""
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
+msgid "CICD|Disable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr ""
+
+msgid "CICD|Enable Auto DevOps"
+msgstr ""
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr ""
+
+msgid "CICD|Instance default (%{state})"
+msgstr ""
+
msgid "CICD|Jobs"
msgstr ""
+msgid "CICD|Learn more about Auto DevOps"
+msgstr ""
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr ""
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
+msgid "Can't find HEAD commit for this branch"
+msgstr ""
+
msgid "Cancel"
msgstr "å–消"
+msgid "Cancel this job"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -772,15 +1264,30 @@ msgstr "優é¸æ­¤æ交"
msgid "Cherry-pick this merge request"
msgstr "優é¸æ­¤åˆä½µè«‹æ±‚"
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
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 ""
+msgid "Choose any color."
+msgstr ""
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr ""
+
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
@@ -886,9 +1393,30 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr ""
+
+msgid "Clear search input"
+msgstr ""
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr ""
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr ""
+
+msgid "Click the <strong>Select none</strong> 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 ""
+msgid "Click to expand it."
+msgstr ""
+
msgid "Click to expand text"
msgstr ""
@@ -901,6 +1429,9 @@ msgstr ""
msgid "Client authentication key password"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -910,6 +1441,9 @@ msgstr ""
msgid "Closed"
msgstr ""
+msgid "Closed issues"
+msgstr ""
+
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
@@ -919,10 +1453,13 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
msgid "ClusterIntegration|Applications"
@@ -937,9 +1474,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -955,6 +1489,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -964,22 +1501,25 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Did you know?"
msgstr ""
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
msgstr ""
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgid "ClusterIntegration|Fetching machine types"
msgstr ""
-msgid "ClusterIntegration|Environment scope"
+msgid "ClusterIntegration|Fetching projects"
+msgstr ""
+
+msgid "ClusterIntegration|Fetching zones"
msgstr ""
msgid "ClusterIntegration|GitLab Integration"
@@ -988,7 +1528,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1000,6 +1540,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Hide"
+msgstr ""
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr ""
+
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
msgstr ""
@@ -1027,6 +1573,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1063,7 +1615,13 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about environments"
@@ -1090,6 +1648,18 @@ msgstr ""
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No projects found"
+msgstr ""
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr ""
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr ""
+
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -1102,9 +1672,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1132,19 +1699,37 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
+msgid "ClusterIntegration|Search machine types"
+msgstr ""
+
+msgid "ClusterIntegration|Search projects"
+msgstr ""
+
+msgid "ClusterIntegration|Search zones"
+msgstr ""
+
msgid "ClusterIntegration|Security"
msgstr ""
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|See machine types"
+msgid "ClusterIntegration|Select machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project"
+msgstr ""
+
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr ""
+
+msgid "ClusterIntegration|Select project to choose zone"
msgstr ""
-msgid "ClusterIntegration|See your projects"
+msgid "ClusterIntegration|Select zone"
msgstr ""
-msgid "ClusterIntegration|See zones"
+msgid "ClusterIntegration|Select zone to choose machine type"
msgstr ""
msgid "ClusterIntegration|Service token"
@@ -1177,6 +1762,9 @@ msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Validating project billing status"
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1207,13 +1795,22 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "ClusterIntegration|sign up"
+msgstr ""
+
+msgid "Cohorts"
+msgstr ""
+
msgid "Collapse"
msgstr ""
-msgid "Comment and resolve discussion"
+msgid "Collapse sidebar"
+msgstr ""
+
+msgid "Comment & resolve discussion"
msgstr ""
-msgid "Comment and unresolve discussion"
+msgid "Comment & unresolve discussion"
msgstr ""
msgid "Comments"
@@ -1278,6 +1875,9 @@ msgstr ""
msgid "Committed by"
msgstr "æ交者:"
+msgid "Commit…"
+msgstr ""
+
msgid "Compare"
msgstr "比較"
@@ -1326,6 +1926,9 @@ msgstr ""
msgid "Configure limits for web and API requests."
msgstr ""
+msgid "Configure push and pull mirrors."
+msgstr ""
+
msgid "Configure storage path and circuit breaker settings."
msgstr ""
@@ -1392,15 +1995,30 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr ""
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
+msgid "Contribute to GitLab"
+msgstr ""
+
msgid "Contribution"
msgstr ""
msgid "Contribution guide"
msgstr "è²¢ç»æŒ‡å—"
+msgid "Contributions per group member"
+msgstr ""
+
msgid "Contributors"
msgstr "è²¢ç»è€…"
@@ -1416,12 +2034,21 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Control the maximum concurrency of repository backfill for this secondary node"
msgstr ""
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "ConvDev Index"
+msgstr ""
+
msgid "Copy SSH public key to clipboard"
msgstr ""
@@ -1437,9 +2064,21 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr "複製æ交 SHA 到剪貼æ¿"
+msgid "Copy file path to clipboard"
+msgstr ""
+
+msgid "Copy incoming email address to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1452,12 +2091,18 @@ msgstr ""
msgid "Create a new branch and merge request"
msgstr ""
+msgid "Create a new issue"
+msgstr ""
+
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "在帳戶上創建個人訪å•ä»¤ç‰Œï¼Œä»¥é€šéŽ %{protocol} 來拉å–或推é€ã€‚"
msgid "Create branch"
msgstr ""
+msgid "Create commit"
+msgstr ""
+
msgid "Create directory"
msgstr "創建目錄"
@@ -1470,9 +2115,15 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
+msgid "Create issue"
+msgstr ""
+
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
@@ -1491,6 +2142,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1509,10 +2163,16 @@ msgstr "標籤"
msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "創建個人訪å•ä»¤ç‰Œ"
-msgid "Creates a new branch from %{branchName}"
+msgid "Created"
+msgstr ""
+
+msgid "Created At"
msgstr ""
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
+msgid "Created by me"
+msgstr ""
+
+msgid "Created on:"
msgstr ""
msgid "Creating epic"
@@ -1527,6 +2187,15 @@ msgstr "Cron 語法"
msgid "Current node"
msgstr ""
+msgid "CurrentUser|Profile"
+msgstr ""
+
+msgid "CurrentUser|Settings"
+msgstr ""
+
+msgid "Custom CI config path"
+msgstr ""
+
msgid "Custom notification events"
msgstr "自定義通知事件"
@@ -1536,6 +2205,12 @@ msgstr "自定義通知級別繼承自åƒèˆ‡ç´šåˆ¥ã€‚使用自定義通知級別
msgid "Customize colors"
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 "Cycle Analytics"
msgstr "週期分æž"
@@ -1560,6 +2235,9 @@ msgstr "é ç™¼å¸ƒ"
msgid "CycleAnalyticsStage|Test"
msgstr "測試"
+msgid "Dashboard"
+msgstr ""
+
msgid "DashboardProjects|All"
msgstr ""
@@ -1572,15 +2250,36 @@ msgstr ""
msgid "December"
msgstr ""
+msgid "Decline and sign out"
+msgstr ""
+
msgid "Default classification label"
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 ""
+
msgid "Define a custom pattern with cron syntax"
msgstr "使用 Cron 語法定義自定義模å¼"
msgid "Delete"
msgstr "刪除"
+msgid "Delete Snippet"
+msgstr ""
+
+msgid "Delete list"
+msgstr ""
+
+msgid "Deleted"
+msgstr ""
+
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "部署"
@@ -1588,39 +2287,195 @@ msgstr[0] "部署"
msgid "Deploy Keys"
msgstr ""
+msgid "DeployKeys|+%{count} others"
+msgstr ""
+
+msgid "DeployKeys|Current project"
+msgstr ""
+
+msgid "DeployKeys|Deploy key"
+msgstr ""
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr ""
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr ""
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr ""
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr ""
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr ""
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Project usage"
+msgstr ""
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr ""
+
+msgid "DeployKeys|Read access only"
+msgstr ""
+
+msgid "DeployKeys|Write access allowed"
+msgstr ""
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr ""
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr ""
+
+msgid "DeployTokens|Add a deploy token"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr ""
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr ""
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr ""
+
+msgid "DeployTokens|Create deploy token"
+msgstr ""
+
+msgid "DeployTokens|Created"
+msgstr ""
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr ""
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr ""
+
+msgid "DeployTokens|Expires"
+msgstr ""
+
+msgid "DeployTokens|Name"
+msgstr ""
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr ""
+
+msgid "DeployTokens|Revoke"
+msgstr ""
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr ""
+
+msgid "DeployTokens|Scopes"
+msgstr ""
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr ""
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr ""
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr ""
+
+msgid "DeployTokens|Use this username as a login."
+msgstr ""
+
+msgid "DeployTokens|Username"
+msgstr ""
+
+msgid "DeployTokens|You are about to revoke"
+msgstr ""
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr ""
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr ""
+
+msgid "Deprioritize label"
+msgstr ""
+
+msgid "Descending"
+msgstr ""
+
msgid "Description"
msgstr "æè¿°"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr "詳情"
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr ""
+
msgid "Directory name"
msgstr "目錄å稱"
msgid "Disable"
msgstr ""
+msgid "Disable for this project"
+msgstr ""
+
+msgid "Disable group Runners"
+msgstr ""
+
+msgid "Discard changes"
+msgstr ""
+
msgid "Discard draft"
msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Documentation for popular identity providers"
msgstr ""
+msgid "Domain"
+msgstr ""
+
msgid "Don't show again"
msgstr "ä¸å†é¡¯ç¤º"
@@ -1663,16 +2518,31 @@ msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr ""
+msgid "Each Runner can be in one of the following states:"
+msgstr ""
+
msgid "Edit"
msgstr "編輯"
+msgid "Edit Label"
+msgstr ""
+
msgid "Edit Pipeline Schedule %{id}"
msgstr "編輯 %{id} æµæ°´ç·šè¨ˆåŠƒ"
+msgid "Edit Snippet"
+msgstr ""
+
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
-msgid "Editing"
+msgid "Edit group: %{group_name}"
+msgstr ""
+
+msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Elasticsearch"
@@ -1684,15 +2554,24 @@ msgstr ""
msgid "Email"
msgstr ""
+msgid "Email patch"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Embed"
+msgstr ""
+
msgid "Enable"
msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable Pseudonymizer data collection"
+msgstr ""
+
msgid "Enable SAML authentication for this group"
msgstr ""
@@ -1708,6 +2587,18 @@ msgstr ""
msgid "Enable classification control using an external service"
msgstr ""
+msgid "Enable for this project"
+msgstr ""
+
+msgid "Enable group Runners"
+msgstr ""
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr ""
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -1720,15 +2611,30 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Ends at (UTC)"
+msgstr ""
+
+msgid "Environments"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -1741,33 +2647,54 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|No pod name has been specified"
+msgstr ""
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
+msgstr ""
+
+msgid "Environments|Pod logs from"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
+msgid "Environments|Stop"
+msgstr ""
+
+msgid "Environments|Stop environment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Epic"
+msgstr ""
+
msgid "Epic will be removed! Are you sure?"
msgstr ""
@@ -1783,12 +2710,6 @@ msgstr ""
msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
-
msgid "Error creating epic"
msgstr ""
@@ -1807,6 +2728,21 @@ msgstr ""
msgid "Error fetching usage ping data."
msgstr ""
+msgid "Error loading branch data. Please try again."
+msgstr ""
+
+msgid "Error loading last commit."
+msgstr ""
+
+msgid "Error loading markdown preview"
+msgstr ""
+
+msgid "Error loading merge requests."
+msgstr ""
+
+msgid "Error loading project data. Please try again."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1819,6 +2755,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Estimated"
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "全部"
@@ -1846,9 +2785,30 @@ msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆ 1 日淩晨 4 點)"
msgid "Every week (Sundays at 4:00am)"
msgstr "æ¯é€±åŸ·è¡Œï¼ˆå‘¨æ—¥æ·©æ™¨ 4 點)"
+msgid "Everyone can contribute"
+msgstr ""
+
msgid "Expand"
msgstr ""
+msgid "Expand all"
+msgstr ""
+
+msgid "Expand sidebar"
+msgstr ""
+
+msgid "Explore"
+msgstr ""
+
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1876,6 +2836,9 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr ""
+msgid "Facebook"
+msgstr ""
+
msgid "Failed"
msgstr ""
@@ -1885,6 +2848,9 @@ msgstr ""
msgid "Failed to change the owner"
msgstr "無法變更所有者"
+msgid "Failed to check related branches."
+msgstr ""
+
msgid "Failed to remove issue from board, please try again."
msgstr ""
@@ -1894,6 +2860,12 @@ msgstr "無法刪除æµæ°´ç·šè¨ˆåŠƒ"
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1903,9 +2875,6 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
-msgid "File name"
-msgstr ""
-
msgid "Files"
msgstr "文件"
@@ -1915,6 +2884,9 @@ msgstr ""
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
+msgid "Filter"
+msgstr ""
+
msgid "Filter by commit message"
msgstr "按æ交消æ¯éŽæ¿¾"
@@ -1924,6 +2896,12 @@ msgstr "按路徑查找"
msgid "Find file"
msgstr "查找文件"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -1933,12 +2911,39 @@ msgstr "首次推é€"
msgid "FirstPushedBy|pushed by"
msgstr "推é€è€…:"
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "Font Color"
msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr ""
+
msgid "Fork"
msgid_plural "Forks"
msgstr[0] "派生"
@@ -1955,9 +2960,24 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr "從創建議題到部署到生產環境"
@@ -1970,6 +2990,12 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "General"
+msgstr ""
+
+msgid "General pipelines"
+msgstr ""
+
msgid "Generate a default set of labels"
msgstr ""
@@ -1988,7 +3014,10 @@ msgstr ""
msgid "GeoNodes|Checksummed"
msgstr ""
-msgid "GeoNodes|Database replication lag:"
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr ""
+
+msgid "GeoNodes|Data replication lag"
msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
@@ -2003,31 +3032,43 @@ msgstr ""
msgid "GeoNodes|Full"
msgstr ""
+msgid "GeoNodes|GitLab version"
+msgstr ""
+
msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodes|GitLab version:"
+msgid "GeoNodes|Health status"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary"
msgstr ""
-msgid "GeoNodes|Health status:"
+msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
-msgid "GeoNodes|Last event ID processed by cursor:"
+msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
-msgid "GeoNodes|Last event ID seen from primary:"
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodes|Local Attachments:"
+msgid "GeoNodes|Local LFS objects"
msgstr ""
-msgid "GeoNodes|Local LFS objects:"
+msgid "GeoNodes|Local attachments"
msgstr ""
-msgid "GeoNodes|Local job artifacts:"
+msgid "GeoNodes|Local job artifacts"
msgstr ""
msgid "GeoNodes|New node"
@@ -2048,19 +3089,25 @@ msgstr ""
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Replication slot WAL:"
+msgid "GeoNodes|Replication slot WAL"
+msgstr ""
+
+msgid "GeoNodes|Replication slots"
msgstr ""
-msgid "GeoNodes|Replication slots:"
+msgid "GeoNodes|Repositories"
msgstr ""
-msgid "GeoNodes|Repositories checksummed:"
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
-msgid "GeoNodes|Repositories:"
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
-msgid "GeoNodes|Repository checksums verified:"
+msgid "GeoNodes|Repository checksum progress"
+msgstr ""
+
+msgid "GeoNodes|Repository verification progress"
msgstr ""
msgid "GeoNodes|Selective"
@@ -2069,16 +3116,19 @@ msgstr ""
msgid "GeoNodes|Something went wrong while changing node status"
msgstr ""
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr ""
+
msgid "GeoNodes|Something went wrong while removing node"
msgstr ""
msgid "GeoNodes|Something went wrong while repairing node"
msgstr ""
-msgid "GeoNodes|Storage config:"
+msgid "GeoNodes|Storage config"
msgstr ""
-msgid "GeoNodes|Sync settings:"
+msgid "GeoNodes|Sync settings"
msgstr ""
msgid "GeoNodes|Synced"
@@ -2096,13 +3146,19 @@ msgstr ""
msgid "GeoNodes|Verified"
msgstr ""
-msgid "GeoNodes|Wiki checksums verified:"
+msgid "GeoNodes|Wiki checksum progress"
msgstr ""
-msgid "GeoNodes|Wikis checksummed:"
+msgid "GeoNodes|Wiki verification progress"
msgstr ""
-msgid "GeoNodes|Wikis:"
+msgid "GeoNodes|Wikis"
+msgstr ""
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr ""
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
@@ -2132,6 +3188,12 @@ msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
+msgid "Geo|Verification capacity"
+msgstr ""
+
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2141,6 +3203,9 @@ msgstr ""
msgid "Git storage health information has been reset"
msgstr "Git 存儲å¥åº·ä¿¡æ¯å·²é‡ç½®"
+msgid "Git strategy for pipelines"
+msgstr ""
+
msgid "Git version"
msgstr ""
@@ -2153,34 +3218,100 @@ msgstr ""
msgid "GitLab Geo"
msgstr ""
-msgid "GitLab Runner section"
-msgstr "GitLab Runner 介紹"
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr ""
+
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
msgid "GitLab single sign on URL"
msgstr ""
+msgid "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."
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
msgid "Gitaly Servers"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
+msgid "Go Back"
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr "跳轉到派生項目"
msgid "GoToYourFork|Fork"
msgstr "跳轉到派生項目"
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
msgid "Got it!"
msgstr ""
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
+msgid "Graph"
+msgstr ""
+
+msgid "Group"
+msgstr ""
+
+msgid "Group CI/CD settings"
+msgstr ""
+
+msgid "Group Git LFS status:"
+msgstr ""
+
+msgid "Group ID"
+msgstr ""
+
+msgid "Group Runners"
+msgstr ""
+
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group details"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr ""
+
+msgid "Group: %{group_name}"
msgstr ""
msgid "GroupRoadmap|From %{dateWord}"
@@ -2192,7 +3323,28 @@ msgstr ""
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr ""
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr ""
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
msgstr ""
msgid "GroupRoadmap|Until %{dateWord}"
@@ -2222,6 +3374,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr ""
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2298,15 +3477,54 @@ msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
+msgid "Hide whitespace changes"
+msgstr ""
+
msgid "History"
msgstr ""
msgid "Housekeeping successfully started"
msgstr "已開始維護"
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "ID"
+msgstr ""
+
+msgid "IDE|Commit"
+msgstr ""
+
+msgid "IDE|Edit"
+msgstr ""
+
+msgid "IDE|Go back"
+msgstr ""
+
+msgid "IDE|Open in file view"
+msgstr ""
+
+msgid "IDE|Review"
+msgstr ""
+
+msgid "Identifier"
+msgstr ""
+
+msgid "Identities"
+msgstr ""
+
msgid "Identity provider single sign on URL"
msgstr ""
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr ""
+
+msgid "If enabled"
+msgstr ""
+
msgid "If enabled, access to projects will be validated on an external service using their classification label."
msgstr ""
@@ -2319,15 +3537,54 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2346,11 +3603,23 @@ msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "安è£å£¹å€‹èˆ‡ GitLab CI 兼容的 Runner"
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr ""
+
+msgid "Incompatible Project"
+msgstr ""
+
+msgid "Inline"
+msgstr ""
+
+msgid "Install GitLab Runner"
+msgstr ""
+
+msgid "Install Runner on Kubernetes"
+msgstr ""
msgid "Instance"
msgid_plural "Instances"
@@ -2362,6 +3631,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
+msgid "Integrations Settings"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -2377,6 +3649,9 @@ msgstr "循環週期"
msgid "Introducing Cycle Analytics"
msgstr "週期分æžç°¡ä»‹"
+msgid "Issue Boards"
+msgstr ""
+
msgid "Issue board focus mode"
msgstr ""
@@ -2395,12 +3670,21 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
+msgid "Issues closed"
+msgstr ""
+
msgid "Jan"
msgstr ""
msgid "January"
msgstr ""
+msgid "Job"
+msgstr ""
+
+msgid "Job has been erased"
+msgstr ""
+
msgid "Jobs"
msgstr ""
@@ -2419,6 +3703,9 @@ msgstr ""
msgid "Koding"
msgstr ""
+msgid "Koding Dashboard"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -2443,6 +3730,9 @@ msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr ""
+msgid "LFS"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "åœç”¨"
@@ -2452,12 +3742,21 @@ msgstr "啟用"
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
+msgid "Label lists show all issues with the selected label."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
msgstr ""
+msgid "LabelSelect|Labels"
+msgstr ""
+
msgid "Labels"
msgstr ""
@@ -2467,6 +3766,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2501,6 +3803,9 @@ msgstr "您推é€äº†"
msgid "LastPushEvent|at"
msgstr "在"
+msgid "Latest changes"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -2525,18 +3830,36 @@ msgstr "退出群組"
msgid "Leave project"
msgstr "退出項目"
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "License"
msgstr ""
+msgid "LinkedIn"
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
+msgid "Loading contribution stats for group members"
+msgstr ""
+
msgid "Loading the GitLab IDE..."
msgstr ""
+msgid "Loading..."
+msgstr ""
+
msgid "Lock"
msgstr ""
@@ -2546,24 +3869,45 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock to current projects"
+msgstr ""
+
msgid "Locked"
msgstr ""
msgid "Locked Files"
msgstr ""
+msgid "Locked to current projects"
+msgstr ""
+
msgid "Locks give the ability to lock specific file or folder."
msgstr ""
-msgid "Login"
+msgid "Logs"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr ""
+
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2576,13 +3920,34 @@ msgstr ""
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+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"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
-msgid "Mark done"
+msgid "Mark todo as done"
+msgstr ""
+
+msgid "Markdown enabled"
msgstr ""
msgid "Maximum git storage failures"
@@ -2600,24 +3965,60 @@ msgstr ""
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
+msgid "Merge Request:"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
+msgid "Merge Requests created"
+msgstr ""
+
msgid "Merge events"
msgstr "åˆä½µäº‹ä»¶ (merge event)"
msgid "Merge request"
msgstr ""
+msgid "Merge request approvals"
+msgstr ""
+
+msgid "Merge requests"
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr ""
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr ""
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr ""
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr ""
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr ""
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr ""
+
msgid "Merged"
msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
@@ -2627,18 +4028,27 @@ msgstr ""
msgid "Metrics|Business"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Edit metric"
msgstr ""
+msgid "Metrics|Environment"
+msgstr ""
+
msgid "Metrics|For grouping similar metrics"
msgstr ""
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
msgstr ""
+msgid "Metrics|Learn about environments"
+msgstr ""
+
msgid "Metrics|Legend label (optional)"
msgstr ""
@@ -2651,6 +4061,9 @@ msgstr ""
msgid "Metrics|New metric"
msgstr ""
+msgid "Metrics|No deployed environments"
+msgstr ""
+
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
@@ -2663,9 +4076,27 @@ msgstr ""
msgid "Metrics|System"
msgstr ""
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|There was an error while retrieving metrics"
+msgstr ""
+
msgid "Metrics|Type"
msgstr ""
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Metrics|Unit label"
msgstr ""
@@ -2696,6 +4127,9 @@ msgstr ""
msgid "Milestone"
msgstr ""
+msgid "Milestones"
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -2729,6 +4163,15 @@ msgstr ""
msgid "Monitoring"
msgstr ""
+msgid "Months"
+msgstr ""
+
+msgid "More"
+msgstr ""
+
+msgid "More actions"
+msgstr ""
+
msgid "More info"
msgstr ""
@@ -2738,6 +4181,9 @@ msgstr ""
msgid "More information is available|here"
msgstr "幫助文檔"
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2747,22 +4193,61 @@ msgstr ""
msgid "Multiple issue boards"
msgstr ""
+msgid "Name"
+msgstr ""
+
msgid "Name new label"
msgstr ""
+msgid "Name your individual key via a title"
+msgstr ""
+
+msgid "Name:"
+msgstr ""
+
+msgid "Nav|Help"
+msgstr ""
+
+msgid "Nav|Home"
+msgstr ""
+
+msgid "Nav|Sign In / Register"
+msgstr ""
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr ""
+
+msgid "Network"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
+msgid "New Identity"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "新建議題"
-msgid "New Kubernetes Cluster"
-msgstr ""
-
-msgid "New Kubernetes cluster"
+msgid "New Label"
msgstr ""
msgid "New Pipeline Schedule"
msgstr "創建æµæ°´ç·šè¨ˆåŠƒ"
+msgid "New Snippet"
+msgstr ""
+
+msgid "New Snippets"
+msgstr ""
+
msgid "New branch"
msgstr "新增分支"
@@ -2781,6 +4266,9 @@ msgstr "新增文件"
msgid "New group"
msgstr ""
+msgid "New identity"
+msgstr ""
+
msgid "New issue"
msgstr "新議題"
@@ -2790,6 +4278,9 @@ msgstr ""
msgid "New merge request"
msgstr "新增åˆä½µè«‹æ±‚"
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr ""
+
msgid "New project"
msgstr ""
@@ -2805,6 +4296,12 @@ msgstr ""
msgid "New tag"
msgstr "新增標籤"
+msgid "New..."
+msgstr ""
+
+msgid "No"
+msgstr ""
+
msgid "No Label"
msgstr ""
@@ -2826,7 +4323,37 @@ msgstr ""
msgid "No file chosen"
msgstr ""
-msgid "No labels created yet."
+msgid "No files found"
+msgstr ""
+
+msgid "No files found."
+msgstr ""
+
+msgid "No issues for the selected time period."
+msgstr ""
+
+msgid "No labels with such name or description"
+msgstr ""
+
+msgid "No merge requests for the selected time period."
+msgstr ""
+
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
+msgstr ""
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
+msgstr ""
+
+msgid "No public groups"
+msgstr ""
+
+msgid "No pushes for the selected time period."
msgstr ""
msgid "No repository"
@@ -2835,6 +4362,9 @@ msgstr "沒有存儲庫"
msgid "No schedules"
msgstr "沒有計劃"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -2871,6 +4401,9 @@ msgstr ""
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
msgstr ""
+msgid "Notes|Are you sure you want to cancel creating this comment?"
+msgstr ""
+
msgid "Notification events"
msgstr "通知事件"
@@ -2958,27 +4491,72 @@ msgstr "篩é¸"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
msgstr ""
+msgid "One or more of your Bitbucket 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 "Online IDE integration settings."
msgstr ""
+msgid "Only comments from the following commit are shown below"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open"
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
msgid "Opened"
msgstr ""
+msgid "Opened MR"
+msgstr ""
+
+msgid "Opened issues"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr "開始於"
msgid "Opens in a new window"
msgstr ""
+msgid "Operations"
+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 "æ“作"
+msgid "Or you can choose one of the suggested colors below"
+msgstr ""
+
+msgid "Other Labels"
+msgstr ""
+
+msgid "Other information"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -3012,12 +4590,30 @@ msgstr ""
msgid "Password"
msgstr ""
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr ""
+
+msgid "Path:"
+msgstr ""
+
+msgid "Pause"
+msgstr ""
+
msgid "Pending"
msgstr ""
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr ""
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr ""
+
msgid "Performance optimization"
msgstr ""
+msgid "Permissions"
+msgstr ""
+
msgid "Personal Access Token"
msgstr ""
@@ -3036,6 +4632,9 @@ msgstr "æµæ°´ç·šè¨ˆåŠƒ"
msgid "Pipeline quota"
msgstr ""
+msgid "Pipeline triggers"
+msgstr ""
+
msgid "PipelineCharts|Failed:"
msgstr "失敗:"
@@ -3132,10 +4731,22 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
-msgid "Pipeline|Retry pipeline"
+msgid "Pipeline|Create for"
+msgstr ""
+
+msgid "Pipeline|Create pipeline"
msgstr ""
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
+msgid "Pipeline|Existing branch name or tag"
+msgstr ""
+
+msgid "Pipeline|Run Pipeline"
+msgstr ""
+
+msgid "Pipeline|Search branches"
+msgstr ""
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
msgstr ""
msgid "Pipeline|Stop pipeline"
@@ -3144,7 +4755,7 @@ msgstr ""
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr ""
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
+msgid "Pipeline|Variables"
msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
@@ -3162,18 +4773,42 @@ msgstr "於階段"
msgid "Pipeline|with stages"
msgstr "於階段"
+msgid "Plain diff"
+msgstr ""
+
+msgid "Planned finish date"
+msgstr ""
+
+msgid "Planned start date"
+msgstr ""
+
msgid "PlantUML"
msgstr ""
msgid "Play"
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please accept the Terms of Service before continuing."
+msgstr ""
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
+msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA"
msgstr ""
+msgid "Please try again"
+msgstr ""
+
msgid "Please wait while we connect to your repository. Refresh at will."
msgstr ""
@@ -3183,9 +4818,24 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
msgid "Primary"
msgstr ""
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3198,9 +4848,21 @@ msgstr ""
msgid "Profile"
msgstr ""
+msgid "Profile Settings"
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
+msgid "Profiles|Change username"
+msgstr ""
+
+msgid "Profiles|Current path: %{path}"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -3219,9 +4881,27 @@ msgstr ""
msgid "Profiles|Invalid username"
msgstr ""
+msgid "Profiles|Path"
+msgstr ""
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
+msgid "Profiles|Update username"
+msgstr ""
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr ""
+
+msgid "Profiles|Username successfully changed"
+msgstr ""
+
msgid "Profiles|You don't have access to delete this user."
msgstr ""
@@ -3231,6 +4911,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3240,6 +4923,12 @@ msgstr ""
msgid "Programming languages used in this repository"
msgstr ""
+msgid "Progress"
+msgstr ""
+
+msgid "Project"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -3252,6 +4941,9 @@ msgstr "é …ç›® '%{project_name}' 已創建æˆåŠŸã€‚"
msgid "Project '%{project_name}' was successfully updated."
msgstr "é …ç›® '%{project_name}' 已更新完æˆã€‚"
+msgid "Project Badges"
+msgstr ""
+
msgid "Project access must be granted explicitly to each user."
msgstr "項目訪å•æ¬Šé™å¿…須明確授權給æ¯å€‹ç”¨æˆ¶ã€‚"
@@ -3276,6 +4968,9 @@ msgstr "項目導出éˆæŽ¥å·²éŽæœŸã€‚請從項目設置中é‡æ–°ç”Ÿæˆé …目導
msgid "Project export started. A download link will be sent by email."
msgstr "項目導出已開始。下載éˆæŽ¥å°‡é€šéŽé›»å­éƒµä»¶ç™¼é€ã€‚"
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "訂閱"
@@ -3285,24 +4980,15 @@ msgstr ""
msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
-msgid "ProjectCreationLevel|Developers + Masters"
+msgid "ProjectCreationLevel|Developers + Maintainers"
msgstr ""
-msgid "ProjectCreationLevel|Masters"
+msgid "ProjectCreationLevel|Maintainers"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
-msgid "ProjectFeature|Disabled"
-msgstr "åœç”¨"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "任何人都å¯è¨ªå•"
-
-msgid "ProjectFeature|Only team members"
-msgstr "åªé™åœ˜éšŠæˆå“¡"
-
msgid "ProjectFileTree|Name"
msgstr "å稱"
@@ -3312,12 +4998,18 @@ msgstr "從未"
msgid "ProjectLifecycle|Stage"
msgstr "階段"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "分支圖"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr ""
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr ""
+
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
@@ -3336,6 +5028,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3354,7 +5049,37 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgid "PrometheusAlerts|Add alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error creating alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error deleting alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error fetching alert"
+msgstr ""
+
+msgid "PrometheusAlerts|Error saving alert"
+msgstr ""
+
+msgid "PrometheusAlerts|No alert set"
+msgstr ""
+
+msgid "PrometheusAlerts|Operator"
+msgstr ""
+
+msgid "PrometheusAlerts|Threshold"
+msgstr ""
+
+msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
@@ -3435,21 +5160,45 @@ msgstr ""
msgid "Promote"
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
+msgid "Promote to group label"
+msgstr ""
+
+msgid "Promotions|Don't show me this again"
+msgstr ""
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr ""
+
+msgid "Promotions|This feature is locked."
+msgstr ""
+
+msgid "Promotions|Upgrade plan"
+msgstr ""
+
msgid "Protip:"
msgstr ""
+msgid "Provider"
+msgstr ""
+
+msgid "Pseudonymizer data collection"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public pipelines"
+msgstr ""
+
msgid "Push Rules"
msgstr ""
@@ -3465,30 +5214,45 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Pushed"
+msgstr ""
+
+msgid "Pushes"
+msgstr ""
+
+msgid "Quarters"
+msgstr ""
+
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
msgstr "了解更多"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "自述文件"
msgid "Real-time features"
msgstr ""
-msgid "RefSwitcher|Branches"
-msgstr "分支"
-
-msgid "RefSwitcher|Tags"
-msgstr "標籤"
-
msgid "Reference:"
msgstr ""
+msgid "Refresh"
+msgstr ""
+
msgid "Register / Sign In"
msgstr ""
+msgid "Register and see your runners for this group."
+msgstr ""
+
+msgid "Register and see your runners for this project."
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -3519,36 +5283,60 @@ msgstr "ç¨å¾Œæ醒"
msgid "Remove"
msgstr ""
+msgid "Remove Runner"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr "刪除項目"
msgid "Repair authentication"
msgstr ""
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
msgid "Repo by URL"
msgstr ""
msgid "Repository"
msgstr "存儲庫"
+msgid "Repository Settings"
+msgstr ""
+
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository has no locks."
msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr ""
+
msgid "Request Access"
msgstr "申請權é™"
+msgid "Requests Profiles"
+msgstr ""
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr ""
+
msgid "Reset git storage health information"
msgstr "é‡ç½® Git 存儲的å¥åº·ä¿¡æ¯"
@@ -3558,10 +5346,28 @@ msgstr "é‡ç½®å¥åº·æª¢æŸ¥è¨ªå•ä»¤ç‰Œ"
msgid "Reset runners registration token"
msgstr "é‡ç½® Runner 註冊令牌"
+msgid "Resolve all discussions in new issue"
+msgstr ""
+
+msgid "Resolve conflicts on source branch"
+msgstr ""
+
msgid "Resolve discussion"
msgstr ""
-msgid "Response"
+msgid "Response metrics (Custom)"
+msgstr ""
+
+msgid "Resume"
+msgstr ""
+
+msgid "Retry"
+msgstr ""
+
+msgid "Retry this job"
+msgstr ""
+
+msgid "Retry verification"
msgstr ""
msgid "Reveal value"
@@ -3574,6 +5380,9 @@ msgstr "還原此æ交"
msgid "Revert this merge request"
msgstr "還原此åˆä½µè«‹æ±‚"
+msgid "Review"
+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 ""
@@ -3583,18 +5392,36 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Roadmap"
msgstr ""
msgid "Run CI/CD pipelines for external repositories"
msgstr ""
+msgid "Runner token"
+msgstr ""
+
msgid "Runners"
msgstr ""
+msgid "Runners API"
+msgstr ""
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr ""
+
msgid "Running"
msgstr ""
+msgid "SAML SSO"
+msgstr ""
+
+msgid "SAML SSO for %{group_name}"
+msgstr ""
+
msgid "SAML Single Sign On"
msgstr ""
@@ -3607,6 +5434,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
+msgid "SSL Verification"
+msgstr ""
+
+msgid "Save"
+msgstr ""
+
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3628,15 +5464,39 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr "æµæ°´ç·šè¨ˆåŠƒ"
+msgid "Scope"
+msgstr ""
+
msgid "Scoped issue boards"
msgstr ""
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
+msgid "Search branches"
+msgstr ""
+
msgid "Search branches and tags"
msgstr "æœç´¢åˆ†æ”¯å’Œæ¨™ç±¤"
+msgid "Search files"
+msgstr ""
+
+msgid "Search for projects, issues, etc."
+msgstr ""
+
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3652,15 +5512,30 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
-msgid "Secret variables"
+msgid "Secret:"
+msgstr ""
+
+msgid "Security Dashboard"
msgstr ""
msgid "Security report"
msgstr ""
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr ""
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr ""
+
+msgid "Select"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "é¸æ“‡ä¸‹è¼‰æ ¼å¼"
+msgid "Select a namespace to fork the project"
+msgstr ""
+
msgid "Select a timezone"
msgstr "é¸æ“‡æ™‚å€"
@@ -3673,9 +5548,27 @@ msgstr ""
msgid "Select branch/tag"
msgstr ""
+msgid "Select project"
+msgstr ""
+
+msgid "Select project and zone to choose machine type"
+msgstr ""
+
+msgid "Select project to choose zone"
+msgstr ""
+
+msgid "Select projects you want to import."
+msgstr ""
+
+msgid "Select source branch"
+msgstr ""
+
msgid "Select target branch"
msgstr "é¸æ“‡ç›®æ¨™åˆ†æ”¯"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Selective synchronization"
msgstr ""
@@ -3691,6 +5584,9 @@ msgstr ""
msgid "Server version"
msgstr ""
+msgid "Service Desk"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -3733,9 +5629,15 @@ msgstr ""
msgid "Setup a specific Runner automatically"
msgstr ""
+msgid "Share"
+msgstr ""
+
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr ""
+msgid "Shared Runners"
+msgstr ""
+
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
msgstr ""
@@ -3745,31 +5647,64 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr ""
+msgid "Sherlock Transactions"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
+msgid "Show latest version"
+msgstr ""
+
+msgid "Show latest version of the diff"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
msgid "Show parent subgroups"
msgstr ""
+msgid "Show whitespace changes"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "顯示 %d 個事件"
-msgid "Sidebar|Change weight"
+msgid "Side-by-side"
msgstr ""
-msgid "Sidebar|No"
+msgid "Sidebar|Change weight"
msgstr ""
msgid "Sidebar|None"
msgstr ""
+msgid "Sidebar|Only numeral characters allowed"
+msgstr ""
+
msgid "Sidebar|Weight"
msgstr ""
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
+msgid "Sign in to %{group_name}"
+msgstr ""
+
+msgid "Sign in with Single Sign-On"
+msgstr ""
+
+msgid "Sign out"
+msgstr ""
+
msgid "Sign-in restrictions"
msgstr ""
@@ -3782,6 +5717,9 @@ msgstr ""
msgid "Slack application"
msgstr ""
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr ""
+
msgid "Snippets"
msgstr ""
@@ -3791,13 +5729,19 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong on our end. Please try again!"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching Dependency Scanning."
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while fetching assignees list"
msgstr ""
-msgid "Something went wrong while fetching SAST."
+msgid "Something went wrong while fetching group member contributions"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -3806,9 +5750,18 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr ""
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
+msgid "Sorry, no epics matched your search"
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -3926,9 +5879,36 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Specific Runners"
+msgstr ""
+
msgid "Specify the following URL during the Runner setup:"
msgstr "在 Runner 設置時指定以下 URL:"
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
+msgid "Stage & Commit"
+msgstr ""
+
+msgid "Stage all changes"
+msgstr ""
+
+msgid "Stage changes"
+msgstr ""
+
+msgid "Staged"
+msgstr ""
+
+msgid "Staged %{type}"
+msgstr ""
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr "星標"
@@ -3950,33 +5930,66 @@ msgstr "é‹ä½œ Runner!"
msgid "Started"
msgstr ""
+msgid "Starts at (UTC)"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Stop impersonation"
+msgstr ""
+
+msgid "Stop this environment"
+msgstr ""
+
msgid "Stopped"
msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
+msgid "Submit as spam"
+msgstr ""
+
+msgid "Submit search"
+msgstr ""
+
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr "切æ›åˆ†æ”¯/標籤"
-msgid "System"
+msgid "Sync information"
msgstr ""
msgid "System Hooks"
msgstr ""
+msgid "System Info"
+msgstr ""
+
msgid "System header and footer:"
msgstr ""
+msgid "System metrics (Custom)"
+msgstr ""
+
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
@@ -3984,6 +5997,9 @@ msgstr[0] ""
msgid "Tags"
msgstr "標籤"
+msgid "Tags:"
+msgstr ""
+
msgid "TagsPage|Browse commits"
msgstr ""
@@ -4047,7 +6063,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4062,6 +6078,15 @@ msgstr ""
msgid "Team"
msgstr "團隊"
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr ""
+
+msgid "Terms of Service and Privacy Policy"
+msgstr ""
+
+msgid "Test coverage parsing"
+msgstr ""
+
msgid "Thanks! Don't show me this again"
msgstr ""
@@ -4101,12 +6126,15 @@ msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr ""
+
msgid "The phase of the development lifecycle."
msgstr "項目生命週期中的å„個階段。"
@@ -4125,6 +6153,9 @@ msgstr "該項目å…許已登錄的用戶訪å•ã€‚"
msgid "The project can be accessed without any authentication."
msgstr "該項目å…許任何人訪å•ã€‚"
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr "此項目的存儲庫ä¸å­˜åœ¨ã€‚"
@@ -4140,6 +6171,9 @@ msgstr "評審階段概述了從創建åˆä½µè«‹æ±‚到åˆä½µçš„時間。當創建
msgid "The roadmap shows the progress of your epics along a timeline"
msgstr ""
+msgid "The secure token used by the Runner to checkout the project"
+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 "é ç™¼å¸ƒéšŽæ®µæ¦‚述了åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²ä»£ç¢¼åˆ°ç”Ÿç”¢ç’°å¢ƒçš„總時間。當首次部署到生產環境後,數據將自動添加到此處。"
@@ -4152,27 +6186,33 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 "中ä½æ•¸æ˜¯å£¹å€‹æ•¸åˆ—中最中間的值。例如在 3ã€5ã€9 之間,中ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,中ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"
msgid "There are no issues to show"
msgstr ""
+msgid "There are no labels yet"
+msgstr ""
+
msgid "There are no merge requests to show"
msgstr ""
msgid "There are problems accessing Git storage: "
msgstr "è¨ªå• Git 存儲時出ç¾å•é¡Œï¼š"
-msgid "There was an error loading results"
-msgstr ""
-
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -4191,12 +6231,39 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "They can be managed using the %{link}."
+msgstr ""
+
+msgid "Third party offers"
+msgstr ""
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr ""
+
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
+msgid "This board's scope is reduced"
+msgstr ""
+
+msgid "This diff is collapsed."
msgstr ""
msgid "This directory"
msgstr ""
+msgid "This group"
+msgstr ""
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr ""
+
+msgid "This group does not provide any group Runners yet."
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -4218,6 +6285,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job does not have a trace."
+msgstr ""
+
+msgid "This job has been canceled"
+msgstr ""
+
+msgid "This job has been skipped"
+msgstr ""
+
msgid "This job has not been triggered yet"
msgstr ""
@@ -4236,15 +6312,30 @@ msgstr "在創建壹個空的存儲庫或導入ç¾æœ‰å­˜å„²åº«ä¹‹å‰ï¼Œæ‚¨å°‡ç„¡
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled while you still have unstaged changes"
+msgstr ""
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr ""
+msgid "This page will be removed in a future release."
+msgstr ""
+
msgid "This project"
msgstr ""
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr ""
+
msgid "This repository"
msgstr ""
+msgid "This source diff could not be displayed because it is too large."
+msgstr ""
+
+msgid "This user has no identities"
+msgstr ""
+
msgid "This will delete the custom metric, Are you sure?"
msgstr ""
@@ -4260,10 +6351,13 @@ msgstr "開始進行編碼å‰çš„時間"
msgid "Time between merge request creation and merge/close"
msgstr "從創建åˆä½µè«‹æ±‚到被åˆä½µæˆ–關閉的時間"
-msgid "Time between updates and capacity settings."
+msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgstr ""
+
+msgid "Time remaining"
msgstr ""
-msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
+msgid "Time spent"
msgstr ""
msgid "Time tracking"
@@ -4287,6 +6381,9 @@ msgstr " %s 天å‰"
msgid "Timeago|%s days remaining"
msgstr "剩餘 %s 天"
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr "剩餘 %s å°æ™‚"
@@ -4302,6 +6399,9 @@ msgstr " %s 個月å‰"
msgid "Timeago|%s months remaining"
msgstr "剩餘 %s 月"
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr "剩餘 %s 秒"
@@ -4317,48 +6417,45 @@ msgstr " %s å¹´å‰"
msgid "Timeago|%s years remaining"
msgstr "剩餘 %s 年"
+msgid "Timeago|1 day ago"
+msgstr ""
+
msgid "Timeago|1 day remaining"
msgstr "剩餘 1 天"
+msgid "Timeago|1 hour ago"
+msgstr ""
+
msgid "Timeago|1 hour remaining"
msgstr "剩餘 1 å°æ™‚"
+msgid "Timeago|1 minute ago"
+msgstr ""
+
msgid "Timeago|1 minute remaining"
msgstr "剩餘 1 分é˜"
+msgid "Timeago|1 month ago"
+msgstr ""
+
msgid "Timeago|1 month remaining"
msgstr "剩餘 1 個月"
+msgid "Timeago|1 week ago"
+msgstr ""
+
msgid "Timeago|1 week remaining"
msgstr "剩餘 1 星期"
+msgid "Timeago|1 year ago"
+msgstr ""
+
msgid "Timeago|1 year remaining"
msgstr "剩餘 1 年"
msgid "Timeago|Past due"
msgstr "逾期"
-msgid "Timeago|a day ago"
-msgstr " 1 天å‰"
-
-msgid "Timeago|a month ago"
-msgstr " 1 個月å‰"
-
-msgid "Timeago|a week ago"
-msgstr " 1 星期å‰"
-
-msgid "Timeago|a year ago"
-msgstr " 1 å¹´å‰"
-
-msgid "Timeago|about %s hours ago"
-msgstr "ç´„ %s å°æ™‚å‰"
-
-msgid "Timeago|about a minute ago"
-msgstr "ç´„ 1 分é˜å‰"
-
-msgid "Timeago|about an hour ago"
-msgstr "ç´„ 1 å°æ™‚å‰"
-
msgid "Timeago|in %s days"
msgstr " %s 天後"
@@ -4398,11 +6495,14 @@ msgstr " 1 星期後"
msgid "Timeago|in 1 year"
msgstr " 1 年後"
-msgid "Timeago|in a while"
+msgid "Timeago|just now"
+msgstr ""
+
+msgid "Timeago|right now"
msgstr ""
-msgid "Timeago|less than a minute ago"
-msgstr "ä¸åˆ° 1 分é˜å‰"
+msgid "Timeout"
+msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4424,6 +6524,9 @@ msgstr ""
msgid "To GitLab"
msgstr ""
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr ""
+
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
msgstr ""
@@ -4433,6 +6536,12 @@ msgstr ""
msgid "To connect an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4442,21 +6551,45 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr ""
+
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
msgstr ""
+msgid "To widen your search, change or remove filters."
+msgstr ""
+
msgid "Todo"
msgstr ""
+msgid "Todos"
+msgstr ""
+
+msgid "Toggle Sidebar"
+msgstr ""
+
+msgid "Toggle discussion"
+msgstr ""
+
+msgid "Toggle navigation"
+msgstr ""
+
msgid "Toggle sidebar"
msgstr ""
@@ -4466,6 +6599,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Too many changes to show."
+msgstr ""
+
+msgid "Total Contributions"
+msgstr ""
+
msgid "Total Time"
msgstr "總時間"
@@ -4484,12 +6623,30 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr ""
+
+msgid "Try again"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Twitter"
+msgstr ""
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr ""
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr ""
+
msgid "Unknown"
msgstr ""
@@ -4502,12 +6659,45 @@ msgstr ""
msgid "Unresolve discussion"
msgstr ""
+msgid "Unstage all changes"
+msgstr ""
+
+msgid "Unstage changes"
+msgstr ""
+
+msgid "Unstaged"
+msgstr ""
+
+msgid "Unstaged %{type}"
+msgstr ""
+
+msgid "Unstaged and staged %{type}"
+msgstr ""
+
msgid "Unstar"
msgstr "å–消星標"
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
+msgid "Unverified"
+msgstr ""
+
msgid "Up to date"
msgstr ""
+msgid "Update"
+msgstr ""
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr ""
+
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -4523,6 +6713,9 @@ msgstr ""
msgid "Upgrade your plan to improve Issue boards."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr "上傳新文件"
@@ -4541,9 +6734,18 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr ""
+
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr "在安è£éŽç¨‹ä¸­ä½¿ç”¨ä»¥ä¸‹è¨»å†Šä»¤ç‰Œï¼š"
@@ -4553,9 +6755,21 @@ msgstr "使用全局通知設置"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
+msgid "User Settings"
+msgstr ""
+
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
+msgid "Users"
+msgstr ""
+
+msgid "Variables"
+msgstr ""
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4568,7 +6782,10 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "View and edit lines"
+msgid "Verification information"
+msgstr ""
+
+msgid "Verified"
msgstr ""
msgid "View epics list"
@@ -4580,9 +6797,21 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View issue"
+msgstr ""
+
+msgid "View it on GitLab"
+msgstr ""
+
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr "查看開啟的åˆä¸¦è«‹æ±‚"
@@ -4595,6 +6824,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr "內部"
@@ -4610,7 +6845,7 @@ msgstr "未知"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "權é™ä¸è¶³ã€‚如需查看相關數據,請å‘管ç†å“¡ç”³è«‹æ¬Šé™ã€‚"
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
@@ -4628,10 +6863,22 @@ msgstr ""
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr ""
+msgid "Weeks"
+msgstr ""
+
msgid "Weight"
msgstr ""
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
+msgid "Weight %{weight}"
+msgstr ""
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr ""
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr ""
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
msgid "Wiki"
@@ -4658,7 +6905,31 @@ msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr ""
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr ""
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr ""
+
+msgid "WikiEmpty|Create your first page"
+msgstr ""
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr ""
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr ""
+
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr ""
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
msgstr ""
msgid "WikiHistoricalPage|This is an old version of this page."
@@ -4694,6 +6965,12 @@ msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr ""
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr ""
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -4709,7 +6986,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4721,9 +6998,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
-msgid "Wiki|Empty page"
-msgstr ""
-
msgid "Wiki|More Pages"
msgstr ""
@@ -4748,7 +7022,16 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "å–消權é™ç”³è¯·"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr ""
+
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
@@ -4766,7 +7049,10 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr ""
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr ""
msgid "You can also create a project from the command line."
@@ -4775,6 +7061,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4787,22 +7079,40 @@ msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šæ·»åŠ æ–‡ä»¶"
msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "您已é”到項目數é‡é™åˆ¶"
-msgid "You must have master access to force delete a lock"
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
+msgid "You must have maintainer access to force delete a lock"
msgstr ""
msgid "You must sign in to star a project"
@@ -4811,6 +7121,9 @@ msgstr "必須登錄æ‰èƒ½å°é …目加星標"
msgid "You need a different license to enable FileLocks feature"
msgstr ""
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr ""
+
msgid "You need permission."
msgstr "需è¦ç›¸é—œçš„權é™ã€‚"
@@ -4841,9 +7154,18 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
msgstr ""
+msgid "YouTube"
+msgstr ""
+
msgid "Your Groups"
msgstr ""
@@ -4859,6 +7181,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -4877,10 +7205,13 @@ msgstr "您的åå­—"
msgid "Your projects"
msgstr ""
+msgid "ago"
+msgstr ""
+
msgid "among other things"
msgstr ""
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
msgstr[0] ""
@@ -4893,45 +7224,138 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr ""
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr ""
+
+msgid "ciReport|%{packagesString} and "
+msgstr ""
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr ""
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr ""
+
+msgid "ciReport|%{reportName} is loading"
+msgstr ""
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr ""
+
msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr ""
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr ""
+
+msgid "ciReport|Class"
+msgstr ""
+
msgid "ciReport|Code quality"
msgstr ""
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgid "ciReport|Confidence"
+msgstr ""
+
+msgid "ciReport|Container scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning"
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr ""
+
+msgid "ciReport|Container scanning is loading"
+msgstr ""
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|DAST detected"
+msgstr ""
+
+msgid "ciReport|DAST is loading"
+msgstr ""
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr ""
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr ""
msgid "ciReport|Dependency scanning detected"
msgstr ""
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
+msgid "ciReport|Dependency scanning is loading"
+msgstr ""
+
+msgid "ciReport|Dependency scanning resulted in error while loading results"
msgstr ""
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
+msgid "ciReport|Description"
+msgstr ""
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr ""
+
+msgid "ciReport|Dismissed by"
+msgstr ""
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr ""
msgid "ciReport|Failed to load %{reportName} report"
msgstr ""
+msgid "ciReport|File"
+msgstr ""
+
msgid "ciReport|Fixed:"
msgstr ""
+msgid "ciReport|Identifiers"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr ""
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr ""
+
msgid "ciReport|Learn more about whitelisting"
msgstr ""
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr ""
+
+msgid "ciReport|License management detected no new licenses"
+msgstr ""
+
+msgid "ciReport|Links"
+msgstr ""
+
msgid "ciReport|Loading %{reportName} report"
msgstr ""
+msgid "ciReport|Method"
+msgstr ""
+
+msgid "ciReport|Namespace"
+msgstr ""
+
msgid "ciReport|No changes to code quality"
msgstr ""
@@ -4941,19 +7365,16 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|SAST"
+msgid "ciReport|Revert dismissal"
msgstr ""
msgid "ciReport|SAST detected"
msgstr ""
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST detected no security vulnerabilities"
+msgid "ciReport|SAST is loading"
msgstr ""
-msgid "ciReport|SAST:container no vulnerabilities were found"
+msgid "ciReport|SAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Security scanning"
@@ -4962,15 +7383,54 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
+msgid "ciReport|Security scanning is loading"
+msgstr ""
+
+msgid "ciReport|Severity"
+msgstr ""
+
+msgid "ciReport|Solution"
+msgstr ""
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr ""
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr ""
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading SAST report"
+msgstr ""
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr ""
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr ""
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
msgstr ""
+msgid "ciReport|on pipeline"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
@@ -4980,10 +7440,16 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "天"
+msgid "deploy token"
+msgstr ""
+
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] ""
@@ -4995,16 +7461,28 @@ msgstr[0] ""
msgid "detected no vulnerabilities"
msgstr ""
+msgid "disabled"
+msgstr ""
+
+msgid "done"
+msgstr ""
+
+msgid "enabled"
+msgstr ""
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "for this project"
+msgstr ""
+
msgid "here"
msgstr ""
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
+msgid "importing"
msgstr ""
msgid "is invalid because there is downstream lock"
@@ -5016,6 +7494,9 @@ msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""
+msgid "latest version"
+msgstr ""
+
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
@@ -5038,24 +7519,18 @@ msgstr ""
msgid "mrWidget|Add approval"
msgstr ""
-msgid "mrWidget|Allows edits from maintainers"
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr ""
msgid "mrWidget|An error occured while removing your approval."
msgstr ""
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
msgstr ""
-msgid "mrWidget|Approved"
-msgstr ""
-
msgid "mrWidget|Approved by"
msgstr ""
@@ -5083,6 +7558,9 @@ msgstr ""
msgid "mrWidget|Closes"
msgstr ""
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr ""
+
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
@@ -5116,9 +7594,24 @@ msgstr ""
msgid "mrWidget|Merge locally"
msgstr ""
+msgid "mrWidget|Merge request approved"
+msgstr ""
+
+msgid "mrWidget|Merge request approved; you can approve additionally"
+msgstr ""
+
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|No Approval required"
+msgstr ""
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr ""
+
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5179,6 +7672,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts"
msgstr ""
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr ""
+
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr ""
@@ -5188,9 +7684,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5231,15 +7724,24 @@ msgstr ""
msgid "private key does not match certificate."
msgstr ""
+msgid "remaining"
+msgstr ""
+
msgid "remove due date"
msgstr ""
+msgid "remove weight"
+msgstr ""
+
msgid "source"
msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
@@ -5252,6 +7754,13 @@ msgstr ""
msgid "uses Kubernetes clusters to deploy your code!"
msgstr ""
+msgid "view it on GitLab"
+msgstr ""
+
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index 553050d06a1..0ca496b0395 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -2,8 +2,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 19:35+0200\n"
-"PO-Revision-Date: 2018-04-05 03:39-0400\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Chinese Traditional\n"
"Language: zh_TW\n"
@@ -15,13 +13,26 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: zh-TW\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+"PO-Revision-Date: 2018-08-01 11:41\n"
msgid " and"
msgstr " 和"
+msgid " degraded on %d point"
+msgid_plural " degraded on %d points"
+msgstr[0] " 在 %d 點上é™ç´š"
+
+msgid " improved on %d point"
+msgid_plural " improved on %d points"
+msgstr[0] " 在 %d 點上改善"
+
+msgid "%d changed file"
+msgid_plural "%d changed files"
+msgstr[0] "%d 個變更的檔案"
+
msgid "%d commit"
msgid_plural "%d commits"
-msgstr[0] "%d 個更動 (commit)"
+msgstr[0] "%d 個更動"
msgid "%d commit behind"
msgid_plural "%d commits behind"
@@ -47,28 +58,59 @@ msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] "%d 指標"
+msgid "%d new license"
+msgid_plural "%d new licenses"
+msgstr[0] "%d 個新授權"
+
+msgid "%d staged change"
+msgid_plural "%d staged changes"
+msgstr[0] "%d 個暫存變更"
+
+msgid "%d unstaged change"
+msgid_plural "%d unstaged changes"
+msgstr[0] "%d 個未暫存變更"
+
+msgid "%d vulnerability"
+msgid_plural "%d vulnerabilities"
+msgstr[0] "%d 個æ¼æ´ž"
+
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] "因效能考é‡ï¼Œå·²éš±è— %s 個更動 (commit)。"
msgid "%{actionText} & %{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{actionText} 和 %{openOrClose} %{noteable}"
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr "ç”± %{commit_author_link} æ交於 %{commit_timeago}"
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr "%{counter_storage} (%{counter_repositories} 個版本庫ã€%{counter_build_artifacts} 個編譯產物ã€å’Œ %{counter_lfs_objects} 個 LFS)"
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} åƒèˆ‡è€…"
+msgid "%{filePath} deleted"
+msgstr "已刪除 %{filePath}"
+
+msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
+msgstr "%{group_docs_link_start}群組%{group_docs_link_end} å…許您管ç†ã€å”作多個專案。群組的æˆå“¡å¯ä»¥è¨ªå•ç¾¤çµ„下的所有專案。"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} 開始"
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr "%{lock_path} 被使用者 %{lock_user_id} 鎖定"
+msgid "%{name}'s avatar"
+msgstr "%{name} çš„é ­è²¼"
+
+msgid "%{nip_domain} can be used as an alternative to a custom domain."
+msgstr "%{nip_domain} å¯ä»¥ä½¿ç”¨ç‚ºè‡ªè¨‚網域的替代方案。"
+
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
-msgstr ""
+msgstr "%{number_commits_behind} 個è½å¾Œ %{default_branch} 分支的修訂版æ交,%{number_commits_ahead} 個超å‰çš„修訂版更動"
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "ç›®å‰å·²å¤±æ•— %{number_of_failures} 次。GitLab å…許在 %{maximum_failures} 次之內å¯å†å˜—è©¦è®€å– ã€‚"
@@ -79,22 +121,70 @@ msgstr "已失敗 %{number_of_failures} / %{maximum_failures} 次,GitLab å°‡ä¸
msgid "%{openOrClose} %{noteable}"
msgstr "%{openOrClose} %{noteable}"
+msgid "%{percent}%% complete"
+msgstr "%{percent}%% 完æˆ"
+
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
msgstr[0] "%{storage_name}:已存å–此主機失敗 %{failed_attempts} 次"
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] "%{text} %{files} 檔案"
+
msgid "%{text} is available"
msgstr "%{text} å¯ç”¨"
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr "(如何安è£è«‹åƒé–± %{link})"
+msgid "%{title} changes"
+msgstr "%{title} 變更"
+
+msgid "%{type} detected 1 vulnerability"
+msgid_plural "%{type} detected %{vulnerabilityCount} vulnerabilities"
+msgstr[0] "%{type} åµæ¸¬åˆ° %{vulnerabilityCount} 個æ¼æ´ž"
+
+msgid "%{unstaged} unstaged and %{staged} staged changes"
+msgstr "%{unstaged} 個未暫存和 %{staged} 個已暫存變更"
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} 更多"
+msgid "- Runner is active and can process any new jobs"
+msgstr "- Runner 為啟用狀態,並且å¯ä»¥è™•ç†ä»»ä½•æ–°çš„工作。"
+
+msgid "- Runner is paused and will not receive any new jobs"
+msgstr "- Runner 為暫åœç‹€æ…‹ï¼Œä¸”å°‡ä¸æœƒæŽ¥å—任何新的工作"
+
msgid "- show less"
msgstr "顯示較少"
+msgid "1 %{type} addition"
+msgid_plural "%{count} %{type} additions"
+msgstr[0] "%{count}%{type} 個附加"
+
+msgid "1 %{type} modification"
+msgid_plural "%{count} %{type} modifications"
+msgstr[0] "%{count}%{type} 個變更"
+
+msgid "1 closed issue"
+msgid_plural "%d closed issues"
+msgstr[0] "%d 個關閉的議題"
+
+msgid "1 closed merge request"
+msgid_plural "%d closed merge requests"
+msgstr[0] "%d 個關閉的åˆä½µè«‹æ±‚"
+
+msgid "1 merged merge request"
+msgid_plural "%d merged merge requests"
+msgstr[0] "%d 個已åˆä½µçš„åˆä½µè«‹æ±‚"
+
+msgid "1 open issue"
+msgid_plural "%d open issues"
+msgstr[0] "%d 個進行中的議題"
+
+msgid "1 open merge request"
+msgid_plural "%d open merge requests"
+msgstr[0] "%d 個進行中的åˆä½µè«‹æ±‚"
+
msgid "1 pipeline"
msgid_plural "%d pipelines"
msgstr[0] "%d æ¢æµæ°´ç·š"
@@ -105,11 +195,53 @@ msgstr "第一次å”作"
msgid "2FA enabled"
msgstr "已啟用雙é‡èªè­‰"
+msgid "403|Please contact your GitLab administrator to get the permission."
+msgstr "403 |è«‹è¯ç¹«æ‚¨çš„GitLab管ç†å“¡ä»¥ç²å–權é™ã€‚"
+
+msgid "403|You don't have the permission to access this page."
+msgstr "您無權使用這個é é¢ã€‚"
+
+msgid "404|Make sure the address is correct and the page hasn't moved."
+msgstr "404 |請確ä¿ç¶²å€æ­£ç¢ºä¸”網é ä½ç½®æ²’有被更改。"
+
+msgid "404|Page Not Found"
+msgstr "無法找到網é "
+
+msgid "404|Please contact your GitLab administrator if you think this is a mistake."
+msgstr "404 |如果您èªç‚ºé€™æ˜¯éŒ¯èª¤ï¼Œè«‹è¯ç¹«æ‚¨çš„GitLab管ç†å“¡ã€‚"
+
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> 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>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" 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 "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
+msgstr "建立 <strong>%{created_count}</strong> 個,åŒæ„ <strong>%{accepted_count}</strong> 個。"
+
+msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
+msgstr "建立 <strong>%{created_count}</strong> 個,關閉 <strong>%{closed_count}</strong> 個。"
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr "<strong>%{group_name}</strong> 群組æˆå“¡"
+
+msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
+msgstr "æŽ¨é€ <strong>%{pushes}</strong> 個,<strong>%{people}</strong> 個貢ç»è€…æäº¤äº†è¶…éŽ <strong>%{commits}</strong> 個æ交。"
+
msgid "<strong>Removes</strong> source branch"
msgstr "<strong>刪除</strong>來æºåˆ†æ”¯"
+msgid "A 'Runner' is a process which runs a job. You can setup as many Runners as you need."
+msgstr "一個「執行器ã€æ˜¯ä¸€å€‹åŸ·è¡Œå·¥ä½œçš„進程。你å¯ä»¥å®‰è£ä½ éœ€è¦çš„多個執行器。"
+
msgid "A collection of graphs regarding Continuous Integration"
-msgstr "æŒçºŒæ•´åˆ (CI) 相關的圖表"
+msgstr "æŒçºŒæ•´åˆç›¸é—œçš„圖表"
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "將會å†å‰µå»ºä¸€å€‹æ–°çš„分支,並建立一個新的åˆä½µè«‹æ±‚。"
@@ -117,38 +249,68 @@ 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 "一個專案æ供了以下功能,存放你的文件(存儲庫),計劃你的工作(è­°é¡Œ),並發布你的文件(維基), %{among_other_things_link}。"
+msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
+msgstr "正則表é”å¼ï¼Œå°‡ç”¨æ–¼åœ¨ä½œæ¥­è·Ÿè¸ªä¸­æŸ¥æ‰¾æ¸¬è©¦è¦†è“‹çŽ‡çš„輸出。留空時åœç”¨æ­¤åŠŸèƒ½"
+
msgid "A user with write access to the source branch selected this option"
msgstr "一個有存å–原始分支權é™çš„使用者,é¸æ“‡äº†æ­¤é …ç›®"
+msgid "About GitLab"
+msgstr "關於 GitLab"
+
+msgid "About GitLab CE"
+msgstr "關於 GitLab CE"
+
msgid "About auto deploy"
msgstr "關於自動部署"
+msgid "About this feature"
+msgstr "關於此功能"
+
msgid "Abuse Reports"
msgstr "濫用報告"
msgid "Abuse reports"
-msgstr ""
+msgstr "濫用報告"
+
+msgid "Accept terms"
+msgstr "接å—æ¢æ¬¾"
+
+msgid "Accepted MR"
+msgstr "接å—çš„åˆä½µè«‹æ±‚"
msgid "Access Tokens"
-msgstr "å­˜å–憑證 (access token)"
+msgstr "å­˜å–憑證"
+
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr "å­˜å–被拒ï¼è«‹é©—證您是å¦å¯ä»¥åœ¨æ­¤ç‰ˆæœ¬åº«éƒ¨å±¬é‡‘鑰。"
+
+msgid "Access to '%{classification_label}' not allowed"
+msgstr "ä¸å…許存å–「%{classification_label}ã€"
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 "已暫時åœç”¨å¤±æ•—çš„ Git 儲存空間。當儲存空間æ¢å¾©æ­£å¸¸å¾Œï¼Œè«‹é‡ç½®å„²å­˜ç©ºé–“å¥åº·æŒ‡æ•¸ã€‚"
+msgid "Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report."
+msgstr "å­˜å–您執行器憑證,自定義您的æµæ°´ç·šé…置,並查看你的æµæ°´ç¾ç‹€æ…‹åŠæ¸¬è©¦æ¶µè“‹çŽ‡å ±å‘Šã€‚"
+
msgid "Account"
msgstr "帳號"
-msgid "Account and limit settings"
-msgstr ""
+msgid "Account and limit"
+msgstr "帳戶和é™åˆ¶"
msgid "Active"
msgstr "啟用"
+msgid "Active Sessions"
+msgstr "連線中的è£ç½®"
+
msgid "Activity"
msgstr "活動"
msgid "Add"
-msgstr "增加"
+msgstr "新增"
msgid "Add Changelog"
msgstr "新增更新日誌"
@@ -157,7 +319,7 @@ msgid "Add Contribution guide"
msgstr "新增å”作指å—"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr "啟用群組 Webhooks åŠ GitLab ä¼æ¥­ç‰ˆã€‚"
+msgstr "新增群組 Webhook å’Œ GitLab ä¼æ¥­ç‰ˆæœ¬ã€‚"
msgid "Add Kubernetes cluster"
msgstr "增加 Kubernetes å¢é›†"
@@ -168,12 +330,39 @@ msgstr "新增授權æ¢æ¬¾"
msgid "Add Readme"
msgstr "增加說明檔案"
+msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr "增加顯示於所有電å­éƒµä»¶å°è©±çš„附加文字。最多 %{character_limit} 個字。"
+
+msgid "Add new application"
+msgstr "建立新應用程å¼"
+
msgid "Add new directory"
msgstr "新增目錄"
+msgid "Add reaction"
+msgstr "添加回應"
+
msgid "Add todo"
msgstr "增加待辦事項"
+msgid "Add user(s) to the group:"
+msgstr "加入使用者至群組:"
+
+msgid "Add users to group"
+msgstr "加入使用者到群組"
+
+msgid "Additional text"
+msgstr "附加文字"
+
+msgid "Admin Area"
+msgstr "管ç†å€å¡Š"
+
+msgid "Admin Overview"
+msgstr "管ç†å“¡æ¦‚覽"
+
+msgid "Admin area"
+msgstr "管ç†å€å¡Š"
+
msgid "AdminArea|Stop all jobs"
msgstr "åœæ­¢æ‰€æœ‰ä»»å‹™"
@@ -202,7 +391,7 @@ msgid "AdminProjects|Delete project"
msgstr "刪除專案"
msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
-msgstr ""
+msgstr "為æ¯å€‹å°ˆæ¡ˆçš„自動複閱應用åŠè‡ªå‹•éƒ¨ç½²æŒ‡å®šä¸€å€‹é è¨­çš„網域"
msgid "AdminUsers|Block user"
msgstr "å°éŽ–使用者"
@@ -226,7 +415,7 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr "請輸入 %{username} 以進行確èª"
msgid "Advanced"
-msgstr "進階設置"
+msgstr "進階"
msgid "Advanced settings"
msgstr "進階設定"
@@ -240,30 +429,75 @@ msgstr "所有改變都已經æ交"
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr "從模æ¿å»ºç«‹æˆ–導入專案時將啟用所有功能,您å¯ä»¥åœ¨å°ˆæ¡ˆè¨­ç½®ä¸­å°‡å…¶é—œé–‰ã€‚"
-msgid "Allow edits from maintainers."
-msgstr "å…許維護人員編輯。"
+msgid "Allow commits from members who can merge to the target branch."
+msgstr "å…許å¯ä»¥åˆä½µè‡³ç›®æ¨™åˆ†æ”¯çš„æˆå“¡æ交"
+
+msgid "Allow public access to pipelines and job details, including output logs and artifacts"
+msgstr "å…許公開存å–æµæ°´ç·šå’Œä»»å‹™è©³ç´°è³‡è¨Šï¼ŒåŒ…å«è¼¸å‡ºæ—¥èªŒå’Œç”¢ç‰©"
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
-msgstr ""
+msgstr "å…許在 Asciidoc 文件中渲染 PlantUML64"
msgid "Allow requests to the local network from hooks and services."
-msgstr ""
+msgstr "å…許來自鉤å­å’Œæœå‹™çš„å°æœ¬åœ°ç¶²çµ¡çš„請求。"
msgid "Allows you to add and manage Kubernetes clusters."
msgstr "å…許您增加和管ç†Kuberneteså¢é›†ã€‚"
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
-msgstr ""
+msgstr "也被稱為「議題æ“有者ã€æˆ–「ä¾è³´æ–¹ä¿¡ä»»æ¨™è­˜æ¨™ç±¤ã€"
msgid "Also called \"Relying party service URL\" or \"Reply URL\""
-msgstr ""
+msgstr "也稱為「ä¾è³´æ–¹æœå‹™ URLã€æˆ–「回覆 URLã€"
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr "或者,你å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。當你建立你的個人存å–權æ–時,你將需è¦é¸æ“‡<code>檔案庫</code>範åœï¼Œæ‰€ä»¥æˆ‘們將會顯示你公開åŠç§äººçš„檔案庫清單進行連çµã€‚"
+msgstr "或者,你å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。當你建立你的個人存å–憑證時,你將需è¦é¸æ“‡<code>版本庫</code>範åœï¼Œæ‰€ä»¥æˆ‘們å¯ä»¥é¡¯ç¤ºä½ å¯ä»¥é€£ç·šä¹‹å…¬é–‹åŠç§å¯†çš„版本庫清單。"
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr "或者,你å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。當你建立你的個人存å–權æ–時,你將需è¦é¸æ“‡<code>檔案庫</code>範åœï¼Œæ‰€ä»¥æˆ‘們將會顯示你公開åŠç§äººçš„檔案庫清單進行匯入。"
+
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr "一個自稱 %{link_to_client} 的應用程å¼è«‹æ±‚您 GitLab 帳號的存å–權。"
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
+msgid "An error accured whilst committing your changes."
+msgstr "æ交您的變更時發生錯誤"
+
+msgid "An error has occurred"
+msgstr "發生了一個錯誤"
+
+msgid "An error occured creating the new branch."
+msgstr "創建新分支時發生錯誤。"
+
+msgid "An error occured whilst fetching the job trace."
+msgstr "å–得工作追蹤資訊時發生錯誤"
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr "å–得最新æµæ°´ç·šæ™‚發生錯誤"
+
+msgid "An error occured whilst loading all the files."
+msgstr "讀å–檔案時發生錯誤。"
+
+msgid "An error occured whilst loading the file content."
+msgstr "讀å–檔案內容時發生錯誤。"
+
+msgid "An error occured whilst loading the file."
+msgstr "讀å–檔案時發生錯誤。"
+
+msgid "An error occured whilst loading the merge request changes."
+msgstr "讀å–åˆä½µè«‹æ±‚的更改時發生錯誤。"
+
+msgid "An error occured whilst loading the merge request version data."
+msgstr "讀å–åˆä½µè«‹æ±‚的版本資訊時發生錯誤。"
+
+msgid "An error occured whilst loading the merge request."
+msgstr "讀å–åˆä½µè«‹æ±‚時發生錯誤。"
+
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr "載入æµæ°´ç·šå·¥ä½œçš„åŒæ™‚發生了錯誤"
+
msgid "An error occurred previewing the blob"
msgstr "é è¦½ blob 檔案時發生錯誤"
@@ -271,83 +505,110 @@ msgid "An error occurred when toggling the notification subscription"
msgstr "切æ›è¨‚閱通知時發生錯誤"
msgid "An error occurred when updating the issue weight"
-msgstr ""
+msgstr "æ›´æ–°å•é¡Œæ¬Šé‡æ™‚發生錯誤"
msgid "An error occurred while adding approver"
-msgstr ""
+msgstr "增加審核者時發生錯誤"
msgid "An error occurred while detecting host keys"
-msgstr ""
+msgstr "åµæ¸¬ä¸»æ©Ÿé‡‘鑰時發生錯誤"
+
+msgid "An error occurred while dismissing the alert. Refresh the page and try again."
+msgstr "當解除通知時錯誤發生。請嘗試é‡æ–°æ•´ç†é é¢ä¸¦é‡è©¦ã€‚"
msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
msgstr "解除亮高顯示時發生錯誤,請é‡æ–°æ•´ç†é é¢å†æ¬¡å˜—試。"
msgid "An error occurred while fetching markdown preview"
-msgstr ""
+msgstr "è®€å– markdown é è¦½æ™‚發生錯誤"
msgid "An error occurred while fetching sidebar data"
-msgstr ""
+msgstr "讀å–å´é‚Šæ¬„資料時發生錯誤"
msgid "An error occurred while fetching the pipeline."
-msgstr ""
+msgstr "讀å–æµæ°´ç·šæ™‚發生錯誤"
msgid "An error occurred while getting projects"
-msgstr ""
+msgstr "讀å–專案時發生錯誤"
-msgid "An error occurred while importing project"
-msgstr ""
+msgid "An error occurred while importing project: ${details}"
+msgstr "匯入專案時發生錯誤: ${details}"
msgid "An error occurred while initializing path locks"
-msgstr ""
+msgstr "åˆå§‹åŒ–路徑鎖時發生錯誤"
-msgid "An error occurred while loading commits"
-msgstr ""
+msgid "An error occurred while loading commit signatures"
+msgstr "載入æ交簽å時發生錯誤"
msgid "An error occurred while loading diff"
-msgstr ""
+msgstr "讀å–差異檔時發生錯誤"
msgid "An error occurred while loading filenames"
-msgstr ""
+msgstr "讀å–檔案å稱時發生錯誤"
msgid "An error occurred while loading the file"
-msgstr ""
+msgstr "讀å–檔案時發生錯誤"
msgid "An error occurred while making the request."
-msgstr ""
+msgstr "建立請求時發生錯誤"
msgid "An error occurred while removing approver"
-msgstr ""
+msgstr "刪除審核者時發生錯誤"
msgid "An error occurred while rendering KaTeX"
-msgstr ""
+msgstr "渲染 KaTex 時發生錯誤"
msgid "An error occurred while rendering preview broadcast message"
-msgstr ""
+msgstr "在產生廣播消æ¯æ™‚發生錯誤"
msgid "An error occurred while retrieving calendar activity"
-msgstr ""
+msgstr "讀å–行事曆時發生錯誤"
msgid "An error occurred while retrieving diff"
-msgstr ""
+msgstr "讀å–差異檔時發生錯誤"
msgid "An error occurred while saving LDAP override status. Please try again."
-msgstr ""
+msgstr "儲存 LDAP 覆蓋狀態時發生錯誤,請é‡è©¦ã€‚"
msgid "An error occurred while saving assignees"
msgstr "儲存被指派人時發生錯誤"
+msgid "An error occurred while subscribing to notifications."
+msgstr "訂閱通知時發生錯誤。"
+
+msgid "An error occurred while unsubscribing to notifications."
+msgstr "當å–消訂閱通知時發生錯誤"
+
msgid "An error occurred while validating username"
-msgstr ""
+msgstr "驗證使用者å稱時發生錯誤"
msgid "An error occurred. Please try again."
msgstr "發生錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
+msgid "Anonymous"
+msgstr "匿å"
+
+msgid "Anti-spam verification"
+msgstr "防廣告驗證"
+
+msgid "Any"
+msgstr "任何"
+
msgid "Any Label"
msgstr "ä»»æ„標籤"
msgid "Appearance"
msgstr "外觀"
+msgid "Application"
+msgstr "應用程å¼"
+
+msgid "Application Id"
+msgstr "æ‡‰ç”¨ç¨‹å¼ ID"
+
+msgid "Application: %{name}"
+msgstr "應用程å¼ï¼š%{name}"
+
msgid "Applications"
msgstr "應用程å¼"
@@ -357,20 +618,29 @@ msgstr "四月"
msgid "April"
msgstr "四月"
-msgid "Archived project! Repository is read-only"
-msgstr "此專案已å°å­˜ï¼æª”案庫 (repository) 為唯讀狀態"
+msgid "Archived project! Repository and other project resources are read-only"
+msgstr "這是個已經å°å­˜çš„專案ï¼å…¶æª”案庫與其其他專案資æºç‚ºå”¯è®€ç‹€æ…‹ã€‚"
msgid "Are you sure you want to delete this pipeline schedule?"
-msgstr "確定è¦åˆªé™¤æ­¤æµæ°´ç·š (pipeline) 排程嗎?"
+msgstr "確定è¦åˆªé™¤æ­¤æµæ°´ç·šæŽ’程嗎?"
+
+msgid "Are you sure you want to lose unsaved changes?"
+msgstr ""
+
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr "確定移除 %{group_name}?"
+
+msgid "Are you sure you want to remove this identity?"
+msgstr "您確定è¦åˆªé™¤æ­¤èº«ä»½å—Ž?"
msgid "Are you sure you want to reset registration token?"
-msgstr "確定è¦é‡ç½®è¨»å†Šæ†‘è­‰ (registration token) 嗎?"
+msgstr "確定è¦é‡ç½®è¨»å†Šæ†‘證嗎?"
msgid "Are you sure you want to reset the health check token?"
-msgstr "確定è¦é‡ç½®å¥åº·æª¢æŸ¥å­˜å–憑證 (access token) 嗎?"
+msgstr "確定è¦é‡ç½®å¥åº·æª¢æŸ¥å­˜å–憑證嗎?"
msgid "Are you sure you want to unlock %{path_lock_path}?"
-msgstr ""
+msgstr "你確定è¦è§£éŽ– %{path_lock_path} ?"
msgid "Are you sure?"
msgstr "確定嗎?"
@@ -378,8 +648,14 @@ msgstr "確定嗎?"
msgid "Artifacts"
msgstr "產物"
+msgid "Ascending"
+msgstr "é †åº"
+
+msgid "Ask your group maintainer to setup a group Runner."
+msgstr "è©¢å•æ‚¨çš„群組維護者以安è£ç¾¤çµ„執行器。"
+
msgid "Assertion consumer service URL"
-msgstr ""
+msgstr "Assertion 客戶æœå‹™é€£çµ"
msgid "Assign custom color like #FF0000"
msgstr "自定義é¡è‰²ï¼Œä¾‹å¦‚ #FF0000"
@@ -394,20 +670,35 @@ msgid "Assign to"
msgstr "指派給"
msgid "Assigned Issues"
-msgstr ""
+msgstr "已指派的議題"
msgid "Assigned Merge Requests"
-msgstr ""
+msgstr "已指派的åˆä½µè«‹æ±‚"
msgid "Assigned to :name"
-msgstr ""
+msgstr "指派給:人å"
+
+msgid "Assigned to me"
+msgstr "指派給我"
msgid "Assignee"
msgstr "指派人"
+msgid "Assignee boards not available with your current license"
+msgstr "您目å‰çš„授權並ä¸æ”¯æ´æŒ‡æ´¾çœ‹æ¿"
+
+msgid "Assignee lists show all issues assigned to the selected user."
+msgstr "指派列表顯示了分é…給é¸å®šä½¿ç”¨è€…的所有議題"
+
+msgid "Assignee(s)"
+msgstr "執行者"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "拖放檔案到此處或者 %{upload_link}"
+msgid "Audit Events"
+msgstr "審計活動"
+
msgid "Aug"
msgstr "八月"
@@ -417,29 +708,56 @@ msgstr "八月"
msgid "Authentication Log"
msgstr "登入紀錄"
+msgid "Authentication log"
+msgstr "èªè­‰è¨˜éŒ„"
+
msgid "Author"
msgstr "作者"
+msgid "Authorization code:"
+msgstr "授權碼:"
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr "é€éŽåœ¨æ‡‰ç”¨ç¨‹å¼ä¸­è¼¸å…¥æ‚¨çš„使用者帳號與密碼,以給予授權權é™ã€‚"
+
+msgid "Authorize"
+msgstr "授權"
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr "授權 %{link_to_client} 使用您的帳號?"
+
+msgid "Authorized At"
+msgstr "授權於"
+
+msgid "Authorized applications (%{size})"
+msgstr "æŽˆæ¬Šçš„æ‡‰ç”¨ç¨‹å¼ (%{size})"
+
msgid "Authors: %{authors}"
msgstr "作者:%{authors}"
+msgid "Auto DevOps"
+msgstr "自動 DevOps"
+
msgid "Auto DevOps enabled"
msgstr "啟動自動 DevOps"
msgid "Auto DevOps, runners and job artifacts"
-msgstr ""
+msgstr "自動 DevOpsã€åŸ·è¡Œå™¨èˆ‡å·¥ä½œç”¢å‡ºç‰©"
msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
-msgstr ""
+msgstr "自動複閱應用åŠè‡ªå‹•éƒ¨ç½²éœ€è¦ %{kubernetes} æ‰èƒ½æ­£å¸¸å·¥ä½œã€‚"
msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
-msgstr ""
+msgstr "自動復閱åŠè‡ªå‹•éƒ¨ç½²éœ€è¦ä¸€å€‹ç¶²åŸŸå’Œ %{kubernetes} æ‰èƒ½æ­£å¸¸å·¥ä½œ"
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
-msgstr "自動複閱應用 (review apps) 與自動部署需è¦ç¶²åŸŸæ‰èƒ½é‹ä½œã€‚"
+msgstr "自動複閱應用與自動部署需è¦ç¶²åŸŸæ‰èƒ½é‹ä½œã€‚"
+
+msgid "Auto-cancel redundant, pending pipelines"
+msgstr "自動å–消多餘ã€å¾…處ç†çš„æµæ°´ç·š"
-msgid "AutoDevOps|Auto DevOps (Beta)"
-msgstr "DevOps 自動化(beta)"
+msgid "AutoDevOps|Auto DevOps"
+msgstr "自動 DevOps"
msgid "AutoDevOps|Auto DevOps documentation"
msgstr "「DevOps 自動化〠文件"
@@ -454,17 +772,23 @@ msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "了解更多於 %{link_to_documentation}"
msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
-msgstr ""
+msgstr "如果 %{link_to_auto_devops_settings} 這個專案,您å¯ä»¥è‡ªå‹•å»ºç½®å’Œæ¸¬è©¦ä½ çš„æ‡‰ç”¨ç¨‹å¼ %{link_to_add_kubernetes_cluster} 則å¯ä»¥è®“你自動部署"
msgid "AutoDevOps|add a Kubernetes cluster"
-msgstr ""
+msgstr "添加 Kubernetes å¢é›†"
-msgid "AutoDevOps|enable Auto DevOps (Beta)"
-msgstr "啟用自動 DevOps (測試版)"
+msgid "AutoDevOps|enable Auto DevOps"
+msgstr "啟用自動 DevOps"
msgid "Available"
msgstr "能é‹åšçš„"
+msgid "Available group Runners : %{runners}"
+msgstr "å¯ä½¿ç”¨çš„群組執行器:%{runners}"
+
+msgid "Available group Runners : %{runners}."
+msgstr "å¯ä½¿ç”¨çš„群組執行器:%{runners}。"
+
msgid "Avatar will be removed. Are you sure?"
msgstr "大頭貼將被刪除。你確定嗎?"
@@ -472,98 +796,203 @@ msgid "Average per day: %{average}"
msgstr "å¹³å‡æ¯å¤©ï¼š%{average}"
msgid "Background Color"
-msgstr ""
+msgstr "背景é¡è‰²"
+
+msgid "Background Jobs"
+msgstr "背景作業"
+
+msgid "Background color"
+msgstr "背景é¡è‰²"
msgid "Background jobs"
-msgstr ""
+msgstr "背景工作"
+
+msgid "Badges"
+msgstr "徽章"
+
+msgid "Badges|A new badge was added."
+msgstr "添加了新的徽章"
+
+msgid "Badges|Add badge"
+msgstr "新增徽章"
+
+msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
+msgstr "新增徽章失敗,請確èªè¼¸å…¥çš„網å€ï¼Œå†è©¦ä¸€æ¬¡ã€‚"
+
+msgid "Badges|Badge image URL"
+msgstr "徽章圖片網å€"
+
+msgid "Badges|Badge image preview"
+msgstr "徽章圖片é è¦½"
+
+msgid "Badges|Delete badge"
+msgstr "刪除徽章"
+
+msgid "Badges|Delete badge?"
+msgstr "你確定è¦åˆªé™¤å¾½ç« å—Žï¼Ÿ"
+
+msgid "Badges|Deleting the badge failed, please try again."
+msgstr "刪除徽章失敗,請ç¨å€™é‡è©¦"
+
+msgid "Badges|Group Badge"
+msgstr "群組徽章"
+
+msgid "Badges|Link"
+msgstr "連çµ"
+
+msgid "Badges|No badge image"
+msgstr "沒有徽章圖片"
+
+msgid "Badges|No image to preview"
+msgstr "沒有圖片å¯ä»¥é è¦½"
+
+msgid "Badges|Project Badge"
+msgstr "專案徽章"
+
+msgid "Badges|Reload badge image"
+msgstr "é‡æ–°è®€å–徽章圖片"
+
+msgid "Badges|Save changes"
+msgstr "儲存變更"
+
+msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
+msgstr "儲存徽章失敗,請檢查輸入的連çµä¸¦é‡è©¦ã€‚"
+
+msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
+msgstr "%{docsLinkStart} 變數%{docsLinkEnd} GitLab 支æ´ï¼š%{placeholders}"
+
+msgid "Badges|The badge was deleted."
+msgstr "此徽章已移除"
+
+msgid "Badges|The badge was saved."
+msgstr "此徽章已儲存。"
+
+msgid "Badges|This group has no badges"
+msgstr "此群組無徽章。"
+
+msgid "Badges|This project has no badges"
+msgstr "此專案無徽章"
+
+msgid "Badges|Your badges"
+msgstr "您的徽章"
msgid "Begin with the selected commit"
msgstr "從é¸å®šçš„變更紀錄開始"
+msgid "Below are examples of regex for existing tools:"
+msgstr "以下是ç¾æœ‰å·¥å…·çš„æ­£è¦è¡¨é”å¼ç¤ºä¾‹ï¼š"
+
+msgid "Below you will find all the groups that are public."
+msgstr "您將會在底下找到所有公開的群組。"
+
msgid "Billing"
-msgstr "帳單"
+msgstr "方案"
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
-msgstr ""
+msgstr "%{group_name} ç›®å‰ä½¿ç”¨ %{plan_link} 方案。"
msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
-msgstr ""
+msgstr "在部份的方案中,ä¸æ”¯æ´è‡ªå‹•å‡ç´šæˆ–é™ç´šã€‚"
msgid "BillingPlans|Current plan"
-msgstr ""
+msgstr "ç›®å‰æ–¹æ¡ˆ"
msgid "BillingPlans|Customer Support"
-msgstr ""
+msgstr "客戶æœå‹™"
msgid "BillingPlans|Downgrade"
+msgstr "é™ç´š"
+
+msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
msgstr ""
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
-msgstr ""
+msgstr "é€éŽé–±è®€ %{faq_link} 了解更多我們的æ¯å€‹æ–¹æ¡ˆ"
msgid "BillingPlans|Manage plan"
-msgstr ""
+msgstr "管ç†æ–¹æ¡ˆ"
msgid "BillingPlans|Please contact %{customer_support_link} in that case."
-msgstr ""
+msgstr "在這情æ³ä¸‹ï¼Œè«‹è¯ç¹« %{customer_support_link}"
msgid "BillingPlans|See all %{plan_name} features"
-msgstr ""
+msgstr "查看更多 %{plan_name} 功能"
msgid "BillingPlans|This group uses the plan associated with its parent group."
-msgstr ""
+msgstr "此群組使用和上層群組相關的方案。"
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
-msgstr ""
+msgstr "è‹¥è¦ç®¡ç†é€™å€‹ç¾¤çµ„的方案,請ç€è¦½ %{parent_billing_page_link} 的方案部分。"
msgid "BillingPlans|Upgrade"
-msgstr ""
+msgstr "å‡ç´š"
msgid "BillingPlans|You are currently on the %{plan_link} plan."
+msgstr "ä½ ç›®å‰æ­£åœ¨ä½¿ç”¨æ–¹æ¡ˆ %{plan_link}"
+
+msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
msgstr ""
-msgid "BillingPlans|frequently asked questions"
+msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
msgstr ""
+msgid "BillingPlans|features"
+msgstr "功能"
+
+msgid "BillingPlans|frequently asked questions"
+msgstr "常見å•é¡Œ"
+
msgid "BillingPlans|monthly"
-msgstr ""
+msgstr "æ¯å€‹æœˆ"
msgid "BillingPlans|paid annually at %{price_per_year}"
-msgstr ""
+msgstr "æ¯å¹´æ”¶å– %{price_per_year}"
msgid "BillingPlans|per user"
-msgstr ""
+msgstr "æ¯ä½ä½¿ç”¨è€…"
+
+msgid "Bitbucket import"
+msgstr "匯入 Bitbucket"
+
+msgid "Blog"
+msgstr "部è½æ ¼"
+
+msgid "Boards"
+msgstr "看æ¿"
+
+msgid "Branch %{branchName} was not found in this project's repository."
+msgstr "在這個專案的檔案庫中找ä¸åˆ° %{branchName} 分支。"
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
-msgstr[0] ""
+msgstr[0] "分支 (%{branch_count})"
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 "已建立分支 (branch) <strong>%{branch_name}</strong> 。如需設定自動部署, 在é¸æ“‡åˆé©çš„ GitLab CI Yaml 模æ¿å¾Œï¼Œè«‹é€äº¤ (commit) 您的編輯內容。%{link_to_autodeploy_doc}"
+msgstr "已建立分支 <strong>%{branch_name}</strong> 。如需設定自動部署, 在é¸æ“‡åˆé©çš„ GitLab CI Yaml 模æ¿å¾Œï¼Œè«‹æ交您的編輯內容。%{link_to_autodeploy_doc}"
msgid "Branch has changed"
-msgstr "分支(branch)已變更"
+msgstr "分支已變更"
msgid "Branch is already taken"
-msgstr ""
+msgstr "分支已經被更新éŽã€‚"
msgid "Branch name"
-msgstr ""
+msgstr "分支å稱"
msgid "BranchSwitcherPlaceholder|Search branches"
-msgstr "æœå°‹åˆ†æ”¯ (branches)"
+msgstr "æœå°‹åˆ†æ”¯"
msgid "BranchSwitcherTitle|Switch branch"
-msgstr "切æ›åˆ†æ”¯ (branch)"
+msgstr "切æ›åˆ†æ”¯"
msgid "Branches"
-msgstr "分支 (branch) "
+msgstr "分支"
msgid "Branches|Active"
-msgstr ""
+msgstr "æ´»èºçš„"
msgid "Branches|Active branches"
-msgstr ""
+msgstr "æ´»èºçš„分支"
msgid "Branches|All"
msgstr "全部"
@@ -610,44 +1039,44 @@ msgstr "找ä¸åˆ°åˆ†æ”¯"
msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered."
msgstr "一旦你確èªä¸¦æŒ‰ä¸‹ %{delete_protected_branch} 之後,此動作將無法撤銷或還原。"
-msgid "Branches|Only a project master or owner can delete a protected branch"
-msgstr "åªæœ‰å°ˆæ¡ˆç®¡ç†è€…或æ“有者æ‰èƒ½åˆªé™¤è¢«ä¿è­·çš„分支。"
+msgid "Branches|Only a project maintainer or owner can delete a protected branch"
+msgstr "åªæœ‰å°ˆæ¡ˆç¶­è­·è€…或æ“有者å¯ä»¥ç§»é™¤å—ä¿è­·çš„分支。"
msgid "Branches|Overview"
-msgstr ""
+msgstr "概覽"
msgid "Branches|Protected branches can be managed in %{project_settings_link}."
-msgstr ""
+msgstr "å—ä¿è­·çš„分支å¯ä»¥åœ¨ %{project_settings_link} 進行管ç†ã€‚"
msgid "Branches|Show active branches"
-msgstr ""
+msgstr "顯示活èºçš„分支"
msgid "Branches|Show all branches"
-msgstr ""
+msgstr "顯示所有分支"
msgid "Branches|Show more active branches"
-msgstr ""
+msgstr "顯示更多活èºçš„分支"
msgid "Branches|Show more stale branches"
-msgstr ""
+msgstr "顯示更多沉éœçš„分支"
msgid "Branches|Show overview of the branches"
-msgstr ""
+msgstr "顯示分支概覽"
msgid "Branches|Show stale branches"
-msgstr ""
+msgstr "顯示沉éœçš„分支"
msgid "Branches|Sort by"
msgstr "排åºè‡ª"
msgid "Branches|Stale"
-msgstr ""
+msgstr "沉éœçš„"
msgid "Branches|Stale branches"
-msgstr ""
+msgstr "沉éœçš„分支"
msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
-msgstr ""
+msgstr "分支無法自動更新,因為與上游分支存在差異。"
msgid "Branches|The default branch cannot be deleted"
msgstr "無法刪除é è¨­åˆ†æ”¯"
@@ -662,13 +1091,13 @@ msgid "Branches|To confirm, type %{branch_name_confirmation}:"
msgstr "請輸入 %{branch_name_confirmation} ä»¥é€²è¡Œç¢ºèª ï¼š"
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
-msgstr ""
+msgstr "è‹¥è¦æ”¾æ£„本機變更並使用上游版本覆寫這個分支,請先從這裡刪除å†æŒ‰ä¸‹ä¸Šæ–¹çš„「立刻更新ã€ã€‚"
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
msgstr "你將永久刪除å—ä¿è­·çš„ %{branch_name} 分支。"
msgid "Branches|diverged from upstream"
-msgstr ""
+msgstr "與上游存在差異"
msgid "Branches|merged"
msgstr "å·²åˆä½µ"
@@ -691,8 +1120,8 @@ msgstr "ç€è¦½æª”案"
msgid "Browse files"
msgstr "ç€è¦½æª”案"
-msgid "Business"
-msgstr ""
+msgid "Business metrics (Custom)"
+msgstr "ä¼æ¥­æŒ‡æ¨™ï¼ˆè‡ªè¨‚)"
msgid "ByAuthor|by"
msgstr "作者:"
@@ -700,41 +1129,104 @@ msgstr "作者:"
msgid "CI / CD"
msgstr "CI / CD"
+msgid "CI / CD Settings"
+msgstr "CI / CD 設定"
+
msgid "CI/CD"
-msgstr ""
+msgstr "CI / CD"
msgid "CI/CD configuration"
msgstr "CI/CD 設定"
msgid "CI/CD for external repo"
-msgstr ""
+msgstr "外部儲存庫的 CI / CD"
+
+msgid "CI/CD settings"
+msgstr "CI / CD 設定"
+
+msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
+msgstr "明確的 %{ci_file} 需è¦åœ¨ä½ å¯ä»¥é–‹å§‹ä½¿ç”¨ã€ŒæŒçºŒæ•´åˆã€èˆ‡ã€Œå‚³éžã€ä¹‹å‰æŒ‡å®šã€‚"
+
+msgid "CICD|Auto DevOps"
+msgstr "自動 DevOps"
+
+msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
+msgstr "自動 DevOps 將會自動編譯ã€æ¸¬è©¦ã€ä¸¦åœ¨åŸºæ–¼é ç·¨è­¯ã€ŒæŒçºŒæ•´åˆã€å’Œã€Œå‚³éžã€è¨­å®šçš„環境部署您的應用程å¼"
+
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr "至動部屬到模擬環境,手動部屬到正å¼ç’°å¢ƒ"
+
+msgid "CICD|Continuous deployment to production"
+msgstr "æŒçºŒéƒ¨å±¬åˆ°æ­£å¼ç’°å¢ƒ"
+
+msgid "CICD|Deployment strategy"
+msgstr "部屬策略"
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr "部屬策略需è¦ç¶²åŸŸæ‰èƒ½æ­£å¸¸å·¥ä½œ"
+
+msgid "CICD|Disable Auto DevOps"
+msgstr "åœç”¨è‡ªå‹• DevOps"
+
+msgid "CICD|Do not set up a domain here if you are setting up multiple Kubernetes clusters with Auto DevOps."
+msgstr "如果你設定了多個使用「自動 DevOpsã€çš„ Kubernetes å¢é›†ï¼Œè«‹ä¸è¦åœ¨é€™è£¡è¨­å®šç¶²åŸŸã€‚"
+
+msgid "CICD|Enable Auto DevOps"
+msgstr "啟用自動 DevOps"
+
+msgid "CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}."
+msgstr "跟隨著實例é è¨­å€¼ï¼Œä»¥åœ¨æ²’有專案指定 %{ci_file} 的情æ³ä¸‹å•Ÿç”¨æˆ–åœç”¨è‡ªå‹• DevOps 環境。"
+
+msgid "CICD|Instance default (%{state})"
+msgstr "é è¨­(%{state})"
msgid "CICD|Jobs"
msgstr "作業"
+msgid "CICD|Learn more about Auto DevOps"
+msgstr "學習更多關於 Auto DevOps 的相關訊æ¯"
+
+msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
+msgstr "當專案沒有 %{ci_file} 檔案,將啟用 Auto DevOps çš„æµæ°´ç·šè¨­ç½®ã€‚"
+
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr "如果你想è¦ä½¿ç”¨è‡ªå‹•è¤‡é–±ç¨‹å¼åŠè‡ªå‹•éƒ¨ç½²ï¼Œè«‹æŒ‡å®šç¶²åŸŸã€‚"
+
+msgid "Callback URL"
+msgstr "回呼 URL"
+
+msgid "Callback url"
+msgstr "回呼 url"
+
+msgid "Can't find HEAD commit for this branch"
+msgstr "找ä¸åˆ°æ­¤åˆ†æ”¯çš„ HEAD 更動。"
+
msgid "Cancel"
msgstr "å–消"
+msgid "Cancel this job"
+msgstr "å–消此任務"
+
msgid "Cannot be merged automatically"
-msgstr ""
+msgstr "ä¸èƒ½è‡ªå‹•åˆä½µ"
msgid "Cannot modify managed Kubernetes cluster"
-msgstr ""
+msgstr "無法變更託管的 Kubernetes å¢é›†"
msgid "Certificate fingerprint"
-msgstr ""
+msgstr "憑證指紋"
msgid "Change Weight"
-msgstr ""
+msgstr "變更權é‡"
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
-msgstr ""
+msgstr "更改此數值將影響到 GitLab UI 拉å–更新的頻率。"
msgid "ChangeTypeActionLabel|Pick into branch"
-msgstr "挑é¸åˆ°åˆ†æ”¯ (branch) "
+msgstr "挑é¸åˆ°åˆ†æ”¯"
msgid "ChangeTypeActionLabel|Revert in branch"
-msgstr "還原分支 (branch) "
+msgstr "還原分支"
msgid "ChangeTypeAction|Cherry-pick"
msgstr "挑é¸"
@@ -743,13 +1235,13 @@ msgid "ChangeTypeAction|Revert"
msgstr "還原"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
-msgstr ""
+msgstr "這將會建立新的更動紀錄,以還原ç¾æœ‰çš„更改。"
msgid "Changelog"
msgstr "更新日誌"
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
-msgstr ""
+msgstr "顯示更改,如果<b>來æº</b>修訂版正在åˆä½µåˆ°<b>目標</b>中修訂版本。"
msgid "Charts"
msgstr "統計圖"
@@ -758,40 +1250,55 @@ msgid "Chat"
msgstr "å³æ™‚通訊"
msgid "Check interval"
-msgstr ""
+msgstr "檢查間隔"
msgid "Checking %{text} availability…"
-msgstr ""
+msgstr "正在檢查%{text} å¯ç”¨æ€§.."
msgid "Checking branch availability..."
-msgstr ""
+msgstr "正在檢查分支是å¦å·²ç¶“有人使用..."
msgid "Cherry-pick this commit"
-msgstr "挑é¸æ­¤æ›´å‹•è¨˜éŒ„ (commit) "
+msgstr "挑é¸æ­¤æ›´å‹•è¨˜éŒ„"
msgid "Cherry-pick this merge request"
-msgstr "挑é¸æ­¤åˆä½µè«‹æ±‚ (merge request) "
+msgstr "挑é¸æ­¤åˆä½µè«‹æ±‚"
+
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
msgid "Choose File ..."
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 ""
+msgstr "é¸æ“‡åˆ†æ”¯/標籤(例如:%{master})或者輸入更動紀錄(例如:%{sha})以查看更改內容或建立åˆä½µè«‹æ±‚。"
+
+msgid "Choose any color."
+msgstr "é¸å–é¡è‰²ã€‚"
+
+msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
+msgstr "é¸æ“‡ <code>複製</code> 或 <code>抓å–</code> 以ç²å–最新的應用程å¼åŽŸå§‹ç¢¼"
msgid "Choose file..."
msgstr "é¸æ“‡æª”案⋯"
-msgid "Choose which groups you wish to synchronize to this secondary node."
+msgid "Choose the top-level group for your repository imports."
msgstr ""
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr "é¸æ“‡æ‚¨å¸Œæœ›èˆ‡é€™å€‹æ¬¡è¦ç¯€é»žåŒæ­¥çš„群組"
+
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
-msgstr ""
+msgstr "é¸æ“‡ä½ æƒ³è¦é€£ç·šä¸¦åŸ·è¡Œ CI / CD æµæ°´ç·šçš„版本庫。"
msgid "Choose which repositories you want to import."
-msgstr ""
+msgstr "é¸æ“‡ä½ æƒ³åŒ¯å…¥çš„檔案庫。"
msgid "Choose which shards you wish to synchronize to this secondary node."
-msgstr ""
+msgstr "é¸æ“‡ä½ æƒ³è¦ç”¨ä¾†èˆ‡é€™å€‹æ¬¡è¦ç¯€é»žåŒæ­¥çš„碎片 (shards)。"
msgid "CiStatusLabel|canceled"
msgstr "å·²å–消"
@@ -848,358 +1355,439 @@ msgid "CiStatus|running"
msgstr "執行中"
msgid "CiVariables|Input variable key"
-msgstr ""
+msgstr "輸入變數å稱"
msgid "CiVariables|Input variable value"
-msgstr ""
+msgstr "輸入變數的值"
msgid "CiVariables|Remove variable row"
-msgstr ""
+msgstr "刪除變數"
msgid "CiVariable|* (All environments)"
-msgstr ""
+msgstr "* (所有環境)"
msgid "CiVariable|All environments"
-msgstr ""
+msgstr "所有環境"
msgid "CiVariable|Create wildcard"
-msgstr ""
+msgstr "建立è¬ç”¨å­—å…ƒ"
msgid "CiVariable|Error occured while saving variables"
-msgstr ""
+msgstr "儲存變數時發生錯誤"
msgid "CiVariable|New environment"
-msgstr ""
+msgstr "建立新的環境"
msgid "CiVariable|Protected"
-msgstr ""
+msgstr "å—ä¿è­·çš„"
msgid "CiVariable|Search environments"
-msgstr ""
+msgstr "æœå°‹ç’°å¢ƒ"
msgid "CiVariable|Toggle protected"
-msgstr ""
+msgstr "切æ›ç‚ºä¿è­·ç‹€æ…‹"
msgid "CiVariable|Validation failed"
-msgstr ""
+msgstr "驗證失敗"
msgid "CircuitBreakerApiLink|circuitbreaker api"
-msgstr "斷路器 (circuitbreaker) API"
+msgstr "斷路器 Api"
-msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
+msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
+msgstr "無法使用:%{reason}"
+
+msgid "Clear search input"
+msgstr "清除æœå°‹ç´€éŒ„"
+
+msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
+msgstr "在專案列表點擊任何<strong>專案å稱</strong>,將轉跳到專案的里程碑。"
+
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
msgstr ""
-msgid "Click to expand text"
+msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
+msgstr "點擊左上角的<strong>å‡ç´š</strong>按鈕,將æå‡è‡³ç¾¤çµ„里程碑。"
+
+msgid "Click the <strong>Select none</strong> 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 "點擊下é¢çš„按鈕,連çµåˆ° Kubernetes é é¢é–‹å§‹å®‰è£ã€‚"
+
+msgid "Click to expand it."
+msgstr "按一下以展開。"
+
+msgid "Click to expand text"
+msgstr "點擊以展開內容"
+
msgid "Client authentication certificate"
-msgstr ""
+msgstr "客戶端èªè­‰æ†‘è­‰"
msgid "Client authentication key"
-msgstr ""
+msgstr "客戶端驗證金鑰"
msgid "Client authentication key password"
-msgstr ""
+msgstr "客戶端驗證金鑰密碼"
+
+msgid "Clients"
+msgstr "客戶端"
msgid "Clone repository"
-msgstr "複製(clone)檔案庫(repository)"
+msgstr "複製檔案庫"
msgid "Close"
-msgstr ""
+msgstr "關閉"
msgid "Closed"
-msgstr ""
+msgstr "已關閉"
+
+msgid "Closed issues"
+msgstr "已關閉議題"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
-msgstr ""
+msgstr "%{appList} 已經æˆåŠŸå®‰è£åˆ°æ‚¨çš„ Kubernetes å¢é›†"
msgid "ClusterIntegration|API URL"
-msgstr ""
+msgstr "Api 網å€"
msgid "ClusterIntegration|Add Kubernetes cluster"
-msgstr ""
-
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
-msgstr ""
+msgstr "增加 Kubernetes å¢é›†"
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
-msgstr ""
+msgstr "Kubernetes å¢é›†æ•´åˆçš„進階設定"
+
+msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgstr "當嘗試ç²å–專案å€åŸŸæ™‚錯誤發生:%{error}"
+
+msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
+msgstr "當嘗試ç²å–您的專案時發生錯誤:%{error}"
msgid "ClusterIntegration|Applications"
-msgstr ""
+msgstr "應用程å¼"
msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
-msgstr ""
+msgstr "您確定è¦åˆªé™¤æ­¤ Kubernetes å¢é›†çš„æ•´åˆè¨­å®šå—Žï¼Ÿ 這將會刪除您實際的 Kubernetes å¢é›†ã€‚"
msgid "ClusterIntegration|CA Certificate"
-msgstr ""
+msgstr "CAèªè­‰"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
-msgstr ""
-
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
+msgstr "憑證 (PEMæ ¼å¼)"
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
-msgstr ""
+msgstr "é¸æ“‡æ‚¨è¦ä½¿ç”¨æ­¤ Kubernetes å¢é›†çš„專案環境"
msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
-msgstr ""
+msgstr "控制 Kubernetes å¢é›†å¦‚何與 GitLab æ•´åˆ"
msgid "ClusterIntegration|Copy API URL"
-msgstr ""
+msgstr "複製 API 連çµ"
msgid "ClusterIntegration|Copy CA Certificate"
-msgstr ""
+msgstr "複製 CA 憑證"
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
-msgstr ""
+msgstr "è¤‡è£½å…¥å£ IP ä½ç½®åˆ°å‰ªè²¼è¤²"
+
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr "複製 Jupyter 主機å稱至剪貼簿"
msgid "ClusterIntegration|Copy Kubernetes cluster name"
-msgstr ""
+msgstr "複製 Kubernetes å¢é›†å稱"
msgid "ClusterIntegration|Copy Token"
-msgstr ""
+msgstr "複製權æ–"
msgid "ClusterIntegration|Create Kubernetes cluster"
-msgstr ""
+msgstr "建立 Kubernetes å¢é›†"
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
-msgstr ""
+msgid "ClusterIntegration|Did you know?"
+msgstr "你知é“嗎?"
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr ""
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr "輸入您的 Kubernetes å¢é›†è©³ç´°è³‡è¨Š"
-msgid "ClusterIntegration|Create on GKE"
-msgstr ""
+msgid "ClusterIntegration|Environment scope"
+msgstr "環境範åœ"
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr ""
+msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab's Google Kubernetes Engine Integration."
+msgstr "æ¯å€‹æ–°çš„ Google Cloud Platform (GCP) 帳號都會在 %{sign_up_link} 收到 300 元的優惠。因與 Google åˆä½œï¼ŒGitLab å°‡å¯ä»¥ç‚ºæ–°å»ºç«‹åŠç¾æœ‰çš„ GCP 帳號æä¾›é¡å¤–çš„ 200 元優惠,以嘗試 GitLab çš„ Google Kubernetes Engine æ•´åˆã€‚"
-msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
-msgstr ""
+msgid "ClusterIntegration|Fetching machine types"
+msgstr "正在ç²å–機器類型"
-msgid "ClusterIntegration|Environment scope"
-msgstr ""
+msgid "ClusterIntegration|Fetching projects"
+msgstr "正在ç²å–專案"
+
+msgid "ClusterIntegration|Fetching zones"
+msgstr "正在ç²å–å€åŸŸ"
msgid "ClusterIntegration|GitLab Integration"
-msgstr ""
+msgstr "GitLab æ•´åˆ"
msgid "ClusterIntegration|GitLab Runner"
-msgstr ""
+msgstr "Gitlab Runner"
-msgid "ClusterIntegration|Google Cloud Platform project ID"
-msgstr "Google 雲端專案 ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
+msgstr "Google 雲端專案"
msgid "ClusterIntegration|Google Kubernetes Engine"
-msgstr ""
+msgstr "Google Kubernetes Engine"
msgid "ClusterIntegration|Google Kubernetes Engine project"
-msgstr ""
+msgstr "Google Kubernetes Engine 專案"
msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
+msgstr "Helm Tiller"
+
+msgid "ClusterIntegration|Hide"
+msgstr "éš±è—"
+
+msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
+msgstr "如果你正在設定多個å¢é›†ä¸”正在使用自動 DevOps,%{help_link_start}請先閱讀這個說明%{help_link_end}。"
msgid "ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data."
-msgstr ""
+msgstr "為了è¦é¡¯ç¤ºå¢é›†çš„å¥åº·ç‹€æ³ï¼Œæˆ‘們需è¦ç‚ºæ‚¨çš„å¢é›†è¨­å®š Prometheus 以收集所需的資訊。"
msgid "ClusterIntegration|Ingress"
-msgstr ""
+msgstr "å…¥å£"
msgid "ClusterIntegration|Ingress IP Address"
-msgstr ""
+msgstr "å…¥å£ IP ä½ç½®"
msgid "ClusterIntegration|Install"
-msgstr ""
+msgstr "安è£"
msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
+msgstr "å®‰è£ Prometheus"
msgid "ClusterIntegration|Installed"
-msgstr ""
+msgstr "已安è£"
msgid "ClusterIntegration|Installing"
-msgstr ""
+msgstr "安è£ä¸­"
msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
-msgstr ""
+msgstr "æ•´åˆ Kubernetes å¢é›†è‡ªå‹•åŒ–"
msgid "ClusterIntegration|Integration status"
-msgstr ""
+msgstr "æ•´åˆç‹€æ…‹"
+
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr "Jupyter 主機å稱"
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr "JupyterHub"
msgid "ClusterIntegration|Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes å¢é›†"
msgid "ClusterIntegration|Kubernetes cluster details"
-msgstr ""
+msgstr "Kubernetes å¢é›†è©³ç´°è³‡è¨Š"
msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
+msgstr "Kubernetes å¢é›†å¥åº·ç¨‹åº¦"
msgid "ClusterIntegration|Kubernetes cluster integration"
-msgstr ""
+msgstr "Kubernetes å¢é›†æ•´åˆ"
msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
-msgstr ""
+msgstr "此專案ç¦ç”¨ Kubernetes å¢é›†æ•´åˆ"
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
-msgstr ""
+msgstr "此專案已啟用 Kubernetes å¢é›†æ•´åˆ"
msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
+msgstr "此專案已經啟用 Kubernetes å¢é›†æ•´åˆã€‚關閉此整åˆä¸¦ä¸æœƒå¼•éŸ¿æ‚¨çš„ Kubernetes å¢é›†ï¼Œä»–åªæœƒæš«æ™‚性的關閉 GitLab 與它的連çµã€‚"
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
-msgstr ""
+msgstr "Kubernetes å¢é›†å·²ç¶“在 Google Kubernetes Engine 上被建立"
msgid "ClusterIntegration|Kubernetes cluster name"
-msgstr ""
+msgstr "Kubernetes å¢é›†å稱"
msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
-msgstr ""
+msgstr "Kubernetes å¢é›†å·²ç¶“æˆåŠŸè¢«å»ºç«‹æ–¼ Google Kubernetes Engine,é‡æ–°æ•´ç†æ­¤é é¢æ‚¨å°‡æœƒçœ‹åˆ° Kubernetes å¢é›†çš„詳細資訊"
msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr ""
+msgstr "å¢é›†è®“你用簡單的方å¼å¯©æŸ¥ã€éƒ¨ç½²æ‡‰ç”¨ç¨‹å¼ï¼Œé‹è¡Œæµæ°´ç·šç­‰åŠŸèƒ½ã€‚詳情請閱:%{link_to_help_page}"
msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
-msgstr ""
+msgstr "Kubernetes å¢é›†å¯ä»¥ç”¨æ–¼éƒ¨ç½²æ‡‰ç”¨ç¨‹å¼ä»¥åŠç‚ºæ­¤å°ˆæ¡ˆæ供代碼審查工具"
+
+msgid "ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}."
+msgstr "瞭解有關 %{help_link_start_machine_type}機器類型%{help_link_end} 和 %{help_link_start_pricing}定價%{help_link_end} 的詳細資訊。"
+
+msgid "ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}."
+msgstr "瞭解有關 %{help_link_start}Kubernetes%{help_link_end} 的詳細資訊。"
-msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
-msgstr "學習更多有關於%{link_to_documentation}"
+msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
+msgstr "瞭解有關 %{help_link_start}å€åŸŸ%{help_link_end} 的詳細資訊。"
msgid "ClusterIntegration|Learn more about environments"
-msgstr ""
+msgstr "了解關於環境的更多資訊"
msgid "ClusterIntegration|Learn more about security configuration"
-msgstr ""
+msgstr "了解更多安全性設定"
msgid "ClusterIntegration|Machine type"
msgstr "機器型別"
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
-msgstr ""
+msgstr "請確èªæ‚¨çš„帳戶中 %{link_to_requirements} 是å¦å¯ä»¥å»ºç«‹ Kubernetes å¢é›†"
msgid "ClusterIntegration|Manage"
-msgstr ""
+msgstr "管ç†"
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
-msgstr ""
+msgstr "請至 %{link_gke} 管ç†æ‚¨çš„ Kubernetes å¢é›†"
msgid "ClusterIntegration|More information"
-msgstr ""
+msgstr "詳細資料"
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
-msgstr ""
+msgstr "多個 Kubernetes å¢é›†æ–¼ GitLab ä¼æ¥­ç‰ˆæœ¬ Premium å’Œ Ultimate 中支æ´"
+
+msgid "ClusterIntegration|No machine types matched your search"
+msgstr "沒有機器類型符åˆæ‚¨çš„æœå°‹"
+
+msgid "ClusterIntegration|No projects found"
+msgstr "找ä¸åˆ°ä»»ä½•å°ˆæ¡ˆ"
+
+msgid "ClusterIntegration|No projects matched your search"
+msgstr "沒有任何專案符åˆæ‚¨çš„æœå°‹"
+
+msgid "ClusterIntegration|No zones matched your search"
+msgstr "沒有å€åŸŸç¬¦åˆæ‚¨çš„æœå°‹"
msgid "ClusterIntegration|Note:"
-msgstr ""
+msgstr "注æ„:"
msgid "ClusterIntegration|Number of nodes"
msgstr "所有的端點數é‡"
msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
-msgstr ""
+msgstr "請為你的 Kubernetes å¢é›†è¼¸å…¥å­˜å–訊æ¯ï¼Œå¦‚果你需è¦å¹«åŠ©ï¼Œå¯ä»¥é–±è®€æˆ‘們關於 Kubernetes å¢é›†çš„ %{link_to_help_page}"
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "請確èªä½ çš„ Google 帳號是å¦ç¬¦åˆé€™äº›æ¢ä»¶"
-msgid "ClusterIntegration|Project ID"
-msgstr ""
-
msgid "ClusterIntegration|Project namespace"
-msgstr ""
+msgstr "專案命å空間"
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr "專案命å空間(é¸å¡«ï¼Œä¸å¯é‡è¤‡ï¼‰"
msgid "ClusterIntegration|Prometheus"
-msgstr ""
+msgstr "Prometheus"
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
-msgstr ""
+msgstr "閱讀我們關於 Kubernetes å¢é›†æ•´åˆçš„ %{link_to_help_page}。"
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
-msgstr ""
+msgstr "刪除 Kubernetes å¢é›†æ•´åˆ"
msgid "ClusterIntegration|Remove integration"
msgstr "刪除整åˆ"
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
-msgstr ""
+msgstr "刪除此 Kubernetes å¢é›†è¨­å®šï¼Œé€™ä¸æœƒåˆªé™¤å¯¦éš›çš„ Kubernetes å¢é›†ã€‚"
msgid "ClusterIntegration|Request to begin installing failed"
-msgstr ""
+msgstr "請求安è£å¤±æ•—"
msgid "ClusterIntegration|Save changes"
-msgstr ""
+msgstr "儲存修改"
+
+msgid "ClusterIntegration|Search machine types"
+msgstr "æœå°‹æ©Ÿå™¨é¡žåž‹"
+
+msgid "ClusterIntegration|Search projects"
+msgstr "æœå°‹å°ˆæ¡ˆ"
+
+msgid "ClusterIntegration|Search zones"
+msgstr "æœå°‹å€åŸŸ"
msgid "ClusterIntegration|Security"
-msgstr ""
+msgstr "安全"
msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
-msgstr ""
+msgstr "查看與編輯您的 Kubernetes å¢é›†è©³ç´°è³‡è¨Š"
-msgid "ClusterIntegration|See machine types"
-msgstr "查看機器型別"
+msgid "ClusterIntegration|Select machine type"
+msgstr "é¸æ“‡æ©Ÿå™¨é¡žåž‹"
-msgid "ClusterIntegration|See your projects"
-msgstr "查看您的專案"
+msgid "ClusterIntegration|Select project"
+msgstr "é¸æ“‡å°ˆæ¡ˆ"
-msgid "ClusterIntegration|See zones"
-msgstr "查看å€åŸŸ"
+msgid "ClusterIntegration|Select project and zone to choose machine type"
+msgstr "é¸æ“‡å°ˆæ¡ˆèˆ‡ä½ç½®ä¾†é¸æ“‡æ©Ÿå™¨é¡žåž‹"
+
+msgid "ClusterIntegration|Select project to choose zone"
+msgstr "é¸æ“‡å°ˆæ¡ˆä¾†é¸æ“‡æ™‚å€"
+
+msgid "ClusterIntegration|Select zone"
+msgstr "é¸æ“‡å€åŸŸ"
+
+msgid "ClusterIntegration|Select zone to choose machine type"
+msgstr "é¸æ“‡å€åŸŸä¾†é¸æ“‡æ©Ÿå™¨é¡žåž‹"
msgid "ClusterIntegration|Service token"
-msgstr ""
+msgstr "æœå‹™æ¬Šæ–"
msgid "ClusterIntegration|Show"
-msgstr ""
+msgstr "顯示"
msgid "ClusterIntegration|Something went wrong on our end."
msgstr "內部發生了錯誤"
msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
-msgstr ""
+msgstr "在 Google Kubernetes Engine 上建立å¢é›†æ™‚發生錯誤"
msgid "ClusterIntegration|Something went wrong while installing %{title}"
-msgstr ""
+msgstr "å®‰è£ %{title} 時發生錯誤"
msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
-msgstr ""
+msgstr "é è¨­çš„å¢é›†è¨­å®šï¼Œå…許存å–廣泛的功能,用以建置和部署å¯ä»¥ä½¿ç”¨å®¹å™¨æŠ€è¡“擴充的應用程å¼ã€‚"
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
-msgstr ""
+msgstr "此帳戶必須æ“有在 %{link_to_container_project} 中建立 Kubernetes å¢é›†çš„權é™"
msgid "ClusterIntegration|Toggle Kubernetes Cluster"
-msgstr ""
+msgstr "åˆ‡æ› Kubernetes å¢é›†"
msgid "ClusterIntegration|Toggle Kubernetes cluster"
-msgstr ""
+msgstr "åˆ‡æ› Kubernetes å¢é›†"
msgid "ClusterIntegration|Token"
-msgstr ""
+msgstr "權æ–"
+
+msgid "ClusterIntegration|Validating project billing status"
+msgstr "正在驗證è£ç½®è¨ˆè²»ç‹€æ…‹"
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr ""
+msgstr "當å¢é›†é€£çµåˆ°æ­¤å°ˆæ¡ˆï¼Œä½ å¯ä»¥ä½¿ç”¨è¤‡é–±æ‡‰ç”¨ï¼Œéƒ¨ç½²ä½ çš„應用程å¼ï¼ŒåŸ·è¡Œä½ çš„æµæ°´ç·šï¼Œé‚„有更多容易上手的方å¼å¯ä»¥ä½¿ç”¨ã€‚"
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
-msgstr ""
+msgstr "您的帳號必須有 %{link_to_kubernetes_engine}"
msgid "ClusterIntegration|Zone"
msgstr "å€åŸŸ"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
-msgstr ""
+msgstr "å­˜å– Google Container Engine"
msgid "ClusterIntegration|check the pricing here"
-msgstr ""
+msgstr "在這裡檢查價格"
msgid "ClusterIntegration|documentation"
-msgstr ""
+msgstr "文件"
msgid "ClusterIntegration|help page"
msgstr "說明é é¢"
msgid "ClusterIntegration|installing applications"
-msgstr ""
+msgstr "安è£æ‡‰ç”¨ç¨‹åº"
msgid "ClusterIntegration|meets the requirements"
msgstr "符åˆéœ€æ±‚"
@@ -1207,25 +1795,34 @@ msgstr "符åˆéœ€æ±‚"
msgid "ClusterIntegration|properly configured"
msgstr "設定正確"
+msgid "ClusterIntegration|sign up"
+msgstr "註冊"
+
+msgid "Cohorts"
+msgstr "Cohorts"
+
msgid "Collapse"
-msgstr ""
+msgstr "收起"
-msgid "Comment and resolve discussion"
-msgstr ""
+msgid "Collapse sidebar"
+msgstr "收起å´é‚Šæ¬„"
-msgid "Comment and unresolve discussion"
-msgstr ""
+msgid "Comment & resolve discussion"
+msgstr "評論並關閉討論"
+
+msgid "Comment & unresolve discussion"
+msgstr "評論並é‡æ–°è¨Žè«–"
msgid "Comments"
msgstr "留言"
msgid "Commit"
msgid_plural "Commits"
-msgstr[0] "更動記錄 (commit) "
+msgstr[0] "更動記錄"
msgid "Commit (%{commit_count})"
msgid_plural "Commits (%{commit_count})"
-msgstr[0] ""
+msgstr[0] "更動紀錄 (%{commit_count})"
msgid "Commit Message"
msgstr "更動訊æ¯"
@@ -1234,13 +1831,13 @@ msgid "Commit duration in minutes for last 30 commits"
msgstr "最近 30 次更動所花費的時間(分é˜ï¼‰"
msgid "Commit message"
-msgstr "更動說明 (commit) "
+msgstr "更動說明"
msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
-msgstr ""
+msgstr "更動紀錄統計 %{ref} %{start_time} - %{end_time}"
msgid "Commit to %{branchName} branch"
-msgstr ""
+msgstr "æ交至 %{branchName} 分支"
msgid "CommitBoxTitle|Commit"
msgstr "é€äº¤"
@@ -1249,103 +1846,109 @@ msgid "CommitMessage|Add %{file_name}"
msgstr "建立 %{file_name}"
msgid "Commits"
-msgstr "更動記錄 (commit) "
+msgstr "更動記錄"
msgid "Commits feed"
-msgstr "æ›´å‹•æ‘˜è¦ (commit feed)"
+msgstr "更動摘è¦"
msgid "Commits per day hour (UTC)"
-msgstr ""
+msgstr "ä¾å°æ™‚統計æ交次數(UTC)"
msgid "Commits per day of month"
-msgstr ""
+msgstr "ä¾æ—¥æœŸçµ±è¨ˆæ交次數"
msgid "Commits per weekday"
-msgstr ""
+msgstr "ä¾æ˜ŸæœŸçµ±è¨ˆæ交次數"
msgid "Commits|An error occurred while fetching merge requests data."
-msgstr ""
+msgstr "讀å–請求åˆä½µè³‡æ–™æ™‚發生錯誤"
msgid "Commits|Commit: %{commitText}"
-msgstr ""
+msgstr "更動紀錄: %{commitText}"
msgid "Commits|History"
-msgstr "更動紀錄 (commit)"
+msgstr "æ­·å²ç´€éŒ„"
msgid "Commits|No related merge requests found"
-msgstr ""
+msgstr "找ä¸åˆ°ç›¸é—œçš„åˆä½µè«‹æ±‚"
msgid "Committed by"
msgstr "é€äº¤è€…為 "
+msgid "Commit…"
+msgstr "æ交…"
+
msgid "Compare"
msgstr "比較"
msgid "Compare Git revisions"
-msgstr ""
+msgstr "比較 Git 修訂版本"
msgid "Compare Revisions"
-msgstr ""
+msgstr "比較修訂版本"
msgid "Compare changes with the last commit"
-msgstr ""
+msgstr "與上一次的更動紀錄進行比較"
msgid "Compare changes with the merge request target branch"
-msgstr ""
+msgstr "將更改與åˆä½µè«‹æ±‚目標分支進行比較"
msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
-msgstr ""
+msgstr "%{source_branch} 和 %{target_branch} 是一樣的"
msgid "CompareBranches|Compare"
-msgstr ""
+msgstr "比較"
msgid "CompareBranches|Source"
-msgstr ""
+msgstr "來æº"
msgid "CompareBranches|Target"
-msgstr ""
+msgstr "目標"
msgid "CompareBranches|There isn't anything to compare."
-msgstr ""
+msgstr "沒有任何地方å¯ä»¥æ¯”較的"
msgid "Confidential"
-msgstr ""
+msgstr "機密"
msgid "Confidentiality"
-msgstr ""
+msgstr "機密的"
msgid "Configure Gitaly timeouts."
-msgstr ""
+msgstr "設定 Gitaly 延é²éŽä¹…。"
msgid "Configure Sidekiq job throttling."
-msgstr ""
+msgstr "設定 Sidekiq 工作é™åˆ¶ã€‚"
msgid "Configure automatic git checks and housekeeping on repositories."
-msgstr ""
+msgstr "於檔案庫上設定自動 Git 檢查與內務管ç†"
msgid "Configure limits for web and API requests."
-msgstr ""
+msgstr "為網路與 API 請求設定é™åˆ¶ã€‚"
+
+msgid "Configure push and pull mirrors."
+msgstr "設定推é€å’Œæå–çš„é¡åƒã€‚"
msgid "Configure storage path and circuit breaker settings."
-msgstr ""
+msgstr "設定儲存路徑與斷路器設定。"
msgid "Configure the way a user creates a new account."
-msgstr ""
+msgstr "設定使用者建立新帳號的方å¼ã€‚"
msgid "Connect"
-msgstr ""
+msgstr "連線"
msgid "Connect all repositories"
-msgstr ""
+msgstr "連接所有版本庫"
msgid "Connect repositories from GitHub"
-msgstr ""
+msgstr "é€£çµ GitHub 檔案庫"
msgid "Connect your external repositories, and CI/CD pipelines will run for new commits. A GitLab project will be created with only CI/CD features enabled."
-msgstr ""
+msgstr "連çµæ‚¨çš„外部版本庫,CI / CD æµæ°´ç·šå°‡æœƒç‚ºæ–°çš„æ交執行。åªæœƒå»ºç«‹åªå•Ÿç”¨ CI / CD 功能的 GitLab 專案。"
msgid "Connecting..."
-msgstr ""
+msgstr "正在連çµâ€¦"
msgid "Container Registry"
msgstr "Container Registry"
@@ -1369,16 +1972,16 @@ msgid "ContainerRegistry|No tags in Container Registry for this container image.
msgstr "在這個 Container Registry 中ä¸åŒ…å«ä»»ä½•æœ‰æ¨™ç±¤çš„容器映åƒæª”"
msgid "ContainerRegistry|Once you log in, you&rsquo;re free to create and upload a container image using the common %{build} and %{push} commands"
-msgstr "當您登入後,您å¯ä»¥é€éŽ %{build} å’Œ %{push} 指令來自由建立和上傳容器映åƒæª” (container image)"
+msgstr "當您登入後,您å¯ä»¥é€éŽ %{build} å’Œ %{push} 指令來自由建立和上傳容器映åƒæª”"
msgid "ContainerRegistry|Remove repository"
-msgstr "刪除檔案庫(repository)"
+msgstr "刪除檔案庫"
msgid "ContainerRegistry|Remove tag"
msgstr "刪除標籤"
msgid "ContainerRegistry|Size"
-msgstr ""
+msgstr "大å°"
msgid "ContainerRegistry|Tag"
msgstr "標籤"
@@ -1392,98 +1995,146 @@ msgstr "使用ä¸åŒçš„映åƒæª”å稱"
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr "å°‡ Docker Container Registry æ•´åˆåˆ° GitLab 中後,æ¯å€‹å°ˆæ¡ˆéƒ½å¯ä»¥æœ‰è‡ªå·±çš„空間來儲存 Docker 的映åƒæª”"
+msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
+msgstr "您也å¯ä»¥ä½¿ç”¨ %{deploy_token} 為 Registry 映åƒæª”進行唯讀存å–"
+
+msgid "Continue"
+msgstr "繼續"
+
+msgid "Continue to the next step"
+msgstr "繼續下個步驟"
+
msgid "Continuous Integration and Deployment"
-msgstr ""
+msgstr "æŒçºŒæ•´åˆèˆ‡éƒ¨å±¬"
+
+msgid "Contribute to GitLab"
+msgstr "è²¢ç»çµ¦GitLab"
msgid "Contribution"
-msgstr ""
+msgstr "è²¢ç»"
msgid "Contribution guide"
msgstr "å”作指å—"
+msgid "Contributions per group member"
+msgstr "æ¯å€‹ç¾¤çµ„æˆå“¡çš„è²¢ç»"
+
msgid "Contributors"
msgstr "å”作者"
msgid "ContributorsPage|%{startDate} – %{endDate}"
-msgstr ""
+msgstr "%{startDate} - %{endDate}"
msgid "ContributorsPage|Building repository graph."
-msgstr ""
+msgstr "建立檔案庫的圖形"
msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
-msgstr ""
+msgstr "æ交到%{branch_name},ä¸åŒ…å«åˆä½µæ交,é™åˆ¶æ–¼ 6,000 次更動。"
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
+msgstr "è«‹ç¨ç­‰ç‰‡åˆ»ï¼Œé€™å€‹é é¢æœƒæº–備好時自動刷新。"
+
+msgid "Control the display of third party offers."
msgstr ""
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
-msgstr ""
+msgstr "控制此次è¦ç¯€é»žçš„ LFS 和附加檔案的最大併發é‡"
msgid "Control the maximum concurrency of repository backfill for this secondary node"
-msgstr ""
+msgstr "控制此次è¦ç¯€é»žçš„版本庫最大並行é‡"
+
+msgid "Control the maximum concurrency of verification operations for this Geo node"
+msgstr "控制此 Geo 節點驗證æ“作的最大並行é‡"
+
+msgid "ConvDev Index"
+msgstr "ConvDev 索引"
msgid "Copy SSH public key to clipboard"
-msgstr ""
+msgstr "複製 SSH 公鑰到剪貼簿"
msgid "Copy URL to clipboard"
msgstr "複製網å€åˆ°å‰ªè²¼ç°¿"
msgid "Copy branch name to clipboard"
-msgstr ""
+msgstr "複製分支å稱"
msgid "Copy command to clipboard"
-msgstr ""
+msgstr "複製指令"
msgid "Copy commit SHA to clipboard"
-msgstr "複製更動記錄 (commit) 的 SHA 值到剪貼簿"
+msgstr "複製更動記錄的 SHA 值到剪貼簿"
-msgid "Copy reference to clipboard"
+msgid "Copy file path to clipboard"
+msgstr "複製檔案路徑到剪貼簿"
+
+msgid "Copy incoming email address to clipboard"
msgstr ""
+msgid "Copy reference to clipboard"
+msgstr "複製連çµ"
+
+msgid "Copy to clipboard"
+msgstr "複製至剪貼簿"
+
+msgid "Copy token to clipboard"
+msgstr "複製憑證到剪貼簿"
+
msgid "Create"
-msgstr ""
+msgstr "建立"
msgid "Create New Directory"
msgstr "建立新目錄"
msgid "Create a new branch"
-msgstr ""
+msgstr "建立新分支"
msgid "Create a new branch and merge request"
-msgstr ""
+msgstr "建立新分支åŠåˆä½µè«‹æ±‚"
+
+msgid "Create a new issue"
+msgstr "建立新議題"
msgid "Create a personal access token on your account to pull or push via %{protocol}."
-msgstr "建立個人存å–憑證 (access token) 以使用 %{protocol} 來上傳 (push) 或下載 (pull) 。"
+msgstr "建立個人存å–憑證以使用 %{protocol} 來上傳或下載。"
msgid "Create branch"
-msgstr ""
+msgstr "建立分支"
+
+msgid "Create commit"
+msgstr "建立æ交"
msgid "Create directory"
msgstr "建立目錄"
msgid "Create empty repository"
-msgstr ""
+msgstr "建立空的檔案庫"
msgid "Create epic"
-msgstr ""
+msgstr "建立 epic"
msgid "Create file"
msgstr "新增檔案"
+msgid "Create group"
+msgstr "建立群組"
+
msgid "Create group label"
-msgstr ""
+msgstr "建立群組標籤"
+
+msgid "Create issue"
+msgstr "建立議題"
msgid "Create lists from labels. Issues with that label appear in that list."
-msgstr ""
+msgstr "建立標籤列表。å«æœ‰æ­¤æ¨™ç±¤çš„議題將會顯示於清單中。"
msgid "Create merge request"
-msgstr "發出åˆä½µè«‹æ±‚ (merge request) "
+msgstr "發出åˆä½µè«‹æ±‚"
msgid "Create merge request and branch"
-msgstr ""
+msgstr "建立åˆä½µè«‹æ±‚åŠåˆ†æ”¯"
msgid "Create new branch"
-msgstr "新增分支(branch)"
+msgstr "新增分支"
msgid "Create new directory"
msgstr "新增資料夾"
@@ -1491,32 +2142,41 @@ msgstr "新增資料夾"
msgid "Create new file"
msgstr "新增檔案"
+msgid "Create new file or directory"
+msgstr "建立新檔案或目錄"
+
msgid "Create new label"
-msgstr ""
+msgstr "建立新的標籤"
msgid "Create new..."
msgstr "建立..."
msgid "Create project label"
-msgstr ""
+msgstr "建立專案標籤"
msgid "CreateNewFork|Fork"
-msgstr "分支 (fork) "
+msgstr "分支"
msgid "CreateTag|Tag"
msgstr "建立標籤"
msgid "CreateTokenToCloneLink|create a personal access token"
-msgstr "建立個人存å–憑證 (access token)"
+msgstr "建立個人存å–憑證"
-msgid "Creates a new branch from %{branchName}"
-msgstr ""
+msgid "Created"
+msgstr "已建立"
-msgid "Creates a new branch from %{branchName} and re-directs to create a new merge request"
-msgstr ""
+msgid "Created At"
+msgstr "建立於"
+
+msgid "Created by me"
+msgstr "由我創建"
+
+msgid "Created on:"
+msgstr "建立於:"
msgid "Creating epic"
-msgstr ""
+msgstr "建立 epic"
msgid "Cron Timezone"
msgstr "Cron 時å€"
@@ -1525,7 +2185,16 @@ msgid "Cron syntax"
msgstr "Cron 語法"
msgid "Current node"
-msgstr ""
+msgstr "ç›®å‰ç¯€é»ž"
+
+msgid "CurrentUser|Profile"
+msgstr "個人資料"
+
+msgid "CurrentUser|Settings"
+msgstr "設定"
+
+msgid "Custom CI config path"
+msgstr "自定義 CI é…置路徑"
msgid "Custom notification events"
msgstr "自訂事件通知"
@@ -1534,6 +2203,12 @@ msgid "Custom notification levels are the same as participating levels. With cus
msgstr "自訂通知的等級與åƒèˆ‡åº¦è¨­å®šç›¸åŒã€‚使用自訂通知讓你åªæ”¶åˆ°ç‰¹å®šçš„事件通知。想了解更多資訊,請查閱 %{notification_link} 。"
msgid "Customize colors"
+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 "Cycle Analytics"
@@ -1543,7 +2218,7 @@ msgid "CycleAnalyticsStage|Code"
msgstr "程å¼é–‹ç™¼"
msgid "CycleAnalyticsStage|Issue"
-msgstr "議題 (issue) "
+msgstr "議題"
msgid "CycleAnalyticsStage|Plan"
msgstr "計劃"
@@ -1560,6 +2235,9 @@ msgstr "試營é‹"
msgid "CycleAnalyticsStage|Test"
msgstr "測試"
+msgid "Dashboard"
+msgstr "監控é¢æ¿"
+
msgid "DashboardProjects|All"
msgstr "全部"
@@ -1567,12 +2245,21 @@ msgid "DashboardProjects|Personal"
msgstr "個人"
msgid "Dec"
-msgstr ""
+msgstr "å二月"
msgid "December"
-msgstr ""
+msgstr "å二月"
+
+msgid "Decline and sign out"
+msgstr "拒絕並登出"
msgid "Default classification label"
+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 ""
msgid "Define a custom pattern with cron syntax"
@@ -1581,6 +2268,18 @@ msgstr "使用 Cron 語法自訂排程"
msgid "Delete"
msgstr "刪除"
+msgid "Delete Snippet"
+msgstr "刪除片段"
+
+msgid "Delete list"
+msgstr "刪除清單"
+
+msgid "Deleted"
+msgstr "已刪除"
+
+msgid "Deny"
+msgstr "拒絕"
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "部署"
@@ -1588,44 +2287,200 @@ msgstr[0] "部署"
msgid "Deploy Keys"
msgstr "部署金鑰"
+msgid "DeployKeys|+%{count} others"
+msgstr "其他 +%{count} 個"
+
+msgid "DeployKeys|Current project"
+msgstr "ç›®å‰å°ˆæ¡ˆ"
+
+msgid "DeployKeys|Deploy key"
+msgstr "部屬金鑰"
+
+msgid "DeployKeys|Enabled deploy keys"
+msgstr "已啟用部署金鑰"
+
+msgid "DeployKeys|Error enabling deploy key"
+msgstr "啟用部署金鑰時失敗"
+
+msgid "DeployKeys|Error getting deploy keys"
+msgstr "å–得部署金鑰時失敗"
+
+msgid "DeployKeys|Error removing deploy key"
+msgstr "移除部屬金鑰時失敗"
+
+msgid "DeployKeys|Expand %{count} other projects"
+msgstr "展開其他 %{count} 個專案"
+
+msgid "DeployKeys|Loading deploy keys"
+msgstr "正在載入部署金鑰"
+
+msgid "DeployKeys|No deploy keys found. Create one with the form above."
+msgstr "找ä¸åˆ°ä»»ä½•éƒ¨å±¬é‡‘鑰。請使用上é¢çš„表格建立一個。"
+
+msgid "DeployKeys|Privately accessible deploy keys"
+msgstr "å¯å­˜å–çš„ç§äººéƒ¨ç½²é‡‘é‘°"
+
+msgid "DeployKeys|Project usage"
+msgstr "專案用é‡"
+
+msgid "DeployKeys|Publicly accessible deploy keys"
+msgstr "å¯å­˜å–的公開部署金鑰"
+
+msgid "DeployKeys|Read access only"
+msgstr "åªè®€å­˜å–"
+
+msgid "DeployKeys|Write access allowed"
+msgstr "å·²å…許寫入權é™"
+
+msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
+msgstr "你準備è¦ç§»é™¤é€™å€‹éƒ¨ç½²é‡‘鑰,你確定嗎?"
+
+msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
+msgstr "啟用部屬憑證(%{active_tokens})"
+
+msgid "DeployTokens|Add a deploy token"
+msgstr "建立一個部屬憑證"
+
+msgid "DeployTokens|Allows read-only access to the registry images"
+msgstr "å…許 Registry 映åƒæª”çš„åªè®€å­˜å–"
+
+msgid "DeployTokens|Allows read-only access to the repository"
+msgstr "å…許此檔案庫的åªè®€å­˜å–"
+
+msgid "DeployTokens|Copy deploy token to clipboard"
+msgstr "複製部署憑證至剪貼簿"
+
+msgid "DeployTokens|Copy username to clipboard"
+msgstr "複製使用者å稱到剪貼簿"
+
+msgid "DeployTokens|Create deploy token"
+msgstr "建立部屬憑證"
+
+msgid "DeployTokens|Created"
+msgstr "已建立"
+
+msgid "DeployTokens|Deploy Tokens"
+msgstr "部屬憑證"
+
+msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgstr "部屬憑證å…許您檔案庫的åªè®€å­˜å–與 Registry 映åƒæª”。"
+
+msgid "DeployTokens|Expires"
+msgstr "éŽæœŸçš„"
+
+msgid "DeployTokens|Name"
+msgstr "å稱"
+
+msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
+msgstr "為您的應用程å¼å–個å稱,然後我們將會給您一個ç¨ç‰¹çš„部屬憑證。"
+
+msgid "DeployTokens|Revoke"
+msgstr "撤回"
+
+msgid "DeployTokens|Revoke %{name}"
+msgstr "撤回 %{name}"
+
+msgid "DeployTokens|Scopes"
+msgstr "範åœ"
+
+msgid "DeployTokens|This action cannot be undone."
+msgstr "此動作將無法還原。"
+
+msgid "DeployTokens|This project has no active Deploy Tokens."
+msgstr "此專案沒有任何啟用的部屬憑證。"
+
+msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
+msgstr "把這個憑證當åšå¯†ç¢¼ä½¿ç”¨ã€‚ä¿è­‰ä½ å„²å­˜äº†å®ƒ - 因為你將無法å†æ¬¡å­˜å–它。"
+
+msgid "DeployTokens|Use this username as a login."
+msgstr "將使用者å稱當作登入å稱。"
+
+msgid "DeployTokens|Username"
+msgstr "使用者å稱"
+
+msgid "DeployTokens|You are about to revoke"
+msgstr "您正打算撤銷"
+
+msgid "DeployTokens|Your New Deploy Token"
+msgstr "您的新部屬憑證"
+
+msgid "DeployTokens|Your new project deploy token has been created."
+msgstr "您的新專案部屬憑證已經建立。"
+
+msgid "Deprioritize label"
+msgstr "優先標籤"
+
+msgid "Descending"
+msgstr "é™å†ª"
+
msgid "Description"
msgstr "æè¿°"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
-msgstr ""
+msgstr "《æ述範本》å…許你為您專案的議題或åˆä½µè«‹æ±‚之æ述欄ä½å®šç¾©ç¬¦åˆä¸Šä¸‹æ–‡çš„範本。"
+
+msgid "Description:"
+msgstr "說明:"
+
+msgid "Destroy"
+msgstr "銷毀"
msgid "Details"
msgstr "細節"
msgid "Diffs|No file name available"
-msgstr ""
+msgstr "沒有å¯ç”¨çš„檔案å稱"
+
+msgid "Diffs|Something went wrong while fetching diff lines."
+msgstr "讀å–差異檔時發生了一些錯誤."
msgid "Directory name"
msgstr "目錄å稱"
msgid "Disable"
-msgstr ""
+msgstr "åœç”¨"
+
+msgid "Disable for this project"
+msgstr "為此專案åœç”¨"
+
+msgid "Disable group Runners"
+msgstr "åœç”¨ç¾¤çµ„執行器"
+
+msgid "Discard changes"
+msgstr "撤銷變更"
msgid "Discard draft"
-msgstr ""
+msgstr "放棄è‰ç¨¿"
msgid "Discover GitLab Geo."
+msgstr "探索 GitLab Geo。"
+
+msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr ""
+msgid "Dismiss"
+msgstr "忽略"
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "關閉循環分æžä»‹ç´¹è¦–窗"
msgid "Dismiss Merge Request promotion"
+msgstr "忽略åˆä½µè«‹æ±‚的促銷廣告"
+
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
msgstr ""
msgid "Documentation for popular identity providers"
-msgstr ""
+msgstr "知å身份æ供商的文件"
+
+msgid "Domain"
+msgstr "網域"
msgid "Don't show again"
msgstr "ä¸å†é¡¯ç¤º"
msgid "Done"
-msgstr ""
+msgstr "完æˆ"
msgid "Download"
msgstr "下載"
@@ -1646,178 +2501,262 @@ msgid "DownloadArtifacts|Download"
msgstr "下載"
msgid "DownloadCommit|Email Patches"
-msgstr "é›»å­éƒµä»¶ä¿®è£œæª”案 (patch)"
+msgstr "é›»å­éƒµä»¶ä¿®è£œæª”案"
msgid "DownloadCommit|Plain Diff"
-msgstr "差異檔 (diff)"
+msgstr "差異檔"
msgid "DownloadSource|Download"
msgstr "下載原始碼"
msgid "Downvotes"
-msgstr ""
+msgstr "噓"
msgid "Due date"
-msgstr ""
+msgstr "截止日期"
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
-msgstr ""
+msgstr "在這éŽç¨‹ä¸­ï¼Œæ‚¨å°‡æœƒè¢«è©¢å• GitLab 方的連çµï¼Œè«‹ä½¿ç”¨åº•ä¸‹é¡¯ç¤ºçš„連çµã€‚"
+
+msgid "Each Runner can be in one of the following states:"
+msgstr "æ¯å€‹åŸ·è¡Œå™¨å¯ä»¥è™•æ–¼ä»¥ä¸‹ç‹€æ…‹ä¹‹ä¸€ï¼š"
msgid "Edit"
msgstr "編輯"
+msgid "Edit Label"
+msgstr "編輯標籤"
+
msgid "Edit Pipeline Schedule %{id}"
-msgstr "編輯 %{id} æµæ°´ç·š (pipeline) 排程"
+msgstr "編輯 %{id} æµæ°´ç·šæŽ’程"
+
+msgid "Edit Snippet"
+msgstr "編輯片段"
+
+msgid "Edit application"
+msgstr "編輯應用程å¼"
msgid "Edit files in the editor and commit changes here"
-msgstr ""
+msgstr "在編輯器中編輯æ交更動紀錄"
-msgid "Editing"
-msgstr ""
+msgid "Edit group: %{group_name}"
+msgstr "編輯群組:%{group_name}"
+
+msgid "Edit identity for %{user_name}"
+msgstr "編輯 %{user_name} 的身份"
msgid "Elasticsearch"
-msgstr ""
+msgstr "Elasticsearch"
msgid "Elasticsearch intergration. Elasticsearch AWS IAM."
-msgstr ""
+msgstr "Elasticsearch æ•´åˆã€‚ Elasticsearch AWS IAM。"
msgid "Email"
-msgstr ""
+msgstr "é›»å­ä¿¡ç®±"
+
+msgid "Email patch"
+msgstr "é›»å­éƒµä»¶è£œä¸"
msgid "Emails"
msgstr "é›»å­éƒµä»¶"
+msgid "Embed"
+msgstr "內嵌"
+
msgid "Enable"
-msgstr ""
+msgstr "啟用"
msgid "Enable Auto DevOps"
-msgstr ""
+msgstr "啟用自動 DevOps"
+
+msgid "Enable Pseudonymizer data collection"
+msgstr "啟用 Pseudonymizer 的資料收集"
msgid "Enable SAML authentication for this group"
-msgstr ""
+msgstr "為此群組啟用 SAML 驗證"
msgid "Enable Sentry for error reporting and logging."
-msgstr ""
+msgstr "為錯誤報告與日誌啟用 Sentry。"
msgid "Enable and configure InfluxDB metrics."
-msgstr ""
+msgstr "啟用與設定 InfluxDB 指標。"
msgid "Enable and configure Prometheus metrics."
-msgstr ""
+msgstr "啟用與設定 Prometheus 指標。"
msgid "Enable classification control using an external service"
-msgstr ""
+msgstr "使用外部æœå‹™é€²è¡Œåˆ†é¡žæŽ§åˆ¶"
+
+msgid "Enable for this project"
+msgstr "為此專案啟用"
+
+msgid "Enable group Runners"
+msgstr "啟用群組執行器"
+
+msgid "Enable or disable certain group features and choose access levels."
+msgstr "啟用或åœç”¨æŸäº›ç¾¤çµ„功能,並é¸æ“‡å­˜å–等級。"
+
+msgid "Enable or disable the Pseudonymizer data collection."
+msgstr "啟用或åœç”¨ Pseudonymizer 的資料收集。"
msgid "Enable or disable version check and usage ping."
-msgstr ""
+msgstr "啟用或åœç”¨ç‰ˆæœ¬æª¢æŸ¥å’Œä½¿ç”¨ ping。"
msgid "Enable reCAPTCHA or Akismet and set IP limits."
-msgstr ""
+msgstr "啟用 reCAPTCHA 或 Akismet 並設定 IP 上é™ã€‚"
msgid "Enable the Performance Bar for a given group."
-msgstr ""
+msgstr "啟用æ供群組的效能桿。"
msgid "Enabled"
-msgstr ""
+msgstr "已啟用"
+
+msgid "Ends at (UTC)"
+msgstr "æ–¼ (UTC) çµæŸ"
+
+msgid "Environments"
+msgstr "環境"
msgid "Environments|An error occurred while fetching the environments."
-msgstr ""
+msgstr "ç²å–環境時發生錯誤。"
msgid "Environments|An error occurred while making the request."
+msgstr "發é€è«‹æ±‚時發生錯誤"
+
+msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
-msgid "Environments|Commit"
+msgid "Environments|Are you sure you want to stop this environment?"
msgstr ""
+msgid "Environments|Commit"
+msgstr "æ›´å‹•"
+
+msgid "Environments|Deploy to..."
+msgstr "部屬到…"
+
msgid "Environments|Deployment"
-msgstr ""
+msgstr "部署"
msgid "Environments|Environment"
-msgstr ""
+msgstr "環境"
msgid "Environments|Environments"
-msgstr ""
+msgstr "環境"
msgid "Environments|Job"
+msgstr "任務"
+
+msgid "Environments|Learn more about stopping environments"
msgstr ""
msgid "Environments|New environment"
-msgstr ""
+msgstr "新建環境"
msgid "Environments|No deployments yet"
+msgstr "尚未部署"
+
+msgid "Environments|No pod name has been specified"
+msgstr "沒有指定 pod å稱"
+
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action†being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|Open live environment"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Pod logs from"
+msgstr "Pod 日誌由"
+
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
-msgstr ""
+msgstr "了解有關環境的更多訊æ¯"
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
-msgstr ""
+msgstr "顯示全部"
+
+msgid "Environments|Stop"
+msgstr "åœæ­¢"
+
+msgid "Environments|Stop environment"
+msgstr "åœæ­¢ç’°å¢ƒ"
msgid "Environments|Updated"
-msgstr ""
+msgstr "已更新"
msgid "Environments|You don't have any environments right now."
-msgstr ""
+msgstr "你還沒有設置環境"
+
+msgid "Epic"
+msgstr "Epic"
msgid "Epic will be removed! Are you sure?"
-msgstr ""
+msgstr "將會移除 Epicï¼æ‚¨ç¢ºå®šå—Žï¼Ÿ"
msgid "Epics"
-msgstr ""
+msgstr "Epic"
msgid "Epics Roadmap"
-msgstr ""
+msgstr "Epics 開發è—圖"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr ""
+msgstr "Epics 讓你能更有效率且花費更少功夫的管ç†æ‚¨å°ˆæ¡ˆçš„組åˆã€‚"
msgid "Error Reporting and Logging"
-msgstr ""
-
-msgid "Error checking branch data. Please try again."
-msgstr ""
-
-msgid "Error committing changes. Please try again."
-msgstr ""
+msgstr "錯誤報告與日誌"
msgid "Error creating epic"
-msgstr ""
+msgstr "建立 epic 時發生錯誤"
msgid "Error fetching contributors data."
-msgstr ""
+msgstr "讀å–è²¢ç»è€…資料時發生錯誤。"
msgid "Error fetching labels."
-msgstr ""
+msgstr "讀å–標籤時發生錯誤。"
msgid "Error fetching network graph."
-msgstr ""
+msgstr "讀å–分支圖時發生錯誤。"
msgid "Error fetching refs"
-msgstr ""
+msgstr "讀å–分支資料時發生錯誤。"
msgid "Error fetching usage ping data."
-msgstr ""
+msgstr "讀å–ä½¿ç”¨æƒ…æ³ ping 資料時發生錯誤。"
+
+msgid "Error loading branch data. Please try again."
+msgstr "載入分支資料時錯誤,請é‡è©¦ã€‚"
+
+msgid "Error loading last commit."
+msgstr "載入最新更動紀錄時失敗。"
+
+msgid "Error loading markdown preview"
+msgstr "載入 Markdown é è¦½æ™‚發生å•é¡Œ"
+
+msgid "Error loading merge requests."
+msgstr "載入åˆä½µè«‹æ±‚時錯誤。"
+
+msgid "Error loading project data. Please try again."
+msgstr "載入專案資料時錯誤,請é‡è©¦ã€‚"
msgid "Error occurred when toggling the notification subscription"
-msgstr ""
+msgstr "切æ›è¨‚閱通知時發生錯誤"
msgid "Error saving label update."
-msgstr ""
+msgstr "更新標籤時發生錯誤。"
msgid "Error updating status for all todos."
-msgstr ""
+msgstr "更新代辦事項清單時發生錯誤"
msgid "Error updating todo status."
-msgstr ""
+msgstr "更新代辦事項時發生錯誤。"
+
+msgid "Estimated"
+msgstr "é ä¼°"
msgid "EventFilterBy|Filter by all"
msgstr "顯示全部"
@@ -1826,13 +2765,13 @@ msgid "EventFilterBy|Filter by comments"
msgstr "以留言篩é¸"
msgid "EventFilterBy|Filter by issue events"
-msgstr "以議題 (issue) 事件篩é¸"
+msgstr "以議題事件篩é¸"
msgid "EventFilterBy|Filter by merge events"
-msgstr "以åˆä½µ (merge) 事件篩é¸"
+msgstr "以åˆä½µäº‹ä»¶ç¯©é¸"
msgid "EventFilterBy|Filter by push events"
-msgstr "ä»¥æŽ¨é€ (push) 事件篩é¸"
+msgstr "以推é€äº‹ä»¶ç¯©é¸"
msgid "EventFilterBy|Filter by team"
msgstr "以團隊篩é¸"
@@ -1846,8 +2785,29 @@ msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆä¸€æ—¥æ·©æ™¨å››é»žï¼‰"
msgid "Every week (Sundays at 4:00am)"
msgstr "æ¯é€±åŸ·è¡Œï¼ˆé€±æ—¥æ·©æ™¨ 四點)"
+msgid "Everyone can contribute"
+msgstr "æ¯å€‹äººéƒ½èƒ½è²¢ç»"
+
msgid "Expand"
-msgstr ""
+msgstr "展開"
+
+msgid "Expand all"
+msgstr "展開全部"
+
+msgid "Expand sidebar"
+msgstr "展開å´é‚Šæ¬„"
+
+msgid "Explore"
+msgstr "ç€è¦½"
+
+msgid "Explore GitLab"
+msgstr "ç€è¦½ GitLab"
+
+msgid "Explore Groups"
+msgstr "ç€è¦½ç¾¤çµ„"
+
+msgid "Explore groups"
+msgstr "ç€è¦½ç¾¤çµ„"
msgid "Explore projects"
msgstr "ç€è¦½å°ˆæ¡ˆ"
@@ -1856,63 +2816,75 @@ msgid "Explore public groups"
msgstr "æœå°‹å…¬é–‹çš„群組"
msgid "External Classification Policy Authorization"
-msgstr ""
+msgstr "外部分類策略èªè­‰"
msgid "External authentication"
-msgstr ""
+msgstr "外部èªè­‰"
msgid "External authorization denied access to this project"
-msgstr ""
+msgstr "外部èªè­‰æ‹’絕存å–這個專案。"
msgid "External authorization request timeout"
-msgstr ""
+msgstr "外部èªè­‰è«‹æ±‚等待éŽä¹…"
msgid "ExternalAuthorizationService|Classification Label"
-msgstr ""
+msgstr "分類標籤"
msgid "ExternalAuthorizationService|Classification label"
-msgstr ""
+msgstr "分類標籤"
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
-msgstr ""
+msgstr "當沒有設定分類標籤時,將會使用é è¨­æ¨™ç±¤ `%{default_label}`。"
+
+msgid "Facebook"
+msgstr "Facebook"
msgid "Failed"
-msgstr ""
+msgstr "失敗"
msgid "Failed Jobs"
-msgstr ""
+msgstr "失敗的任務"
msgid "Failed to change the owner"
msgstr "無法變更所有權"
+msgid "Failed to check related branches."
+msgstr "檢查相關分支失敗。"
+
msgid "Failed to remove issue from board, please try again."
-msgstr ""
+msgstr "從看æ¿åˆªé™¤è­°é¡Œæ™‚發生錯誤,請ç¨å€™é‡è©¦"
msgid "Failed to remove the pipeline schedule"
-msgstr "無法刪除æµæ°´ç·š (pipeline) 排程"
+msgstr "無法刪除æµæ°´ç·šæŽ’程"
msgid "Failed to update issues, please try again."
-msgstr ""
+msgstr "更新議題時發生錯誤,請ç¨å¾Œé‡è©¦ã€‚"
+
+msgid "Failure"
+msgstr "失敗"
+
+msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
+msgstr "較快,因為它å†æ¬¡ä½¿ç”¨å°ˆæ¡ˆå·¥ä½œç©ºé–“ (如果它ä¸å­˜åœ¨ï¼Œå‰‡è¿”回到複製)"
msgid "Feb"
-msgstr ""
+msgstr "二月"
msgid "February"
-msgstr ""
+msgstr "二月"
msgid "Fields on this page are now uneditable, you can configure"
-msgstr ""
-
-msgid "File name"
-msgstr "檔案å稱"
+msgstr "æ­¤é é¢æ¬„ä½ç¾åœ¨ç„¡æ³•ç·¨è¼¯ï¼Œä½ å¯ä»¥è¨­ç½®"
msgid "Files"
msgstr "檔案"
msgid "Files (%{human_size})"
-msgstr ""
+msgstr "檔案 (%{human_size})"
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
+msgstr "填寫以下的欄ä½ï¼Œé–‹å•Ÿ <strong>%{enable_label}</strong> 後按下 <strong>%{save_changes}</strong>"
+
+msgid "Filter"
msgstr ""
msgid "Filter by commit message"
@@ -1924,285 +2896,465 @@ msgstr "以路徑æœå°‹"
msgid "Find file"
msgstr "æœå°‹æª”案"
-msgid "Finished"
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Finished"
+msgstr "已完æˆ"
+
msgid "FirstPushedBy|First"
-msgstr "é¦–æ¬¡æŽ¨é€ (push) "
+msgstr "首次"
msgid "FirstPushedBy|pushed by"
-msgstr "推é€è€… (push) :"
+msgstr "推é€è€…:"
-msgid "Font Color"
+msgid "FogBugz Email"
+msgstr "FogBugz é›»å­ä¿¡ç®±"
+
+msgid "FogBugz Import"
+msgstr "匯入 FogBugz"
+
+msgid "FogBugz Password"
+msgstr "FogBugz 密碼"
+
+msgid "FogBugz URL"
+msgstr "FogBugz URL"
+
+msgid "FogBugz import"
+msgstr "匯入 FogBugz"
+
+msgid "Follow the steps below to export your Google Code project data."
msgstr ""
+msgid "Font Color"
+msgstr "å­—é«”é¡è‰²"
+
msgid "Footer message"
-msgstr ""
+msgstr "é å°¾è¨Šæ¯"
+
+msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
+msgstr "å°æ–¼å…§éƒ¨å°ˆæ¡ˆï¼Œä»»ä½•ç™»éŒ„的使用者都å¯ä»¥æŸ¥çœ‹æµæ°´ç·šä¸¦å­˜å–工作詳細資訊 (輸出日誌和產物)"
+
+msgid "For private projects, any member (guest or higher) can view pipelines and access job details (output logs and artifacts)"
+msgstr "å°æ–¼ç§äººå°ˆæ¡ˆï¼Œä»»ä½•æˆå“¡ (訪客或以上) 都å¯ä»¥æŸ¥çœ‹æµæ°´ç·šä¸¦å­˜å–工作詳細資訊 (輸出日誌和產物)"
+
+msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
+msgstr "å°æ–¼å…¬é–‹å°ˆæ¡ˆï¼Œä»»ä½•äººéƒ½å¯ä»¥æŸ¥çœ‹æµæ°´ç·šä¸¦å­˜å–工作詳細資訊 (輸出日誌和產物)"
msgid "Fork"
msgid_plural "Forks"
-msgstr[0] "分支 (fork) "
+msgstr[0] "分支"
msgid "ForkedFromProjectPath|Forked from"
-msgstr "分支 (fork) 自"
+msgstr "分支自"
msgid "ForkedFromProjectPath|Forked from %{project_name} (deleted)"
-msgstr "從 %{project_name} Fork. (deleted)"
+msgstr "從 %{project_name} 分支. (已刪除)"
msgid "Forking in progress"
-msgstr ""
+msgstr "正在建立分å‰"
msgid "Format"
msgstr "æ ¼å¼"
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr "在您的 .gitlab-ci.yml 中找到錯誤:"
+
msgid "From %{provider_title}"
-msgstr ""
+msgstr "來自 %{provider_title}"
+
+msgid "From Bitbucket"
+msgstr "從 Bitbucket"
+
+msgid "From FogBugz"
+msgstr "從 FogBugz"
+
+msgid "From GitLab.com"
+msgstr "從 GitLab.com"
+
+msgid "From Google Code"
+msgstr "從 Google Code"
msgid "From issue creation until deploy to production"
-msgstr "從議題 (issue) 建立直到部署至營é‹ç’°å¢ƒ"
+msgstr "從議題建立直到部署至營é‹ç’°å¢ƒ"
msgid "From merge request merge until deploy to production"
-msgstr "從請求被åˆä½µå¾Œ (merge request merged) 直到部署至營é‹ç’°å¢ƒ"
+msgstr "從請求被åˆä½µå¾Œç›´åˆ°éƒ¨ç½²è‡³ç‡Ÿé‹ç’°å¢ƒ"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
-msgstr ""
+msgstr "從 Kubernetes å¢é›†è©³ç´°è³‡æ–™é ï¼Œå¾žæ‡‰ç”¨ç¨‹å¼åˆ—表中安è£é‹è¡Œå™¨"
msgid "GPG Keys"
msgstr "GPG 金鑰"
+msgid "General"
+msgstr "一般"
+
+msgid "General pipelines"
+msgstr "一般æµæ°´ç·š"
+
msgid "Generate a default set of labels"
-msgstr ""
+msgstr "產生é è¨­çš„標籤"
msgid "Geo Nodes"
-msgstr ""
+msgstr "Geo 節點"
msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
-msgstr ""
+msgstr "Geo å…許您將您的 GitLab 實例複製到其他地ç†ä½ç½®ã€‚"
msgid "GeoNodeSyncStatus|Node is failing or broken."
-msgstr ""
+msgstr "節點發生錯誤或æ壞。"
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
-msgstr ""
+msgstr "節點éŽæ…¢ã€è¶…載或剛從åœæ©Ÿä¸­é‚„原。"
msgid "GeoNodes|Checksummed"
-msgstr ""
+msgstr "已進行雜湊值驗證"
-msgid "GeoNodes|Database replication lag:"
-msgstr ""
+msgid "GeoNodes|Data is out of date from %{timeago}"
+msgstr "資料已經éŽæœŸäº† %{timeago} 之久"
+
+msgid "GeoNodes|Data replication lag"
+msgstr "資料複製延é²"
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
-msgstr ""
+msgstr "åœç”¨ç¯€é»žå°‡æœƒåœæ­¢åŒæ­¥ç¨‹åºï¼Œæ‚¨ç¢ºå®šè¦é€™éº¼åšå—Žï¼Ÿ"
msgid "GeoNodes|Does not match the primary storage configuration"
-msgstr ""
+msgstr "ä¸ç¬¦åˆä¸»è¦å„²å­˜ç©ºé–“é…ç½®"
msgid "GeoNodes|Failed"
-msgstr ""
+msgstr "失敗"
msgid "GeoNodes|Full"
-msgstr ""
+msgstr "全部"
+
+msgid "GeoNodes|GitLab version"
+msgstr "GitLab 版本"
msgid "GeoNodes|GitLab version does not match the primary node version"
-msgstr ""
+msgstr "GitLab 版本與主è¦ç¯€é»žç‰ˆæœ¬ä¸ç¬¦"
-msgid "GeoNodes|GitLab version:"
-msgstr ""
+msgid "GeoNodes|Health status"
+msgstr "å¥åº·ç‹€æ³"
-msgid "GeoNodes|Health status:"
-msgstr ""
+msgid "GeoNodes|Last event ID processed by cursor"
+msgstr "利用游標處ç†æœ€å¾Œä¸€å€‹äº‹ä»¶ ID"
-msgid "GeoNodes|Last event ID processed by cursor:"
-msgstr ""
+msgid "GeoNodes|Last event ID seen from primary"
+msgstr "從主畫é¢çœ‹åˆ°çš„最新活動 ID"
-msgid "GeoNodes|Last event ID seen from primary:"
-msgstr ""
+msgid "GeoNodes|Learn more about Repository checksum progress"
+msgstr "了解關於版本庫計算雜湊值éŽç¨‹çš„更多資訊"
+
+msgid "GeoNodes|Learn more about Repository verification"
+msgstr "了解關於版本庫驗證的更多資訊"
+
+msgid "GeoNodes|Learn more about Wiki checksum progress"
+msgstr "了解關於維基雜湊值éŽç¨‹çš„更多資訊"
+
+msgid "GeoNodes|Learn more about Wiki verification"
+msgstr "關於維基èªè­‰çš„更多資訊"
msgid "GeoNodes|Loading nodes"
-msgstr ""
+msgstr "正在載入節點"
-msgid "GeoNodes|Local Attachments:"
-msgstr ""
+msgid "GeoNodes|Local LFS objects"
+msgstr "本機 LFS 物件"
-msgid "GeoNodes|Local LFS objects:"
-msgstr ""
+msgid "GeoNodes|Local attachments"
+msgstr "本機附件"
-msgid "GeoNodes|Local job artifacts:"
-msgstr ""
+msgid "GeoNodes|Local job artifacts"
+msgstr "本機任務文件"
msgid "GeoNodes|New node"
-msgstr ""
+msgstr "新增節點"
msgid "GeoNodes|Node Authentication was successfully repaired."
-msgstr ""
+msgstr "節點èªè­‰å·²æˆåŠŸä¿®å¾©ã€‚"
msgid "GeoNodes|Node was successfully removed."
-msgstr ""
+msgstr "已順利移除節點。"
msgid "GeoNodes|Not checksummed"
-msgstr ""
+msgstr "未檢查雜湊值"
msgid "GeoNodes|Out of sync"
-msgstr ""
+msgstr "ä¸åŒæ­¥"
msgid "GeoNodes|Removing a node stops the sync process. Are you sure?"
-msgstr ""
+msgstr "移除節點將會åœæ­¢åŒæ­¥ç¨‹åºï¼Œç¢ºå®šï¼Ÿ"
-msgid "GeoNodes|Replication slot WAL:"
-msgstr ""
+msgid "GeoNodes|Replication slot WAL"
+msgstr "複製 WAL æ’槽"
-msgid "GeoNodes|Replication slots:"
-msgstr ""
+msgid "GeoNodes|Replication slots"
+msgstr "複製æ’槽"
-msgid "GeoNodes|Repositories checksummed:"
-msgstr ""
+msgid "GeoNodes|Repositories"
+msgstr "版本庫"
-msgid "GeoNodes|Repositories:"
-msgstr ""
+msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
+msgstr "輔助節點上與å°æ‡‰æ–¹èªè­‰çš„版本庫已算出雜湊值"
-msgid "GeoNodes|Repository checksums verified:"
-msgstr ""
+msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
+msgstr "主節點上與å°æ‡‰æ–¹çš„版本庫已驗證"
+
+msgid "GeoNodes|Repository checksum progress"
+msgstr "版本庫雜湊值程åº"
+
+msgid "GeoNodes|Repository verification progress"
+msgstr "版本庫驗證程åº"
msgid "GeoNodes|Selective"
-msgstr ""
+msgstr "é¸æ“‡"
msgid "GeoNodes|Something went wrong while changing node status"
-msgstr ""
+msgstr "變更節點狀態時發生錯誤"
+
+msgid "GeoNodes|Something went wrong while fetching nodes"
+msgstr "抓å–節點時發生錯誤"
msgid "GeoNodes|Something went wrong while removing node"
-msgstr ""
+msgstr "移除節點時發生錯誤"
msgid "GeoNodes|Something went wrong while repairing node"
-msgstr ""
+msgstr "修復節點時發生錯誤"
-msgid "GeoNodes|Storage config:"
-msgstr ""
+msgid "GeoNodes|Storage config"
+msgstr "儲存空間設定"
-msgid "GeoNodes|Sync settings:"
-msgstr ""
+msgid "GeoNodes|Sync settings"
+msgstr "åŒæ­¥è¨­å®š"
msgid "GeoNodes|Synced"
-msgstr ""
+msgstr "å·²åŒæ­¥"
msgid "GeoNodes|Unused slots"
-msgstr ""
+msgstr "未使用æ’槽"
msgid "GeoNodes|Unverified"
-msgstr ""
+msgstr "未驗證"
msgid "GeoNodes|Used slots"
-msgstr ""
+msgstr "使用的æ’槽"
msgid "GeoNodes|Verified"
-msgstr ""
+msgstr "已驗證"
-msgid "GeoNodes|Wiki checksums verified:"
-msgstr ""
+msgid "GeoNodes|Wiki checksum progress"
+msgstr "維基雜湊值進度"
-msgid "GeoNodes|Wikis checksummed:"
-msgstr ""
+msgid "GeoNodes|Wiki verification progress"
+msgstr "維基驗證進度"
-msgid "GeoNodes|Wikis:"
-msgstr ""
+msgid "GeoNodes|Wikis"
+msgstr "維基"
+
+msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes"
+msgstr "主節點上與å°æ‡‰æ–¹é©—證的維基已算出雜湊值"
+
+msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
+msgstr "Wiki 已在主è¦ç¯€é»žçš„å°æ‡‰æ–¹é©—證完æˆ"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
-msgstr ""
+msgstr "您使用ä¸å®‰å…¨çš„ HTTP 連線設定 Geo 節點。我們建議使用 HTTPS。"
msgid "Geo|All projects"
-msgstr ""
+msgstr "所有專案"
msgid "Geo|File sync capacity"
-msgstr ""
+msgstr "檔案åŒæ­¥é‡"
msgid "Geo|Groups to synchronize"
-msgstr ""
+msgstr "è¦åŒæ­¥çš„群組"
msgid "Geo|Projects in certain groups"
-msgstr ""
+msgstr "æŸäº›ç¾¤çµ„中的專案"
msgid "Geo|Projects in certain storage shards"
-msgstr ""
+msgstr "æŸäº›å„²å­˜ç©ºé–“碎片中的專案"
msgid "Geo|Repository sync capacity"
-msgstr ""
+msgstr "版本庫åŒæ­¥é‡"
msgid "Geo|Select groups to replicate."
-msgstr ""
+msgstr "é¸æ“‡è¦è¤‡è£½çš„群組。"
msgid "Geo|Shards to synchronize"
-msgstr ""
+msgstr "è¦åŒæ­¥çš„碎片"
+
+msgid "Geo|Verification capacity"
+msgstr "驗證能力"
+
+msgid "Git"
+msgstr "Git"
msgid "Git repository URL"
-msgstr ""
+msgstr "Git 檔案庫網å€"
msgid "Git revision"
-msgstr ""
+msgstr "Git 修訂版本"
msgid "Git storage health information has been reset"
msgstr "Git 儲存空間å¥åº·æŒ‡æ•¸å·²é‡ç½®"
+msgid "Git strategy for pipelines"
+msgstr "Git æµæ°´ç·šç­–ç•¥"
+
msgid "Git version"
-msgstr ""
+msgstr "Git 版本"
msgid "GitHub import"
-msgstr ""
+msgstr "GitHub 匯入"
msgid "GitLab CI Linter has been moved"
-msgstr ""
+msgstr "GitLab CI Linter 已經æ¬ç§»"
msgid "GitLab Geo"
-msgstr ""
+msgstr "GitLab Geo"
+
+msgid "GitLab Group Runners can execute code for all the projects in this group."
+msgstr "GitLab 群組執行器å¯ä»¥ç‚ºæ­¤ç¾¤çµ„的所有專案執行程å¼ç¢¼ã€‚"
+
+msgid "GitLab Import"
+msgstr "匯入 GitLab"
+
+msgid "GitLab User"
+msgstr "GitLab 使用者"
-msgid "GitLab Runner section"
-msgstr "GitLab Runner"
+msgid "GitLab project export"
+msgstr "匯出 GitLab 專案"
msgid "GitLab single sign on URL"
-msgstr ""
+msgstr "GitLab URL 單一登入"
+
+msgid "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."
+msgstr "GitLab 將會在後å°é€²è¡Œç”¢ç”Ÿ GitLab 資料庫å‡å CSV 的作業,該資料庫將會上傳到您設定的å°è±¡å„²å­˜ç›®éŒ„。"
+
+msgid "GitLab.com import"
+msgstr "匯入 GitLab.com"
msgid "Gitaly"
-msgstr ""
+msgstr "Gitaly"
msgid "Gitaly Servers"
-msgstr ""
+msgstr "Gitaly 伺æœå™¨"
+
+msgid "Gitaly|Address"
+msgstr "地å€"
+
+msgid "Gitea Host URL"
+msgstr "Gitea 主機 URL"
+
+msgid "Gitea Import"
+msgstr "匯入 Gitea"
+
+msgid "Go Back"
+msgstr "返回"
msgid "Go back"
+msgstr "上一é "
+
+msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Go to your fork"
-msgstr "å‰å¾€æ‚¨çš„分支 (fork) "
+msgstr "å‰å¾€æ‚¨çš„分支"
msgid "GoToYourFork|Fork"
-msgstr "å‰å¾€æ‚¨çš„分支 (fork) "
+msgstr "å‰å¾€æ‚¨çš„分支"
+
+msgid "Google Code import"
+msgstr "匯入 Google Code"
+
+msgid "Google Takeout"
+msgstr "Google Takeout"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "Google 身份驗證ä¸æ˜¯ %{link_to_documentation}。如果您想使用此æœå‹™ï¼Œè«‹è«®è©¢ç®¡ç†å“¡ã€‚"
msgid "Got it!"
-msgstr ""
+msgstr "明白ï¼"
-msgid "GroupRoadmap|Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr ""
+msgid "Graph"
+msgstr "圖表"
+
+msgid "Group"
+msgstr "群組"
+
+msgid "Group CI/CD settings"
+msgstr "群組 CI / CD 設定"
+
+msgid "Group Git LFS status:"
+msgstr "群組 Git LFS 狀態:"
+
+msgid "Group ID"
+msgstr "群組 ID"
+
+msgid "Group Runners"
+msgstr "群組執行器"
+
+msgid "Group avatar"
+msgstr "群組頭åƒ"
+
+msgid "Group details"
+msgstr "群組詳細資訊"
+
+msgid "Group info:"
+msgstr "群組資訊:"
+
+msgid "Group maintainers can register group runners in the %{link}"
+msgstr "群組維護者å¯ä»¥åœ¨ %{link} 註冊群組執行器"
+
+msgid "Group: %{group_name}"
+msgstr "群組:%{group_name}"
msgid "GroupRoadmap|From %{dateWord}"
-msgstr ""
+msgstr "從 %{dateWord}"
msgid "GroupRoadmap|Loading roadmap"
-msgstr ""
+msgstr "正在讀å–開發è—圖"
msgid "GroupRoadmap|Something went wrong while fetching epics"
-msgstr ""
+msgstr "å–å¾— Epic 時發生錯誤。"
-msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown &ndash; from %{startDate} to %{endDate}."
-msgstr ""
+msgid "GroupRoadmap|Sorry, no epics matched your search"
+msgstr "抱歉,沒有任何 Epic 符åˆæ‚¨çš„æœå°‹ã€‚"
+
+msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
+msgstr "開發è—圖顯示時間軸上您的 Epic 進度"
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "è‹¥è¦æª¢è¦–開發è—圖,請新增這個群組或其å­ç¾¤çµ„中一個 Epic 的計畫開始與完æˆæ—¥æœŸã€‚月檢視中,åªæœƒé¡¯ç¤ºä¸Šå€‹æœˆã€é€™å€‹æœˆã€å’ŒæŽ¥ä¸‹ä¾†äº”個月的 Epic &ndash; 從 %{startDate} 到 %{endDate}。"
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "è‹¥è¦æª¢è¦–開發è—圖,請新增這個群組或其å­ç¾¤çµ„中一個 Epic 的計畫開始與完æˆæ—¥æœŸã€‚季度檢視中,åªæœƒé¡¯ç¤ºä¸Šå€‹å­£åº¦ã€é€™å€‹å­£åº¦ã€å’ŒæŽ¥ä¸‹ä¾†å››å€‹å­£åº¦çš„ Epic &ndash; 從 %{startDate} 到 %{endDate}。"
+
+msgid "GroupRoadmap|To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "è‹¥è¦æª¢è¦–開發è—圖,請新增這個群組或其å­ç¾¤çµ„中一個 Epic 的計畫開始與完æˆæ—¥æœŸã€‚週檢視中,åªæœƒé¡¯ç¤ºä¸Šé€±ã€é€™é€±ã€å’ŒæŽ¥ä¸‹ä¾†å››é€±çš„ Epic &ndash; 從 %{startDate} 到 %{endDate}。"
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the months view, only epics in the past month, current month, and next 5 months are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "è‹¥è¦æ“´å¤§æ‚¨çš„æœå°‹ï¼Œè«‹å˜—試修改或移除篩é¸å™¨ã€‚月檢視中,åªæœƒé¡¯ç¤ºä¸Šå€‹æœˆã€é€™å€‹æœˆã€å’ŒæŽ¥ä¸‹ä¾†äº”個月的 Epic &ndash; 從 %{startDate} 到 %{endDate}。"
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the quarters view, only epics in the past quarter, current quarter, and next 4 quarters are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "è‹¥è¦æ“´å¤§æ‚¨çš„æœå°‹ï¼Œè«‹å˜—試修改或移除篩é¸å™¨ã€‚季度檢視中,åªæœƒé¡¯ç¤ºä¸Šå€‹å­£åº¦ã€é€™å€‹å­£åº¦ã€å’ŒæŽ¥ä¸‹ä¾†å››å€‹å­£åº¦çš„ Epic &ndash; 從 %{startDate} 到 %{endDate}。"
+
+msgid "GroupRoadmap|To widen your search, change or remove filters. In the weeks view, only epics in the past week, current week, and next 4 weeks are shown &ndash; from %{startDate} to %{endDate}."
+msgstr "è‹¥è¦æ“´å¤§æ‚¨çš„æœå°‹ï¼Œè«‹å˜—試修改或移除篩é¸å™¨ã€‚週檢視中,åªæœƒé¡¯ç¤ºä¸Šé€±ã€é€™é€±ã€å’ŒæŽ¥ä¸‹ä¾†å››é€±çš„ Epic &ndash; 從 %{startDate} 到 %{endDate}。"
msgid "GroupRoadmap|Until %{dateWord}"
-msgstr ""
+msgstr "直到 %{dateWord}"
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "ç¦æ­¢èˆ‡å…¶ä»–群組共享 %{group} 中的專案"
msgid "GroupSettings|Share with group lock"
-msgstr ""
+msgstr "分享群組鎖"
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
msgstr "這個設定已經套用至 %{ancestor_group},並覆蓋了它的å­ç¾¤çµ„設定。"
@@ -2222,6 +3374,33 @@ msgstr "無法啟用上級群組的「共享群組鎖ã€ã€‚åªæœ‰ä¸Šç´šç¾¤çµ„çš„
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr "從 %{ancestor_group_name} 中移除共享群組鎖"
+msgid "Groups"
+msgstr "群組"
+
+msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
+msgstr "群組也å¯ä»¥å‰µå»º %{subgroup_docs_link_start}å­ç¾¤çµ„%{subgroup_docs_link_end}。"
+
+msgid "GroupsDropdown|Frequently visited"
+msgstr "最近ç€è¦½"
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr "你經常ç€è¦½çš„群組將會在這裡顯示"
+
+msgid "GroupsDropdown|Loading groups"
+msgstr "正在載入群組"
+
+msgid "GroupsDropdown|Search your groups"
+msgstr "æœå°‹æ‚¨çš„群組"
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr "çµæŸæ™‚發生錯誤。"
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr "抱歉,沒有符åˆæ‚¨æœå°‹çš„群組。"
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr "這個功能需è¦ç€è¦½å™¨çš„ localStorage 支æ´"
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr "群組是數個專案的集åˆã€‚"
@@ -2262,10 +3441,10 @@ msgid "GroupsTree|Sorry, no groups or projects matched your search"
msgstr "ä¸å¥½æ„æ€ï¼Œæ²’有æœå°‹åˆ°ä»»ä½•ç¬¦åˆæ¢ä»¶çš„群組或專案"
msgid "Have your users email"
-msgstr ""
+msgstr "有來自您使用者的信件"
msgid "Header message"
-msgstr ""
+msgstr "標頭訊æ¯"
msgid "Health Check"
msgstr "å¥åº·æª¢æŸ¥"
@@ -2274,7 +3453,7 @@ msgid "Health information can be retrieved from the following endpoints. More in
msgstr "å¥åº·è³‡è¨Šå¯å¾žä»¥ä¸‹é€£çµå–得。想了解更多請åƒé–±"
msgid "HealthCheck|Access token is"
-msgstr "å­˜å–憑證 (access token) 是"
+msgstr "å­˜å–憑證是"
msgid "HealthCheck|Healthy"
msgstr "å¥åº·"
@@ -2286,17 +3465,20 @@ msgid "HealthCheck|Unhealthy"
msgstr "ä¸è‰¯"
msgid "Help"
-msgstr ""
+msgstr "説明"
msgid "Help page"
-msgstr ""
+msgstr "說明é é¢"
msgid "Help page text and support page url."
-msgstr ""
+msgstr "說明é é¢æ–‡å­—與支æ´é é¢é€£çµ"
msgid "Hide value"
msgid_plural "Hide values"
-msgstr[0] ""
+msgstr[0] "éš±è—資料"
+
+msgid "Hide whitespace changes"
+msgstr "éš±è—空白變化"
msgid "History"
msgstr "æ­·å²"
@@ -2304,66 +3486,156 @@ msgstr "æ­·å²"
msgid "Housekeeping successfully started"
msgstr "已開始維護"
+msgid "I accept the %{terms_link}"
+msgstr "æˆ‘æŽ¥å— %{terms_link}"
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr "æœå‹™æ¢æ¬¾å’Œéš±ç§æ”¿ç­–"
+
+msgid "ID"
+msgstr "ID"
+
+msgid "IDE|Commit"
+msgstr "æ交"
+
+msgid "IDE|Edit"
+msgstr "編輯"
+
+msgid "IDE|Go back"
+msgstr "返回"
+
+msgid "IDE|Open in file view"
+msgstr "以檔案顯示開啟"
+
+msgid "IDE|Review"
+msgstr "審閱"
+
+msgid "Identifier"
+msgstr "識別碼"
+
+msgid "Identities"
+msgstr "身份"
+
msgid "Identity provider single sign on URL"
-msgstr ""
+msgstr "身份æ供者的 URL 單一登入"
+
+msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgstr "如果ç¦ç”¨ï¼Œå­˜å–等級將å–決於使用者在專案中的權é™ã€‚"
+
+msgid "If enabled"
+msgstr "如果啟用"
msgid "If enabled, access to projects will be validated on an external service using their classification label."
-msgstr ""
+msgstr "如果啟用,將會使用其分類標籤在外部æœå‹™ä¸Šé©—證專案的存å–。"
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
-msgstr ""
+msgstr "如果使用 GitHub,你將會看到用於æ交與推é€è«‹æ±‚çš„ GitHub æµæ°´ç·šç‹€æ…‹ã€‚%{more_info_link}"
msgid "If you already have files you can push them using the %{link_to_cli} below."
-msgstr ""
+msgstr "如果你已經æ“有檔案,你å¯ä»¥ä½¿ç”¨ %{link_to_cli} 推é€ä»–們。"
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
-msgstr ""
+msgstr "如果你的 HTTP 檔案庫沒有公開存å–,請增加驗證欄ä½åˆ°ç¶²å€ä¸Šï¼š<code>https://username:password@gitlab.company.com/group/project.git</code>."
+
+msgid "ImageDiffViewer|2-up"
+msgstr "2-up"
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr "Onion 主題"
+
+msgid "ImageDiffViewer|Swipe"
+msgstr "Swipe"
msgid "Import"
-msgstr ""
+msgstr "匯入"
+
+msgid "Import Projects from Gitea"
+msgstr "從 Gitea 匯入專案"
+
+msgid "Import all compatible projects"
+msgstr "匯入所有相容的項目"
+
+msgid "Import all projects"
+msgstr "匯入所有專案"
msgid "Import all repositories"
+msgstr "匯入所有檔案庫"
+
+msgid "Import an exported GitLab project"
msgstr ""
msgid "Import in progress"
+msgstr "匯入中..."
+
+msgid "Import multiple repositories by uploading a manifest file."
msgstr ""
-msgid "Import repositories from GitHub"
+msgid "Import project"
+msgstr "匯入專案"
+
+msgid "Import projects from Bitbucket"
msgstr ""
+msgid "Import projects from FogBugz"
+msgstr "從 FogBugz 匯入專案"
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
+msgid "Import repositories from GitHub"
+msgstr "從 GitHub 匯入檔案庫"
+
msgid "Import repository"
-msgstr "匯入檔案庫 (repository)"
+msgstr "匯入檔案庫"
msgid "ImportButtons|Connect repositories from"
-msgstr ""
+msgstr "連線版本庫自"
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr ""
+msgstr "改善 GitLab ä¼æ¥­ç‰ˆæœ¬çš„議題看æ¿ã€‚"
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
-msgstr ""
+msgstr "é€éŽè­°é¡Œæ¬Šé‡å’Œ GitLab ä¼æ¥­ç‰ˆæœ¬æ”¹å–„議題管ç†ã€‚"
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
-msgstr ""
+msgstr "使用進階全域æœå°‹å’Œ GitLab ä¼æ¥­ç‰ˆæœ¬ä¾†æ”¹é€²æœå°‹ã€‚"
-msgid "Install Runner on Kubernetes"
+msgid "In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
-msgstr "安è£èˆ‡ GitLab CI 相容的 Runner"
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
+msgstr "包括所有用戶必須接å—çš„æœå‹™æ¢æ¬¾å”議和隱ç§æ”¿ç­–。"
+
+msgid "Incompatible Project"
+msgstr "ä¸ç›¸å®¹çš„專案"
+
+msgid "Inline"
+msgstr "內嵌"
+
+msgid "Install GitLab Runner"
+msgstr "å®‰è£ GitHub 執行器"
+
+msgid "Install Runner on Kubernetes"
+msgstr "在 Kubernetes 上安è£é‹è¡Œå™¨"
msgid "Instance"
msgid_plural "Instances"
-msgstr[0] ""
+msgstr[0] "實例"
msgid "Instance does not support multiple Kubernetes clusters"
-msgstr ""
+msgstr "主機沒有支æ´å¤šå€‹ Kubernetes å¢é›†"
msgid "Integrations"
-msgstr ""
+msgstr "æ•´åˆ"
+
+msgid "Integrations Settings"
+msgstr "æ•´åˆè¨­å®š"
msgid "Interested parties can even contribute by pushing commits if they want to."
-msgstr ""
+msgstr "有興趣的人甚至å¯ä»¥é€šéŽæŽ¨é€æ›´å‹•ç´€éŒ„åšå‡ºè²¢ç»ï¼Œå¦‚果他們願æ„的話。"
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "內部 - 任何登入的使用者都å¯ä»¥æŸ¥çœ‹è©²ç¾¤çµ„åŠå…¶å°ˆæ¡ˆ"
@@ -2377,71 +3649,89 @@ msgstr "循環週期"
msgid "Introducing Cycle Analytics"
msgstr "週期分æžç°¡ä»‹"
+msgid "Issue Boards"
+msgstr "議題看æ¿"
+
msgid "Issue board focus mode"
-msgstr ""
+msgstr "議題看æ¿ç„¦é»žæ¨¡å¼"
msgid "Issue events"
-msgstr "議題 (issue) 事件"
+msgstr "議題事件"
msgid "IssueBoards|Board"
msgstr "看æ¿"
msgid "IssueBoards|Boards"
-msgstr ""
+msgstr "看æ¿"
msgid "Issues"
msgstr "議題"
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
-msgstr ""
+msgstr "è­°é¡Œå¯ä»¥æ˜¯bug,任務或想法來討論。此外,å•é¡Œæ˜¯å¯æœå°‹å’ŒéŽæ¿¾çš„。"
+
+msgid "Issues closed"
+msgstr "議題已關閉"
msgid "Jan"
-msgstr ""
+msgstr "一月"
msgid "January"
-msgstr ""
+msgstr "一月"
+
+msgid "Job"
+msgstr "任務"
+
+msgid "Job has been erased"
+msgstr "工作已被抹除"
msgid "Jobs"
-msgstr ""
+msgstr "任務"
msgid "Jul"
-msgstr ""
+msgstr "七月"
msgid "July"
-msgstr ""
+msgstr "七月"
msgid "Jun"
-msgstr ""
+msgstr "六月"
msgid "June"
-msgstr ""
+msgstr "六月"
msgid "Koding"
-msgstr ""
+msgstr "Koding"
+
+msgid "Koding Dashboard"
+msgstr "Koding 控制é¢æ¿"
msgid "Kubernetes"
-msgstr ""
+msgstr "Kubernetes"
msgid "Kubernetes Cluster"
-msgstr ""
+msgstr "Kuberneteså¢é›†"
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
-msgstr ""
+msgstr "Kuberneteså¢é›†å»ºç«‹è¶…時;%{timeout}"
msgid "Kubernetes cluster integration was not removed."
-msgstr ""
+msgstr "Kubernetes å¢é›†æ•´åˆæœªè¢«åˆªé™¤ã€‚"
msgid "Kubernetes cluster integration was successfully removed."
-msgstr ""
+msgstr "Kubernetes å¢é›†æ•´åˆå·²æˆåŠŸè¢«åˆªé™¤ã€‚"
msgid "Kubernetes cluster was successfully updated."
-msgstr ""
+msgstr "Kubernetes å¢é›†å·²æˆåŠŸæ›´æ–°ã€‚"
msgid "Kubernetes configured"
-msgstr ""
+msgstr "Kubernetes å·²é…ç½®"
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
-msgstr ""
+msgstr "Kubernetesæœå‹™æ•´åˆå·²è¢«æ£„用。 %{deprecated_message_content} 您的 Kubernetes å¢é›†ä½¿ç”¨æ–°çš„ <a href=\"%{url}\"/>Kubernetes å¢é›†</a> é é¢"
+
+msgid "LFS"
+msgstr "LFS"
msgid "LFSStatus|Disabled"
msgstr "åœç”¨"
@@ -2450,38 +3740,50 @@ msgid "LFSStatus|Enabled"
msgstr "啟用"
msgid "Label"
-msgstr ""
+msgstr "標籤"
+
+msgid "Label actions dropdown"
+msgstr "標籤æ“作下拉èœå–®"
+
+msgid "Label lists show all issues with the selected label."
+msgstr "標籤列表顯示了é¸å–標籤的所有å•é¡Œã€‚"
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
-msgstr ""
+msgstr "%{firstLabelName} +%{remainingLabelCount} 更多"
msgid "LabelSelect|%{labelsString}, and %{remainingLabelCount} more"
-msgstr ""
+msgstr "%{labelsString},和%{remainingLabelCount} 更多"
+
+msgid "LabelSelect|Labels"
+msgstr "標籤"
msgid "Labels"
msgstr "標籤"
msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
-msgstr ""
+msgstr "標籤å¯ä»¥æ‡‰ç”¨æ–¼ %{features}。群組標籤å¯ä»¥ç”¨æ–¼ä»»ä½•ç¾¤çµ„內的項目。"
msgid "Labels can be applied to issues and merge requests to categorize them."
-msgstr ""
+msgstr "標籤å¯ä»¥ç”¨æ–¼è­°é¡Œå’Œåˆä½µè«‹æ±‚以å°å®ƒå€‘進行分類。"
+
+msgid "Labels can be applied to issues and merge requests."
+msgstr "標籤å¯ä»¥ç”¨æ–¼è­°é¡Œå’Œåˆä½µè«‹æ±‚。"
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
-msgstr ""
+msgstr "<span>è¦è®“標籤</span> %{labelTitle} <span>æå‡åˆ°ç¾¤çµ„標籤嗎?</span>"
msgid "Labels|Promote Label"
-msgstr ""
+msgstr "æå‡æ¨™ç±¤"
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "最近 %d 天"
msgid "Last Pipeline"
-msgstr "最新æµæ°´ç·š (pipeline) "
+msgstr "最新æµæ°´ç·š"
msgid "Last commit"
-msgstr "最後更動記錄 (commit) "
+msgstr "最後更動記錄"
msgid "Last edited %{date}"
msgstr "最後編輯於 %{date}"
@@ -2496,25 +3798,28 @@ msgid "Last updated"
msgstr "上次更新"
msgid "LastPushEvent|You pushed to"
-msgstr "您上傳 (push) 了"
+msgstr "您上傳了"
msgid "LastPushEvent|at"
msgstr "æ–¼"
+msgid "Latest changes"
+msgstr "最新修改"
+
msgid "Learn more"
-msgstr ""
+msgstr "進一步了解"
msgid "Learn more about Kubernetes"
-msgstr ""
+msgstr "了解更多端於 Kubernetes 的資訊"
msgid "Learn more about protected branches"
-msgstr ""
+msgstr "進一步了解å—ä¿è­·çš„分支"
msgid "Learn more in the"
msgstr "了解更多"
msgid "Learn more in the|pipeline schedules documentation"
-msgstr "æµæ°´ç·š (pipeline) 排程說明文件"
+msgstr "æµæ°´ç·šæŽ’程說明文件"
msgid "Leave"
msgstr "離開"
@@ -2525,71 +3830,131 @@ msgstr "退出群組"
msgid "Leave project"
msgstr "退出專案"
-msgid "License"
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
msgstr ""
+msgid "License"
+msgstr "授權"
+
+msgid "LinkedIn"
+msgstr "LinkedIn"
+
msgid "List"
-msgstr ""
+msgstr "清單"
+
+msgid "List Your Gitea Repositories"
+msgstr "列出您的 Gitea 版本庫"
+
+msgid "List available repositories"
+msgstr "列出å¯ç”¨çš„版本庫"
msgid "List your GitHub repositories"
-msgstr ""
+msgstr "列出您 GitHub 的檔案庫"
+
+msgid "Loading contribution stats for group members"
+msgstr "正在讀å–群組æˆå“¡çš„è²¢ç»çµ±è¨ˆ"
msgid "Loading the GitLab IDE..."
-msgstr ""
+msgstr "è®€å– GitLab IDE..."
+
+msgid "Loading..."
+msgstr "載入中…"
msgid "Lock"
msgstr "鎖定"
msgid "Lock %{issuableDisplayName}"
-msgstr ""
+msgstr "鎖定 %{issuableDisplayName}"
msgid "Lock not found"
-msgstr ""
+msgstr "找ä¸åˆ°éŽ–定的檔案"
+
+msgid "Lock to current projects"
+msgstr "鎖定目å‰å°ˆæ¡ˆ"
msgid "Locked"
msgstr "鎖定"
msgid "Locked Files"
-msgstr ""
+msgstr "鎖定的檔案"
+
+msgid "Locked to current projects"
+msgstr "已鎖定目å‰å°ˆæ¡ˆ"
msgid "Locks give the ability to lock specific file or folder."
-msgstr ""
+msgstr "《鎖定》æ供了鎖定特定檔案或資料夾的能力。"
-msgid "Login"
-msgstr "登入"
+msgid "Logs"
+msgstr "記錄檔"
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr "無論身在何處,都能讓您團隊中的æ¯å€‹äººéƒ½æ›´æœ‰æ•ˆçŽ‡ã€‚GitLab Geo 建立了您 GitLab 主機的唯讀é¡åƒï¼Œæ‰€ä»¥æ‚¨å¯ä»¥ç¸®çŸ­è¤‡è£½å’ŒæŠ“å–大型版本庫的時間。"
+
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
msgstr ""
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
+msgid "Manage access"
+msgstr "管ç†å­˜å–"
+
msgid "Manage all notifications"
+msgstr "管ç†æ‰€æœ‰é€šçŸ¥"
+
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
msgstr ""
-msgid "Manage group labels"
+msgid "Manage applications that you've authorized to use your account."
msgstr ""
+msgid "Manage group labels"
+msgstr "管ç†ç¾¤çµ„標籤"
+
msgid "Manage labels"
-msgstr ""
+msgstr "管ç†æ¨™ç±¤"
msgid "Manage project labels"
-msgstr ""
+msgstr "管ç†å°ˆæ¡ˆæ¨™ç±¤"
msgid "Manage your group’s membership while adding another level of security with SAML."
+msgstr "當é€éŽ SAML 增加其他安全性等級時,管ç†æ‚¨çš„群組æˆå“¡èº«ä»½ã€‚"
+
+msgid "Manifest"
+msgstr "Manifest"
+
+msgid "Manifest file import"
+msgstr "匯入 Manifest 檔案"
+
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
-msgid "Mar"
+msgid "Map a Google Code user to a GitLab user"
msgstr ""
-msgid "March"
+msgid "Map a Google Code user to a full email address"
msgstr ""
-msgid "Mark done"
+msgid "Map a Google Code user to a full name"
msgstr ""
+msgid "Mar"
+msgstr "三月"
+
+msgid "March"
+msgstr "三月"
+
+msgid "Mark todo as done"
+msgstr "標記「å³å°‡å®Œæˆã€ç‚ºå®Œæˆã€‚"
+
+msgid "Markdown enabled"
+msgstr "已啟用 Markdown"
+
msgid "Maximum git storage failures"
msgstr "最大 git 儲存失敗"
msgid "May"
-msgstr ""
+msgstr "五月"
msgid "Median"
msgstr "中ä½æ•¸"
@@ -2598,182 +3963,302 @@ msgid "Members"
msgstr "æˆå“¡"
msgid "Members will be forwarded here when signing in to your group. Get this from your identity provider, where it can also be called \"SSO Service Location\", \"SAML Token Issuance Endpoint\", or \"SAML 2.0/W-Federation URL\"."
-msgstr ""
+msgstr "當登入至您的群組,æˆå“¡å€‘將會被轉移到這裡。從您的身份æä¾›å–得這個,其å¯èƒ½è¢«ç¨±ç‚ºã€ŒSSO æœå‹™ä½ç½®ã€ã€ã€ŒSAML 憑證頒發端點ã€æˆ–「SAML 2.0/W-Federation URLã€ã€‚"
+
+msgid "Merge Request"
+msgstr "åˆä½µè«‹æ±‚"
+
+msgid "Merge Request:"
+msgstr "åˆä½µè«‹æ±‚:"
msgid "Merge Requests"
-msgstr "åˆä½µè«‹æ±‚ (merge request)"
+msgstr "åˆä½µè«‹æ±‚"
+
+msgid "Merge Requests created"
+msgstr "已建立åˆä½µè«‹æ±‚"
msgid "Merge events"
-msgstr "åˆä½µ (merge) 事件"
+msgstr "åˆä½µäº‹ä»¶"
msgid "Merge request"
msgstr "åˆä½µè«‹æ±‚"
+msgid "Merge request approvals"
+msgstr "批准åˆä½µè«‹æ±‚"
+
+msgid "Merge requests"
+msgstr "åˆä½µè«‹æ±‚"
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
-msgstr ""
+msgstr "åˆä½µè«‹æ±‚是一個讓其他人æ出更改建議並討論的地方"
+
+msgid "MergeRequests|Resolve this discussion in a new issue"
+msgstr "在新議題中解決此討論"
+
+msgid "MergeRequests|Saving the comment failed"
+msgstr "儲存評論失敗"
+
+msgid "MergeRequests|Toggle comments for this file"
+msgstr "切æ›æ­¤æ–‡ä»¶çš„註釋"
+
+msgid "MergeRequests|Updating discussions failed"
+msgstr "更新討論失敗"
+
+msgid "MergeRequests|View file @ %{commitId}"
+msgstr "查看文件@ %{commitId}"
+
+msgid "MergeRequests|View replaced file @ %{commitId}"
+msgstr "查看替æ›æ–‡ä»¶@ %{commitId}"
msgid "Merged"
-msgstr ""
+msgstr "å·²åˆä½µ"
msgid "Messages"
msgstr "公告"
+msgid "Metrics"
+msgstr "指標"
+
msgid "Metrics - Influx"
-msgstr ""
+msgstr "指標 - Influx"
msgid "Metrics - Prometheus"
-msgstr ""
+msgstr "指標 - Prometheus"
msgid "Metrics|Business"
-msgstr ""
+msgstr "ä¼æ¥­"
+
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr "查看有關部署到環境的 CI / CD 文件。"
msgid "Metrics|Create metric"
-msgstr ""
+msgstr "建立指標"
msgid "Metrics|Edit metric"
-msgstr ""
+msgstr "編輯指標"
+
+msgid "Metrics|Environment"
+msgstr "環境"
msgid "Metrics|For grouping similar metrics"
-msgstr ""
+msgstr "å°ç›¸ä¼¼çš„指標進行分組"
msgid "Metrics|Label of the chart's vertical axis. Usually the type of the unit being charted. The horizontal axis (X-axis) always represents time."
-msgstr ""
+msgstr "圖表垂直軸的標籤,通常是繪製單ä½çš„類型。水平軸 (X 軸) 總是表示時間。"
+
+msgid "Metrics|Learn about environments"
+msgstr "了解關於環境的資訊"
msgid "Metrics|Legend label (optional)"
-msgstr ""
+msgstr "圖例標籤(éžå¿…è¦ï¼‰"
msgid "Metrics|Must be a valid PromQL query."
-msgstr ""
+msgstr "必須是有效的 PromQL 查詢。"
msgid "Metrics|Name"
-msgstr ""
+msgstr "å稱"
msgid "Metrics|New metric"
-msgstr ""
+msgstr "建立指標"
+
+msgid "Metrics|No deployed environments"
+msgstr "沒有已經佈署的環境"
msgid "Metrics|Prometheus Query Documentation"
-msgstr ""
+msgstr "Prometheus 查詢文件"
msgid "Metrics|Query"
-msgstr ""
+msgstr "查詢"
msgid "Metrics|Response"
-msgstr ""
+msgstr "回應"
msgid "Metrics|System"
+msgstr "系統"
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr "å–得環境資訊時發生錯誤,請é‡è©¦ã€‚"
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr "å–得部署資訊時發生錯誤。"
+
+msgid "Metrics|There was an error getting environments information."
+msgstr "å–得部署資訊時發生錯誤。"
+
+msgid "Metrics|There was an error while retrieving metrics"
msgstr ""
msgid "Metrics|Type"
-msgstr ""
+msgstr "類別"
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr "來自 Prometheus 端點的éžé æœŸéƒ¨å±¬è³‡æ–™å›žæ‡‰"
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr "來自 Prometheus 端點的éžé æœŸæŒ‡æ¨™è³‡æ–™å›žæ‡‰"
msgid "Metrics|Unit label"
-msgstr ""
+msgstr "å–®ä½æ¨™ç±¤"
msgid "Metrics|Used as a title for the chart"
-msgstr ""
+msgstr "用來當作圖表的標題"
msgid "Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response."
-msgstr ""
+msgstr "如果查詢回傳了單個åºåˆ—則使用,如果其回傳多個åºåˆ—,則會從回應中å–出其圖例標籤。"
msgid "Metrics|Y-axis label"
-msgstr ""
+msgstr "Y 軸標籤"
msgid "Metrics|e.g. HTTP requests"
-msgstr ""
+msgstr "例如 HTTP 請求"
msgid "Metrics|e.g. Requests/second"
-msgstr ""
+msgstr "例如:æ¯ç§’請求"
msgid "Metrics|e.g. Throughput"
-msgstr ""
+msgstr "例如:Throughput"
msgid "Metrics|e.g. rate(http_requests_total[5m])"
-msgstr ""
+msgstr "例如:rate(http_requests_total[5m])"
msgid "Metrics|e.g. req/sec"
-msgstr ""
+msgstr "例如:æ¯ç§’請求"
msgid "Milestone"
-msgstr ""
+msgstr "里程碑"
+
+msgid "Milestones"
+msgstr "里程碑"
msgid "Milestones|Delete milestone"
-msgstr ""
+msgstr "刪除里程碑"
msgid "Milestones|Delete milestone %{milestoneTitle}?"
-msgstr ""
+msgstr "刪除里程碑 %{milestoneTitle} ?"
msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
-msgstr ""
+msgstr "刪除里程碑 %{milestoneTitle} 時發生錯誤"
msgid "Milestones|Milestone %{milestoneTitle} was not found"
-msgstr ""
+msgstr "找ä¸åˆ°é‡Œç¨‹ç¢‘ %{milestoneTitle}"
msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
-msgstr ""
+msgstr "å°‡ %{milestoneTitle} æå‡æˆç¾¤çµ„里程碑?"
msgid "Milestones|Promote Milestone"
-msgstr ""
+msgstr "推動里程碑"
msgid "Milestones|This action cannot be reversed."
-msgstr ""
+msgstr "這動作無法復原。"
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "新增 SSH 金鑰"
msgid "Modal|Cancel"
-msgstr ""
+msgstr "å–消"
msgid "Modal|Close"
-msgstr ""
+msgstr "關閉"
msgid "Monitoring"
msgstr "監控"
+msgid "Months"
+msgstr "月"
+
+msgid "More"
+msgstr "更多"
+
+msgid "More actions"
+msgstr "更多動作"
+
msgid "More info"
-msgstr ""
+msgstr "更多資訊"
msgid "More information"
-msgstr ""
+msgstr "更多資訊"
msgid "More information is available|here"
msgstr "å¥åº·æª¢æŸ¥"
+msgid "Most stars"
+msgstr "最多星數"
+
msgid "Move"
-msgstr ""
+msgstr "移動"
msgid "Move issue"
-msgstr ""
+msgstr "移動議題"
msgid "Multiple issue boards"
-msgstr ""
+msgstr "多個議題看æ¿"
+
+msgid "Name"
+msgstr "å稱"
msgid "Name new label"
-msgstr ""
+msgstr "命å新標籤"
+
+msgid "Name your individual key via a title"
+msgstr "é€éŽæ¨™é¡Œç‚ºæ‚¨çš„個人密鑰命å"
+
+msgid "Name:"
+msgstr "å稱:"
+
+msgid "Nav|Help"
+msgstr "幫助"
+
+msgid "Nav|Home"
+msgstr "首é "
+
+msgid "Nav|Sign In / Register"
+msgstr "註冊 / 登入"
+
+msgid "Nav|Sign out and sign in with a different account"
+msgstr "登出,並使用其他帳號登入"
+
+msgid "Network"
+msgstr "網路"
+
+msgid "New"
+msgstr "新增"
+
+msgid "New Application"
+msgstr "新增應用程å¼"
+
+msgid "New Group"
+msgstr "新增群組"
+
+msgid "New Identity"
+msgstr "新增身份"
msgid "New Issue"
msgid_plural "New Issues"
-msgstr[0] "建立議題 (issue) "
-
-msgid "New Kubernetes Cluster"
-msgstr ""
+msgstr[0] "建立議題"
-msgid "New Kubernetes cluster"
-msgstr ""
+msgid "New Label"
+msgstr "新增標籤"
msgid "New Pipeline Schedule"
-msgstr "建立æµæ°´ç·š (pipeline) 排程"
+msgstr "建立æµæ°´ç·šæŽ’程"
+
+msgid "New Snippet"
+msgstr "新增片段"
+
+msgid "New Snippets"
+msgstr "新增片段"
msgid "New branch"
-msgstr "新分支 (branch) "
+msgstr "新分支"
msgid "New branch unavailable"
-msgstr ""
+msgstr "新的分支ä¸å¯ç”¨"
msgid "New directory"
msgstr "新增目錄"
msgid "New epic"
-msgstr ""
+msgstr "æ–°çš„ Epic"
msgid "New file"
msgstr "新增檔案"
@@ -2781,14 +4266,20 @@ msgstr "新增檔案"
msgid "New group"
msgstr "新群組"
+msgid "New identity"
+msgstr "新增身份"
+
msgid "New issue"
-msgstr "新增議題 (issue) "
+msgstr "新增議題"
msgid "New label"
-msgstr ""
+msgstr "建立標籤"
msgid "New merge request"
-msgstr "新增åˆä½µè«‹æ±‚ (merge request) "
+msgstr "新增åˆä½µè«‹æ±‚"
+
+msgid "New pipelines will cancel older, pending pipelines on the same branch"
+msgstr "æ–°çš„æµæ°´ç·šå°‡å–消åŒä¸€åˆ†æ”¯ä¸Šè¼ƒèˆŠã€æœªè§£æ±ºçš„æµæ°´ç·š"
msgid "New project"
msgstr "新專案"
@@ -2805,107 +4296,149 @@ msgstr "æ–°å­ç¾¤çµ„"
msgid "New tag"
msgstr "新增標籤"
+msgid "New..."
+msgstr "新增…"
+
+msgid "No"
+msgstr "å¦"
+
msgid "No Label"
-msgstr ""
+msgstr "沒有標籤"
msgid "No assignee"
-msgstr ""
+msgstr "未指派"
msgid "No changes"
-msgstr ""
+msgstr "沒有改變"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
-msgstr ""
+msgstr "無法連接到 Gitaly 伺æœå™¨ï¼Œè«‹æª¢æŸ¥æ‚¨çš„日誌ï¼"
msgid "No due date"
-msgstr ""
+msgstr "沒有到期日"
msgid "No estimate or time spent"
-msgstr ""
+msgstr "沒有é ä¼°æˆ–花費時間"
msgid "No file chosen"
+msgstr "未é¸æ“‡ä»»ä½•æª”案"
+
+msgid "No files found"
+msgstr "找ä¸åˆ°ä»»ä½•æª”案"
+
+msgid "No files found."
+msgstr "找ä¸åˆ°ä»»ä½•æª”案"
+
+msgid "No issues for the selected time period."
+msgstr "é¸å–的時間範åœä¸­æ²’有議題。"
+
+msgid "No labels with such name or description"
msgstr ""
-msgid "No labels created yet."
+msgid "No merge requests for the selected time period."
+msgstr "é¸å–的時間範åœä¸­æ²’有åˆä½µè«‹æ±‚。"
+
+msgid "No merge requests found"
+msgstr "找ä¸åˆ°åˆä½µè«‹æ±‚"
+
+msgid "No messages were logged"
+msgstr "沒有消æ¯è¢«è¨˜éŒ„"
+
+msgid "No other labels with such name or description"
+msgstr ""
+
+msgid "No prioritised labels with such name or description"
msgstr ""
+msgid "No public groups"
+msgstr "沒有公開群組"
+
+msgid "No pushes for the selected time period."
+msgstr "é¸å–的時間範åœä¸­æ²’有推é€ã€‚"
+
msgid "No repository"
-msgstr "找ä¸åˆ°æª”案庫 (repository)"
+msgstr "找ä¸åˆ°æª”案庫"
msgid "No schedules"
msgstr "沒有排程"
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr "ç„¡"
msgid "Not allowed to merge"
-msgstr ""
+msgstr "ä¸å…許åˆä½µ"
msgid "Not available"
msgstr "無法使用"
msgid "Not available for private projects"
-msgstr ""
+msgstr "ä¸é©ç”¨æ–¼ç§äººå°ˆæ¡ˆ"
msgid "Not available for protected branches"
-msgstr ""
+msgstr "ä¸é©ç”¨æ–¼å—ä¿è­·çš„分支"
msgid "Not confidential"
-msgstr ""
+msgstr "éžæ©Ÿå¯†"
msgid "Not enough data"
msgstr "資料ä¸è¶³"
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
-msgstr ""
+msgstr "請注æ„,master 分支é è¨­ç‚ºå—ä¿è­·çš„。 %{link_to_protected_branches}"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "注æ„:作為管ç†å“¡ï¼Œæ‚¨å¯èƒ½å¸Œæœ›è¨­å®š %{github_integration_link},其å¯ä»¥å…許é€éŽ GitHub 登入,並å…許在未產生個人存å–憑證的情æ³ä¸‹é€£ç·šç‰ˆæœ¬åº«ã€‚"
msgid "Note: As an administrator you may like to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "注æ„:作為管ç†å“¡ï¼Œæ‚¨å¯èƒ½å¸Œæœ›é…ç½® %{github_integration_link},這將å…è¨±é€šéŽ GitHub 登入並å…許匯入存儲庫而ä¸ç”Ÿæˆå€‹äººå­˜å–憑證。"
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow connecting repositories without generating a Personal Access Token."
-msgstr ""
+msgstr "注æ„:請考慮讓您的 GitLab 管ç†å“¡è¨­å®š %{github_integration_link},讓其å…許é€éŽ GitHub 登入並å…許在沒有產生個人存å–憑證的情æ³ä¸‹é€£ç·šåˆ°ç‰ˆæœ¬åº«ã€‚"
msgid "Note: Consider asking your GitLab administrator to configure %{github_integration_link}, which will allow login via GitHub and allow importing repositories without generating a Personal Access Token."
+msgstr "注æ„:請考慮讓您的 GitLab 管ç†å“¡è¨­å®š %{github_integration_link},這將å…許使用者é€éŽ GitHub 登入並å…許匯入儲存庫而ä¸ç”Ÿæˆå€‹äººå­˜å–憑證。"
+
+msgid "Notes|Are you sure you want to cancel creating this comment?"
msgstr ""
msgid "Notification events"
msgstr "事件通知"
msgid "NotificationEvent|Close issue"
-msgstr "關閉議題 (issue) "
+msgstr "關閉議題"
msgid "NotificationEvent|Close merge request"
-msgstr "關閉åˆä½µè«‹æ±‚ (merge request) "
+msgstr "關閉åˆä½µè«‹æ±‚"
msgid "NotificationEvent|Failed pipeline"
-msgstr "æµæ°´ç·š (pipeline) 失敗"
+msgstr "æµæ°´ç·šå¤±æ•—"
msgid "NotificationEvent|Merge merge request"
-msgstr "åˆä½µè«‹æ±‚ (merge request) 被åˆä½µ"
+msgstr "åˆä½µè«‹æ±‚被åˆä½µ"
msgid "NotificationEvent|New issue"
-msgstr "新增議題 (issue) "
+msgstr "新增議題"
msgid "NotificationEvent|New merge request"
-msgstr "新增åˆä½µè«‹æ±‚ (merge request) "
+msgstr "新增åˆä½µè«‹æ±‚"
msgid "NotificationEvent|New note"
msgstr "新增評論"
msgid "NotificationEvent|Reassign issue"
-msgstr "é‡æ–°æŒ‡æ´¾è­°é¡Œ (issue) "
+msgstr "é‡æ–°æŒ‡æ´¾è­°é¡Œ"
msgid "NotificationEvent|Reassign merge request"
-msgstr "é‡æ–°æŒ‡æ´¾åˆä½µè«‹æ±‚ (merge request) "
+msgstr "é‡æ–°æŒ‡æ´¾åˆä½µè«‹æ±‚"
msgid "NotificationEvent|Reopen issue"
-msgstr "é‡å•Ÿè­°é¡Œ (issue)"
+msgstr "é‡å•Ÿè­°é¡Œ"
msgid "NotificationEvent|Successful pipeline"
-msgstr "æµæ°´ç·š (pipeline) æˆåŠŸå®Œæˆ"
+msgstr "æµæ°´ç·šæˆåŠŸå®Œæˆ"
msgid "NotificationLevel|Custom"
msgstr "自訂"
@@ -2929,46 +4462,73 @@ msgid "Notifications"
msgstr "通知"
msgid "Notifications off"
-msgstr ""
+msgstr "關閉通知"
msgid "Notifications on"
-msgstr ""
+msgstr "開啟通知"
msgid "Nov"
-msgstr ""
+msgstr "å一月"
msgid "November"
-msgstr ""
+msgstr "å一月"
msgid "Number of access attempts"
msgstr "嘗試存å–的次數"
msgid "OK"
-msgstr ""
+msgstr "確定"
msgid "Oct"
-msgstr ""
+msgstr "å月"
msgid "October"
-msgstr ""
+msgstr "å月"
msgid "OfSearchInADropdown|Filter"
msgstr "篩é¸"
msgid "Once imported, repositories can be mirrored over SSH. Read more %{ssh_link}"
+msgstr "一旦匯入,就å¯ä»¥é€éŽ SSH é¡åƒç‰ˆæœ¬åº«ã€‚閱讀 %{ssh_link} 以了解更多資訊"
+
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "Online IDE integration settings."
+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 "Online IDE integration settings."
+msgstr "線上 IDE æ•´åˆè¨­å®šã€‚"
+
+msgid "Only comments from the following commit are shown below"
+msgstr "下é¢åƒ…顯示來自以下æ交的註釋"
+
msgid "Only project members can comment."
msgstr "åªæœ‰ç¾¤çµ„æˆå“¡æ‰èƒ½ç•™è¨€ã€‚"
+msgid "Oops, are you sure?"
+msgstr "喔喔…你確定嗎?"
+
msgid "Open"
+msgstr "é–‹å•Ÿ"
+
+msgid "Open in Xcode"
+msgstr "在 Xcode 開啟"
+
+msgid "Open sidebar"
+msgstr "é–‹å•Ÿå´é‚Šæ¬„"
+
+msgid "Open source software to collaborate on code"
msgstr ""
msgid "Opened"
-msgstr ""
+msgstr "é–‹å•Ÿçš„"
+
+msgid "Opened MR"
+msgstr "é–‹å•Ÿçš„åˆä½µè«‹æ±‚ (MR)"
+
+msgid "Opened issues"
+msgstr "開啟的議題"
msgid "OpenedNDaysAgo|Opened"
msgstr "開始於"
@@ -2976,14 +4536,32 @@ msgstr "開始於"
msgid "Opens in a new window"
msgstr "於新視窗開啟"
+msgid "Operations"
+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 "é¸é …"
+msgid "Or you can choose one of the suggested colors below"
+msgstr "或者您å¯ä»¥åœ¨ä¸‹æ–¹å»ºè­°çš„é¡è‰²ä¸­é¸æ“‡ä¸€å€‹"
+
+msgid "Other Labels"
+msgstr "其他標籤"
+
+msgid "Other information"
+msgstr "其他資訊"
+
msgid "Otherwise it is recommended you start with one of the options below."
-msgstr ""
+msgstr "此外,建議您從下é¢çš„一個é¸é …開始。"
msgid "Outbound requests"
-msgstr ""
+msgstr "Outbound 請求"
msgid "Overview"
msgstr "總覽"
@@ -2992,7 +4570,7 @@ msgid "Owner"
msgstr "所有權"
msgid "Pages"
-msgstr ""
+msgstr "é é¢"
msgid "Pagination|Last »"
msgstr "æœ€æœ«é  Â»"
@@ -3007,34 +4585,55 @@ msgid "Pagination|« First"
msgstr "« 第一é "
msgid "Part of merge request changes"
-msgstr ""
+msgstr "åˆä½µè«‹æ±‚更改的部分"
msgid "Password"
msgstr "密碼"
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgstr "貼上您的SSH公鑰,通常放置在 '~/.ssh/id_rsa.pub',並以 'ssh-rsa' 開頭。ä¸è¦ä½¿ç”¨æ‚¨çš„SSHç§é‘°ã€‚"
+
+msgid "Path:"
+msgstr "ä½ç½®ï¼š"
+
+msgid "Pause"
+msgstr "æš«åœ"
+
msgid "Pending"
-msgstr ""
+msgstr "等待處ç†"
+
+msgid "Per job. If a job passes this threshold, it will be marked as failed"
+msgstr "æ¯ä»½ä»»å‹™ã€‚如果任務通éŽæ­¤é–¾å€¼ï¼Œå®ƒå°‡è¢«æ¨™è¨˜ç‚ºå¤±æ•—"
+
+msgid "Perform advanced options such as changing path, transferring, or removing the group."
+msgstr "執行進階é¸é …,例如更改路徑ã€å‚³è¼¸æˆ–移除群組。"
msgid "Performance optimization"
-msgstr ""
+msgstr "效能最佳化"
+
+msgid "Permissions"
+msgstr "權é™"
msgid "Personal Access Token"
-msgstr ""
+msgstr "個人訪å•æ†‘è­‰"
msgid "Pipeline"
-msgstr "æµæ°´ç·š (pipeline) "
+msgstr "æµæ°´ç·š"
msgid "Pipeline Health"
-msgstr "æµæ°´ç·š (pipeline) å¥åº·æŒ‡æ•¸"
+msgstr "æµæ°´ç·šå¥åº·æŒ‡æ•¸"
msgid "Pipeline Schedule"
-msgstr "æµæ°´ç·š (pipeline) 排程"
+msgstr "æµæ°´ç·šæŽ’程"
msgid "Pipeline Schedules"
-msgstr "æµæ°´ç·š (pipeline) 排程"
+msgstr "æµæ°´ç·šæŽ’程"
msgid "Pipeline quota"
-msgstr ""
+msgstr "æµæ°´ç·šé¡åº¦"
+
+msgid "Pipeline triggers"
+msgstr "æµæ°´ç·šè§¸ç™¼å™¨"
msgid "PipelineCharts|Failed:"
msgstr "失敗:"
@@ -3070,7 +4669,7 @@ msgid "PipelineSchedules|None"
msgstr "ç„¡"
msgid "PipelineSchedules|Provide a short description for this pipeline"
-msgstr "請簡單說明此æµæ°´ç·š (pipeline) "
+msgstr "請簡單說明此æµæ°´ç·š"
msgid "PipelineSchedules|Take ownership"
msgstr "å–得所有權"
@@ -3085,10 +4684,10 @@ msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "自訂"
msgid "Pipelines"
-msgstr "æµæ°´ç·š (pipeline) "
+msgstr "æµæ°´ç·š"
msgid "Pipelines charts"
-msgstr "æµæ°´ç·š (pipeline) 圖表"
+msgstr "æµæ°´ç·šåœ–表"
msgid "Pipelines for last month"
msgstr "上個月的æµæ°´ç·š"
@@ -3100,7 +4699,7 @@ msgid "Pipelines for last year"
msgstr "去年的æµæ°´ç·š"
msgid "Pipelines|Build with confidence"
-msgstr ""
+msgstr "信心滿滿的建立"
msgid "Pipelines|CI Lint"
msgstr "CI Lint"
@@ -3109,46 +4708,58 @@ msgid "Pipelines|Clear Runner Caches"
msgstr "清除é‹è¡Œå™¨å¿«å–"
msgid "Pipelines|Get started with Pipelines"
-msgstr ""
+msgstr "開始"
msgid "Pipelines|Loading Pipelines"
-msgstr ""
+msgstr "讀å–æµæ°´ç·š"
msgid "Pipelines|Project cache successfully reset."
-msgstr ""
+msgstr "專案快å–已經æˆåŠŸé‡ç½®ã€‚"
msgid "Pipelines|Run Pipeline"
-msgstr ""
+msgstr "執行æµæ°´ç·š"
msgid "Pipelines|Something went wrong while cleaning runners cache."
-msgstr ""
+msgstr "清除é‹è¡Œå™¨å¿«å–時發生錯誤。"
msgid "Pipelines|There are currently no %{scope} pipelines."
-msgstr ""
+msgstr "ç›®å‰æ²’有 %{scope} æµæ°´ç·šã€‚"
msgid "Pipelines|There are currently no pipelines."
-msgstr ""
+msgstr "ç›®å‰æ²’有æµæ°´ç·šã€‚"
msgid "Pipelines|This project is not currently set up to run pipelines."
-msgstr ""
+msgstr "這個專案目å‰é‚„沒設定æµæ°´ç·šã€‚"
-msgid "Pipeline|Retry pipeline"
-msgstr ""
+msgid "Pipeline|Create for"
+msgstr "建立"
-msgid "Pipeline|Retry pipeline #%{pipelineId}?"
-msgstr ""
+msgid "Pipeline|Create pipeline"
+msgstr "建立æµæ°´ç·š"
+
+msgid "Pipeline|Existing branch name or tag"
+msgstr "存在的分支å稱或標籤"
+
+msgid "Pipeline|Run Pipeline"
+msgstr "執行æµæ°´ç·š"
+
+msgid "Pipeline|Search branches"
+msgstr "æœå°‹åˆ†æ”¯"
+
+msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default."
+msgstr "指定è¦ä½¿ç”¨åœ¨æ­¤æ¬¡åŸ·è¡Œçš„變數值。%{settings_link} 中指定的值將會使用為é è¨­å€¼ã€‚"
msgid "Pipeline|Stop pipeline"
-msgstr ""
+msgstr "åœæ­¢æµæ°´ç·š"
msgid "Pipeline|Stop pipeline #%{pipelineId}?"
-msgstr ""
+msgstr "åœæ­¢æµæ°´ç·š #%{pipelineId}?"
-msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
-msgstr ""
+msgid "Pipeline|Variables"
+msgstr "變數"
msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
-msgstr ""
+msgstr "ä½ å°‡åœæ­¢æµæ°´ç·š %{pipelineId}。"
msgid "Pipeline|all"
msgstr "所有"
@@ -3162,29 +4773,68 @@ msgstr "於階段"
msgid "Pipeline|with stages"
msgstr "於階段"
+msgid "Plain diff"
+msgstr "本文差異"
+
+msgid "Planned finish date"
+msgstr "計畫完æˆæ—¥æœŸ"
+
+msgid "Planned start date"
+msgstr "計畫開始日期"
+
msgid "PlantUML"
-msgstr ""
+msgstr "PlantUML"
msgid "Play"
+msgstr "執行"
+
+msgid "Please accept the Terms of Service before continuing."
+msgstr "在繼續之å‰ï¼Œè«‹åŒæ„æœå‹™æ¢æ¬¾"
+
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
msgstr ""
-msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
-msgid "Please solve the reCAPTCHA"
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please select at least one filter to see results"
+msgstr "è«‹é¸æ“‡è‡³å°‘一個篩é¸å™¨ä»¥æª¢è¦–çµæžœ"
+
+msgid "Please solve the reCAPTCHA"
+msgstr "請填寫驗證碼"
+
+msgid "Please try again"
+msgstr "è«‹å†è©¦ä¸€æ¬¡"
+
msgid "Please wait while we connect to your repository. Refresh at will."
-msgstr ""
+msgstr "è«‹ç¨å€™ï¼Œæˆ‘們正在連çµåˆ°æ‚¨çš„版本庫,ç¨å¾Œè«‹é‡æ–°æ•´ç†ã€‚"
msgid "Please wait while we import the repository for you. Refresh at will."
-msgstr ""
+msgstr "è«‹ç¨å€™ï¼Œæˆ‘們正在匯入您的檔案庫,ç¨å¾Œè«‹é‡æ–°æ•´ç†ã€‚"
msgid "Preferences"
msgstr "å好設定"
+msgid "Preferences|Navigation theme"
+msgstr "導航主題"
+
msgid "Primary"
-msgstr ""
+msgstr "主è¦"
+
+msgid "Prioritize"
+msgstr "優先"
+
+msgid "Prioritize label"
+msgstr "優先標籤"
+
+msgid "Prioritized Labels"
+msgstr "優先標籤"
+
+msgid "Prioritized label"
+msgstr "優先標籤"
msgid "Private - Project access must be granted explicitly to each user."
msgstr "ç§æœ‰ - 專案權é™å¿…須一一指派給æ¯å€‹ä½¿ç”¨è€…"
@@ -3193,14 +4843,26 @@ msgid "Private - The group and its projects can only be viewed by members."
msgstr "ç§æœ‰ - 群組åŠæ——下專案åªèƒ½è¢«è©²ç¾¤çµ„æˆå“¡æŸ¥çœ‹"
msgid "Private projects can be created in your personal namespace with:"
-msgstr ""
+msgstr "å¯ä»¥åœ¨æ‚¨çš„個人命å空間中建立ç§äººå°ˆæ¡ˆ:"
msgid "Profile"
msgstr "個人資料"
+msgid "Profile Settings"
+msgstr "個人資料設定"
+
msgid "Profiles|Account scheduled for removal."
msgstr "帳號將會被刪除"
+msgid "Profiles|Add key"
+msgstr "新增金鑰"
+
+msgid "Profiles|Change username"
+msgstr "變更使用者å稱"
+
+msgid "Profiles|Current path: %{path}"
+msgstr "ç›®å‰è·¯å¾‘:%{path}"
+
msgid "Profiles|Delete Account"
msgstr "刪除帳號"
@@ -3219,9 +4881,27 @@ msgstr "無效的密碼"
msgid "Profiles|Invalid username"
msgstr "無效的使用者å稱"
+msgid "Profiles|Path"
+msgstr "ä½ç½®"
+
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr "這看起來ä¸åƒæ˜¯ SSH 公鑰,您確定è¦å¢žåŠ å®ƒå—Žï¼Ÿ"
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "輸入您的 %{confirmationValue} 以確èªï¼š"
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr "通常以 \"ssh-rsa …\" 起頭"
+
+msgid "Profiles|Update username"
+msgstr "更新使用者å稱"
+
+msgid "Profiles|Username change failed - %{message}"
+msgstr "使用者å稱更改失敗 - %{message}"
+
+msgid "Profiles|Username successfully changed"
+msgstr "使用者å稱順利變更"
+
msgid "Profiles|You don't have access to delete this user."
msgstr "您沒有權é™åˆªé™¤æ­¤å¸³è™Ÿ"
@@ -3231,14 +4911,23 @@ msgstr "你必須轉æ›ä½ çš„所有權或在你刪除你帳號å‰åˆªé™¤é€™äº›ç¾¤
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "你的帳號目å‰æ“有這些群組:"
+msgid "Profiles|e.g. My MacBook key"
+msgstr "例如:我的 MacBook 金鑰"
+
msgid "Profiles|your account"
msgstr "你的帳號"
msgid "Profiling - Performance bar"
-msgstr ""
+msgstr "效能欄"
msgid "Programming languages used in this repository"
-msgstr ""
+msgstr "在這個檔案庫中使用的程å¼èªžè¨€"
+
+msgid "Progress"
+msgstr "進度"
+
+msgid "Project"
+msgstr "專案"
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "專案 \"%{project_name}\" 正在被刪除。"
@@ -3252,14 +4941,17 @@ msgstr "專案 '%{project_name}' 建立完æˆã€‚"
msgid "Project '%{project_name}' was successfully updated."
msgstr "專案 '%{project_name}' 更新完æˆã€‚"
+msgid "Project Badges"
+msgstr "專案徽章"
+
msgid "Project access must be granted explicitly to each user."
msgstr "專案權é™å¿…須一一指派給æ¯å€‹ä½¿ç”¨è€…。"
msgid "Project avatar"
-msgstr ""
+msgstr "專案圖åƒ"
msgid "Project avatar in repository: %{link}"
-msgstr ""
+msgstr "專案圖åƒåœ¨æª”案庫中: %{link}"
msgid "Project details"
msgstr "專案細節"
@@ -3276,32 +4968,26 @@ msgstr "專案的匯出連çµå·²å¤±æ•ˆã€‚請到專案設定中產生新的連çµ
msgid "Project export started. A download link will be sent by email."
msgstr "專案導出已開始。完æˆå¾Œä¸‹è¼‰é€£çµæœƒé€åˆ°æ‚¨çš„信箱。"
+msgid "Project name"
+msgstr "專案å稱"
+
msgid "ProjectActivityRSS|Subscribe"
msgstr "訂閱"
msgid "ProjectCreationLevel|Allowed to create projects"
-msgstr ""
+msgstr "å…許建立專案"
msgid "ProjectCreationLevel|Default project creation protection"
-msgstr ""
+msgstr "é è¨­å°ˆæ¡ˆå»ºç«‹ä¿è­·"
-msgid "ProjectCreationLevel|Developers + Masters"
-msgstr ""
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr "開發者 + 維護者"
-msgid "ProjectCreationLevel|Masters"
-msgstr ""
+msgid "ProjectCreationLevel|Maintainers"
+msgstr "維護者"
msgid "ProjectCreationLevel|No one"
-msgstr ""
-
-msgid "ProjectFeature|Disabled"
-msgstr "åœç”¨"
-
-msgid "ProjectFeature|Everyone with access"
-msgstr "任何人都å¯å­˜å–"
-
-msgid "ProjectFeature|Only team members"
-msgstr "åªæœ‰åœ˜éšŠæˆå“¡å¯ä»¥å­˜å–"
+msgstr "完全沒有"
msgid "ProjectFileTree|Name"
msgstr "å稱"
@@ -3312,30 +4998,39 @@ msgstr "從未"
msgid "ProjectLifecycle|Stage"
msgstr "階段"
-msgid "ProjectNetworkGraph|Graph"
-msgstr "分支圖"
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
-msgstr ""
+msgstr "è¯çµ¡ç®¡ç†å“¡ä»¥è®Šæ›´é€™å€‹è¨­å®šã€‚"
+
+msgid "ProjectSettings|Failed to protect the tag"
+msgstr "無法ä¿è­·æ¨™ç±¤"
+
+msgid "ProjectSettings|Failed to update tag!"
+msgstr "無法更新標籤ï¼"
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
-msgstr ""
+msgstr "åªæœ‰å·²ç°½ç½²çš„變更æ‰èƒ½æŽ¨é€åˆ°ç‰ˆæœ¬åº«ã€‚"
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
-msgstr ""
+msgstr "此設定已經套用於伺æœå™¨å±¤ç´šï¼Œä¸¦ä¸”å¯è¢«ç®¡ç†å“¡è¦†å¯«ã€‚"
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
-msgstr ""
+msgstr "此設定已經套用於伺æœå™¨ç´šåˆ¥ï¼Œä½†å·²ç¶“在這個專案被覆寫。"
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
-msgstr ""
+msgstr "此設定將套用於所有專案,除éžè¢«ç®¡ç†å“¡è¦†å¯«ã€‚"
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
-msgstr ""
+msgstr "使用者åªèƒ½é€éŽä»–們自己通éŽé©—證的電å­ä¿¡ç®±ä¹‹ä¸€æ交,æ‰èƒ½æŽ¨é€æ交到這個版本庫。"
msgid "Projects"
msgstr "專案"
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr "經常使用"
@@ -3354,95 +5049,146 @@ msgstr "發生了內部錯誤"
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr "抱歉,沒有符åˆæœå°‹æ¢ä»¶çš„專案"
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr "此功能需è¦ç€è¦½å™¨æ”¯æ´ localStorage"
+msgid "PrometheusAlerts|Add alert"
+msgstr "增加警報"
-msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgid "PrometheusAlerts|Alert set"
+msgstr "警報設定"
+
+msgid "PrometheusAlerts|Edit alert"
+msgstr "編輯警報"
+
+msgid "PrometheusAlerts|Error creating alert"
msgstr ""
-msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgid "PrometheusAlerts|Error deleting alert"
msgstr ""
-msgid "PrometheusService|Active"
+msgid "PrometheusAlerts|Error fetching alert"
msgstr ""
-msgid "PrometheusService|Auto configuration"
+msgid "PrometheusAlerts|Error saving alert"
msgstr ""
+msgid "PrometheusAlerts|No alert set"
+msgstr "未設定警報"
+
+msgid "PrometheusAlerts|Operator"
+msgstr "æ“作者"
+
+msgid "PrometheusAlerts|Threshold"
+msgstr "門檻"
+
+msgid "PrometheusDashboard|Time"
+msgstr "時間"
+
+msgid "PrometheusService|%{exporters} with %{metrics} were found"
+msgstr "ç™¼ç¾ %{exporters} åŠ %{metrics}"
+
+msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>"
+msgstr "<p class=\"text-tertiary\">沒有找到<a href=\"%{docsUrl}\">常見指標</a></p>"
+
+msgid "PrometheusService|Active"
+msgstr "啟用"
+
+msgid "PrometheusService|Auto configuration"
+msgstr "自動設定"
+
msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
-msgstr ""
+msgstr "在你的群集上自動部署和é…ç½® Prometheus 以監控您的專案環境"
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
-msgstr ""
+msgstr "é è¨­ Prometheus ç›£è½ â€˜http://localhost:9090’。ä¸å»ºè­°æ›´æ”¹é è¨­çš„網å€å’Œç›£è½åŸ ï¼Œå› ç‚ºé€™å¯èƒ½æœƒå¼•éŸ¿åˆ° GitLab 伺æœå™¨ä¸Šé‹è¡Œçš„其他æœå‹™ã€‚"
msgid "PrometheusService|Common metrics"
-msgstr ""
+msgstr "常見指標"
msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters."
-msgstr ""
+msgstr "常用的指標會根據å—匯出者歡迎的指標庫來自動監控。"
msgid "PrometheusService|Custom metrics"
-msgstr ""
+msgstr "自訂指標"
msgid "PrometheusService|Finding and configuring metrics..."
-msgstr ""
+msgstr "尋找和é…置指標⋯⋯"
msgid "PrometheusService|Finding custom metrics..."
-msgstr ""
+msgstr "正在æœå°‹è‡ªè¨‚指標…"
msgid "PrometheusService|Install Prometheus on clusters"
-msgstr ""
+msgstr "在å¢é›†ä¸­å®‰è£ Prometheus"
msgid "PrometheusService|Manage clusters"
-msgstr ""
+msgstr "管ç†å¢é›†"
msgid "PrometheusService|Manual configuration"
-msgstr ""
+msgstr "手動設置"
msgid "PrometheusService|Metrics"
-msgstr ""
+msgstr "指標"
msgid "PrometheusService|Missing environment variable"
-msgstr ""
+msgstr "缺少環境變數"
msgid "PrometheusService|More information"
-msgstr ""
+msgstr "更多訊æ¯"
msgid "PrometheusService|New metric"
-msgstr ""
+msgstr "新增指標"
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
-msgstr ""
+msgstr "Prometheus API 地å€ï¼Œä¾‹å¦‚ http://prometheus.example.com/"
msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
-msgstr ""
+msgstr "Prometheus 正在自動管ç†æ‚¨çš„å¢é›†"
msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
-msgstr ""
+msgstr "這些指標åªæœƒåœ¨ä½ ç¬¬ä¸€æ¬¡éƒ¨å±¬ç’°å¢ƒæ™‚æ‰æœƒç›£æŽ§"
msgid "PrometheusService|Time-series monitoring service"
-msgstr ""
+msgstr "時間監控æœå‹™"
msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
-msgstr ""
+msgstr "è¦èµ·ç”¨æ‰‹å‹•é…置,請從群集中å¸è¼‰ Prometheus"
msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
-msgstr ""
+msgstr "如果å…è¨±å®‰è£ Prometheus 在您的群集上,請å–消下é¢çš„手動é…ç½®"
msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics"
-msgstr ""
+msgstr "等您首次部署到環境後以查詢常用指標"
msgid "Promote"
-msgstr ""
+msgstr "å‡ç´š"
-msgid "Promote to Group Label"
-msgstr ""
+msgid "Promote these project milestones into a group milestone."
+msgstr "將這些項目里程碑åˆä½µåˆ°åˆ°ä¸€å€‹ç¾¤çµ„里程碑。"
msgid "Promote to Group Milestone"
-msgstr ""
+msgstr "æå‡è‡³ç¾¤çµ„里程碑"
+
+msgid "Promote to group label"
+msgstr "æå‡è‡³ç¾¤çµ„標籤"
+
+msgid "Promotions|Don't show me this again"
+msgstr "ä¸è¦å†é¡¯ç¤º"
+
+msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
+msgstr "Epic 讓您更有效率且更少工作é‡çš„管ç†æ‚¨çš„專案組åˆï¼Œé€éŽè¿½è¹¤ç›¸åŒä¸»é¡Œã€è·¨å°ˆæ¡ˆèˆ‡é‡Œç¨‹ç¢‘的群組議題。"
+
+msgid "Promotions|This feature is locked."
+msgstr "這個功能已被鎖定。"
+
+msgid "Promotions|Upgrade plan"
+msgstr "å‡ç´šæ–¹æ¡ˆ"
msgid "Protip:"
-msgstr ""
+msgstr "æ示:"
+
+msgid "Provider"
+msgstr "æ供者"
+
+msgid "Pseudonymizer data collection"
+msgstr "Pseudonymizer 資料收集"
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "公開 - 未登入的情æ³ä¸‹ä¾ç„¶å¯ä»¥æŸ¥çœ‹ä»»ä½•å…¬é–‹å°ˆæ¡ˆ"
@@ -3450,201 +5196,315 @@ msgstr "公開 - 未登入的情æ³ä¸‹ä¾ç„¶å¯ä»¥æŸ¥çœ‹ä»»ä½•å…¬é–‹å°ˆæ¡ˆ"
msgid "Public - The project can be accessed without any authentication."
msgstr "公開 - 無須任何身份驗證å³å¯å­˜å–該專案"
+msgid "Public pipelines"
+msgstr "公共æµæ°´ç·š"
+
msgid "Push Rules"
-msgstr ""
+msgstr "推é€è¦å‰‡"
msgid "Push events"
-msgstr "æŽ¨é€ (push) 事件"
+msgstr "推é€äº‹ä»¶"
msgid "Push project from command line"
-msgstr ""
+msgstr "é€éŽæŒ‡ä»¤æŽ¨é€å°ˆæ¡ˆ"
msgid "Push to create a project"
-msgstr ""
+msgstr "é€éŽæŽ¨é€å»ºç«‹å°ˆæ¡ˆ"
msgid "PushRule|Committer restriction"
-msgstr ""
+msgstr "é™åˆ¶æ交者"
+
+msgid "Pushed"
+msgstr "推é€çš„"
+
+msgid "Pushes"
+msgstr "推é€"
+
+msgid "Quarters"
+msgstr "季度"
msgid "Quick actions can be used in the issues description and comment boxes."
-msgstr ""
+msgstr "快速æ“作å¯ä»¥ç”¨æ–¼å•é¡Œæ述和評論框。"
msgid "Read more"
msgstr "瞭解更多"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
+msgstr ""
+
msgid "Readme"
msgstr "說明檔"
msgid "Real-time features"
-msgstr ""
-
-msgid "RefSwitcher|Branches"
-msgstr "分支 (branch) "
-
-msgid "RefSwitcher|Tags"
-msgstr "標籤"
+msgstr "å³æ™‚功能"
msgid "Reference:"
-msgstr ""
+msgstr "åƒè€ƒä¾†æº:"
+
+msgid "Refresh"
+msgstr "é‡æ–°æ•´ç†"
msgid "Register / Sign In"
-msgstr ""
+msgstr "註冊 / 登入"
+
+msgid "Register and see your runners for this group."
+msgstr "註冊ã€ä¸¦è§€å¯Ÿæ‚¨åœ¨é€™å€‹ç¾¤çµ„的執行器。"
+
+msgid "Register and see your runners for this project."
+msgstr "註冊ã€ä¸¦è§€å¯Ÿæ‚¨åœ¨é€™å€‹å°ˆæ¡ˆçš„執行器。"
msgid "Registry"
-msgstr ""
+msgstr "註冊表"
msgid "Related Commits"
-msgstr "相關的更動記錄 (commit) "
+msgstr "相關的更動記錄"
msgid "Related Deployed Jobs"
msgstr "相關的部署作業"
msgid "Related Issues"
-msgstr "相關的議題 (issue) "
+msgstr "相關的議題"
msgid "Related Jobs"
msgstr "相關的作業"
msgid "Related Merge Requests"
-msgstr "相關的åˆä½µè«‹æ±‚ (merge request) "
+msgstr "相關的åˆä½µè«‹æ±‚"
msgid "Related Merged Requests"
msgstr "相關已åˆä½µçš„請求"
msgid "Related merge requests"
-msgstr ""
+msgstr "相關的åˆä½µè«‹æ±‚"
msgid "Remind later"
msgstr "ç¨å¾Œæ醒"
msgid "Remove"
-msgstr ""
+msgstr "刪除"
+
+msgid "Remove Runner"
+msgstr "移除執行器"
msgid "Remove avatar"
-msgstr ""
+msgstr "刪除大頭貼"
+
+msgid "Remove priority"
+msgstr "刪除優先權"
msgid "Remove project"
msgstr "刪除專案"
msgid "Repair authentication"
+msgstr "修復èªè­‰"
+
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
msgstr ""
msgid "Repo by URL"
-msgstr ""
+msgstr "來自 URL 的版本庫"
msgid "Repository"
-msgstr "檔案庫 (repository)"
+msgstr "檔案庫"
+
+msgid "Repository Settings"
+msgstr "檔案庫設置"
+
+msgid "Repository URL"
+msgstr "版本庫 URL"
msgid "Repository has no locks."
-msgstr ""
+msgstr "版本庫沒有上鎖。"
msgid "Repository maintenance"
-msgstr ""
+msgstr "檔案庫維護"
-msgid "Repository mirror settings"
-msgstr ""
+msgid "Repository mirror"
+msgstr "é¡åƒæª”案庫"
msgid "Repository storage"
-msgstr ""
+msgstr "檔案庫儲存空間"
+
+msgid "RepositorySettingsAccessLevel|Select"
+msgstr "é¸æ“‡"
msgid "Request Access"
msgstr "申請權é™"
+msgid "Requests Profiles"
+msgstr "請求個人資料"
+
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
+msgstr "è¦æ±‚所有用戶在訪å•GitLab時接å—æœå‹™æ¢æ¬¾å’Œéš±ç§æ”¿ç­–。"
+
msgid "Reset git storage health information"
msgstr "é‡ç½® Git 儲存空間å¥åº·æŒ‡æ•¸"
msgid "Reset health check access token"
-msgstr "é‡ç½®å¥åº·æª¢æŸ¥å­˜å–憑證 (access token)"
+msgstr "é‡ç½®å¥åº·æª¢æŸ¥å­˜å–憑證"
msgid "Reset runners registration token"
-msgstr "é‡ç½® Runner 註冊憑證 (registration token)"
+msgstr "é‡ç½® Runner 註冊憑證"
+
+msgid "Resolve all discussions in new issue"
+msgstr "建立新議題以解決所有討論"
+
+msgid "Resolve conflicts on source branch"
+msgstr "解決來æºåˆ†æ”¯ä¸Šçš„è¡çª"
msgid "Resolve discussion"
-msgstr ""
+msgstr "關閉討論"
-msgid "Response"
-msgstr ""
+msgid "Response metrics (Custom)"
+msgstr "接收指標 (自訂)"
+
+msgid "Resume"
+msgstr "繼續"
+
+msgid "Retry"
+msgstr "é‡è©¦"
+
+msgid "Retry this job"
+msgstr "é‡è©¦æ­¤å·¥ä½œ"
+
+msgid "Retry verification"
+msgstr "é‡è©¦é©—è­‰"
msgid "Reveal value"
msgid_plural "Reveal values"
-msgstr[0] ""
+msgstr[0] "顯示隱è—的資料"
msgid "Revert this commit"
-msgstr "還原此更動記錄 (commit)"
+msgstr "還原此更動記錄"
msgid "Revert this merge request"
-msgstr "還原此åˆä½µè«‹æ±‚ (merge request) "
+msgstr "還原此åˆä½µ"
+
+msgid "Review"
+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 ""
+msgstr "審閱您的身份æ供者中設定æœå‹™æ供者的éŽç¨‹ -- 如果這樣,GitLab 就是「æœå‹™æ供者ã€æˆ–「ä¾è³´æ–¹ã€ã€‚"
msgid "Reviewing"
-msgstr ""
+msgstr "審查"
msgid "Reviewing (merge request !%{mergeRequestId})"
-msgstr ""
+msgstr "正在審閱中(åˆä½µè«‹æ±‚ !%{mergeRequestId})"
+
+msgid "Revoke"
+msgstr "撤回"
msgid "Roadmap"
-msgstr ""
+msgstr "開發è—圖"
msgid "Run CI/CD pipelines for external repositories"
-msgstr ""
+msgstr "執行外部版本庫的 CI / CD æµæ°´ç·šã€‚"
+
+msgid "Runner token"
+msgstr "執行器憑證"
msgid "Runners"
-msgstr ""
+msgstr "執行器"
+
+msgid "Runners API"
+msgstr "執行器 API"
+
+msgid "Runners can be placed on separate users, servers, and even on your local machine."
+msgstr "執行器å¯æ”¾ç½®æ–¼ä¸åŒçš„使用者ã€ä¼ºæœå™¨ï¼Œç”šè‡³åœ¨æ‚¨çš„本地機器上。"
msgid "Running"
-msgstr ""
+msgstr "執行中"
+
+msgid "SAML SSO"
+msgstr "SAML SSO"
+
+msgid "SAML SSO for %{group_name}"
+msgstr "%{group_name} çš„ SAML SSO"
msgid "SAML Single Sign On"
-msgstr ""
+msgstr "SAML 單一登入"
msgid "SAML Single Sign On Settings"
-msgstr ""
+msgstr "SAML 單一登入設定"
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
-msgstr ""
+msgstr "SAML 憑證登入憑證的 SHA1 指紋。從您的身份æ供者å–得這個指紋 -- 它å¯èƒ½è¢«ç¨±ç‚ºã€ŒThumbprintã€"
msgid "SSH Keys"
msgstr "SSH 金鑰"
+msgid "SSL Verification"
+msgstr "SSL é©—è­‰"
+
+msgid "Save"
+msgstr "儲存"
+
+msgid "Save application"
+msgstr "儲存應用程å¼"
+
msgid "Save changes"
msgstr "儲存變更"
msgid "Save pipeline schedule"
-msgstr "儲存æµæ°´ç·š (pipeline) 排程"
+msgstr "儲存æµæ°´ç·šæŽ’程"
msgid "Save variables"
-msgstr ""
+msgstr "儲存變數"
msgid "Schedule a new pipeline"
-msgstr "建立æµæ°´ç·š (pipeline) 排程"
+msgstr "建立æµæ°´ç·šæŽ’程"
msgid "Scheduled"
-msgstr ""
+msgstr "已排程"
msgid "Schedules"
msgstr "排程"
msgid "Scheduling Pipelines"
-msgstr "æµæ°´ç·š (pipeline) 排程"
+msgstr "æµæ°´ç·šæŽ’程"
+
+msgid "Scope"
+msgstr "範åœ"
msgid "Scoped issue boards"
+msgstr "å€åŸŸæ€§çš„議題看æ¿"
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
msgstr ""
+msgid "Scroll to bottom"
+msgstr "滾到底部"
+
+msgid "Scroll to top"
+msgstr "滾到頂部"
+
msgid "Search"
-msgstr ""
+msgstr "æœå°‹"
+
+msgid "Search branches"
+msgstr "æœå°‹åˆ†æ”¯"
msgid "Search branches and tags"
-msgstr "æœå°‹åˆ†æ”¯ (branch) 和標籤"
+msgstr "æœå°‹åˆ†æ”¯å’Œæ¨™ç±¤"
+
+msgid "Search files"
+msgstr "æœå°‹æª”案"
+
+msgid "Search for projects, issues, etc."
+msgstr "æœå°‹å°ˆæ¡ˆã€è­°é¡Œç­‰ç­‰"
+
+msgid "Search merge requests"
+msgstr "æœå°‹åˆä½µè«‹æ±‚"
msgid "Search milestones"
-msgstr ""
+msgstr "æœå°‹é‡Œç¨‹ç¢‘"
msgid "Search project"
-msgstr ""
+msgstr "æœå°‹å°ˆæ¡ˆ"
msgid "Search users"
-msgstr ""
+msgstr "æœå°‹ä½¿ç”¨è€…"
msgid "Seconds before reseting failure information"
msgstr "é‡ç½®å¤±æ•—訊æ¯ç­‰å¾…時間(秒)"
@@ -3652,77 +5512,113 @@ msgstr "é‡ç½®å¤±æ•—訊æ¯ç­‰å¾…時間(秒)"
msgid "Seconds to wait for a storage access attempt"
msgstr "等待存å–儲存空間的嘗試時間(秒)"
-msgid "Secret variables"
-msgstr ""
+msgid "Secret:"
+msgstr "金鑰:"
+
+msgid "Security Dashboard"
+msgstr "安全儀表æ¿"
msgid "Security report"
-msgstr ""
+msgstr "安全性報告"
+
+msgid "SecurityDashboard|Monitor vulnerabilities in your code"
+msgstr "監控程å¼ç¢¼ä¸­çš„æ¼æ´ž"
+
+msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
+msgstr "觸發了æµæ°´ç·š %{pipelineLink}"
+
+msgid "Select"
+msgstr "é¸æ“‡"
msgid "Select Archive Format"
msgstr "é¸æ“‡ä¸‹è¼‰æ ¼å¼"
+msgid "Select a namespace to fork the project"
+msgstr "é¸æ“‡ä¸€å€‹å‘½å空間,以複製這個專案"
+
msgid "Select a timezone"
msgstr "é¸æ“‡æ™‚å€"
msgid "Select an existing Kubernetes cluster or create a new one"
-msgstr ""
+msgstr "é¸æ“‡ä¸€å€‹ç¾æœ‰çš„ Kubernetes å¢é›†æˆ–新增一個"
msgid "Select assignee"
-msgstr ""
+msgstr "é¸æ“‡æŒ‡æ´¾äºº"
msgid "Select branch/tag"
+msgstr "é¸æ“‡åˆ†æ”¯/標籤"
+
+msgid "Select project"
+msgstr "é¸æ“‡å°ˆæ¡ˆ"
+
+msgid "Select project and zone to choose machine type"
+msgstr "é¸æ“‡å°ˆæ¡ˆèˆ‡ä½ç½®ä¾†é¸æ“‡æ©Ÿå™¨é¡žåž‹"
+
+msgid "Select project to choose zone"
+msgstr "é¸æ“‡å°ˆæ¡ˆä»¥é¸æ“‡ä½ç½®"
+
+msgid "Select projects you want to import."
msgstr ""
+msgid "Select source branch"
+msgstr "é¸æ“‡ä¾†æºåˆ†æ”¯"
+
msgid "Select target branch"
-msgstr "é¸æ“‡ç›®æ¨™åˆ†æ”¯ (branch) "
+msgstr "é¸æ“‡ç›®æ¨™åˆ†æ”¯"
-msgid "Selective synchronization"
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
+msgid "Selective synchronization"
+msgstr "é¸æ“‡æ€§åŒæ­¥"
+
msgid "Send email"
-msgstr ""
+msgstr "傳é€é›»å­éƒµä»¶"
msgid "Sep"
-msgstr ""
+msgstr "ä¹æœˆ"
msgid "September"
-msgstr ""
+msgstr "ä¹æœˆ"
msgid "Server version"
-msgstr ""
+msgstr "伺æœå™¨ç‰ˆæœ¬"
+
+msgid "Service Desk"
+msgstr "æœå‹™å€"
msgid "Service Templates"
msgstr "æœå‹™ç¯„本"
msgid "Service URL"
-msgstr ""
+msgstr "æœå‹™ URL"
msgid "Session expiration, projects limit and attachment size."
-msgstr ""
+msgstr "éŽæœŸå·¥ä½œéšŽæ®µã€å°ˆæ¡ˆä¸Šé™èˆ‡é™„件大å°ã€‚"
msgid "Set a password on your account to pull or push via %{protocol}."
-msgstr "請先設定密碼,æ‰èƒ½ä½¿ç”¨ %{protocol} 來上傳 (push) 或下載 (pull) 。"
+msgstr "請先設定密碼,æ‰èƒ½ä½¿ç”¨ %{protocol} 來上傳或下載。"
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
-msgstr ""
+msgstr "設定為é è¨­å€¼ï¼Œä¸¦é™åˆ¶å¯è¦‹ç­‰ç´šã€‚設定匯入來æºå’Œ git å­˜å–連接å”定。"
msgid "Set max session time for web terminal."
-msgstr ""
+msgstr "為網é çµ‚端器設定最長工作階段時間"
msgid "Set notification email for abuse reports."
-msgstr ""
+msgstr "為濫用回報設定通知電å­ä¿¡ç®±ã€‚"
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
-msgstr ""
+msgstr "設定使用者登入的需求。啟用強制性的兩步驟驗證。"
msgid "Set up CI/CD"
-msgstr ""
+msgstr "設定 CI/CD"
msgid "Set up Koding"
msgstr "設定 Koding"
msgid "Set up assertions/attributes/claims (email, first_name, last_name) and NameID according to %{docsLinkStart}the documentation %{icon}%{docsLinkEnd}"
-msgstr ""
+msgstr "根據 %{docsLinkStart}這個文件%{icon}%{docsLinkEnd} 設定斷言 / 屬性 / è²æ˜Ž (email, first_name, last_name) å’Œ NameID"
msgid "SetPasswordToCloneLink|set a password"
msgstr "設定密碼"
@@ -3731,22 +5627,40 @@ msgid "Settings"
msgstr "設定"
msgid "Setup a specific Runner automatically"
-msgstr ""
+msgstr "自動設置特定的執行器"
+
+msgid "Share"
+msgstr "分享"
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
-msgstr ""
+msgstr "與æˆå“¡åˆ†äº« <strong>%{sso_label}</strong>,使他們能é€éŽæ‚¨çš„身份æ供者登入您的群組。"
+
+msgid "Shared Runners"
+msgstr "分享的執行器"
msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
-msgstr ""
+msgstr "通éŽé‡è¨­æ­¤å‘½å空間的æµæ°´ç·šåŸ·è¡Œæ™‚間,目å‰ä½¿ç”¨çš„執行時間將會被歸零。"
msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
-msgstr ""
+msgstr "é‡è¨­æµæ°´ç·šåŸ·è¡Œæ™‚é–“"
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
-msgstr ""
+msgstr "é‡è¨­æµæ°´ç·šå·²ç”¨æ™‚é–“"
+
+msgid "Sherlock Transactions"
+msgstr "Sherlock 交易"
msgid "Show command"
-msgstr ""
+msgstr "顯示指令"
+
+msgid "Show complete raw log"
+msgstr "顯示完整的原始日誌"
+
+msgid "Show latest version"
+msgstr "顯示最新版本"
+
+msgid "Show latest version of the diff"
+msgstr "顯示與最新版本的差異"
msgid "Show parent pages"
msgstr "顯示上層é é¢"
@@ -3754,51 +5668,81 @@ msgstr "顯示上層é é¢"
msgid "Show parent subgroups"
msgstr "顯示群組中的å­ç¾¤çµ„"
+msgid "Show whitespace changes"
+msgstr "顯示空白變化"
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "顯示 %d 個事件"
-msgid "Sidebar|Change weight"
-msgstr ""
+msgid "Side-by-side"
+msgstr "並排"
-msgid "Sidebar|No"
-msgstr ""
+msgid "Sidebar|Change weight"
+msgstr "變更權é‡"
msgid "Sidebar|None"
-msgstr ""
+msgstr "ç„¡"
+
+msgid "Sidebar|Only numeral characters allowed"
+msgstr "åªå…許數字字元"
msgid "Sidebar|Weight"
-msgstr ""
+msgstr "權é‡"
+
+msgid "Sign in"
+msgstr "登入"
+
+msgid "Sign in / Register"
+msgstr "登入 / 註冊"
+
+msgid "Sign in to %{group_name}"
+msgstr "登入至 %{group_name}"
+
+msgid "Sign in with Single Sign-On"
+msgstr "é€éŽå–®ä¸€ç™»å…¥é€²è¡Œç™»å…¥"
+
+msgid "Sign out"
+msgstr "登出"
msgid "Sign-in restrictions"
-msgstr ""
+msgstr "登錄é™åˆ¶"
msgid "Sign-up restrictions"
-msgstr ""
+msgstr "註冊é™åˆ¶"
msgid "Size and domain settings for static websites"
-msgstr ""
+msgstr "為éœæ…‹ç¶²ç«™çš„大å°èˆ‡ç¶²åŸŸè¨­å®š"
msgid "Slack application"
-msgstr ""
+msgstr "Slack 應用程å¼"
+
+msgid "Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job"
+msgstr "速度較慢,但確ä¿é …目工作空間是原始的,因為它為æ¯é …工作從頭開始複製檔案庫"
msgid "Snippets"
-msgstr "文字片段"
+msgstr "程å¼ç¢¼ç‰‡æ®µ"
msgid "Something went wrong on our end"
-msgstr ""
+msgstr "發生了錯誤。"
msgid "Something went wrong on our end."
-msgstr ""
+msgstr "發生了錯誤。"
+
+msgid "Something went wrong on our end. Please try again!"
+msgstr "未知錯誤,請é‡è©¦!"
msgid "Something went wrong when toggling the button"
-msgstr ""
+msgstr "切æ›æŒ‰éˆ•æ™‚發生未知的錯誤"
-msgid "Something went wrong while fetching Dependency Scanning."
-msgstr ""
+msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgstr "在關閉議題 %{issuable} 時出ç¾å•é¡Œï¼Œè«‹ç¨å¾Œå†è©¦"
-msgid "Something went wrong while fetching SAST."
-msgstr ""
+msgid "Something went wrong while fetching assignees list"
+msgstr "抓å–指派者清單時發生錯誤。"
+
+msgid "Something went wrong while fetching group member contributions"
+msgstr "å–得群組æˆå“¡è²¢ç»æ™‚發生錯誤。"
msgid "Something went wrong while fetching the projects."
msgstr "讀å–專案時發生錯誤。"
@@ -3806,8 +5750,17 @@ msgstr "讀å–專案時發生錯誤。"
msgid "Something went wrong while fetching the registry list."
msgstr "讀å–註冊列表時發生錯誤。"
+msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgstr "é‡æ–°é–‹å•Ÿ %{issuable} 議題時發生錯誤。請ç¨å¾Œå†è©¦"
+
+msgid "Something went wrong while resolving this discussion. Please try again."
+msgstr "解決此議題時出ç¾å•é¡Œã€‚è«‹å†è©¦ä¸€æ¬¡ã€‚"
+
msgid "Something went wrong. Please try again."
-msgstr ""
+msgstr "發生了未知的錯誤,請ç¨å¾Œå†è©¦"
+
+msgid "Sorry, no epics matched your search"
+msgstr "抱歉,沒有符åˆæ‚¨æœå°‹çš„ Epic"
msgid "Sort by"
msgstr "排åº"
@@ -3816,7 +5769,7 @@ msgid "SortOptions|Access level, ascending"
msgstr "å­˜å–層級,以å‡å†ªæŽ’列"
msgid "SortOptions|Access level, descending"
-msgstr ""
+msgstr "å­˜å–層級,以é™å†ªæŽ’列"
msgid "SortOptions|Created date"
msgstr "建立日期"
@@ -3837,7 +5790,7 @@ msgid "SortOptions|Largest group"
msgstr "最大群組"
msgid "SortOptions|Largest repository"
-msgstr "最大檔案庫(repository)"
+msgstr "最大儲存庫"
msgid "SortOptions|Last created"
msgstr "最近建立"
@@ -3852,7 +5805,7 @@ msgid "SortOptions|Least popular"
msgstr "最ä¸å—æ­¡è¿Ž"
msgid "SortOptions|Less weight"
-msgstr ""
+msgstr "é™ä½Žæ¬Šé‡"
msgid "SortOptions|Milestone"
msgstr "里程碑"
@@ -3864,7 +5817,7 @@ msgid "SortOptions|Milestone due soon"
msgstr "å³å°‡æˆªæ­¢çš„里程碑"
msgid "SortOptions|More weight"
-msgstr ""
+msgstr "加大權é‡"
msgid "SortOptions|Most popular"
msgstr "最å—æ­¡è¿Ž"
@@ -3906,37 +5859,64 @@ msgid "SortOptions|Start soon"
msgstr "ç¾åœ¨é–‹å§‹"
msgid "SortOptions|Weight"
-msgstr ""
+msgstr "權é‡"
msgid "Source"
-msgstr ""
+msgstr "來æº"
msgid "Source (branch or tag)"
-msgstr ""
+msgstr "ä¾†æº (分支或標籤)"
msgid "Source code"
msgstr "原始碼"
msgid "Source is not available"
-msgstr ""
+msgstr "來æºä¸å¯ç”¨"
msgid "Spam Logs"
msgstr "垃圾訊æ¯è¨˜éŒ„"
msgid "Spam and Anti-bot Protection"
-msgstr ""
+msgstr "垃圾內容與防機器人ä¿è­·"
+
+msgid "Specific Runners"
+msgstr "指定執行器"
msgid "Specify the following URL during the Runner setup:"
msgstr "åœ¨å®‰è£ Runner 時指定以下 URL:"
+msgid "Squash commits"
+msgstr "åˆä½µæ交"
+
+msgid "Stage"
+msgstr "階段"
+
+msgid "Stage & Commit"
+msgstr "æš«å­˜ & æ交"
+
+msgid "Stage all changes"
+msgstr "暫存所有變更"
+
+msgid "Stage changes"
+msgstr "暫存變更"
+
+msgid "Staged"
+msgstr "已暫存"
+
+msgid "Staged %{type}"
+msgstr "已暫存的 %{type}"
+
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr "為一個標籤標上星號,使其æˆç‚ºå„ªå…ˆæ¨™ç±¤ã€‚通éŽæ‹–動命令優先標籤更改其相å°å„ªå…ˆç´šã€‚"
+
msgid "StarProject|Star"
msgstr "收è—"
msgid "Starred Projects"
-msgstr ""
+msgstr "已星標的專案"
msgid "Starred Projects' Activity"
-msgstr ""
+msgstr "已星標的專案活動"
msgid "Starred projects"
msgstr "星標項目"
@@ -3948,176 +5928,224 @@ msgid "Start the Runner!"
msgstr "å•Ÿå‹• Runner!"
msgid "Started"
-msgstr ""
+msgstr "已開始"
+
+msgid "Starts at (UTC)"
+msgstr "開始於 (UTC)"
msgid "State your message to activate"
-msgstr ""
+msgstr "暫存您的訊æ¯ä»¥å•Ÿç”¨"
msgid "Status"
-msgstr ""
+msgstr "狀態"
+
+msgid "Stop impersonation"
+msgstr "åœæ­¢æ¨¡æ“¬"
+
+msgid "Stop this environment"
+msgstr "åœæ­¢æ­¤ç’°å¢ƒ"
msgid "Stopped"
-msgstr ""
+msgstr "å·²åœæ­¢"
msgid "Storage"
-msgstr ""
+msgstr "儲存"
+
+msgid "Storage:"
+msgstr "儲存空間:"
msgid "Subgroups"
msgstr "å­ç¾¤çµ„"
-msgid "Switch branch/tag"
-msgstr "切æ›åˆ†æ”¯ (branch) 或標籤"
+msgid "Submit as spam"
+msgstr "以垃圾訊æ¯æ交"
-msgid "System"
+msgid "Submit search"
msgstr ""
+msgid "Subscribe"
+msgstr "訂閱"
+
+msgid "Subscribe at group level"
+msgstr "訂閱群組"
+
+msgid "Subscribe at project level"
+msgstr "訂閱專案"
+
+msgid "Switch branch/tag"
+msgstr "切æ›åˆ†æ”¯/標籤"
+
+msgid "Sync information"
+msgstr "åŒæ­¥è©³ç´°è³‡è¨Š"
+
msgid "System Hooks"
msgstr "系統鉤å­"
+msgid "System Info"
+msgstr "系統資訊"
+
msgid "System header and footer:"
-msgstr ""
+msgstr "系統標頭與尾端:"
+
+msgid "System metrics (Custom)"
+msgstr "系統指標 (自訂)"
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
-msgstr[0] ""
+msgstr[0] "標籤(%{tag_count})"
msgid "Tags"
msgstr "標籤"
+msgid "Tags:"
+msgstr "標籤:"
+
msgid "TagsPage|Browse commits"
-msgstr ""
+msgstr "ç€è¦½æ›´å‹•"
msgid "TagsPage|Browse files"
-msgstr ""
+msgstr "ç€è¦½æª”案"
msgid "TagsPage|Can't find HEAD commit for this tag"
-msgstr ""
+msgstr "無法找到此標籤的更動"
msgid "TagsPage|Cancel"
-msgstr ""
+msgstr "å–消"
msgid "TagsPage|Create tag"
-msgstr ""
+msgstr "建立標籤"
msgid "TagsPage|Delete tag"
-msgstr ""
+msgstr "刪除標籤"
msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
-msgstr ""
+msgstr "刪除 %{tag_name} 後將無法æ¢å¾©ï¼Œæ‚¨ç¢ºå®šå—Žï¼Ÿ"
msgid "TagsPage|Edit release notes"
-msgstr ""
+msgstr "編輯發佈訊æ¯"
msgid "TagsPage|Existing branch name, tag, or commit SHA"
-msgstr ""
+msgstr "已存在分支å稱ã€æ¨™ç±¤æˆ–者更動SHA"
msgid "TagsPage|Filter by tag name"
-msgstr ""
+msgstr "根據標籤å稱塞é¸"
msgid "TagsPage|New Tag"
-msgstr ""
+msgstr "新標籤"
msgid "TagsPage|New tag"
-msgstr ""
+msgstr "新標籤"
msgid "TagsPage|Optionally, add a message to the tag."
-msgstr ""
+msgstr "增加標籤的說明(å¯é¸ï¼‰"
msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
-msgstr ""
+msgstr "增加標籤的發佈說明,這將會被儲存在Gitlab資料庫中,並顯示於標籤é ã€‚(å¯é¸ï¼‰"
msgid "TagsPage|Release notes"
-msgstr ""
+msgstr "發佈說明"
msgid "TagsPage|Repository has no tags yet."
-msgstr ""
+msgstr "檔案庫還沒有任何標籤。"
msgid "TagsPage|Sort by"
-msgstr ""
+msgstr "ä¾ç…§æŽ’åº"
msgid "TagsPage|Tags"
-msgstr ""
+msgstr "標籤"
msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
-msgstr ""
+msgstr "標籤具有å†æ交歷å²ä¸Šï¼Œæ¨™æ³¨ç‰¹å®šæ交的功能"
msgid "TagsPage|This tag has no release notes."
-msgstr ""
+msgstr "此標籤沒有發佈說明"
msgid "TagsPage|Use git tag command to add a new one:"
-msgstr ""
+msgstr "使用 git tag 指令增加一個新的標籤:"
-msgid "TagsPage|Write your release notes or drag files here..."
-msgstr ""
+msgid "TagsPage|Write your release notes or drag files here…"
+msgstr "撰寫發行說明或拖放文件到這裡⋯"
msgid "TagsPage|protected"
-msgstr ""
+msgstr "ä¿è­·"
msgid "Target Branch"
-msgstr "目標分支 (branch) "
+msgstr "目標分支"
msgid "Target branch"
-msgstr ""
+msgstr "目標分支"
msgid "Team"
msgstr "團隊"
+msgid "Terms of Service Agreement and Privacy Policy"
+msgstr "æœå‹™æ¢æ¬¾å”議和隱ç§æ”¿ç­–"
+
+msgid "Terms of Service and Privacy Policy"
+msgstr "æœå‹™æ¢æ¬¾å’Œéš±ç§æ”¿ç­–"
+
+msgid "Test coverage parsing"
+msgstr "測試覆蓋率分æž"
+
msgid "Thanks! Don't show me this again"
-msgstr ""
+msgstr "æ„Ÿè¬ï¼è«‹ä¸è¦å†æ¬¡é¡¯ç¤º"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
-msgstr ""
+msgstr "GitLab 的進階全域æœå°‹åŠŸèƒ½æ˜¯éžå¸¸å¼·å¤§çš„æœå°‹æœå‹™ã€‚您å¯ä»¥ç«‹åˆ»æœå°‹å…¶ä»–團隊的代碼以幫助您完善自己項目中的代碼。從而é¿å…建立é‡è¤‡çš„程å¼ç¢¼æµªè²»æ™‚間。"
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
-msgstr ""
+msgstr "議題追蹤器是添加專案中需è¦æ”¹é€²æˆ–解決å•é¡Œçš„地方。"
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
-msgstr ""
+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 ""
+msgstr "當需è¦ç›¸äº’ TLS 與外部èªè­‰æœå‹™è¯çµ¡æ™‚,所使用的 X509 憑證。如果留白,使用 HTTPS 連線時伺æœå™¨çš„憑證ä»ç„¶ç‚ºã€Œå·²èªè­‰ã€ã€‚"
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 "程å¼é–‹ç™¼éšŽæ®µé¡¯ç¤ºå¾žç¬¬ä¸€æ¬¡æ›´å‹•è¨˜éŒ„ (commit) 到建立åˆä½µè«‹æ±‚ (merge request) 的時間。建立第一個åˆä½µè«‹æ±‚後,資料將自動填入。"
+msgstr "程å¼é–‹ç™¼éšŽæ®µé¡¯ç¤ºå¾žç¬¬ä¸€æ¬¡æ›´å‹•è¨˜éŒ„到建立åˆä½µè«‹æ±‚的時間。建立第一個åˆä½µè«‹æ±‚後,資料將自動填入。"
msgid "The collection of events added to the data gathered for that stage."
msgstr "該階段中的相關事件集åˆã€‚"
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
-msgstr ""
+msgstr "該連線將在 %{timeout} 之後超時。å°æ–¼éœ€è¦ä½¿ç”¨æ›´é•·æ™‚間的版本庫,請使用 clone / push 組åˆã€‚"
msgid "The fork relationship has been removed."
-msgstr "åˆ†æ”¯èˆ‡ä¸»å¹¹é–“çš„é—œè¯ (fork relationship) 已被刪除。"
+msgstr "分支與主幹間的關è¯å·²è¢«åˆªé™¤ã€‚"
msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
-msgstr ""
+msgstr "匯入將在 %{timeout} 之後超時。å°æ–¼éœ€è¦æ›´é•·æ™‚間的存儲庫,請使用 clone / push 組åˆã€‚"
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 "è­°é¡Œ (issue) éšŽæ®µé¡¯ç¤ºå¾žè­°é¡Œå»ºç«‹åˆ°è¨­å®šé‡Œç¨‹ç¢‘æ‰€èŠ±çš„æ™‚é–“ï¼Œæˆ–æ˜¯è­°é¡Œè¢«åˆ†é¡žåˆ°è­°é¡Œçœ‹æ¿ (issue board) 中所花的時間。建立第一個議題後,資料將自動填入。"
+msgstr "議題階段顯示從議題建立到設定里程碑所花的時間,或是議題被分類到議題看æ¿ä¸­æ‰€èŠ±çš„時間。建立第一個議題後,資料將自動填入。"
msgid "The maximum file size allowed is 200KB."
-msgstr ""
+msgstr "最大檔案大å°ç‚º 200KB"
msgid "The number of attempts GitLab will make to access a storage."
msgstr "GitLab å­˜å–儲存空間的嘗試次數。"
-msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
-msgstr "GitLab 將阻擋存å–失敗的次數。在管ç†è€…介é¢ä¸­å¯ä»¥é‡ç½®å¤±æ•—次數: %{link_to_health_page} 或使用 %{api_documentation_link}。"
+msgid "The number of failures after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
+msgstr ""
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
-msgstr ""
+msgstr "解密ç§é‘°æ‰€éœ€çš„密碼。這是é¸ç”¨ä¸”該值將會被加密儲存。"
+
+msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
+msgstr "CI 設定檔的路徑。默èªç‚º <code>.gitlab-ci.yml</code>"
msgid "The phase of the development lifecycle."
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 "計劃階段顯示從更動記錄 (commit) 被排程至第一個推é€çš„時間。第一次推é€ä¹‹å¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥ã€‚"
+msgstr "計劃階段顯示從更動記錄被排程至第一個推é€çš„時間。第一次推é€ä¹‹å¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥ã€‚"
msgid "The private key to use when a client certificate is provided. This value is encrypted at rest."
-msgstr ""
+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 "營é‹éšŽæ®µé¡¯ç¤ºå¾žå»ºç«‹è­°é¡Œ (issue) 到部署程å¼ä¸Šç·šæ‰€èŠ±çš„時間。完æˆå¾žç™¼æƒ³åˆ°ä¸Šç·šçš„完整開發週期後,資料將自動填入。"
+msgstr "營é‹éšŽæ®µé¡¯ç¤ºå¾žå»ºç«‹è­°é¡Œåˆ°éƒ¨ç½²ç¨‹å¼ä¸Šç·šæ‰€èŠ±çš„時間。完æˆå¾žç™¼æƒ³åˆ°ä¸Šç·šçš„完整開發週期後,資料將自動填入。"
msgid "The project can be accessed by any logged in user."
msgstr "本專案å¯è®“任何已登入的使用者存å–"
@@ -4125,77 +6153,116 @@ msgstr "本專案å¯è®“任何已登入的使用者存å–"
msgid "The project can be accessed without any authentication."
msgstr "本專案å¯è®“任何人存å–"
+msgid "The pseudonymizer data collection is disabled. 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."
+msgstr "Pseudonymizer 資料è’集已經åœç”¨ã€‚如果啟用,GitLab 將會執行一個背景作業 -- 其將會產生 GitLab 資料庫的å‡å CSV,使其å¯ä»¥ä¸Šå‚³åˆ°æ‚¨è¨­å®šçš„å°è±¡å„²å­˜ç›®éŒ„。"
+
msgid "The repository for this project does not exist."
-msgstr "本專案沒有檔案庫 (repository) "
+msgstr "本專案沒有檔案庫"
msgid "The repository for this project is empty"
-msgstr ""
+msgstr "這個專案的檔案庫是空的"
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
-msgstr ""
+msgstr "該存儲庫必須å¯é€šéŽ <code>http://</code>, <code>https://</code> 或 <code>git://</code>訪å•ã€‚"
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 "複閱階段顯示從åˆä½µè«‹æ±‚ (merge request) 建立後至被åˆä½µçš„時間。當建立第一個åˆä½µè«‹æ±‚ (merge request) 後,資料將自動填入。"
+msgstr "複閱階段顯示從åˆä½µè«‹æ±‚建立後至被åˆä½µçš„時間。當建立第一個åˆä½µè«‹æ±‚後,資料將自動填入。"
msgid "The roadmap shows the progress of your epics along a timeline"
-msgstr ""
+msgstr "開發è—圖顯示了時間軸上您的 Epic 的進度"
+
+msgid "The secure token used by the Runner to checkout the project"
+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 "試營é‹æ®µé¡¯ç¤ºå¾žåˆä½µè«‹æ±‚ (merge request) 被åˆä½µå¾Œè‡³éƒ¨ç½²ç‡Ÿé‹çš„時間。當第一次部署營é‹å¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥"
+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 "測試階段顯示相關åˆä½µè«‹æ±‚ (merge request) çš„æµæ°´ç·š (pipeline) 所花的時間。當第一個æµæ°´ç·š (pipeline) 執行完畢後,資料將自動填入。"
+msgstr "測試階段顯示相關åˆä½µè«‹æ±‚çš„æµæ°´ç·šæ‰€èŠ±çš„時間。當第一個æµæ°´ç·šåŸ·è¡Œå®Œç•¢å¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥ã€‚"
msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
msgstr "GitLab ä¿å­˜å¤±æ•—訊æ¯çš„時間(秒)。在此時間內若沒有發生錯誤,失敗訊æ¯å°‡æœƒè¢«é‡ç½®"
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
-msgstr "GitLab 嘗試存å–檔案庫 (repository) 的時間 (秒)。超éŽæ­¤æ™‚間將會引發逾時錯誤。"
+msgstr "GitLab 嘗試存å–檔案庫的時間 (秒)。超éŽæ­¤æ™‚間將會引發逾時錯誤。"
-msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgid "The time in seconds between storage checks. If a check did not complete yet, GitLab will skip the next check."
msgstr ""
msgid "The time taken by each data entry gathered by that stage."
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>:</code>. 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 ""
+
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 "中ä½æ•¸æ˜¯ä¸€å€‹æ•¸åˆ—中最中間的值。例如在 3ã€5ã€9 之間,中ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,中ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"
msgid "There are no issues to show"
-msgstr ""
+msgstr "沒有å¯ä»¥é¡¯ç¤ºçš„è­°é¡Œ"
+
+msgid "There are no labels yet"
+msgstr "ç›®å‰é‚„沒有標籤"
msgid "There are no merge requests to show"
-msgstr ""
+msgstr "沒有任何的åˆä½µè«‹æ±‚"
msgid "There are problems accessing Git storage: "
msgstr "å­˜å– Git 儲存空間時出ç¾å•é¡Œï¼š"
-msgid "There was an error loading results"
-msgstr ""
-
msgid "There was an error loading users activity calendar."
-msgstr ""
+msgstr "讀å–使用者行事曆活動時發生錯誤"
msgid "There was an error saving your notification settings."
-msgstr ""
+msgstr "ä¿å­˜æ‚¨çš„通知設置時發生錯誤。"
msgid "There was an error subscribing to this label."
-msgstr ""
+msgstr "訂閱此標籤時發生錯誤。"
msgid "There was an error when reseting email token."
-msgstr ""
+msgstr "é‡ç½®é›»å­éƒµä»¶æ†‘證時發生錯誤。"
msgid "There was an error when subscribing to this label."
-msgstr ""
+msgstr "訂閱此標籤時出ç¾éŒ¯èª¤ã€‚"
msgid "There was an error when unsubscribing from this label."
+msgstr "å–消訂閱此標籤時出ç¾éŒ¯èª¤ã€‚"
+
+msgid "They can be managed using the %{link}."
+msgstr "å…¶å¯ä»¥ä½¿ç”¨ %{link} 進行管ç†ã€‚"
+
+msgid "Third party offers"
+msgstr "第三方æä¾›"
+
+msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
+msgstr "æ­¤ GitLab 實例ä¸æ供任何分享的執行器。實例管ç†å“¡å¯ä»¥åœ¨ç®¡ç†å“¡å€åŸŸè¨»å†Šåˆ†äº«çš„執行器。"
+
+msgid "This application was created by %{link_to_owner}."
msgstr ""
-msgid "This board\\'s scope is reduced"
+msgid "This application will be able to:"
msgstr ""
+msgid "This board's scope is reduced"
+msgstr "已縮å°æ­¤çœ‹æ¿ç¯„åœ"
+
+msgid "This diff is collapsed."
+msgstr "此差異已折疊。"
+
msgid "This directory"
-msgstr ""
+msgstr "這個目錄"
+
+msgid "This group"
+msgstr "此群組"
+
+msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
+msgstr "此群組å…許您以您的 %{group_name} 單一登入帳號登入。這將會é‡æ–°å°Žå‘您到外部登入é é¢ã€‚"
+
+msgid "This group does not provide any group Runners yet."
+msgstr "這群組尚未æ供任何群組執行器。"
msgid "This is a confidential issue."
msgstr "這是個隱密å•é¡Œã€‚"
@@ -4204,7 +6271,7 @@ msgid "This is the author's first Merge Request to this project."
msgstr "這是作者第一次åˆä½µè«‹æ±‚至本專案。"
msgid "This issue is confidential"
-msgstr ""
+msgstr "這個議題是ä¿å¯†çš„"
msgid "This issue is confidential and locked."
msgstr "這個å•é¡Œæ˜¯ä¿å¯†ä¸”鎖定的。"
@@ -4213,73 +6280,100 @@ msgid "This issue is locked."
msgstr "這個å•é¡Œå·²è¢«éŽ–定。"
msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
-msgstr ""
+msgstr "這項任務將由使用者觸發,這通常用於部署應用程å¼åˆ°æ­£å¼ç’°å¢ƒ"
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
-msgstr ""
+msgstr "這項工作需è¦ä¸Šä¸€å€‹å·¥ä½œæˆåŠŸæ™‚æ‰èƒ½è§¸ç™¼åŸ·è¡Œå·¥ä½œ"
+
+msgid "This job does not have a trace."
+msgstr "此工作沒有追蹤。"
+
+msgid "This job has been canceled"
+msgstr "此工作已經被å–消"
+
+msgid "This job has been skipped"
+msgstr "此工作已經被跳éŽ"
msgid "This job has not been triggered yet"
-msgstr ""
+msgstr "這份任務還沒被觸發"
msgid "This job has not started yet"
-msgstr ""
+msgstr "這份任務還沒有開始執行"
msgid "This job is in pending state and is waiting to be picked by a runner"
-msgstr ""
+msgstr "這份任務ä½æ–¼ç­‰å¾…狀態,等待 Runner 來執行"
msgid "This job requires a manual action"
-msgstr ""
+msgstr "這份任務需è¦æ‰‹å‹•åŸ·è¡Œ"
msgid "This means you can not push code until you create an empty repository or import existing one."
-msgstr "這代表在您建立一個空的檔案庫 (repository) 或是匯入一個ç¾å­˜çš„檔案庫之å‰ï¼Œæ‚¨å°‡ç„¡æ³•ä¸Šå‚³æ›´æ–° (push) 。"
+msgstr "這代表在您建立一個空的檔案庫或是匯入一個ç¾å­˜çš„檔案庫之å‰ï¼Œæ‚¨å°‡ç„¡æ³•ä¸Šå‚³æ›´æ–°ã€‚"
msgid "This merge request is locked."
msgstr "這個åˆä½µè«‹æ±‚已被鎖定。"
+msgid "This option is disabled while you still have unstaged changes"
+msgstr "當您ä»æ“有未暫存的變更時,此é¸é …將會是åœç”¨çš„"
+
msgid "This page is unavailable because you are not allowed to read information across multiple projects."
-msgstr ""
+msgstr "æ­¤é é¢ä¸å¯ç”¨ï¼Œå› ç‚ºæ‚¨ä¸å…許跨多個專案閱讀信æ¯ã€‚"
+
+msgid "This page will be removed in a future release."
+msgstr "æ­¤é é¢å°‡æœƒåœ¨æœªä¾†ç‰ˆæœ¬ä¸­ç§»é™¤ã€‚"
msgid "This project"
-msgstr ""
+msgstr "這個專案"
+
+msgid "This project does not belong to a group and can therefore not make use of group Runners."
+msgstr "此專案ä¸å±¬æ–¼ä¸€å€‹ç¾¤çµ„,因此ä¸èƒ½ä½¿ç”¨ç¾¤çµ„執行器。"
msgid "This repository"
-msgstr ""
+msgstr "這個檔案庫"
+
+msgid "This source diff could not be displayed because it is too large."
+msgstr "此原始碼差異無法顯示,因為它太大。"
+
+msgid "This user has no identities"
+msgstr "該使用者沒有身份"
msgid "This will delete the custom metric, Are you sure?"
-msgstr ""
+msgstr "這會刪除自訂指標資料,你確定嗎?"
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr ""
+msgstr "這裡列出了那些自動æˆç‚ºè­°é¡Œçš„é›»å­éƒµä»¶ï¼ˆå¾žç•™è¨€è®Šæˆçš„é›»å­éƒµä»¶å°è©±ï¼‰"
msgid "Time before an issue gets scheduled"
-msgstr "議題 (issue) 被列入日程表的時間"
+msgstr "議題被列入日程表的時間"
msgid "Time before an issue starts implementation"
-msgstr "議題 (issue) 等待開始實作的時間"
+msgstr "議題等待開始實作的時間"
msgid "Time between merge request creation and merge/close"
-msgstr "åˆä½µè«‹æ±‚ (merge request) 從建立到被åˆä½µæˆ–是關閉的時間"
-
-msgid "Time between updates and capacity settings."
-msgstr ""
+msgstr "åˆä½µè«‹æ±‚從建立到被åˆä½µæˆ–是關閉的時間"
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr "GitLab 等待外部æœå‹™çš„回應時間(秒)。當æœå‹™æ²’有在時間內回應時,存å–將被拒絕。"
+msgid "Time remaining"
+msgstr "剩餘時間"
+
+msgid "Time spent"
+msgstr "經éŽæ™‚é–“"
+
msgid "Time tracking"
-msgstr ""
+msgstr "時間追蹤"
msgid "Time until first merge request"
-msgstr "第一個åˆä½µè«‹æ±‚ (merge request) 被建立å‰çš„時間"
+msgstr "第一個åˆä½µè«‹æ±‚被建立å‰çš„時間"
msgid "TimeTrackingEstimated|Est"
-msgstr ""
+msgstr "é ä¼°"
msgid "TimeTracking|Estimated:"
-msgstr ""
+msgstr "é ä¼°ï¼š"
msgid "TimeTracking|Spent"
-msgstr ""
+msgstr "已花費:"
msgid "Timeago|%s days ago"
msgstr " %s 天å‰"
@@ -4287,6 +6381,9 @@ msgstr " %s 天å‰"
msgid "Timeago|%s days remaining"
msgstr "剩下 %s 天"
+msgid "Timeago|%s hours ago"
+msgstr "%s å°æ™‚å‰"
+
msgid "Timeago|%s hours remaining"
msgstr "剩下 %s å°æ™‚"
@@ -4302,6 +6399,9 @@ msgstr " %s 個月å‰"
msgid "Timeago|%s months remaining"
msgstr "剩下 %s 月"
+msgid "Timeago|%s seconds ago"
+msgstr "%s 秒å‰"
+
msgid "Timeago|%s seconds remaining"
msgstr "剩下 %s 秒"
@@ -4317,48 +6417,45 @@ msgstr " %s å¹´å‰"
msgid "Timeago|%s years remaining"
msgstr "剩下 %s 年"
+msgid "Timeago|1 day ago"
+msgstr "1 天å‰"
+
msgid "Timeago|1 day remaining"
msgstr "剩下 1 天"
+msgid "Timeago|1 hour ago"
+msgstr "1 å°æ™‚å‰"
+
msgid "Timeago|1 hour remaining"
msgstr "剩下 1 å°æ™‚"
+msgid "Timeago|1 minute ago"
+msgstr "1 分é˜å‰"
+
msgid "Timeago|1 minute remaining"
msgstr "剩下 1 分é˜"
+msgid "Timeago|1 month ago"
+msgstr "1 個月å‰"
+
msgid "Timeago|1 month remaining"
msgstr "剩下 1 個月"
+msgid "Timeago|1 week ago"
+msgstr "1 週å‰"
+
msgid "Timeago|1 week remaining"
msgstr "剩下 1 週"
+msgid "Timeago|1 year ago"
+msgstr "1 å¹´å‰"
+
msgid "Timeago|1 year remaining"
msgstr "剩下 1 年"
msgid "Timeago|Past due"
msgstr "逾期"
-msgid "Timeago|a day ago"
-msgstr " 1 天å‰"
-
-msgid "Timeago|a month ago"
-msgstr " 1 個月å‰"
-
-msgid "Timeago|a week ago"
-msgstr " 1 週å‰"
-
-msgid "Timeago|a year ago"
-msgstr " 1 å¹´å‰"
-
-msgid "Timeago|about %s hours ago"
-msgstr "ç´„ %s å°æ™‚å‰"
-
-msgid "Timeago|about a minute ago"
-msgstr "ç´„ 1 分é˜å‰"
-
-msgid "Timeago|about an hour ago"
-msgstr "ç´„ 1 å°æ™‚å‰"
-
msgid "Timeago|in %s days"
msgstr " %s 天後"
@@ -4398,11 +6495,14 @@ msgstr " 1 週後"
msgid "Timeago|in 1 year"
msgstr " 1 年後"
-msgid "Timeago|in a while"
-msgstr "剛剛"
+msgid "Timeago|just now"
+msgstr "ç¾åœ¨"
+
+msgid "Timeago|right now"
+msgstr "立刻"
-msgid "Timeago|less than a minute ago"
-msgstr "ä¸åˆ° 1 分é˜å‰"
+msgid "Timeout"
+msgstr "逾時"
msgid "Time|hr"
msgid_plural "Time|hrs"
@@ -4416,82 +6516,139 @@ msgid "Time|s"
msgstr "秒"
msgid "Tip:"
-msgstr ""
+msgstr "æ示:"
msgid "Title"
-msgstr ""
+msgstr "標題"
msgid "To GitLab"
-msgstr ""
+msgstr "到 GitLab"
+
+msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
+msgstr "è¦æ·»åŠ SSHå¯†é‘°ï¼Œä½ éœ€è¦ %{generate_link_start}產生一個%{link_end} 或使用 %{existing_link_start}ç¾æœ‰çš„密鑰%{link_end}。"
msgid "To connect GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to connect."
-msgstr ""
+msgstr "è‹¥è¦é€£ç·šåˆ° GitHub 版本庫,您å¯ä»¥ä½¿ç”¨ %{personal_access_token_link} 連çµã€‚當您建立了您的個人存å–憑證,您將需è¦é¸æ“‡ <code>版本庫</code> 範åœï¼Œæ‰€ä»¥æˆ‘們å¯ä»¥é¡¯ç¤ºæ‚¨å¯ä¾›é€£ç·šçš„公開與ç§å¯†ç‰ˆæœ¬åº«åˆ—表。"
msgid "To connect GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
-msgstr ""
+msgstr "è¦é€£æŽ¥ GitHub 版本庫,您首先需è¦æŽˆæ¬Š GitLab å­˜å–您的 GitHub 版本庫列表:"
msgid "To connect an SVN repository, check out %{svn_link}."
+msgstr "è‹¥è¦é€£æŽ¥åˆ°ä¸€å€‹ SVN 版本庫,請åƒé–± %{svn_link}。"
+
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
msgstr ""
-msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
msgstr ""
+msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
+msgstr "è¦åŒ¯å…¥ GitHub 存儲庫,你å¯ä»¥ä½¿ç”¨ %{personal_access_token_link}。當你建立你的個人存å–權æ–時,你將需è¦é¸æ“‡<code>檔案庫</code>範åœï¼Œæ‰€ä»¥æˆ‘們將會顯示你公開åŠç§äººçš„檔案庫清單進行匯入。"
+
msgid "To import GitHub repositories, you first need to authorize GitLab to access the list of your GitHub repositories:"
-msgstr ""
+msgstr "è¦åŒ¯å…¥ GitHub 存儲庫,您首先需è¦æŽˆæ¬Š GitLab 訪å•æ‚¨çš„ GitHub 存儲庫列表:"
msgid "To import an SVN repository, check out %{svn_link}."
+msgstr "è¦åŒ¯å…¥SVN存儲庫,請查看 %{svn_link}。"
+
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
msgstr ""
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
-msgstr ""
+msgstr "è‹¥è¦åªå°å¤–部版本庫使用 CI / CD 功能,請é¸æ“‡ <strong>為外部版本庫的 CI/CD</strong>"
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
+msgstr "è‹¥è¦é€éŽèº«ä»½æ供者(例如 Azureã€Oktaã€Oneloginã€Ping Identity 或您自訂的 SAML 2.0 æ供者)設定 SAML èªè­‰ï¼š"
+
+msgid "To start serving your jobs you can add Runners to your group"
+msgstr "è‹¥è¦é–‹å§‹æ供您的工作,您å¯ä»¥å¢žåŠ åŸ·è¡Œå™¨è‡³é€™å€‹ç¾¤çµ„"
+
+msgid "To this GitLab instance"
msgstr ""
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
-msgstr ""
+msgstr "è‹¥è¦é©—證您的 GitLab CI 設定,請å‰å¾€æ‚¨å°ˆæ¡ˆå…§çš„「CI/CD → æµæ°´ç·šã€ï¼Œä¸¦æŒ‰ä¸‹ã€ŒCI Lintã€æŒ‰éˆ•ã€‚"
msgid "To view the roadmap, add a planned start or finish date to one of your epics in this group or its subgroups. Only epics in the past 3 months and the next 3 months are shown."
-msgstr ""
+msgstr "è‹¥è¦æª¢è¦–開發è—圖,請在您群組或å­ç¾¤çµ„中的其中一個 Epic 中增加計畫開始或完æˆæ™‚間。åªæœƒé¡¯ç¤ºæœ€è¿‘三個月或接下來三個月的 Epic。"
+
+msgid "To widen your search, change or remove filters."
+msgstr "è‹¥è¦æ“´å¤§æœå°‹ç¯„åœï¼Œè«‹è®Šæ›´æˆ–移除篩é¸å™¨ã€‚"
msgid "Todo"
-msgstr ""
+msgstr "待辦事項"
+
+msgid "Todos"
+msgstr "待辦事項"
+
+msgid "Toggle Sidebar"
+msgstr "展開 / 收起å´é‚Šæ¬„"
+
+msgid "Toggle discussion"
+msgstr "切æ›è¨Žè«–"
+
+msgid "Toggle navigation"
+msgstr "切æ›å°Žèˆªæ¬„"
msgid "Toggle sidebar"
-msgstr ""
+msgstr "切æ›å´é‚Šæ¬„"
msgid "ToggleButton|Toggle Status: OFF"
-msgstr ""
+msgstr "切æ›ç‹€æ…‹ï¼šé—œé–‰"
msgid "ToggleButton|Toggle Status: ON"
-msgstr ""
+msgstr "切æ›ç‹€æ…‹ï¼šé–‹å•Ÿ"
+
+msgid "Too many changes to show."
+msgstr "太多的更改。"
+
+msgid "Total Contributions"
+msgstr "總計貢ç»"
msgid "Total Time"
msgstr "總時間"
msgid "Total test time for all commits/merges"
-msgstr "åˆä½µ (merge) 與更動記錄 (commit) 的總測試時間"
+msgstr "åˆä½µ/更動記錄的總測試時間"
msgid "Total: %{total}"
-msgstr ""
+msgstr "總計: %{total}"
msgid "Track activity with Contribution Analytics."
-msgstr ""
+msgstr "追蹤貢ç»åˆ†æžçš„相關動態"
msgid "Track groups of issues that share a theme, across projects and milestones"
-msgstr ""
+msgstr "跟蹤共通主題ã€è·¨å°ˆæ¡ˆå’Œé‡Œç¨‹ç¢‘的議題群組"
msgid "Track time with quick actions"
+msgstr "快速使用時間追蹤工具"
+
+msgid "Trending"
msgstr ""
msgid "Trigger this manual action"
-msgstr ""
+msgstr "啟動此任務"
+
+msgid "Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will impersonate their associated user including their access to projects and their project permissions."
+msgstr "觸發器å¯ä»¥å¼·åˆ¶ä½¿ç”¨API​​調用é‡å»ºç‰¹å®šçš„分支或標記。這些令牌將模仿他們的關è¯ç”¨æˆ¶ï¼ŒåŒ…括他們å°é …目的訪å•æ¬Šé™ã€‚"
+
+msgid "Try again"
+msgstr "é‡è©¦"
msgid "Turn on Service Desk"
-msgstr ""
+msgstr "é–‹å•Ÿæœå‹™å€"
+
+msgid "Twitter"
+msgstr "Twitter"
+
+msgid "Unable to load the diff. %{button_try_again}"
+msgstr "無法載入差異。%{button_try_again}"
+
+msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
+msgstr "因為「%{reason}ã€ï¼Œæ‰€ä»¥ç„¡æ³•è®“ä½ é€éŽ SAML 登入這個群組。"
msgid "Unknown"
-msgstr ""
+msgstr "未知"
msgid "Unlock"
msgstr "解鎖"
@@ -4500,27 +6657,63 @@ msgid "Unlocked"
msgstr "已解鎖"
msgid "Unresolve discussion"
-msgstr ""
+msgstr "é‡æ–°è¨Žè«–"
+
+msgid "Unstage all changes"
+msgstr "å–消暫存所有變更"
+
+msgid "Unstage changes"
+msgstr "未暫存的變更"
+
+msgid "Unstaged"
+msgstr "未暫存的"
+
+msgid "Unstaged %{type}"
+msgstr "未暫存的 %{type}"
+
+msgid "Unstaged and staged %{type}"
+msgstr "暫存與未暫存的 %{type}"
msgid "Unstar"
msgstr "å–消收è—"
+msgid "Unsubscribe"
+msgstr "解除訂閱"
+
+msgid "Unsubscribe at group level"
+msgstr "解除群組訂閱"
+
+msgid "Unsubscribe at project level"
+msgstr "解除專案訂閱"
+
+msgid "Unverified"
+msgstr "未驗證"
+
msgid "Up to date"
-msgstr ""
+msgstr "已經是最新"
+
+msgid "Update"
+msgstr "æ›´æ–°"
+
+msgid "Update your group name, description, avatar, and other general settings."
+msgstr "更新您的群組å稱ã€èªªæ˜Žã€é ­åƒèˆ‡å…¶ä»–一般設定。"
msgid "Upgrade your plan to activate Advanced Global Search."
-msgstr ""
+msgstr "å‡ç´šæ‚¨çš„方案以啟用進階全局æœå°‹ã€‚"
msgid "Upgrade your plan to activate Contribution Analytics."
-msgstr ""
+msgstr "å‡ç´šæ‚¨çš„方案以啟用貢ç»åˆ†æžã€‚"
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr ""
+msgstr "å‡ç´šä½ çš„方案以啟用群組 Webhook。"
msgid "Upgrade your plan to activate Issue weight."
-msgstr ""
+msgstr "å‡ç´šæ‚¨çš„方案以啟用議題權é‡ã€‚"
msgid "Upgrade your plan to improve Issue boards."
+msgstr "å‡ç´šæ‚¨çš„方案以改善議題看æ¿ã€‚"
+
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
msgstr ""
msgid "Upload New File"
@@ -4530,69 +6723,111 @@ msgid "Upload file"
msgstr "上傳檔案"
msgid "Upload new avatar"
-msgstr ""
+msgstr "上傳大頭貼"
msgid "UploadLink|click to upload"
msgstr "點擊上傳"
msgid "Upvotes"
-msgstr ""
+msgstr "讚"
msgid "Usage statistics"
+msgstr "使用情形統計資料"
+
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
-msgstr ""
+msgstr "使用æœå‹™å°åœ¨ GitLab 內部é€éŽé›»å­éƒµä»¶èˆ‡ä½¿ç”¨è€…è¯çµ¡ (例如æ供客戶æœå‹™)"
+
+msgid "Use group milestones to manage issues from multiple projects in the same milestone."
+msgstr "使用群組里程碑從多個專案中於åŒä¸€å€‹é‡Œç¨‹ç¢‘管ç†è­°é¡Œã€‚"
+
+msgid "Use one line per URI"
+msgstr "æ¯å€‹ URL 使用一行"
msgid "Use the following registration token during setup:"
-msgstr "在安è£éŽç¨‹ä¸­ä½¿ç”¨æ­¤è¨»å†Šæ†‘è­‰ (registration token):"
+msgstr "在安è£éŽç¨‹ä¸­ä½¿ç”¨æ­¤è¨»å†Šæ†‘è­‰:"
msgid "Use your global notification setting"
msgstr "使用全域通知設定"
msgid "Used by members to sign in to your group in GitLab"
+msgstr "用來讓æˆå“¡ç™»å…¥æ‚¨åœ¨ GitLab 的群組"
+
+msgid "User Settings"
msgstr ""
msgid "User and IP Rate Limits"
+msgstr "使用者與 IP 速率é™åˆ¶"
+
+msgid "User map"
msgstr ""
+msgid "Users"
+msgstr "使用者"
+
+msgid "Variables"
+msgstr "變數"
+
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
-msgstr ""
+msgstr "變數通éŽé‹è¡Œå™¨æ‡‰ç”¨æ–¼ç’°å¢ƒã€‚å—ä¿è­·çš„變數將åªæ‡‰ç”¨æ–¼å—ä¿è­·çš„分支或標籤。你å¯ä»¥ç”¨æ–¼å„²å­˜å¯†ç¢¼ï¼Œå¯†é‘°æˆ–任何你想è¦çš„變數。"
msgid "Various container registry settings."
-msgstr ""
+msgstr "å„種容器的登錄表設定。"
msgid "Various email settings."
-msgstr ""
+msgstr "å„種電å­ä¿¡ç®±è¨­å®šã€‚"
msgid "Various settings that affect GitLab performance."
-msgstr ""
+msgstr "å„種會影響 GitLab 效能的設定。"
-msgid "View and edit lines"
-msgstr ""
+msgid "Verification information"
+msgstr "èªè­‰è³‡è¨Š"
+
+msgid "Verified"
+msgstr "已驗證"
msgid "View epics list"
-msgstr ""
+msgstr "檢視 Epic 列表"
msgid "View file @ "
msgstr "ç€è¦½æª”案 @ "
msgid "View group labels"
+msgstr "查看群組標籤"
+
+msgid "View issue"
+msgstr "檢視議題"
+
+msgid "View it on GitLab"
msgstr ""
+msgid "View jobs"
+msgstr "查看工作"
+
msgid "View labels"
-msgstr ""
+msgstr "顯示標籤"
+
+msgid "View log"
+msgstr "查看日誌"
msgid "View open merge request"
-msgstr "查看此分支的åˆä½µè«‹æ±‚ (merge request)"
+msgstr "查看此分支的åˆä½µè«‹æ±‚"
msgid "View project labels"
-msgstr ""
+msgstr "查看專案標籤"
msgid "View replaced file @ "
msgstr "ç€è¦½å·²æ›¿æ›æª”案 @ "
msgid "Visibility and access controls"
+msgstr "å¯è¦‹æ€§èˆ‡å­˜å–控制"
+
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
msgstr ""
msgid "VisibilityLevel|Internal"
@@ -4610,35 +6845,47 @@ msgstr "ä¸æ˜Ž"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "權é™ä¸è¶³ã€‚如需查看相關資料,請å‘管ç†å“¡ç”³è«‹æ¬Šé™ã€‚"
-msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr ""
msgid "We don't have enough data to show this stage."
msgstr "因該階段的資料ä¸è¶³è€Œç„¡æ³•é¡¯ç¤ºç›¸é—œè³‡è¨Š"
msgid "We want to be sure it is you, please confirm you are not a robot."
-msgstr ""
+msgstr "我們è¦ç¢ºå®šä½ ä¸æ˜¯æ©Ÿå™¨äººã€‚"
msgid "Web IDE"
-msgstr ""
+msgstr "ç¶²é  IDE"
msgid "Web terminal"
-msgstr ""
+msgstr "網é çµ‚端器"
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr ""
+msgstr "Webhooks å…許你觸發 URL,例如推é€äº†æ–°çš„程å¼ç¢¼ã€æˆ–是建立了新的議題。您å¯ä»¥è¨­å®š Webhook 以監è½æŒ‡å®šçš„活動,例如推é€ã€è­°é¡Œæˆ–åˆä½µè«‹æ±‚。群組 Webhooks 將套用至群組中的所有專案,å…許你è¦ç¯„整個群組中的 Webhook 的功能。"
+
+msgid "Weeks"
+msgstr "週"
msgid "Weight"
-msgstr ""
+msgstr "權é‡"
-msgid "When leaving the URL blank, classification labels can still be specified whitout disabling cross project features or performing external authorization checks."
-msgstr ""
+msgid "Weight %{weight}"
+msgstr "æ¬Šé‡ %{weight}"
+
+msgid "When a runner is locked, it cannot be assigned to other projects"
+msgstr "當執行器被鎖定,其將ä¸èƒ½è¢«å…¶ä»–專案指定。"
+
+msgid "When enabled, users cannot use GitLab until the terms have been accepted."
+msgstr "當啟用,使用者在接å—æ¢æ¬¾ä¹‹å‰éƒ½ä¸èƒ½ä½¿ç”¨ GitLab。"
+
+msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
+msgstr "當 URL 留空時,還是å¯ä»¥æŒ‡å®šåˆ†é¡žæ¨™ç±¤ï¼Œè€Œä¸”ä¸å¿…åœç”¨è·¨å°ˆæ¡ˆåŠŸèƒ½æˆ–執行外部授權檢查。"
msgid "Wiki"
msgstr "Wiki"
msgid "WikiClone|Clone your wiki"
-msgstr "複製(clone)您的 Wiki"
+msgstr "複製您的 Wiki"
msgid "WikiClone|Git Access"
msgstr "Git 讀å–"
@@ -4653,13 +6900,37 @@ msgid "WikiClone|Start Gollum and edit locally"
msgstr "開始你的 Gollum 並在本機編輯。"
msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
-msgstr ""
+msgstr "æ示:你å¯ä»¥åœ¨æ¨™é¡Œé–‹é ­åŠ ä¸Šè·¯å¾‘,這將會移動這個é é¢ã€‚"
msgid "WikiEdit|There is already a page with the same title in that path."
-msgstr ""
+msgstr "在該路徑中已經有相åŒæ¨™é¡Œçš„é é¢ã€‚"
+
+msgid "WikiEmptyIssueMessage|Suggest wiki improvement"
+msgstr "建議 Wiki 改善"
+
+msgid "WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}."
+msgstr "您必須是專案æˆå“¡æ‰èƒ½å»ºç«‹ Wiki é é¢ã€‚如果你有為此專案改善 Wiki 的建議,請考慮在 %{issues_link} 開啟一個議題。"
+
+msgid "WikiEmptyIssueMessage|issue tracker"
+msgstr "議題追蹤器"
+
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
+msgstr "å¯ä»¥å„²å­˜æ‚¨å°ˆæ¡ˆçš„所有詳情。這å¯ä»¥åŒ…å«æ‚¨å»ºç«‹ä»–的原因ã€åŽŸç†ã€å¦‚何使用等等。"
+
+msgid "WikiEmpty|Create your first page"
+msgstr "建立您的第一個é é¢"
+
+msgid "WikiEmpty|Suggest wiki improvement"
+msgstr "建議 Wiki 改善"
+
+msgid "WikiEmpty|The wiki lets you write documentation for your project"
+msgstr "Wiki å…許您為您的專案撰寫文件"
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
-msgstr "你沒有權é™å»ºç«‹ Wiki é é¢"
+msgid "WikiEmpty|This project has no wiki pages"
+msgstr "此專案沒有任何 Wiki é é¢"
+
+msgid "WikiEmpty|You must be a project member in order to add wiki pages."
+msgstr "您必須是專案æˆå“¡æ‰èƒ½å»ºç«‹ Wiki é é¢ã€‚"
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "這個é é¢è¼ƒèˆŠçš„版本。"
@@ -4694,6 +6965,12 @@ msgstr "新的維基é é¢"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr "你確定è¦åˆªé™¤é€™å€‹é é¢ï¼Ÿ"
+msgid "WikiPageConfirmDelete|Delete page"
+msgstr "刪除é é¢"
+
+msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
+msgstr "刪除 %{pageTitle} é é¢ï¼Ÿ"
+
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs."
msgstr "æŸå€‹äººåœ¨åŒä¸€æ™‚間跟你一起編輯了這個é é¢ã€‚請檢查 %{page_link} 並確定你的變更並沒有被他們無æ„中移除。"
@@ -4709,7 +6986,7 @@ msgstr "æ›´æ–° %{page_title}"
msgid "WikiPage|Page slug"
msgstr "é é¢ slug"
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr "填寫內容或拖曳檔案至此..."
msgid "Wiki|Create Page"
@@ -4721,9 +6998,6 @@ msgstr "建立é é¢"
msgid "Wiki|Edit Page"
msgstr "編輯é é¢"
-msgid "Wiki|Empty page"
-msgstr "空白é é¢"
-
msgid "Wiki|More Pages"
msgstr "更多é é¢"
@@ -4743,73 +7017,112 @@ msgid "Wiki|Wiki Pages"
msgstr "維基é é¢"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr ""
+msgstr "é€éŽè²¢ç»åˆ†æžï¼Œæ‚¨å¯ä»¥å–得活動概覽,其包å«äº†æ‚¨çš„組織和其æˆå“¡çš„è­°é¡Œã€åˆä½µè«‹æ±‚與推é€æ´»å‹•ã€‚"
msgid "Withdraw Access Request"
msgstr "å–消權é™ç”³è«‹"
-msgid "Write a commit message..."
+msgid "Yes"
+msgstr "是"
+
+msgid "Yes, add it"
+msgstr "是的,新增它"
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "å°‡è¦åˆªé™¤ %{group_name}。被刪除的群組無法復原ï¼çœŸçš„「確定ã€è¦é€™éº¼åšå—Žï¼Ÿ"
msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
-msgstr ""
+msgstr "å°‡è¦åˆªé™¤ %{project_full_name}。被刪除的專案無法復原ï¼çœŸçš„「確定ã€è¦é€™éº¼åšå—Žï¼Ÿ"
msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
msgstr "å°‡è¦åˆªé™¤æœ¬åˆ†æ”¯å°ˆæ¡ˆèˆ‡ä¸»å¹¹ %{forked_from_project} 的所有關è¯ã€‚ 真的「確定ã€è¦é€™éº¼åšå—Žï¼Ÿ"
msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
-msgstr ""
+msgstr "å°‡è¦æŠŠ %{project_full_name} 的所有權轉移給å¦ä¸€å€‹äººã€‚真的「確定ã€è¦é€™éº¼åšå—Žï¼Ÿ"
msgid "You are on a read-only GitLab instance."
-msgstr ""
+msgstr "您在唯讀的 GitLab 主機上。"
-msgid "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the %{primary_node}."
-msgstr ""
+msgid "You are on a secondary, <b>read-only</b> Geo node. If you want to make changes, you must visit this page on the %{primary_node}."
+msgstr "您在次è¦ä¸”<b>唯讀</b>çš„ Geo 節點上。若您想è¦é€²è¡Œè®Šæ›´ï¼Œæ‚¨å¿…é ˆç€è¦½ %{primary_node} 的這個é é¢"
+
+msgid "You can %{linkStart}view the blob%{linkEnd} instead."
+msgstr "您å¯ä»¥ %{linkStart}查看BLOB%{linkEnd} 。"
msgid "You can also create a project from the command line."
-msgstr ""
+msgstr "您也å¯ä»¥å¾žæŒ‡ä»¤æ–°å¢žä¸€å€‹å°ˆæ¡ˆã€‚"
msgid "You can also star a label to make it a priority label."
+msgstr "您還å¯ä»¥ç‚ºæŸå€‹æ¨™ç±¤åŠ ä¸Šæ˜Ÿæ˜Ÿï¼Œä½¿å…¶æˆç‚ºå„ªå…ˆæ¨™ç±¤ã€‚"
+
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr "您也å¯ä»¥åœ¨ %{linkStart}Lint%{linkEnd} 測試您的 .gitlab-ci.yml"
+
+msgid "You can easily contribute to them by requesting to join these groups."
msgstr ""
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
-msgstr ""
+msgstr "您å¯ä»¥è¼•é¬†åœ°åœ¨ Kubernetes å¢é›†ä¸Šå®‰è£é‹è¡Œå™¨ã€‚ %{link_to_help_page}"
msgid "You can move around the graph by using the arrow keys."
-msgstr ""
+msgstr "您å¯ä»¥ä½¿ç”¨ä¸Šä¸‹å·¦å³éµç§»å‹•åœ–形。"
msgid "You can only add files when you are on a branch"
-msgstr "åªèƒ½åœ¨åˆ†æ”¯ (branch) 上建立檔案"
+msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šå»ºç«‹æª”案"
msgid "You can only edit files when you are on a branch"
-msgstr ""
+msgstr "您åªèƒ½åœ¨åˆ†æ”¯ä¸Šç·¨è¼¯æ–‡ä»¶"
+
+msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
+msgstr "您å¯ä»¥é€éŽä½¿ç”¨äº’動模å¼é¸æ“‡ %{use_ours} 或 %{use_theirs} 按鈕ã€æˆ–者是直接編輯檔案來解決åˆä½µè¡çªï¼Œä¸¦å°‡é€™äº›è®Šæ›´æ交到 %{branch_name}。"
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
-msgstr ""
+msgstr "您ä¸èƒ½å¯«å…¥åˆ°å”¯è®€çš„æ¬¡è¦ GitLab Geo 主機。請改用 %{link_to_primary_node}。"
msgid "You cannot write to this read-only GitLab instance."
msgstr "您ä¸èƒ½ä¿®æ”¹é€™å€‹å”¯è®€çš„ GitLab 主機。"
+msgid "You do not have any assigned merge requests"
+msgstr "您沒有被分é…çš„åˆä½µè«‹æ±‚"
+
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
+msgstr "您沒有權é™ä¾†è¦†è“‹ LDAP 群組åŒæ­¥è¨­å®šã€‚"
+
+msgid "You don't have any applications"
msgstr ""
-msgid "You have no permissions"
+msgid "You don't have any authorized applications"
msgstr ""
+msgid "You have no permissions"
+msgstr "你沒有權é™"
+
+msgid "You have not created any merge requests"
+msgstr "您尚未創建任何åˆä½µè«‹æ±‚"
+
msgid "You have reached your project limit"
msgstr "您已é”到專案數é‡é™åˆ¶"
-msgid "You must have master access to force delete a lock"
-msgstr ""
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr "您必須接å—我們的æœå‹™æ¢æ¬¾å’Œéš±ç§æ”¿ç­–æ‰èƒ½è¨»å†Šå¸³æˆ¶"
+
+msgid "You must have maintainer access to force delete a lock"
+msgstr "您必須æ“有維護者存å–æ‰èƒ½å¼·åˆ¶ç§»é™¤éŽ–定"
msgid "You must sign in to star a project"
msgstr "必須登入æ‰èƒ½æ”¶è—專案"
msgid "You need a different license to enable FileLocks feature"
-msgstr ""
+msgstr "您需è¦ä½¿ç”¨ä¸åŒçš„授權以啟用 FileLocks 功能"
+
+msgid "You need git-lfs version %{min_git_lfs_version} (or greater) to continue. Please visit https://git-lfs.github.com"
+msgstr "æ‚¨éœ€è¦ git-lfs 版本 %{min_git_lfs_version} (或更高版本) æ‰èƒ½ç¹¼çºŒã€‚è«‹å‰å¾€ https://git-lfs.github.com"
msgid "You need permission."
msgstr "需è¦æ¬Šé™æ‰èƒ½é€™éº¼åšã€‚"
@@ -4830,40 +7143,55 @@ msgid "You will receive notifications only for comments in which you were @menti
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} 上傳 (push) 或下載 (pull) 程å¼ç¢¼ã€‚"
+msgstr "在帳號上 %{set_password_link} 之å‰ï¼Œ 將無法使用 %{protocol} 上傳或下載程å¼ç¢¼ã€‚"
msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
-msgstr "在個人帳號中 %{add_ssh_key_link} 之å‰ï¼Œ 將無法使用 SSH 上傳 (push) 或下載 (pull) 程å¼ç¢¼ã€‚"
+msgstr "在個人帳號中 %{add_ssh_key_link} 之å‰ï¼Œ 將無法使用 SSH 上傳或下載程å¼ç¢¼ã€‚"
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr ""
+msgstr "在您的個人資料中添加 SSH 密鑰之å‰ï¼Œä½ ä¸èƒ½é€éŽ SSH 來拉å–或推é€å°ˆæ¡ˆç¨‹å¼ç¢¼ã€‚"
msgid "You'll need to use different branch names to get a valid comparison."
+msgstr "你需è¦é¸æ“‡å…©å€‹ä¸åŒçš„分支,æ‰èƒ½é€²è¡Œæ¯”較。"
+
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
msgstr ""
msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
+msgstr "你收到此電å­éƒµä»¶æ˜¯å› ç‚ºä½ çš„帳戶在 %{host}. %{manage_notifications_link} &middot; %{help_link}"
+
+msgid "YouTube"
msgstr ""
msgid "Your Groups"
-msgstr ""
+msgstr "你的群組"
msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
-msgstr ""
+msgstr "您的Kuberneteså¢é›†è³‡è¨Šä»ç„¶å¯ä»¥ç·¨è¼¯ï¼Œä½†å»ºè­°æ‚¨ç¦ç”¨ä¸¦é‡æ–°è¨­ç½®ã€‚"
msgid "Your Projects (default)"
-msgstr ""
+msgstr "您的專案(é è¨­å€¼ï¼‰"
msgid "Your Projects' Activity"
-msgstr ""
+msgstr "您的專案活動"
msgid "Your Todos"
+msgstr "您的待辦事項"
+
+msgid "Your applications (%{size})"
msgstr ""
-msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgid "Your authorized applications"
msgstr ""
+msgid "Your changes can be committed to %{branch_name} because a merge request is open."
+msgstr "你的更改將å¯ä»¥æ交到 %{branch_name} 因為åˆä½µè«‹æ±‚已打開。"
+
msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
-msgstr ""
+msgstr "您的更改已被æ交。更動紀錄 %{commitId} %{commitStats}"
msgid "Your comment will not be visible to the public."
msgstr "你的留言將ä¸æœƒè¢«å…¬é–‹ã€‚"
@@ -4877,337 +7205,502 @@ msgstr "您的åå­—"
msgid "Your projects"
msgstr "你的計劃"
+msgid "ago"
+msgstr "之å‰"
+
msgid "among other things"
-msgstr ""
+msgstr "除了其他事情"
-msgid "and %d fixed vulnerability"
+msgid "and 1 fixed vulnerability"
msgid_plural "and %d fixed vulnerabilities"
-msgstr[0] ""
+msgstr[0] "å’Œ %d 個已經修復的æ¼æ´ž"
msgid "assign yourself"
-msgstr ""
+msgstr "指派給自己"
msgid "branch name"
-msgstr ""
+msgstr "分支å稱"
msgid "by"
-msgstr ""
+msgstr "來自"
+
+msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
+msgstr "%{linkStartTag}關於容器掃æ的更多資訊%{linkEndTag}"
+
+msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
+msgstr "%{linkStartTag}關於 DAST 的更多資訊%{linkEndTag}"
+
+msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
+msgstr "%{linkStartTag}關於相ä¾æ€§æŽƒæ的更多資訊%{linkEndTag}"
+
+msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
+msgstr "%{linkStartTag}關於 SAST 的更多資訊%{linkEndTag}"
+
+msgid "ciReport|%{namespace} is affected by %{vulnerability}."
+msgstr "%{namespace} 被 %{vulnerability} 影響"
+
+msgid "ciReport|%{packagesString} and "
+msgstr "%{packagesString} 和 "
+
+msgid "ciReport|%{packagesString} and %{lastPackage}"
+msgstr "%{packagesString} 和 %{lastPackage}"
+
+msgid "ciReport|%{remainingPackagesCount} more"
+msgstr "%{remainingPackagesCount} 更多"
+
+msgid "ciReport|%{reportName} is loading"
+msgstr "正在載入 %{reportName}"
+
+msgid "ciReport|%{reportName} resulted in error while loading results"
+msgstr "載入çµæžœæ™‚å›  %{reportName} 而導致錯誤"
msgid "ciReport|%{type} detected no new security vulnerabilities"
-msgstr ""
+msgstr "%{type} 未åµæ¸¬åˆ°æ–°çš„安全性æ¼æ´ž"
msgid "ciReport|%{type} detected no security vulnerabilities"
-msgstr ""
+msgstr "%{type} 未åµæ¸¬åˆ°å®‰å…¨æ€§æ¼æ´ž"
+
+msgid "ciReport|%{type} detected no vulnerabilities"
+msgstr "%{type} 未åµæ¸¬åˆ°æ¼æ´ž"
+
+msgid "ciReport|Class"
+msgstr "類別"
msgid "ciReport|Code quality"
-msgstr ""
+msgstr "程å¼ç¢¼å“質"
-msgid "ciReport|DAST detected no alerts by analyzing the review app"
-msgstr ""
+msgid "ciReport|Confidence"
+msgstr "ä¿¡ä»»"
-msgid "ciReport|Dependency scanning"
-msgstr ""
+msgid "ciReport|Container scanning detected"
+msgstr "åµæ¸¬åˆ°å®¹å™¨æŽƒæ"
+
+msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
+msgstr "容器掃æåµæ¸¬åœ¨æ‚¨çš„ Docker 映åƒä¸­çš„已知æ¼æ´žã€‚"
+
+msgid "ciReport|Container scanning is loading"
+msgstr "正在載入容器掃æ"
+
+msgid "ciReport|Container scanning resulted in error while loading results"
+msgstr "當載入çµæžœæ™‚,因容器掃æ而導致錯誤"
+
+msgid "ciReport|DAST detected"
+msgstr "åµæ¸¬åˆ° DAST"
+
+msgid "ciReport|DAST is loading"
+msgstr "正在載入 DAST"
+
+msgid "ciReport|DAST resulted in error while loading results"
+msgstr "載入çµæžœæ™‚,因 DAST 而導致錯誤"
+
+msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
+msgstr "相ä¾æ€§æŽƒæåµæ¸¬æ‚¨åŽŸå§‹ç¢¼ç›¸ä¾æ€§çš„已知æ¼æ´žã€‚"
msgid "ciReport|Dependency scanning detected"
-msgstr ""
+msgstr "åµæ¸¬åˆ°ç›¸ä¾æ€§æŽƒæ"
-msgid "ciReport|Dependency scanning detected no new security vulnerabilities"
-msgstr ""
+msgid "ciReport|Dependency scanning is loading"
+msgstr "正在載入相ä¾æ€§æŽƒæ"
-msgid "ciReport|Dependency scanning detected no security vulnerabilities"
-msgstr ""
+msgid "ciReport|Dependency scanning resulted in error while loading results"
+msgstr "當載入çµæžœæ™‚因相ä¾æ€§æŽƒæ而導致錯誤"
+
+msgid "ciReport|Description"
+msgstr "說明"
+
+msgid "ciReport|Dismiss vulnerability"
+msgstr "忽略æ¼æ´ž"
+
+msgid "ciReport|Dismissed by"
+msgstr "忽略由"
+
+msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
+msgstr "動態應用程å¼å®‰å…¨æ€§æ¸¬è©¦ (DAST) 在您的網é æ‡‰ç”¨ç¨‹å¼ä¸­åµæ¸¬åˆ°å·²çŸ¥æ¼æ´žã€‚"
msgid "ciReport|Failed to load %{reportName} report"
-msgstr ""
+msgstr "無法載入 %{reportName} 報告"
+
+msgid "ciReport|File"
+msgstr "檔案"
msgid "ciReport|Fixed:"
-msgstr ""
+msgstr "修正的:"
+
+msgid "ciReport|Identifiers"
+msgstr "身份"
msgid "ciReport|Instances"
-msgstr ""
+msgstr "實例"
+
+msgid "ciReport|Learn more about interacting with security reports (Alpha)."
+msgstr "了解關於和安全性報告 (Alpha) 互動的更多資訊。"
msgid "ciReport|Learn more about whitelisting"
-msgstr ""
+msgstr "了解關於白å單的更多資訊"
+
+msgid "ciReport|License management detected %{licenseInfo}"
+msgstr "授權管ç†åµæ¸¬åˆ° %{licenseInfo}"
+
+msgid "ciReport|License management detected no new licenses"
+msgstr "授權管ç†æœªåµæ¸¬åˆ°æ–°æŽˆæ¬Š"
+
+msgid "ciReport|Links"
+msgstr "連çµ"
msgid "ciReport|Loading %{reportName} report"
-msgstr ""
+msgstr "正在載入 %{reportName} 報告"
+
+msgid "ciReport|Method"
+msgstr "方法"
+
+msgid "ciReport|Namespace"
+msgstr "命å空間"
msgid "ciReport|No changes to code quality"
-msgstr ""
+msgstr "沒有程å¼ç¢¼å“質的更動"
msgid "ciReport|No changes to performance metrics"
-msgstr ""
+msgstr "沒有效能指標的變動"
msgid "ciReport|Performance metrics"
-msgstr ""
+msgstr "效能指標"
-msgid "ciReport|SAST"
-msgstr ""
+msgid "ciReport|Revert dismissal"
+msgstr "撤回忽略"
msgid "ciReport|SAST detected"
-msgstr ""
+msgstr "åµæ¸¬åˆ° SAST"
-msgid "ciReport|SAST detected no new security vulnerabilities"
-msgstr ""
+msgid "ciReport|SAST is loading"
+msgstr "正在載入 SAST"
-msgid "ciReport|SAST detected no security vulnerabilities"
-msgstr ""
-
-msgid "ciReport|SAST:container no vulnerabilities were found"
-msgstr ""
+msgid "ciReport|SAST resulted in error while loading results"
+msgstr "當載入çµæžœæ™‚,因 SAST 而導致錯誤"
msgid "ciReport|Security scanning"
-msgstr ""
+msgstr "安全性掃æ"
msgid "ciReport|Security scanning failed loading any results"
-msgstr ""
+msgstr "安全性掃æ無法載入任何çµæžœ"
-msgid "ciReport|Show complete code vulnerabilities report"
-msgstr ""
+msgid "ciReport|Security scanning is loading"
+msgstr "正在載入安全掃æ"
+
+msgid "ciReport|Severity"
+msgstr "åš´é‡æ€§"
+
+msgid "ciReport|Solution"
+msgstr "解決方案"
+
+msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
+msgstr "éœæ…‹æ‡‰ç”¨ç¨‹å¼å®‰å…¨æ€§æ¸¬è©¦ (SAST) 在您的原始碼中åµæ¸¬å·²çŸ¥æ¼æ´žã€‚"
+
+msgid "ciReport|There was an error creating the issue. Please try again."
+msgstr "建立議題時發生錯誤。請é‡è©¦ã€‚"
+
+msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
+msgstr "é§å›žæ­¤æ¼æ´žæ™‚發生錯誤。請é‡è©¦ã€‚"
+
+msgid "ciReport|There was an error loading DAST report"
+msgstr "載入 DAST 報告時發生錯誤。"
-msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgid "ciReport|There was an error loading SAST report"
+msgstr "載入 SAST 報告時發生錯誤。"
+
+msgid "ciReport|There was an error loading container scanning report"
+msgstr "載入容器掃æ報告時發生錯誤。"
+
+msgid "ciReport|There was an error loading dependency scanning report"
+msgstr "載入相ä¾æ€§æŽƒæ報告時發生錯誤。"
+
+msgid "ciReport|There was an error reverting the dismissal. Please try again."
+msgstr "撤回é§å›žæ™‚發生錯誤。請é‡è©¦ã€‚"
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
+msgstr "未批准的æ¼æ´ž (紅色) å¯ä»¥æ¨™è¨˜ç‚ºå·²æ‰¹å‡†ã€‚"
+
+msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
+msgstr "å°‡ %{name} 從 %{version} å‡ç´šåˆ° %{fixed}。"
+
+msgid "ciReport|View full report"
msgstr ""
msgid "ciReport|no vulnerabilities"
-msgstr ""
+msgstr "沒有æ¼æ´ž"
+
+msgid "ciReport|on pipeline"
+msgstr "æµæ°´ç·šä¸Š"
msgid "command line instructions"
-msgstr ""
+msgstr "指令說明"
msgid "connecting"
-msgstr ""
+msgstr "連線中"
msgid "could not read private key, is the passphrase correct?"
+msgstr "無法存å–ç§é‘°ï¼Œæª¢æŸ¥å¯†ç¢¼æ˜¯å¦æ­£ç¢ºï¼Ÿ"
+
+msgid "customize"
msgstr ""
msgid "day"
msgid_plural "days"
-msgstr[0] "天"
+msgstr[0] "æ—¥"
+
+msgid "deploy token"
+msgstr "部署憑證"
msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities"
-msgstr[0] ""
+msgstr[0] "åµæ¸¬åˆ° %d 個已修復æ¼æ´ž"
msgid "detected %d new vulnerability"
msgid_plural "detected %d new vulnerabilities"
-msgstr[0] ""
+msgstr[0] "åµæ¸¬åˆ° %d 個新æ¼æ´ž"
msgid "detected no vulnerabilities"
+msgstr "未åµæ¸¬åˆ°æ¼æ´ž"
+
+msgid "disabled"
+msgstr "å·²åœç”¨"
+
+msgid "done"
msgstr ""
+msgid "enabled"
+msgstr "已啟用"
+
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
-msgstr ""
+msgstr "%{slash_command} 將更新é ä¼°èŠ±è²»æ™‚間。"
+
+msgid "for this project"
+msgstr "為此專案"
msgid "here"
-msgstr ""
+msgstr "這裡"
-msgid "importing"
+msgid "import flow"
msgstr ""
-msgid "in progress"
-msgstr ""
+msgid "importing"
+msgstr "輸入"
msgid "is invalid because there is downstream lock"
-msgstr ""
+msgstr "因為下游鎖定,所以無效"
msgid "is invalid because there is upstream lock"
-msgstr ""
+msgstr "因上游鎖定而無效"
msgid "is not a valid X509 certificate."
-msgstr ""
+msgstr "éžæœ‰æ•ˆ X509 憑證。"
+
+msgid "latest version"
+msgstr "最新版本"
msgid "locked by %{path_lock_user_name} %{created_at}"
-msgstr ""
+msgstr "由 %{path_lock_user_name} 於 %{created_at} 鎖定"
msgid "merge request"
msgid_plural "merge requests"
-msgstr[0] ""
+msgstr[0] "åˆä½µè«‹æ±‚"
msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
-msgstr ""
+msgstr "è«‹æ¢å¾©å®ƒæˆ–使用一個ä¸åŒ %{missingBranchName} 的分支"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} 記憶體 %{metricsLinkEnd} 使用 %{emphasisStart} ä¸‹é™ %{emphasisEnd} 從 %{memoryFrom} MB 到 %{memoryTo} MB"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} 記憶體 %{metricsLinkEnd} 使用 %{emphasisStart} ä¸Šå‡ %{emphasisEnd} 從 %{memoryFrom} MB 到 %{memoryTo} MB"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB"
-msgstr ""
+msgstr "%{metricsLinkStart} 記憶體 %{metricsLinkEnd} 使用 %{emphasisStart} 沒有發生變化 %{emphasisEnd} 於 %{memoryFrom} MB"
msgid "mrWidget|Add approval"
-msgstr ""
+msgstr "新增批准"
-msgid "mrWidget|Allows edits from maintainers"
-msgstr ""
+msgid "mrWidget|Allows commits from members who can merge to the target branch"
+msgstr "å…許å¯ä»¥åˆä½µåˆ°ç›®æ¨™åˆ†æ”¯çš„æˆå“¡æ交"
msgid "mrWidget|An error occured while removing your approval."
-msgstr ""
+msgstr "移除您的批准時發生錯誤。"
-msgid "mrWidget|An error occured while retrieving approval data for this merge request."
-msgstr ""
-
-msgid "mrWidget|An error occured while submitting your approval."
+msgid "mrWidget|An error occurred while submitting your approval."
msgstr ""
msgid "mrWidget|Approve"
-msgstr ""
-
-msgid "mrWidget|Approved"
-msgstr ""
+msgstr "批准"
msgid "mrWidget|Approved by"
-msgstr ""
+msgstr "批准由"
msgid "mrWidget|Cancel automatic merge"
-msgstr ""
+msgstr "å–消自動åˆä½µ"
msgid "mrWidget|Check out branch"
-msgstr ""
+msgstr "é·å‡ºæ­¤åˆ†æ”¯"
msgid "mrWidget|Checking ability to merge automatically"
-msgstr ""
+msgstr "檢查是å¦å¯ä»¥è‡ªå‹•åˆä½µ"
msgid "mrWidget|Cherry-pick"
-msgstr ""
+msgstr "挑é¸"
msgid "mrWidget|Cherry-pick this merge request in a new merge request"
-msgstr ""
+msgstr "挑é¸æ­¤åˆä½µè«‹æ±‚的修訂版本以建立新的åˆä½µè«‹æ±‚"
msgid "mrWidget|Closed"
-msgstr ""
+msgstr "關閉"
msgid "mrWidget|Closed by"
-msgstr ""
+msgstr "關閉"
msgid "mrWidget|Closes"
-msgstr ""
+msgstr "關閉"
+
+msgid "mrWidget|Create an issue to resolve them later"
+msgstr "建立一個議題,以在ç¨å€™è§£æ±º"
msgid "mrWidget|Deployment statistics are not available currently"
-msgstr ""
+msgstr "ç›®å‰ç„¡æ³•ä½¿ç”¨éƒ¨ç½²çš„統計資料"
msgid "mrWidget|Did not close"
-msgstr ""
+msgstr "沒有關閉"
msgid "mrWidget|Email patches"
-msgstr ""
+msgstr "Email 補ä¸"
msgid "mrWidget|Failed to load deployment statistics"
-msgstr ""
+msgstr "讀å–部署統計資料時發生錯誤"
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
-msgstr ""
+msgstr "如果 %{branch} 分支存在於您的本地檔案庫,你å¯ä»¥æ‰‹å‹•åˆä½µæ­¤åˆä½µè«‹æ±‚,藉由"
msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
-msgstr ""
+msgstr "如果 %{missingBranchName} 分支徂在於您的本地檔案庫,您å¯ä»¥æ‰‹å‹•åˆä½µæ­¤åˆä½µè«‹æ±‚藉由此指令"
msgid "mrWidget|Loading deployment statistics"
-msgstr ""
+msgstr "讀å–部署統計資料"
msgid "mrWidget|Mentions"
-msgstr ""
+msgstr "æ到"
msgid "mrWidget|Merge"
-msgstr ""
+msgstr "åˆä½µ"
msgid "mrWidget|Merge failed."
-msgstr ""
+msgstr "åˆä½µå¤±æ•—"
msgid "mrWidget|Merge locally"
+msgstr "本地åˆä½µ"
+
+msgid "mrWidget|Merge request approved"
msgstr ""
-msgid "mrWidget|Merged by"
+msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
+msgid "mrWidget|Merged by"
+msgstr "åˆä½µè€…"
+
+msgid "mrWidget|No Approval required"
+msgstr "無須批准"
+
+msgid "mrWidget|No Approval required; you can still approve"
+msgstr "ä¸éœ€è¦æ‰¹å‡†ï¼›ä½†æ‚¨ä»å¯ä»¥æ‰¹å‡†"
+
+msgid "mrWidget|Open in Web IDE"
+msgstr "åœ¨ç¶²é  IDE 中開啟"
+
msgid "mrWidget|Plain diff"
-msgstr ""
+msgstr "本文差異"
msgid "mrWidget|Refresh"
-msgstr ""
+msgstr "é‡æ–°æ•´ç†"
msgid "mrWidget|Refresh now"
-msgstr ""
+msgstr "ç«‹å³é‡æ–°æ•´ç†"
msgid "mrWidget|Refreshing now"
-msgstr ""
+msgstr "ç«‹å³é‡æ–°æ•´ç†"
msgid "mrWidget|Remove Source Branch"
-msgstr ""
+msgstr "刪除原始分支"
msgid "mrWidget|Remove source branch"
-msgstr ""
+msgstr "刪除原始分支"
msgid "mrWidget|Remove your approval"
-msgstr ""
+msgstr "移除您的批准"
msgid "mrWidget|Request to merge"
-msgstr ""
+msgstr "請求åˆä½µ"
msgid "mrWidget|Resolve conflicts"
-msgstr ""
+msgstr "解決è¡çª"
msgid "mrWidget|Revert"
-msgstr ""
+msgstr "還原"
msgid "mrWidget|Revert this merge request in a new merge request"
-msgstr ""
+msgstr "é€éŽæ–°çš„åˆä½µè«‹æ±‚還原此åˆä½µè«‹æ±‚æ›´å‹•çš„é …ç›®"
msgid "mrWidget|Set by"
-msgstr ""
+msgstr "設置者"
msgid "mrWidget|The changes were merged into"
-msgstr ""
+msgstr "這些更改將會åˆä½µåˆ°"
msgid "mrWidget|The changes were not merged into"
-msgstr ""
+msgstr "這些更改將ä¸æœƒåˆä½µåˆ°"
msgid "mrWidget|The changes will be merged into"
-msgstr ""
+msgstr "這些更改將會åˆä½µåˆ°"
msgid "mrWidget|The source branch has been removed"
-msgstr ""
+msgstr "來æºåˆ†æ”¯å·²ç¶“被刪除"
msgid "mrWidget|The source branch is being removed"
-msgstr ""
+msgstr "來æºåˆ†æ”¯æ­£åœ¨è¢«åˆªé™¤"
msgid "mrWidget|The source branch will be removed"
-msgstr ""
+msgstr "來æºåˆ†æ”¯å°‡æœƒè¢«åˆªé™¤"
msgid "mrWidget|The source branch will not be removed"
-msgstr ""
+msgstr "來æºåˆ†æ”¯å°‡ä¸æœƒè¢«åˆªé™¤"
msgid "mrWidget|There are merge conflicts"
-msgstr ""
+msgstr "發生åˆä½µè¡çª"
+
+msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
+msgstr "有個未解決的討論,請解決這些討論。"
msgid "mrWidget|This merge request failed to be merged automatically"
-msgstr ""
+msgstr "æ­¤åˆä½µè«‹æ±‚未能自動åˆä½µ"
msgid "mrWidget|This merge request is in the process of being merged"
-msgstr ""
+msgstr "æ­¤åˆä½µè«‹æ±‚正在被åˆä½µ"
msgid "mrWidget|This project is archived, write access has been disabled"
-msgstr ""
-
-msgid "mrWidget|Web IDE"
-msgstr ""
+msgstr "這個專案已經被打包,無法寫入"
msgid "mrWidget|You can merge this merge request manually using the"
-msgstr ""
+msgstr "ä½ å¯ä»¥æ‰‹å‹•åˆä½µé€™å€‹åˆä½µè«‹æ±‚,藉由"
msgid "mrWidget|You can remove source branch now"
-msgstr ""
+msgstr "ä½ ç¾åœ¨å¯ä»¥åˆªé™¤ä¾†æºåˆ†æ”¯"
msgid "mrWidget|branch does not exist."
-msgstr ""
+msgstr "分支並ä¸å­˜åœ¨"
msgid "mrWidget|command line"
-msgstr ""
+msgstr "指令"
msgid "mrWidget|into"
-msgstr ""
+msgstr "進入"
msgid "mrWidget|to be merged automatically when the pipeline succeeds"
-msgstr ""
+msgstr "當æµæ°´ç·šå®Œæˆæ™‚將會被自動åˆä½µ"
msgid "new merge request"
msgstr "建立åˆä½µè«‹æ±‚"
@@ -5216,7 +7709,7 @@ msgid "notification emails"
msgstr "通知信"
msgid "or"
-msgstr ""
+msgstr "或"
msgid "parent"
msgid_plural "parents"
@@ -5226,32 +7719,48 @@ msgid "password"
msgstr "密碼"
msgid "personal access token"
-msgstr "ç§äººå­˜å–憑證 (access token)"
+msgstr "ç§äººå­˜å–憑證"
msgid "private key does not match certificate."
-msgstr ""
+msgstr "ç§é‘°ä¸ç¬¦åˆæ†‘證。"
+
+msgid "remaining"
+msgstr "剩餘"
msgid "remove due date"
-msgstr ""
+msgstr "刪除截止日期"
+
+msgid "remove weight"
+msgstr "移除權é‡"
msgid "source"
-msgstr ""
+msgstr "來æº"
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr "%{slash_command} 將會更新ã€åŠ ç¸½æ‰€èŠ±è²»çš„時間"
+
+msgid "started"
msgstr ""
msgid "this document"
-msgstr ""
+msgstr "這份文件"
msgid "to help your contributors communicate effectively!"
-msgstr ""
+msgstr "å”助你的貢ç»è€…進行有效的æºé€šï¼"
msgid "username"
msgstr "使用者å稱"
msgid "uses Kubernetes clusters to deploy your code!"
+msgstr "使用 Kubernetes å¢é›†éƒ¨ç½²æ‚¨çš„程å¼ç¢¼!"
+
+msgid "view it on GitLab"
msgstr ""
msgid "with %{additions} additions, %{deletions} deletions."
-msgstr ""
+msgstr "共增加 %{additions} ,刪除 %{deletions}"
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] "在 %d 分é˜å…§ "
diff --git a/package.json b/package.json
index c42bbbb0351..975dd2619d7 100644
--- a/package.json
+++ b/package.json
@@ -18,11 +18,12 @@
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
},
"dependencies": {
- "@gitlab-org/gitlab-svgs": "^1.24.0",
+ "@gitlab-org/gitlab-svgs": "^1.27.0",
+ "@gitlab-org/gitlab-ui": "1.0.5",
"autosize": "^4.0.0",
"axios": "^0.17.1",
"babel-core": "^6.26.3",
- "babel-loader": "^7.1.4",
+ "babel-loader": "^7.1.5",
"babel-plugin-transform-define": "^1.3.0",
"babel-preset-latest": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
@@ -33,10 +34,12 @@
"chart.js": "1.0.2",
"classlist-polyfill": "^1.2.0",
"clipboard": "^1.7.1",
+ "codesandbox-api": "^0.0.18",
"compression-webpack-plugin": "^1.1.11",
"core-js": "^2.4.1",
"cropper": "^2.3.0",
- "css-loader": "^0.28.11",
+ "css-loader": "^1.0.0",
+ "d3": "4.12.2",
"d3-array": "^1.2.1",
"d3-axis": "^1.0.8",
"d3-brush": "^1.0.4",
@@ -45,6 +48,7 @@
"d3-shape": "^1.2.0",
"d3-time": "^1.0.8",
"d3-time-format": "^2.1.1",
+ "dateformat": "^3.0.3",
"deckar01-task_list": "^2.0.0",
"diff": "^3.4.0",
"document-register-element": "1.3.0",
@@ -52,6 +56,7 @@
"emoji-unicode-version": "^0.2.1",
"exports-loader": "^0.7.0",
"file-loader": "^1.1.11",
+ "formdata-polyfill": "^3.0.11",
"fuzzaldrin-plus": "^0.5.0",
"glob": "^7.1.2",
"imports-loader": "^0.8.0",
@@ -65,7 +70,7 @@
"katex": "^0.8.3",
"marked": "^0.3.12",
"monaco-editor": "0.13.1",
- "monaco-editor-webpack-plugin": "^1.2.1",
+ "monaco-editor-webpack-plugin": "^1.4.0",
"mousetrap": "^1.4.6",
"pikaday": "^1.6.1",
"popper.js": "^1.14.3",
@@ -76,6 +81,7 @@
"sanitize-html": "^1.16.1",
"select2": "3.5.2-browserify",
"sha1": "^1.1.1",
+ "smooshpack": "^0.0.48",
"sortablejs": "^1.7.0",
"sql.js": "^0.4.0",
"stickyfilljs": "^2.0.5",
@@ -89,17 +95,18 @@
"url-loader": "^1.0.1",
"visibilityjs": "^1.2.4",
"vue": "^2.5.16",
- "vue-loader": "^15.2.0",
+ "vue-loader": "^15.2.4",
"vue-resource": "^1.5.0",
"vue-router": "^3.0.1",
"vue-template-compiler": "^2.5.16",
"vue-virtual-scroll-list": "^1.2.5",
"vuex": "^3.0.1",
- "webpack": "^4.11.1",
- "webpack-bundle-analyzer": "^2.11.1",
- "webpack-cli": "^3.0.2",
+ "webpack": "^4.16.0",
+ "webpack-bundle-analyzer": "^2.13.1",
+ "webpack-cli": "^3.0.8",
"webpack-stats-plugin": "^0.2.1",
- "worker-loader": "^2.0.0"
+ "worker-loader": "^2.0.0",
+ "xterm": "^3.5.0"
},
"devDependencies": {
"axios-mock-adapter": "^1.15.0",
@@ -122,15 +129,16 @@
"ignore": "^3.3.7",
"istanbul": "^0.4.5",
"jasmine-core": "^2.9.0",
+ "jasmine-diff": "^0.1.3",
"jasmine-jquery": "^2.1.1",
- "karma": "^2.0.2",
+ "karma": "^2.0.4",
"karma-chrome-launcher": "^2.2.0",
"karma-coverage-istanbul-reporter": "^1.4.2",
- "karma-jasmine": "^1.1.1",
+ "karma-jasmine": "^1.1.2",
"karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.7",
- "karma-webpack": "3.0.0",
- "nodemon": "^1.17.3",
+ "karma-webpack": "^4.0.0-beta.0",
+ "nodemon": "^1.18.2",
"prettier": "1.12.1",
"webpack-dev-server": "^3.1.4"
}
diff --git a/public/404.html b/public/404.html
index 08f328da542..68b4ab0bb34 100644
--- a/public/404.html
+++ b/public/404.html
@@ -66,8 +66,10 @@
</head>
<body>
- <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
+ <a href="/">
+ <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
alt="GitLab Logo" />
+ </a>
<h1>
404
</h1>
diff --git a/public/422.html b/public/422.html
index a67dcd02200..a931e923efb 100644
--- a/public/422.html
+++ b/public/422.html
@@ -66,8 +66,10 @@
</head>
<body>
- <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
+ <a href="/">
+ <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
alt="GitLab Logo" />
+ </a>
<h1>
422
</h1>
diff --git a/public/500.html b/public/500.html
index 7091d14dfc4..df7b22dc9ef 100644
--- a/public/500.html
+++ b/public/500.html
@@ -66,8 +66,10 @@
</head>
<body>
- <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
+ <a href="/">
+ <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
alt="GitLab Logo" />
+ </a>
<h1>
500
</h1>
diff --git a/public/502.html b/public/502.html
index 82afd273248..77835767fa6 100644
--- a/public/502.html
+++ b/public/502.html
@@ -66,8 +66,10 @@
</head>
<body>
- <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
+ <a href="/">
+ <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
alt="GitLab Logo" />
+ </a>
<h1>
502
</h1>
diff --git a/public/503.html b/public/503.html
index f1486bc3e84..ee2da9b1313 100644
--- a/public/503.html
+++ b/public/503.html
@@ -66,8 +66,10 @@
</head>
<body>
- <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
+ <a href="/">
+ <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo="
alt="GitLab Logo" />
+ </a>
<h1>
503
</h1>
diff --git a/qa/README.md b/qa/README.md
index a4b4398645e..be4cf89ebbc 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -55,7 +55,7 @@ Since the arguments would be passed to `rspec`, you could use all `rspec`
options there. For example, passing `--backtrace` and also line number:
```
-bin/qa Test::Instance http://localhost qa/specs/features/login/standard_spec.rb:3 --backtrace
+bin/qa Test::Instance http://localhost qa/specs/features/project/create_spec.rb:3 --backtrace
```
### Overriding the authenticated user
diff --git a/qa/qa.rb b/qa/qa.rb
index 5013024e60f..0b48cf58766 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -1,5 +1,7 @@
$: << File.expand_path(File.dirname(__FILE__))
+Encoding.default_external = 'UTF-8'
+
module QA
##
# GitLab QA runtime classes, mostly singletons.
@@ -40,13 +42,19 @@ module QA
autoload :Issue, 'qa/factory/resource/issue'
autoload :Project, 'qa/factory/resource/project'
autoload :MergeRequest, 'qa/factory/resource/merge_request'
+ autoload :ProjectImportedFromGithub, 'qa/factory/resource/project_imported_from_github'
+ autoload :MergeRequestFromFork, 'qa/factory/resource/merge_request_from_fork'
autoload :DeployKey, 'qa/factory/resource/deploy_key'
autoload :Branch, 'qa/factory/resource/branch'
autoload :SecretVariable, 'qa/factory/resource/secret_variable'
autoload :Runner, 'qa/factory/resource/runner'
autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
autoload :KubernetesCluster, 'qa/factory/resource/kubernetes_cluster'
+ autoload :User, 'qa/factory/resource/user'
+ autoload :ProjectMilestone, 'qa/factory/resource/project_milestone'
autoload :Wiki, 'qa/factory/resource/wiki'
+ autoload :File, 'qa/factory/resource/file'
+ autoload :Fork, 'qa/factory/resource/fork'
end
module Repository
@@ -79,6 +87,7 @@ module QA
autoload :Instance, 'qa/scenario/test/instance'
module Integration
+ autoload :Github, 'qa/scenario/test/integration/github'
autoload :LDAP, 'qa/scenario/test/integration/ldap'
autoload :Kubernetes, 'qa/scenario/test/integration/kubernetes'
autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
@@ -104,6 +113,7 @@ module QA
module Main
autoload :Login, 'qa/page/main/login'
autoload :OAuth, 'qa/page/main/oauth'
+ autoload :SignUp, 'qa/page/main/sign_up'
end
module Settings
@@ -127,11 +137,24 @@ module QA
autoload :Show, 'qa/page/group/show'
end
+ module File
+ autoload :Form, 'qa/page/file/form'
+ autoload :Show, 'qa/page/file/show'
+
+ module Shared
+ autoload :CommitMessage, 'qa/page/file/shared/commit_message'
+ end
+ end
+
module Project
autoload :New, 'qa/page/project/new'
autoload :Show, 'qa/page/project/show'
autoload :Activity, 'qa/page/project/activity'
+ module Import
+ autoload :Github, 'qa/page/project/import/github'
+ end
+
module Pipeline
autoload :Index, 'qa/page/project/pipeline/index'
autoload :Show, 'qa/page/project/pipeline/show'
@@ -160,6 +183,15 @@ module QA
autoload :Index, 'qa/page/project/issue/index'
end
+ module Fork
+ autoload :New, 'qa/page/project/fork/new'
+ end
+
+ module Milestone
+ autoload :New, 'qa/page/project/milestone/new'
+ autoload :Index, 'qa/page/project/milestone/index'
+ end
+
module Operations
module Kubernetes
autoload :Index, 'qa/page/project/operations/kubernetes/index'
@@ -184,6 +216,14 @@ module QA
autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens'
end
+ module Issuable
+ autoload :Sidebar, 'qa/page/issuable/sidebar'
+ end
+
+ module Layout
+ autoload :Banner, 'qa/page/layout/banner'
+ end
+
module MergeRequest
autoload :New, 'qa/page/merge_request/new'
autoload :Show, 'qa/page/merge_request/show'
@@ -206,6 +246,7 @@ module QA
#
module Component
autoload :Dropzone, 'qa/page/component/dropzone'
+ autoload :Select2, 'qa/page/component/select2'
end
end
diff --git a/qa/qa/factory/repository/project_push.rb b/qa/qa/factory/repository/project_push.rb
index 48674c08a8d..4f78098d348 100644
--- a/qa/qa/factory/repository/project_push.rb
+++ b/qa/qa/factory/repository/project_push.rb
@@ -11,6 +11,8 @@ module QA
factory.output
end
+ product(:project) { |factory| factory.project }
+
def initialize
@file_name = 'file.txt'
@file_content = '# This is test project'
diff --git a/qa/qa/factory/repository/push.rb b/qa/qa/factory/repository/push.rb
index 4f97e65b091..5b7ebf6c41f 100644
--- a/qa/qa/factory/repository/push.rb
+++ b/qa/qa/factory/repository/push.rb
@@ -5,7 +5,8 @@ module QA
module Repository
class Push < Factory::Base
attr_accessor :file_name, :file_content, :commit_message,
- :branch_name, :new_branch, :output, :repository_uri
+ :branch_name, :new_branch, :output, :repository_uri,
+ :user
attr_writer :remote_branch
@@ -31,9 +32,20 @@ module QA
def fabricate!
Git::Repository.perform do |repository|
repository.uri = repository_uri
+
repository.use_default_credentials
+ username = 'GitLab QA'
+ email = 'root@gitlab.com'
+
+ if user
+ repository.username = user.username
+ repository.password = user.password
+ username = user.name
+ email = user.email
+ end
+
repository.clone
- repository.configure_identity('GitLab QA', 'root@gitlab.com')
+ repository.configure_identity(username, email)
if new_branch
repository.checkout_new_branch(branch_name)
diff --git a/qa/qa/factory/resource/branch.rb b/qa/qa/factory/resource/branch.rb
index 7fb0633ec90..bc252bf3148 100644
--- a/qa/qa/factory/resource/branch.rb
+++ b/qa/qa/factory/resource/branch.rb
@@ -9,18 +9,6 @@ module QA
project.name = 'protected-branch-project'
end
- product :name do
- Page::Project::Settings::Repository.act do
- expand_protected_branches(&:last_branch_name)
- end
- end
-
- product :push_allowance do
- Page::Project::Settings::Repository.act do
- expand_protected_branches(&:last_push_allowance)
- end
- end
-
def initialize
@branch_name = 'test/branch'
@allow_to_push = true
@@ -80,15 +68,6 @@ module QA
end
page.protect_branch
-
- # Avoid Selenium::WebDriver::Error::StaleElementReferenceError
- # without sleeping. I.e. this completes fast on fast machines.
- page.refresh
-
- # It is possible for the protected branch row to "disappear" at first
- page.wait do
- page.has_content?(branch_name)
- end
end
end
end
diff --git a/qa/qa/factory/resource/file.rb b/qa/qa/factory/resource/file.rb
new file mode 100644
index 00000000000..2016d10ddae
--- /dev/null
+++ b/qa/qa/factory/resource/file.rb
@@ -0,0 +1,34 @@
+module QA
+ module Factory
+ module Resource
+ class File < Factory::Base
+ attr_accessor :name,
+ :content,
+ :commit_message
+
+ dependency Factory::Resource::Project, as: :project do |project|
+ project.name = 'project-with-new-file'
+ end
+
+ def initialize
+ @name = 'QA Test - File name'
+ @content = 'QA Test - File content'
+ @commit_message = 'QA Test - Commit message'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Show.act { go_to_new_file! }
+
+ Page::File::Form.perform do |page|
+ page.add_name(@name)
+ page.add_content(@content)
+ page.add_commit_message(@commit_message)
+ page.commit_changes
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/fork.rb b/qa/qa/factory/resource/fork.rb
new file mode 100644
index 00000000000..1d0c76a3d30
--- /dev/null
+++ b/qa/qa/factory/resource/fork.rb
@@ -0,0 +1,24 @@
+module QA
+ module Factory
+ module Resource
+ class Fork < Factory::Base
+ dependency Factory::Repository::ProjectPush, as: :push
+
+ dependency Factory::Resource::User, as: :user
+
+ product(:user) { |factory| factory.user }
+
+ def fabricate!
+ push.project.visit!
+ Page::Project::Show.act { fork_project }
+
+ Page::Project::Fork::New.perform do |fork_new|
+ fork_new.choose_namespace(user.name)
+ end
+
+ Page::Layout::Banner.act { has_notice?('The project was successfully forked.') }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/group.rb b/qa/qa/factory/resource/group.rb
index 9f13e26f35c..531fccd2ad8 100644
--- a/qa/qa/factory/resource/group.rb
+++ b/qa/qa/factory/resource/group.rb
@@ -23,7 +23,7 @@ module QA
Page::Group::New.perform do |group|
group.set_path(@path)
group.set_description(@description)
- group.set_visibility('Private')
+ group.set_visibility('Public')
group.create
end
end
diff --git a/qa/qa/factory/resource/kubernetes_cluster.rb b/qa/qa/factory/resource/kubernetes_cluster.rb
index f32cf985e9d..ef2ea72b170 100644
--- a/qa/qa/factory/resource/kubernetes_cluster.rb
+++ b/qa/qa/factory/resource/kubernetes_cluster.rb
@@ -36,15 +36,19 @@ module QA
if @install_helm_tiller
Page::Project::Operations::Kubernetes::Show.perform do |page|
+ # We must wait a few seconds for permissions to be setup correctly for new cluster
+ sleep 10
+
# Helm must be installed before everything else
page.install!(:helm)
page.await_installed(:helm)
page.install!(:ingress) if @install_ingress
- page.await_installed(:ingress) if @install_ingress
page.install!(:prometheus) if @install_prometheus
- page.await_installed(:prometheus) if @install_prometheus
page.install!(:runner) if @install_runner
+
+ page.await_installed(:ingress) if @install_ingress
+ page.await_installed(:prometheus) if @install_prometheus
page.await_installed(:runner) if @install_runner
end
end
diff --git a/qa/qa/factory/resource/merge_request.rb b/qa/qa/factory/resource/merge_request.rb
index 24d3597d993..ddb62bd0a68 100644
--- a/qa/qa/factory/resource/merge_request.rb
+++ b/qa/qa/factory/resource/merge_request.rb
@@ -7,7 +7,10 @@ module QA
attr_accessor :title,
:description,
:source_branch,
- :target_branch
+ :target_branch,
+ :assignee,
+ :milestone,
+ :labels
product :project do |factory|
factory.project
@@ -41,16 +44,18 @@ module QA
@description = 'This is a test merge request'
@source_branch = "qa-test-feature-#{SecureRandom.hex(8)}"
@target_branch = "master"
+ @assignee = nil
+ @milestone = nil
+ @labels = []
end
def fabricate!
project.visit!
-
Page::Project::Show.act { new_merge_request }
-
Page::MergeRequest::New.perform do |page|
page.fill_title(@title)
page.fill_description(@description)
+ page.choose_milestone(@milestone) if @milestone
page.create_merge_request
end
end
diff --git a/qa/qa/factory/resource/merge_request_from_fork.rb b/qa/qa/factory/resource/merge_request_from_fork.rb
new file mode 100644
index 00000000000..6caaf65f673
--- /dev/null
+++ b/qa/qa/factory/resource/merge_request_from_fork.rb
@@ -0,0 +1,24 @@
+module QA
+ module Factory
+ module Resource
+ class MergeRequestFromFork < MergeRequest
+ attr_accessor :fork_branch
+
+ dependency Factory::Resource::Fork, as: :fork
+
+ dependency Factory::Repository::ProjectPush, as: :push do |push, factory|
+ push.project = factory.fork
+ push.branch_name = factory.fork_branch
+ push.file_name = 'file2.txt'
+ push.user = factory.fork.user
+ end
+
+ def fabricate!
+ fork.visit!
+ Page::Project::Show.act { new_merge_request }
+ Page::MergeRequest::New.act { create_merge_request }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/project.rb b/qa/qa/factory/resource/project.rb
index cda1b35ba6a..7fff22b5468 100644
--- a/qa/qa/factory/resource/project.rb
+++ b/qa/qa/factory/resource/project.rb
@@ -5,16 +5,12 @@ module QA
module Resource
class Project < Factory::Base
attr_writer :description
+ attr_reader :name
dependency Factory::Resource::Group, as: :group
- def name=(name)
- @name = "#{name}-#{SecureRandom.hex(8)}"
- @description = 'My awesome project'
- end
-
- product :name do
- Page::Project::Show.act { project_name }
+ product :name do |factory|
+ factory.name
end
product :repository_ssh_location do
@@ -24,6 +20,14 @@ module QA
end
end
+ def initialize
+ @description = 'My awesome project'
+ end
+
+ def name=(raw_name)
+ @name = "#{raw_name}-#{SecureRandom.hex(8)}"
+ end
+
def fabricate!
group.visit!
@@ -33,6 +37,7 @@ module QA
page.choose_test_namespace
page.choose_name(@name)
page.add_description(@description)
+ page.set_visibility('Public')
page.create_new_project
end
end
diff --git a/qa/qa/factory/resource/project_imported_from_github.rb b/qa/qa/factory/resource/project_imported_from_github.rb
new file mode 100644
index 00000000000..df2a3340d60
--- /dev/null
+++ b/qa/qa/factory/resource/project_imported_from_github.rb
@@ -0,0 +1,37 @@
+require 'securerandom'
+
+module QA
+ module Factory
+ module Resource
+ class ProjectImportedFromGithub < Resource::Project
+ attr_writer :personal_access_token, :github_repository_path
+
+ dependency Factory::Resource::Group, as: :group
+
+ product :name do |factory|
+ factory.name
+ end
+
+ def fabricate!
+ group.visit!
+
+ Page::Group::Show.act { go_to_new_project }
+
+ Page::Project::New.perform do |page|
+ page.go_to_import_project
+ end
+
+ Page::Project::New.perform do |page|
+ page.go_to_github_import
+ end
+
+ Page::Project::Import::Github.perform do |page|
+ page.add_personal_access_token(@personal_access_token)
+ page.list_repos
+ page.import!(@github_repository_path, @name)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/project_milestone.rb b/qa/qa/factory/resource/project_milestone.rb
new file mode 100644
index 00000000000..47a5e74204f
--- /dev/null
+++ b/qa/qa/factory/resource/project_milestone.rb
@@ -0,0 +1,36 @@
+module QA
+ module Factory
+ module Resource
+ class ProjectMilestone < Factory::Base
+ attr_accessor :description
+ attr_reader :title
+
+ dependency Factory::Resource::Project, as: :project
+
+ product(:title) { |factory| factory.title }
+
+ def title=(title)
+ @title = "#{title}-#{SecureRandom.hex(4)}"
+ @description = 'A milestone'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Menu::Side.act do
+ click_issues
+ click_milestones
+ end
+
+ Page::Project::Milestone::Index.act { click_new_milestone }
+
+ Page::Project::Milestone::New.perform do |milestone_new|
+ milestone_new.set_title(@title)
+ milestone_new.set_description(@description)
+ milestone_new.create_new_milestone
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/sandbox.rb b/qa/qa/factory/resource/sandbox.rb
index ad376988e82..4f6039f300f 100644
--- a/qa/qa/factory/resource/sandbox.rb
+++ b/qa/qa/factory/resource/sandbox.rb
@@ -21,8 +21,8 @@ module QA
Page::Group::New.perform do |group|
group.set_path(@name)
- group.set_description('GitLab QA Sandbox')
- group.set_visibility('Private')
+ group.set_description('GitLab QA Sandbox Group')
+ group.set_visibility('Public')
group.create
end
end
diff --git a/qa/qa/factory/resource/user.rb b/qa/qa/factory/resource/user.rb
new file mode 100644
index 00000000000..e08df9e0cd0
--- /dev/null
+++ b/qa/qa/factory/resource/user.rb
@@ -0,0 +1,34 @@
+require 'securerandom'
+
+module QA
+ module Factory
+ module Resource
+ class User < Factory::Base
+ attr_accessor :name, :username, :email, :password
+
+ def initialize
+ @name = "name-#{SecureRandom.hex(8)}"
+ @username = "username-#{SecureRandom.hex(8)}"
+ @email = "mail#{SecureRandom.hex(8)}@mail.com"
+ @password = 'password'
+ end
+
+ product(:name) { |factory| factory.name }
+
+ product(:username) { |factory| factory.username }
+
+ product(:email) { |factory| factory.email }
+
+ product(:password) { |factory| factory.password }
+
+ def fabricate!
+ Page::Menu::Main.act { sign_out }
+ Page::Main::Login.act { switch_to_register_tab }
+ Page::Main::SignUp.perform do |page|
+ page.sign_up!(name: name, username: username, email: email, password: password)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index fc753554fc4..3df6db05970 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -59,7 +59,7 @@ module QA
end
def add_file(name, contents)
- File.write(name, contents)
+ ::File.write(name, contents)
`git add #{name}`
end
diff --git a/qa/qa/page/admin/settings/repository_storage.rb b/qa/qa/page/admin/settings/repository_storage.rb
index b4a1344216e..68dd23a41e1 100644
--- a/qa/qa/page/admin/settings/repository_storage.rb
+++ b/qa/qa/page/admin/settings/repository_storage.rb
@@ -6,11 +6,11 @@ module QA
view 'app/views/admin/application_settings/_repository_storage.html.haml' do
element :submit, "submit 'Save changes'"
element :hashed_storage,
- 'Create new projects using hashed storage paths'
+ 'Use hashed storage paths for newly created and renamed projects'
end
def enable_hashed_storage
- check 'Create new projects using hashed storage paths'
+ check 'Use hashed storage paths for newly created and renamed projects'
end
def save_settings
diff --git a/qa/qa/page/component/dropzone.rb b/qa/qa/page/component/dropzone.rb
index 15bdc742fda..fd44c57123a 100644
--- a/qa/qa/page/component/dropzone.rb
+++ b/qa/qa/page/component/dropzone.rb
@@ -15,7 +15,7 @@ module QA
# instantiated on one page because there is no distinguishing
# attribute per dropzone file field.
def attach_file(attachment)
- filename = File.basename(attachment)
+ filename = ::File.basename(attachment)
field_style = { visibility: 'visible', height: '', width: '' }
page.attach_file(attachment, class: 'dz-hidden-input', make_visible: field_style)
diff --git a/qa/qa/page/component/select2.rb b/qa/qa/page/component/select2.rb
new file mode 100644
index 00000000000..30829eb0221
--- /dev/null
+++ b/qa/qa/page/component/select2.rb
@@ -0,0 +1,11 @@
+module QA
+ module Page
+ module Component
+ module Select2
+ def select_item(item_text)
+ find('ul.select2-result-sub > li', text: item_text).click
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/file/form.rb b/qa/qa/page/file/form.rb
new file mode 100644
index 00000000000..f6e502f500b
--- /dev/null
+++ b/qa/qa/page/file/form.rb
@@ -0,0 +1,40 @@
+module QA
+ module Page
+ module File
+ class Form < Page::Base
+ include Shared::CommitMessage
+
+ view 'app/views/projects/blob/_editor.html.haml' do
+ element :file_name, "text_field_tag 'file_name'"
+ element :editor, '#editor'
+ end
+
+ view 'app/views/projects/_commit_button.html.haml' do
+ element :commit_changes, "button_tag 'Commit changes'"
+ end
+
+ def add_name(name)
+ fill_in 'file_name', with: name
+ end
+
+ def add_content(content)
+ text_area.set content
+ end
+
+ def remove_content
+ text_area.send_keys([:command, 'a'], :backspace)
+ end
+
+ def commit_changes
+ click_on 'Commit changes'
+ end
+
+ private
+
+ def text_area
+ find('#editor>textarea', visible: false)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/file/shared/commit_message.rb b/qa/qa/page/file/shared/commit_message.rb
new file mode 100644
index 00000000000..5af1a55e2ef
--- /dev/null
+++ b/qa/qa/page/file/shared/commit_message.rb
@@ -0,0 +1,19 @@
+module QA
+ module Page
+ module File
+ module Shared
+ module CommitMessage
+ def self.included(base)
+ base.view 'app/views/shared/_commit_message_container.html.haml' do
+ element :commit_message, "text_area_tag 'commit_message'"
+ end
+ end
+
+ def add_commit_message(message)
+ fill_in 'commit_message', with: message
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb
new file mode 100644
index 00000000000..99f5924b67f
--- /dev/null
+++ b/qa/qa/page/file/show.rb
@@ -0,0 +1,30 @@
+module QA
+ module Page
+ module File
+ class Show < Page::Base
+ include Shared::CommitMessage
+
+ view 'app/helpers/blob_helper.rb' do
+ element :edit_button, "_('Edit')"
+ element :delete_button, /label:\s+"Delete"/
+ end
+
+ view 'app/views/projects/blob/_remove.html.haml' do
+ element :delete_file_button, "button_tag 'Delete file'"
+ end
+
+ def click_edit
+ click_on 'Edit'
+ end
+
+ def click_delete
+ click_on 'Delete'
+ end
+
+ def click_delete_file
+ click_on 'Delete file'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/issuable/sidebar.rb b/qa/qa/page/issuable/sidebar.rb
new file mode 100644
index 00000000000..f207264e24f
--- /dev/null
+++ b/qa/qa/page/issuable/sidebar.rb
@@ -0,0 +1,24 @@
+module QA
+ module Page
+ module Issuable
+ class Sidebar < Page::Base
+ view 'app/views/shared/issuable/_sidebar.html.haml' do
+ element :labels_block, ".issuable-show-labels"
+ element :milestones_block, '.block.milestone'
+ end
+
+ def has_label?(label)
+ page.within('.issuable-show-labels') do
+ !!find('span', text: label)
+ end
+ end
+
+ def has_milestone?(milestone)
+ page.within('.block.milestone') do
+ !!find("[href*='/milestones/']", text: milestone)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/layout/banner.rb b/qa/qa/page/layout/banner.rb
new file mode 100644
index 00000000000..e7654bdafc9
--- /dev/null
+++ b/qa/qa/page/layout/banner.rb
@@ -0,0 +1,17 @@
+module QA
+ module Page
+ module Layout
+ class Banner < Page::Base
+ view 'app/views/layouts/header/_read_only_banner.html.haml' do
+ element :flash_notice, ".flash-notice"
+ end
+
+ def has_notice?(message)
+ page.within('.flash-notice') do
+ !!find('span', text: message)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 26c99efc53d..6cdfbd1c125 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -25,19 +25,24 @@ module QA
element :standard_tab, "link_to 'Standard'"
end
+ view 'app/views/devise/shared/_tabs_normal.html.haml' do
+ element :sign_in_tab, /nav-link.*login-pane.*Sign in/
+ element :register_tab, /nav-link.*register-pane.*Register/
+ end
+
def initialize
# The login page is usually the entry point for all the scenarios so
# we need to wait for the instance to start. That said, in some cases
# we are already logged-in so we check both cases here.
wait(max: 500) do
page.has_css?('.login-page') ||
- Page::Menu::Main.act { has_personal_area? }
+ Page::Menu::Main.act { has_personal_area?(wait: 0) }
end
end
def sign_in_using_credentials
# Don't try to log-in if we're already logged-in
- return if Page::Menu::Main.act { has_personal_area? }
+ return if Page::Menu::Main.act { has_personal_area?(wait: 0) }
using_wait_time 0 do
set_initial_password_if_present
@@ -48,12 +53,22 @@ module QA
sign_in_using_gitlab_credentials
end
end
+
+ Page::Menu::Main.act { has_personal_area? }
end
def self.path
'/users/sign_in'
end
+ def switch_to_sign_in_tab
+ click_on 'Sign in'
+ end
+
+ def switch_to_register_tab
+ click_on 'Register'
+ end
+
private
def sign_in_using_ldap_credentials
diff --git a/qa/qa/page/main/oauth.rb b/qa/qa/page/main/oauth.rb
index 6f548148363..618f114e058 100644
--- a/qa/qa/page/main/oauth.rb
+++ b/qa/qa/page/main/oauth.rb
@@ -3,7 +3,7 @@ module QA
module Main
class OAuth < Page::Base
view 'app/views/doorkeeper/authorizations/new.html.haml' do
- element :authorization_button, 'submit_tag "Authorize"'
+ element :authorization_button, 'submit_tag _("Authorize")'
end
def needs_authorization?
diff --git a/qa/qa/page/main/sign_up.rb b/qa/qa/page/main/sign_up.rb
new file mode 100644
index 00000000000..9a834e94b81
--- /dev/null
+++ b/qa/qa/page/main/sign_up.rb
@@ -0,0 +1,27 @@
+module QA
+ module Page
+ module Main
+ class SignUp < Page::Base
+ view 'app/views/devise/shared/_signup_box.html.haml' do
+ element :name, 'text_field :name'
+ element :username, 'text_field :username'
+ element :email_field, 'email_field :email'
+ element :email_confirmation, 'email_field :email_confirmation'
+ element :password, 'password_field :password'
+ element :register_button, 'submit "Register"'
+ end
+
+ def sign_up!(name:, username:, email:, password:)
+ fill_in :new_user_name, with: name
+ fill_in :new_user_username, with: username
+ fill_in :new_user_email, with: email
+ fill_in :new_user_email_confirmation, with: email
+ fill_in :new_user_password, with: password
+ click_button 'Register'
+
+ Page::Menu::Main.act { has_personal_area? }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/menu/main.rb b/qa/qa/page/menu/main.rb
index fda9c45c091..36e7285f7b7 100644
--- a/qa/qa/page/menu/main.rb
+++ b/qa/qa/page/menu/main.rb
@@ -16,7 +16,7 @@ module QA
view 'app/views/layouts/nav/_dashboard.html.haml' do
element :admin_area_link
element :projects_dropdown
- element :groups_link
+ element :groups_dropdown
end
view 'app/views/layouts/nav/projects_dropdown/_show.html.haml' do
@@ -25,7 +25,13 @@ module QA
end
def go_to_groups
- within_top_menu { click_element :groups_link }
+ within_top_menu do
+ click_element :groups_dropdown
+ end
+
+ page.within('.qa-groups-dropdown-sidebar') do
+ click_element :your_groups_link
+ end
end
def go_to_projects
@@ -54,9 +60,9 @@ module QA
end
end
- def has_personal_area?
+ def has_personal_area?(wait: Capybara.default_max_wait_time)
# No need to wait, either we're logged-in, or not.
- using_wait_time(0) { page.has_selector?('.qa-user-avatar') }
+ using_wait_time(wait) { page.has_selector?('.qa-user-avatar') }
end
private
diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/menu/side.rb
index 6bf4825cf00..354ccec2a5a 100644
--- a/qa/qa/page/menu/side.rb
+++ b/qa/qa/page/menu/side.rb
@@ -5,15 +5,18 @@ module QA
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :settings_item
element :settings_link, 'link_to edit_project_path'
- element :repository_link, "title: 'Repository'"
- element :pipelines_settings_link, "title: 'CI / CD'"
+ element :repository_link, "title: _('Repository')"
+ element :pipelines_settings_link, "title: _('CI / CD')"
element :operations_kubernetes_link, "title: _('Kubernetes')"
element :issues_link, /link_to.*shortcuts-issues/
element :issues_link_text, "Issues"
+ element :merge_requests_link, /link_to.*shortcuts-merge_requests/
+ element :merge_requests_link_text, "Merge Requests"
element :top_level_items, '.sidebar-top-level-items'
element :operations_section, "class: 'shortcuts-operations'"
- element :activity_link, "title: 'Activity'"
+ element :activity_link, "title: _('Activity')"
element :wiki_link_text, "Wiki"
+ element :milestones_link
end
view 'app/assets/javascripts/fly_out_nav.js' do
@@ -62,6 +65,18 @@ module QA
end
end
+ def click_merge_requests
+ within_sidebar do
+ click_link('Merge Requests')
+ end
+ end
+
+ def click_milestones
+ within_sidebar do
+ click_element :milestones_link
+ end
+ end
+
def click_wiki
within_sidebar do
click_link('Wiki')
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
index ec94ff4ac98..83cc4bbbace 100644
--- a/qa/qa/page/merge_request/new.rb
+++ b/qa/qa/page/merge_request/new.rb
@@ -10,10 +10,18 @@ module QA
element :issuable_form_title
end
+ view 'app/views/shared/issuable/form/_metadata.html.haml' do
+ element :issuable_milestone_dropdown
+ end
+
view 'app/views/shared/form_elements/_description.html.haml' do
element :issuable_form_description
end
+ view 'app/views/shared/issuable/_milestone_dropdown.html.haml' do
+ element :issuable_dropdown_menu_milestone
+ end
+
def create_merge_request
click_element :issuable_create_button
end
@@ -25,6 +33,13 @@ module QA
def fill_description(description)
fill_element :issuable_form_description, description
end
+
+ def choose_milestone(milestone)
+ click_element :issuable_milestone_dropdown
+ within_element(:issuable_dropdown_menu_milestone) do
+ click_on milestone.title
+ end
+ end
end
end
end
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 9507f92f4b2..c200f14f4fb 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -20,14 +20,6 @@ module QA
element :squash_checkbox
end
- def rebase!
- click_element :mr_rebase_button
-
- wait(reload: false) do
- has_text?('Fast-forward merge without a merge commit')
- end
- end
-
def fast_forward_possible?
!has_text?('Fast-forward merge is not possible')
end
@@ -38,7 +30,35 @@ module QA
has_selector?('.accept-merge-request')
end
+ def rebase!
+ # The rebase button is disabled on load
+ wait do
+ has_css?(element_selector_css(:mr_rebase_button))
+ end
+
+ # The rebase button is enabled via JS
+ wait(reload: false) do
+ !first(element_selector_css(:mr_rebase_button)).disabled?
+ end
+
+ click_element :mr_rebase_button
+
+ wait(reload: false) do
+ has_text?('Fast-forward merge without a merge commit')
+ end
+ end
+
def merge!
+ # The merge button is disabled on load
+ wait do
+ has_css?(element_selector_css(:merge_button))
+ end
+
+ # The merge button is enabled via JS
+ wait(reload: false) do
+ !first(element_selector_css(:merge_button)).disabled?
+ end
+
click_element :merge_button
wait(reload: false) do
@@ -47,10 +67,16 @@ module QA
end
def mark_to_squash
- wait(reload: true) do
+ # The squash checkbox is disabled on load
+ wait do
has_css?(element_selector_css(:squash_checkbox))
end
+ # The squash checkbox is enabled via JS
+ wait(reload: false) do
+ !first(element_selector_css(:squash_checkbox)).disabled?
+ end
+
click_element :squash_checkbox
end
end
diff --git a/qa/qa/page/project/fork/new.rb b/qa/qa/page/project/fork/new.rb
new file mode 100644
index 00000000000..ed92df956bf
--- /dev/null
+++ b/qa/qa/page/project/fork/new.rb
@@ -0,0 +1,17 @@
+module QA
+ module Page
+ module Project
+ module Fork
+ class New < Page::Base
+ view 'app/views/projects/forks/_fork_button.html.haml' do
+ element :namespace, 'link_to project_forks_path'
+ end
+
+ def choose_namespace(namespace = Runtime::Namespace.path)
+ click_on namespace
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
new file mode 100644
index 00000000000..36567927194
--- /dev/null
+++ b/qa/qa/page/project/import/github.rb
@@ -0,0 +1,66 @@
+module QA
+ module Page
+ module Project
+ module Import
+ class Github < Page::Base
+ include Page::Component::Select2
+
+ view 'app/views/import/github/new.html.haml' do
+ element :personal_access_token_field, 'text_field_tag :personal_access_token'
+ element :list_repos_button, "submit_tag _('List your GitHub repositories')"
+ end
+
+ view 'app/views/import/_githubish_status.html.haml' do
+ element :project_import_row, 'data: { qa: { repo_path: repo.full_name } }'
+ element :project_namespace_select
+ element :project_namespace_field, 'select_tag :namespace_id'
+ element :project_path_field, 'text_field_tag :path, repo.name'
+ element :import_button, "_('Import')"
+ end
+
+ def add_personal_access_token(personal_access_token)
+ fill_in 'personal_access_token', with: personal_access_token
+ end
+
+ def list_repos
+ click_button 'List your GitHub repositories'
+ end
+
+ def import!(full_path, name)
+ choose_test_namespace(full_path)
+ set_path(full_path, name)
+ import_project(full_path)
+ end
+
+ private
+
+ def within_repo_path(full_path)
+ page.within(%Q(tr[data-qa-repo-path="#{full_path}"])) do
+ yield
+ end
+ end
+
+ def choose_test_namespace(full_path)
+ within_repo_path(full_path) do
+ click_element :project_namespace_select
+ end
+
+ select_item(Runtime::Namespace.path)
+ end
+
+ def set_path(full_path, name)
+ within_repo_path(full_path) do
+ fill_in 'path', with: name
+ end
+ end
+
+ def import_project(full_path)
+ within_repo_path(full_path) do
+ click_button 'Import'
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/milestone/index.rb b/qa/qa/page/project/milestone/index.rb
new file mode 100644
index 00000000000..a1519c9ef1c
--- /dev/null
+++ b/qa/qa/page/project/milestone/index.rb
@@ -0,0 +1,17 @@
+module QA
+ module Page
+ module Project
+ module Milestone
+ class Index < Page::Base
+ view 'app/views/projects/milestones/index.html.haml' do
+ element :new_project_milestone
+ end
+
+ def click_new_milestone
+ click_element :new_project_milestone
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/milestone/new.rb b/qa/qa/page/project/milestone/new.rb
new file mode 100644
index 00000000000..992ef89004b
--- /dev/null
+++ b/qa/qa/page/project/milestone/new.rb
@@ -0,0 +1,27 @@
+module QA
+ module Page
+ module Project
+ module Milestone
+ class New < Page::Base
+ view 'app/views/projects/milestones/_form.html.haml' do
+ element :milestone_create_button
+ element :milestone_title
+ element :milestone_description
+ end
+
+ def set_title(title)
+ fill_element :milestone_title, title
+ end
+
+ def set_description(description)
+ fill_element :milestone_description, description
+ end
+
+ def create_new_milestone
+ click_element :milestone_create_button
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index 186a4724326..1fb569b0f29 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -2,18 +2,33 @@ module QA
module Page
module Project
class New < Page::Base
+ include Page::Component::Select2
+
+ view 'app/views/projects/new.html.haml' do
+ element :import_project_tab, "Import project"
+ end
+
view 'app/views/projects/_new_project_fields.html.haml' do
element :project_namespace_select
- element :project_namespace_field, /select :namespace_id.*class: 'select2/
+ element :project_namespace_field, 'namespaces_options'
element :project_path, 'text_field :path'
element :project_description, 'text_area :description'
element :project_create_button, "submit 'Create project'"
+ element :visibility_radios, 'visibility_level:'
+ end
+
+ view 'app/views/projects/_import_project_pane.html.haml' do
+ element :import_github, "icon('github', text: 'GitHub')"
end
def choose_test_namespace
click_element :project_namespace_select
- find('ul.select2-result-sub > li', text: Runtime::Namespace.path).click
+ select_item(Runtime::Namespace.path)
+ end
+
+ def go_to_import_project
+ click_on 'Import project'
end
def choose_name(name)
@@ -27,6 +42,14 @@ module QA
def create_new_project
click_on 'Create project'
end
+
+ def set_visibility(visibility)
+ choose visibility
+ end
+
+ def go_to_github_import
+ click_link 'GitHub'
+ end
end
end
end
diff --git a/qa/qa/page/project/operations/kubernetes/add.rb b/qa/qa/page/project/operations/kubernetes/add.rb
index 9b3c482fa6c..11ebe10fb18 100644
--- a/qa/qa/page/project/operations/kubernetes/add.rb
+++ b/qa/qa/page/project/operations/kubernetes/add.rb
@@ -5,11 +5,11 @@ module QA
module Kubernetes
class Add < Page::Base
view 'app/views/projects/clusters/new.html.haml' do
- element :add_kubernetes_cluster_button, "link_to s_('ClusterIntegration|Add an existing Kubernetes cluster')"
+ element :add_existing_cluster_button, "Add existing cluster"
end
def add_existing_cluster
- click_on 'Add an existing Kubernetes cluster'
+ click_on 'Add existing cluster'
end
end
end
diff --git a/qa/qa/page/project/operations/kubernetes/show.rb b/qa/qa/page/project/operations/kubernetes/show.rb
index 4923304133e..e831edeb89e 100644
--- a/qa/qa/page/project/operations/kubernetes/show.rb
+++ b/qa/qa/page/project/operations/kubernetes/show.rb
@@ -16,6 +16,7 @@ module QA
def install!(application_name)
within(".js-cluster-application-row-#{application_name}") do
+ page.has_button?('Install', wait: 30)
click_on 'Install'
end
end
diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb
index 1466bc2e0bf..752d3d93407 100644
--- a/qa/qa/page/project/settings/ci_cd.rb
+++ b/qa/qa/page/project/settings/ci_cd.rb
@@ -12,11 +12,11 @@ module QA # rubocop:disable Naming/FileName
end
view 'app/views/projects/settings/ci_cd/_autodevops_form.html.haml' do
- element :enable_auto_devops_field, 'radio_button :enabled'
+ element :enable_auto_devops_field, 'check_box :enabled'
element :domain_field, 'text_field :domain'
- element :enable_auto_devops_button, "%strong= s_('CICD|Enable Auto DevOps')"
+ element :enable_auto_devops_button, "%strong= s_('CICD|Default to Auto DevOps pipeline')"
element :domain_input, "%strong= _('Domain')"
- element :save_changes_button, "submit 'Save changes'"
+ element :save_changes_button, "submit _('Save changes')"
end
def expand_runners_settings(&block)
@@ -33,7 +33,7 @@ module QA # rubocop:disable Naming/FileName
def enable_auto_devops_with_domain(domain)
expand_section(:autodevops_settings) do
- choose 'Enable Auto DevOps'
+ check 'Default to Auto DevOps pipeline'
fill_in 'Domain', with: domain
click_on 'Save changes'
end
diff --git a/qa/qa/page/project/settings/deploy_keys.rb b/qa/qa/page/project/settings/deploy_keys.rb
index a8558d7c50a..90a0e7092bd 100644
--- a/qa/qa/page/project/settings/deploy_keys.rb
+++ b/qa/qa/page/project/settings/deploy_keys.rb
@@ -58,7 +58,7 @@ module QA
def within_project_deploy_keys
wait(reload: false) do
- find_element(:project_deploy_keys)
+ has_css?(element_selector_css(:project_deploy_keys))
end
within_element(:project_deploy_keys) do
diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb
index 0bd031e96b5..76591a4e3fe 100644
--- a/qa/qa/page/project/settings/protected_branches.rb
+++ b/qa/qa/page/project/settings/protected_branches.rb
@@ -16,7 +16,6 @@ module QA
end
view 'app/views/projects/protected_branches/_update_protected_branch.html.haml' do
- element :allowed_to_push
element :allowed_to_merge
end
@@ -24,10 +23,6 @@ module QA
element :protected_branches_list
end
- view 'app/views/projects/protected_branches/shared/_protected_branch.html.haml' do
- element :protected_branch_name
- end
-
def select_branch(branch_name)
click_element :protected_branch_select
@@ -44,6 +39,9 @@ module QA
click_allow(:push, 'Developers + Maintainers')
end
+ # @deprecated
+ alias_method :allow_devs_and_masters_to_push, :allow_devs_and_maintainers_to_push
+
def allow_no_one_to_merge
click_allow(:merge, 'No one')
end
@@ -52,22 +50,13 @@ module QA
click_allow(:merge, 'Developers + Maintainers')
end
+ # @deprecated
+ alias_method :allow_devs_and_masters_to_merge, :allow_devs_and_maintainers_to_merge
+
def protect_branch
click_on 'Protect'
end
- def last_branch_name
- within_element(:protected_branches_list) do
- all('.qa-protected-branch-name').last
- end
- end
-
- def last_push_allowance
- within_element(:protected_branches_list) do
- all('.qa-allowed-to-push').last
- end
- end
-
private
def click_allow(action, text)
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index 1406edece17..c751b472535 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -14,7 +14,7 @@ module QA
view 'app/views/layouts/header/_new_dropdown.haml' do
element :new_menu_toggle
- element :new_issue_link, "link_to 'New issue', new_project_issue_path(@project)"
+ element :new_issue_link, "link_to _('New issue'), new_project_issue_path(@project)"
end
view 'app/views/shared/_ref_switcher.html.haml' do
@@ -22,10 +22,27 @@ module QA
element :branches_dropdown
end
+ view 'app/views/projects/buttons/_fork.html.haml' do
+ element :fork_label, "%span= s_('GoToYourFork|Fork')"
+ element :fork_link, "link_to new_project_fork_path(@project)"
+ end
+
+ view 'app/views/projects/_files.html.haml' do
+ element :tree_holder, '.tree-holder'
+ end
+
+ view 'app/presenters/project_presenter.rb' do
+ element :new_file_button, "label: _('New file'),"
+ end
+
def project_name
find('.qa-project-name').text
end
+ def go_to_new_file!
+ click_on 'New file'
+ end
+
def switch_to_branch(branch_name)
find_element(:branches_select).click
@@ -46,11 +63,21 @@ module QA
click_element :create_merge_request
end
+ def wait_for_import
+ wait(reload: true) do
+ has_css?('.tree-holder')
+ end
+ end
+
def go_to_new_issue
click_element :new_menu_toggle
click_link 'New issue'
end
+
+ def fork_project
+ click_on 'Fork'
+ end
end
end
end
diff --git a/qa/qa/page/view.rb b/qa/qa/page/view.rb
index 6635e1ce039..b2a2da4dbf3 100644
--- a/qa/qa/page/view.rb
+++ b/qa/qa/page/view.rb
@@ -9,7 +9,7 @@ module QA
end
def pathname
- @pathname ||= Pathname.new(File.join(__dir__, '../../../', @path))
+ @pathname ||= Pathname.new(::File.join(__dir__, '../../../', @path))
.cleanpath.expand_path
end
@@ -23,7 +23,7 @@ module QA
# elements' existence.
#
@missing ||= @elements.dup.tap do |elements|
- File.foreach(pathname.to_s) do |line|
+ ::File.foreach(pathname.to_s) do |line|
elements.reject! { |element| element.matches?(line) }
end
end
diff --git a/qa/qa/runtime/api/request.rb b/qa/qa/runtime/api/request.rb
index c33ada0de7a..ff9f0004524 100644
--- a/qa/qa/runtime/api/request.rb
+++ b/qa/qa/runtime/api/request.rb
@@ -28,7 +28,7 @@ module QA
#
# Returns the relative path to the requested API resource
def request_path(path, version: API_VERSION, **query_string)
- full_path = File.join('/api', version, path)
+ full_path = ::File.join('/api', version, path)
if query_string.any?
full_path << (path.include?('?') ? '&' : '?')
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index ecd273c6db8..4c64270ce92 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -32,6 +32,12 @@ module QA
end
def self.configure!
+ RSpec.configure do |config|
+ config.define_derived_metadata(file_path: %r{/qa/specs/features/}) do |metadata|
+ metadata[:type] = :feature
+ end
+ end
+
return if Capybara.drivers.include?(:chrome)
Capybara.register_driver :chrome do |app|
@@ -66,6 +72,7 @@ module QA
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
+ clear_local_storage: true,
desired_capabilities: capabilities,
options: options
)
@@ -79,12 +86,16 @@ module QA
driver.browser.save_screenshot(path)
end
+ Capybara::Screenshot.register_filename_prefix_formatter(:rspec) do |example|
+ ::File.join(QA::Runtime::Namespace.name, example.file_path.sub('./qa/specs/features/', ''))
+ end
+
Capybara.configure do |config|
config.default_driver = :chrome
config.javascript_driver = :chrome
config.default_max_wait_time = 10
# https://github.com/mattheworiordan/capybara-screenshot/issues/164
- config.save_path = File.expand_path('../../tmp', __dir__)
+ config.save_path = ::File.expand_path('../../tmp', __dir__)
end
end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 2126ce6b234..5dc194e0aef 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -62,6 +62,21 @@ module QA
def gcloud_zone
ENV.fetch('GCLOUD_ZONE')
end
+
+ def has_gcloud_credentials?
+ %w[GCLOUD_ACCOUNT_KEY GCLOUD_ACCOUNT_EMAIL].none? { |var| ENV[var].to_s.empty? }
+ end
+
+ # Specifies the token that can be used for the GitHub API
+ def github_access_token
+ ENV['GITHUB_ACCESS_TOKEN'].to_s.strip
+ end
+
+ def require_github_access_token!
+ return unless github_access_token.empty?
+
+ raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
+ end
end
end
end
diff --git a/qa/qa/runtime/key/base.rb b/qa/qa/runtime/key/base.rb
index c7e5ebada7b..67a992e2115 100644
--- a/qa/qa/runtime/key/base.rb
+++ b/qa/qa/runtime/key/base.rb
@@ -25,8 +25,8 @@ module QA
end
def populate_key_data(path)
- @private_key = File.binread(path)
- @public_key = File.binread("#{path}.pub")
+ @private_key = ::File.binread(path)
+ @public_key = ::File.binread("#{path}.pub")
@fingerprint =
`ssh-keygen -l -E md5 -f #{path} | cut -d' ' -f2 | cut -d: -f2-`.chomp
end
diff --git a/qa/qa/runtime/namespace.rb b/qa/qa/runtime/namespace.rb
index 8d05b387416..f1c8ef11f94 100644
--- a/qa/qa/runtime/namespace.rb
+++ b/qa/qa/runtime/namespace.rb
@@ -8,7 +8,7 @@ module QA
end
def name
- 'qa-test-' + time.strftime('%d-%m-%Y-%H-%M-%S')
+ "qa-test-#{time.strftime('%Y-%m-%d-%H-%M-%S')}"
end
def path
@@ -16,7 +16,7 @@ module QA
end
def sandbox_name
- Runtime::Env.sandbox_name || 'gitlab-qa-sandbox'
+ Runtime::Env.sandbox_name || 'gitlab-qa-sandbox-group'
end
end
end
diff --git a/qa/qa/runtime/release.rb b/qa/qa/runtime/release.rb
index 12e56404cf6..b1f7ec482c8 100644
--- a/qa/qa/runtime/release.rb
+++ b/qa/qa/runtime/release.rb
@@ -13,7 +13,7 @@ module QA
end
def version
- @version ||= File.directory?("#{__dir__}/../ee") ? :EE : :CE
+ @version ||= ::File.directory?("#{__dir__}/../ee") ? :EE : :CE
end
def strategy
@@ -21,7 +21,7 @@ module QA
end
def self.method_missing(name, *args)
- self.new.strategy.public_send(name, *args) # rubocop:disable GitlabSecurity/PublicSend
+ self.new.strategy.public_send(name, *args)
end
end
end
diff --git a/qa/qa/scenario/test/instance.rb b/qa/qa/scenario/test/instance.rb
index 567e5fd6cca..46eb2dabb11 100644
--- a/qa/qa/scenario/test/instance.rb
+++ b/qa/qa/scenario/test/instance.rb
@@ -26,7 +26,7 @@ module QA
if rspec_options.any?
rspec_options
else
- File.expand_path('../../specs/features', __dir__)
+ ::File.expand_path('../../specs/features', __dir__)
end
end
end
diff --git a/qa/qa/scenario/test/integration/github.rb b/qa/qa/scenario/test/integration/github.rb
new file mode 100644
index 00000000000..1d22b532aa5
--- /dev/null
+++ b/qa/qa/scenario/test/integration/github.rb
@@ -0,0 +1,18 @@
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class Github < Test::Instance
+ tags :github
+
+ def perform(address, *rspec_options)
+ # This test suite requires a GitHub personal access token
+ Runtime::Env.require_github_access_token!
+
+ super
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index 7627c8c7ad9..abd9d53554f 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -50,11 +50,15 @@ module QA
end
def login_if_not_already_logged_in
- account = `gcloud auth list --filter=status:ACTIVE --format="value(account)"`
- if account.empty?
+ if Runtime::Env.has_gcloud_credentials?
attempt_login_with_env_vars
else
- puts "gcloud account found. Using: #{account} for creating K8s cluster."
+ account = `gcloud auth list --filter=status:ACTIVE --format="value(account)"`
+ if account.empty?
+ raise "Failed to login to gcloud. No credentials provided in environment and no credentials found locally."
+ else
+ puts "gcloud account found. Using: #{account} for creating K8s cluster."
+ end
end
end
diff --git a/qa/qa/specs/features/api/basics_spec.rb b/qa/qa/specs/features/api/basics_spec.rb
index 1d7f9d6a03c..6563b56d1b4 100644
--- a/qa/qa/specs/features/api/basics_spec.rb
+++ b/qa/qa/specs/features/api/basics_spec.rb
@@ -1,7 +1,7 @@
require 'securerandom'
module QA
- feature 'API basics', :core do
+ describe 'API basics', :core do
before(:context) do
@api_client = Runtime::API::Client.new(:gitlab)
end
@@ -9,7 +9,7 @@ module QA
let(:project_name) { "api-basics-#{SecureRandom.hex(8)}" }
let(:sanitized_project_path) { CGI.escape("#{Runtime::User.name}/#{project_name}") }
- scenario 'user creates a project with a file and deletes them afterwards' do
+ it 'user creates a project with a file and deletes them afterwards' do
create_project_request = Runtime::API::Request.new(@api_client, '/projects')
post create_project_request.url, path: project_name, name: project_name
diff --git a/qa/qa/specs/features/api/users_spec.rb b/qa/qa/specs/features/api/users_spec.rb
index 0aecf89e1b7..8a63d8095c9 100644
--- a/qa/qa/specs/features/api/users_spec.rb
+++ b/qa/qa/specs/features/api/users_spec.rb
@@ -1,5 +1,5 @@
module QA
- feature 'API users', :core do
+ describe 'API users', :core do
before(:context) do
@api_client = Runtime::API::Client.new(:gitlab)
end
@@ -7,13 +7,13 @@ module QA
context 'when authenticated' do
let(:request) { Runtime::API::Request.new(@api_client, '/users') }
- scenario 'get list of users' do
+ it 'get list of users' do
get request.url
expect_status(200)
end
- scenario 'submit request with a valid user name' do
+ it 'submit request with a valid user name' do
get request.url, { params: { username: Runtime::User.name } }
expect_status(200)
@@ -22,7 +22,7 @@ module QA
)
end
- scenario 'submit request with an invalid user name' do
+ it 'submit request with an invalid user name' do
get request.url, { params: { username: SecureRandom.hex(10) } }
expect_status(200)
@@ -30,7 +30,7 @@ module QA
end
end
- scenario 'submit request with an invalid token' do
+ it 'submit request with an invalid token' do
request = Runtime::API::Request.new(@api_client, '/users', private_token: 'invalid')
get request.url
diff --git a/qa/qa/specs/features/login/ldap_spec.rb b/qa/qa/specs/features/login/ldap_spec.rb
index 737f4d10053..b7a284c584b 100644
--- a/qa/qa/specs/features/login/ldap_spec.rb
+++ b/qa/qa/specs/features/login/ldap_spec.rb
@@ -1,10 +1,10 @@
module QA
- feature 'LDAP user login', :ldap do
+ describe 'LDAP user login', :ldap do
before do
Runtime::Env.user_type = 'ldap'
end
- scenario 'user logs in using LDAP credentials' do
+ it 'user logs in using LDAP credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/login/standard_spec.rb b/qa/qa/specs/features/login/standard_spec.rb
deleted file mode 100644
index 141ffa3cfb7..00000000000
--- a/qa/qa/specs/features/login/standard_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module QA
- feature 'standard user login', :core do
- scenario 'user logs in using credentials' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- # TODO, since `Signed in successfully` message was removed
- # this is the only way to tell if user is signed in correctly.
- #
- Page::Menu::Main.perform do |menu|
- expect(menu).to have_personal_area
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/mattermost/group_create_spec.rb b/qa/qa/specs/features/mattermost/group_create_spec.rb
index 2e27a285223..a59761da341 100644
--- a/qa/qa/specs/features/mattermost/group_create_spec.rb
+++ b/qa/qa/specs/features/mattermost/group_create_spec.rb
@@ -1,6 +1,6 @@
module QA
- feature 'create a new group', :mattermost do
- scenario 'creating a group with a mattermost team' do
+ describe 'create a new group', :mattermost do
+ it 'creating a group with a mattermost team' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Page::Menu::Main.act { go_to_groups }
diff --git a/qa/qa/specs/features/mattermost/login_spec.rb b/qa/qa/specs/features/mattermost/login_spec.rb
index 637bbdd643a..b140191e160 100644
--- a/qa/qa/specs/features/mattermost/login_spec.rb
+++ b/qa/qa/specs/features/mattermost/login_spec.rb
@@ -1,6 +1,6 @@
module QA
- feature 'logging in to Mattermost', :mattermost do
- scenario 'can use gitlab oauth' do
+ describe 'logging in to Mattermost', :mattermost do
+ it 'can use gitlab oauth' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb
index befbc0b281a..36d7efb02e1 100644
--- a/qa/qa/specs/features/merge_request/create_spec.rb
+++ b/qa/qa/specs/features/merge_request/create_spec.rb
@@ -1,17 +1,32 @@
module QA
- feature 'creates a merge request', :core do
- scenario 'user creates a new merge request' do
+ describe 'creates a merge request', :core do
+ it 'user creates a new merge request' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
+ current_project = Factory::Resource::Project.fabricate! do |project|
+ project.name = 'project-with-merge-request-and-milestone'
+ end
+
+ current_milestone = Factory::Resource::ProjectMilestone.fabricate! do |milestone|
+ milestone.title = 'unique-milestone'
+ milestone.project = current_project
+ end
+
Factory::Resource::MergeRequest.fabricate! do |merge_request|
- merge_request.title = 'This is a merge request'
- merge_request.description = 'Great feature'
+ merge_request.title = 'This is a merge request with a milestone'
+ merge_request.description = 'Great feature with milestone'
+ merge_request.project = current_project
+ merge_request.milestone = current_milestone
end
- expect(page).to have_content('This is a merge request')
- expect(page).to have_content('Great feature')
+ expect(page).to have_content('This is a merge request with a milestone')
+ expect(page).to have_content('Great feature with milestone')
expect(page).to have_content(/Opened [\w\s]+ ago/)
+
+ Page::Issuable::Sidebar.perform do |sidebar|
+ expect(sidebar).to have_milestone(current_milestone.title)
+ end
end
end
end
diff --git a/qa/qa/specs/features/merge_request/rebase_spec.rb b/qa/qa/specs/features/merge_request/rebase_spec.rb
index 6a0ed4592c4..163dcbe7963 100644
--- a/qa/qa/specs/features/merge_request/rebase_spec.rb
+++ b/qa/qa/specs/features/merge_request/rebase_spec.rb
@@ -1,6 +1,6 @@
module QA
- feature 'merge request rebase', :core do
- scenario 'rebases source branch of merge request' do
+ describe 'merge request rebase', :core do
+ it 'rebases source branch of merge request' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/merge_request/squash_spec.rb b/qa/qa/specs/features/merge_request/squash_spec.rb
index b68704154cf..4856bbe1a69 100644
--- a/qa/qa/specs/features/merge_request/squash_spec.rb
+++ b/qa/qa/specs/features/merge_request/squash_spec.rb
@@ -1,6 +1,6 @@
module QA
- feature 'merge request squash commits', :core do
- scenario 'when squash commits is marked before merge' do
+ describe 'merge request squash commits', :core do
+ it 'when squash commits is marked before merge' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
@@ -24,6 +24,8 @@ module QA
merge_request.visit!
+ expect(page).to have_text('to be squashed')
+
Page::MergeRequest::Show.perform do |merge_request_page|
merge_request_page.mark_to_squash
merge_request_page.merge!
diff --git a/qa/qa/specs/features/project/activity_spec.rb b/qa/qa/specs/features/project/activity_spec.rb
index 07ac7321aa2..02074e386b6 100644
--- a/qa/qa/specs/features/project/activity_spec.rb
+++ b/qa/qa/specs/features/project/activity_spec.rb
@@ -1,6 +1,6 @@
module QA
- feature 'activity page', :core do
- scenario 'push creates an event in the activity page' do
+ describe 'activity page', :core do
+ it 'push creates an event in the activity page' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/project/add_deploy_key_spec.rb b/qa/qa/specs/features/project/add_deploy_key_spec.rb
index de53613dee1..14642af97ad 100644
--- a/qa/qa/specs/features/project/add_deploy_key_spec.rb
+++ b/qa/qa/specs/features/project/add_deploy_key_spec.rb
@@ -1,6 +1,6 @@
module QA
- feature 'deploy keys support', :core do
- scenario 'user adds a deploy key' do
+ describe 'deploy keys support', :core do
+ it 'user adds a deploy key' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/project/add_secret_variable_spec.rb b/qa/qa/specs/features/project/add_secret_variable_spec.rb
index d1bf7849bd0..32c91dd9d4d 100644
--- a/qa/qa/specs/features/project/add_secret_variable_spec.rb
+++ b/qa/qa/specs/features/project/add_secret_variable_spec.rb
@@ -1,6 +1,6 @@
module QA
- feature 'secret variables support', :core do
- scenario 'user adds a secret variable' do
+ describe 'secret variables support', :core do
+ it 'user adds a secret variable' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/project/auto_devops_spec.rb b/qa/qa/specs/features/project/auto_devops_spec.rb
index c50a13432f5..bc713b46d81 100644
--- a/qa/qa/specs/features/project/auto_devops_spec.rb
+++ b/qa/qa/specs/features/project/auto_devops_spec.rb
@@ -1,12 +1,12 @@
require 'pathname'
module QA
- feature 'Auto Devops', :kubernetes do
+ describe 'Auto Devops', :kubernetes do
after do
@cluster&.remove!
end
- scenario 'user creates a new project and runs auto devops' do
+ it 'user creates a new project and runs auto devops' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/project/create_issue_spec.rb b/qa/qa/specs/features/project/create_issue_spec.rb
index b73f108c2d9..ac2ed2ca2a1 100644
--- a/qa/qa/specs/features/project/create_issue_spec.rb
+++ b/qa/qa/specs/features/project/create_issue_spec.rb
@@ -1,8 +1,8 @@
module QA
- feature 'creates issue', :core do
+ describe 'creates issue', :core do
let(:issue_title) { 'issue title' }
- scenario 'user creates issue' do
+ it 'user creates issue' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/project/create_spec.rb b/qa/qa/specs/features/project/create_spec.rb
index b1c07249892..14ecd464685 100644
--- a/qa/qa/specs/features/project/create_spec.rb
+++ b/qa/qa/specs/features/project/create_spec.rb
@@ -1,6 +1,6 @@
module QA
- feature 'create a new project', :core do
- scenario 'user creates a new project' do
+ describe 'create a new project', :core do
+ it 'user creates a new project' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/project/deploy_key_clone_spec.rb b/qa/qa/specs/features/project/deploy_key_clone_spec.rb
index 10e4cbb6906..054f49b408a 100644
--- a/qa/qa/specs/features/project/deploy_key_clone_spec.rb
+++ b/qa/qa/specs/features/project/deploy_key_clone_spec.rb
@@ -1,7 +1,7 @@
require 'digest/sha1'
module QA
- feature 'cloning code using a deploy key', :core, :docker do
+ describe 'cloning code using a deploy key', :core, :docker do
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
@@ -39,7 +39,7 @@ module QA
]
keys.each do |(key_class, bits)|
- scenario "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do
+ it "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do
key = key_class.new(*bits)
login
diff --git a/qa/qa/specs/features/project/file_spec.rb b/qa/qa/specs/features/project/file_spec.rb
new file mode 100644
index 00000000000..5659784cd5c
--- /dev/null
+++ b/qa/qa/specs/features/project/file_spec.rb
@@ -0,0 +1,54 @@
+module QA
+ describe 'File Functionality', :core do
+ it 'lets a user create, edit and delete a file via WebUI' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ # Create
+ file_name = 'QA Test - File name'
+ file_content = 'QA Test - File content'
+ commit_message_for_create = 'QA Test - Create new file'
+
+ Factory::Resource::File.fabricate! do |file|
+ file.name = file_name
+ file.content = file_content
+ file.commit_message = commit_message_for_create
+ end
+
+ expect(page).to have_content('The file has been successfully created.')
+ expect(page).to have_content(file_name)
+ expect(page).to have_content(file_content)
+ expect(page).to have_content(commit_message_for_create)
+
+ # Edit
+ updated_file_content = 'QA Test - Updated file content'
+ commit_message_for_update = 'QA Test - Update file'
+
+ Page::File::Show.act { click_edit }
+
+ Page::File::Form.act do
+ remove_content
+ add_content(updated_file_content)
+ add_commit_message(commit_message_for_update)
+ commit_changes
+ end
+
+ expect(page).to have_content('Your changes have been successfully committed.')
+ expect(page).to have_content(updated_file_content)
+ expect(page).to have_content(commit_message_for_update)
+
+ # Delete
+ commit_message_for_delete = 'QA Test - Delete file'
+
+ Page::File::Show.act do
+ click_delete
+ add_commit_message(commit_message_for_delete)
+ click_delete_file
+ end
+
+ expect(page).to have_content('The file has been successfully deleted.')
+ expect(page).to have_content(commit_message_for_delete)
+ expect(page).to have_no_content(file_name)
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/fork_project_spec.rb b/qa/qa/specs/features/project/fork_project_spec.rb
new file mode 100644
index 00000000000..8ad0120305a
--- /dev/null
+++ b/qa/qa/specs/features/project/fork_project_spec.rb
@@ -0,0 +1,23 @@
+module QA
+ describe 'Project fork', :core do
+ it 'can submit merge requests to upstream master' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ merge_request = Factory::Resource::MergeRequestFromFork.fabricate! do |merge_request|
+ merge_request.fork_branch = 'feature-branch'
+ end
+
+ Page::Menu::Main.act { sign_out }
+ Page::Main::Login.act do
+ switch_to_sign_in_tab
+ sign_in_using_credentials
+ end
+
+ merge_request.visit!
+ Page::MergeRequest::Show.act { merge! }
+
+ expect(page).to have_content('The changes were merged')
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/import_from_github_spec.rb b/qa/qa/specs/features/project/import_from_github_spec.rb
new file mode 100644
index 00000000000..221b5c27fba
--- /dev/null
+++ b/qa/qa/specs/features/project/import_from_github_spec.rb
@@ -0,0 +1,106 @@
+module QA
+ describe 'user imports a GitHub repo', :core, :github do
+ let(:imported_project) do
+ Factory::Resource::ProjectImportedFromGithub.fabricate! do |project|
+ project.name = 'imported-project'
+ project.personal_access_token = Runtime::Env.github_access_token
+ project.github_repository_path = 'gitlab-qa/test-project'
+ end
+ end
+
+ after do
+ # We need to delete the imported project because it's impossible to import
+ # the same GitHub project twice for a given user.
+ api_client = Runtime::API::Client.new(:gitlab)
+ delete_project_request = Runtime::API::Request.new(api_client, "/projects/#{CGI.escape("#{Runtime::Namespace.path}/#{imported_project.name}")}")
+ delete delete_project_request.url
+
+ expect_status(202)
+ end
+
+ it 'user imports a GitHub repo' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ imported_project # import the project
+
+ Page::Menu::Main.act { go_to_projects }
+ Page::Dashboard::Projects.perform do |dashboard|
+ dashboard.go_to_project(imported_project.name)
+ end
+
+ Page::Project::Show.act { wait_for_import }
+
+ verify_repository_import
+ verify_issues_import
+ verify_merge_requests_import
+ verify_labels_import
+ verify_milestones_import
+ verify_wiki_import
+ end
+
+ def verify_repository_import
+ expect(page).to have_content('This test project is used for automated GitHub import by GitLab QA.')
+ expect(page).to have_content(imported_project.name)
+ end
+
+ def verify_issues_import
+ Page::Menu::Side.act { click_issues }
+ expect(page).to have_content('This is a sample issue')
+
+ click_link 'This is a sample issue'
+
+ expect(page).to have_content('We should populate this project with issues, pull requests and wiki pages.')
+
+ # Comments
+ expect(page).to have_content('This is a comment from @rymai.')
+
+ Page::Issuable::Sidebar.perform do |issuable|
+ expect(issuable).to have_label('enhancement')
+ expect(issuable).to have_label('help wanted')
+ expect(issuable).to have_label('good first issue')
+ end
+ end
+
+ def verify_merge_requests_import
+ Page::Menu::Side.act { click_merge_requests }
+ expect(page).to have_content('Improve README.md')
+
+ click_link 'Improve README.md'
+
+ expect(page).to have_content('This improves the README file a bit.')
+
+ # Review comment are not supported yet
+ expect(page).not_to have_content('Really nice change.')
+
+ # Comments
+ expect(page).to have_content('Nice work! This is a comment from @rymai.')
+
+ # Diff comments
+ expect(page).to have_content('[Review comment] I like that!')
+ expect(page).to have_content('[Review comment] Nice blank line.')
+ expect(page).to have_content('[Single diff comment] Much better without this line!')
+
+ Page::Issuable::Sidebar.perform do |issuable|
+ expect(issuable).to have_label('bug')
+ expect(issuable).to have_label('enhancement')
+ end
+ end
+
+ def verify_labels_import
+ # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19228
+ # to build upon it.
+ end
+
+ def verify_milestones_import
+ # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18727
+ # to build upon it.
+ end
+
+ def verify_wiki_import
+ Page::Menu::Side.act { click_wiki }
+
+ expect(page).to have_content('Welcome to the test-project wiki!')
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/pipelines_spec.rb b/qa/qa/specs/features/project/pipelines_spec.rb
index bdb3d671516..ddedde7a8bc 100644
--- a/qa/qa/specs/features/project/pipelines_spec.rb
+++ b/qa/qa/specs/features/project/pipelines_spec.rb
@@ -1,12 +1,12 @@
module QA
- feature 'CI/CD Pipelines', :core, :docker do
+ describe 'CI/CD Pipelines', :core, :docker do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
after do
Service::Runner.new(executor).remove!
end
- scenario 'user registers a new specific runner' do
+ it 'user registers a new specific runner' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
@@ -25,7 +25,7 @@ module QA
end
end
- scenario 'users creates a new pipeline' do
+ it 'users creates a new pipeline' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/qa/specs/features/project/wikis_spec.rb b/qa/qa/specs/features/project/wikis_spec.rb
index 49290a1a896..59cc455fffc 100644
--- a/qa/qa/specs/features/project/wikis_spec.rb
+++ b/qa/qa/specs/features/project/wikis_spec.rb
@@ -1,5 +1,5 @@
module QA
- feature 'Wiki Functionality', :core do
+ describe 'Wiki Functionality', :core do
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
@@ -14,7 +14,7 @@ module QA
login
end
- scenario 'User creates, edits, clones, and pushes to the wiki' do
+ it 'User creates, edits, clones, and pushes to the wiki' do
wiki = Factory::Resource::Wiki.fabricate! do |resource|
resource.title = 'Home'
resource.content = '# My First Wiki Content'
diff --git a/qa/qa/specs/features/repository/clone_spec.rb b/qa/qa/specs/features/repository/clone_spec.rb
index bc9eb57bdb4..a04ce4e44d9 100644
--- a/qa/qa/specs/features/repository/clone_spec.rb
+++ b/qa/qa/specs/features/repository/clone_spec.rb
@@ -1,7 +1,7 @@
module QA
- feature 'clone code from the repository', :core do
+ describe 'clone code from the repository', :core do
context 'with regular account over http' do
- given(:location) do
+ let(:location) do
Page::Project::Show.act do
choose_repository_clone_http
repository_location
@@ -31,7 +31,7 @@ module QA
end
end
- scenario 'user performs a deep clone' do
+ it 'user performs a deep clone' do
Git::Repository.perform do |repository|
repository.uri = location.uri
repository.use_default_credentials
@@ -42,7 +42,7 @@ module QA
end
end
- scenario 'user performs a shallow clone' do
+ it 'user performs a shallow clone' do
Git::Repository.perform do |repository|
repository.uri = location.uri
repository.use_default_credentials
diff --git a/qa/qa/specs/features/repository/protected_branches_spec.rb b/qa/qa/specs/features/repository/protected_branches_spec.rb
index c5b8c271d7d..c2de94516d9 100644
--- a/qa/qa/specs/features/repository/protected_branches_spec.rb
+++ b/qa/qa/specs/features/repository/protected_branches_spec.rb
@@ -1,8 +1,8 @@
module QA
- feature 'branch protection support', :core do
- given(:branch_name) { 'protected-branch' }
- given(:commit_message) { 'Protected push commit message' }
- given(:project) do
+ describe 'branch protection support', :core do
+ let(:branch_name) { 'protected-branch' }
+ let(:commit_message) { 'Protected push commit message' }
+ let(:project) do
Factory::Resource::Project.fabricate! do |resource|
resource.name = 'protected-branch-project'
end
@@ -13,39 +13,32 @@ module QA
Page::Main::Login.act { sign_in_using_credentials }
end
- after do |example|
+ after do
# We need to clear localStorage because we're using it for the dropdown,
# and capybara doesn't do this for us.
# https://github.com/teamcapybara/capybara/issues/1702
Capybara.execute_script 'localStorage.clear()'
-
- # In order to help diagnose a false failure
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/48241
- log_push_output if example.exception
end
context 'when developers and maintainers are allowed to push to a protected branch' do
- let!(:protected_branch) { create_protected_branch(allow_to_push: true) }
-
- scenario 'user with push rights successfully pushes to the protected branch' do
- expect(protected_branch.name).to have_content(branch_name)
- expect(protected_branch.push_allowance).to have_content('Developers + Maintainers')
+ it 'user with push rights successfully pushes to the protected branch' do
+ create_protected_branch(allow_to_push: true)
- @push = push_new_file(branch_name)
+ push = push_new_file(branch_name)
- expect(@push.output).to match(/remote: To create a merge request for protected-branch, visit/)
+ expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
end
end
context 'when developers and maintainers are not allowed to push to a protected branch' do
- scenario 'user without push rights fails to push to the protected branch' do
+ it 'user without push rights fails to push to the protected branch' do
create_protected_branch(allow_to_push: false)
- @push = push_new_file(branch_name)
+ push = push_new_file(branch_name)
- expect(@push.output)
+ expect(push.output)
.to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
- expect(@push.output)
+ expect(push.output)
.to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
end
@@ -69,13 +62,5 @@ module QA
resource.new_branch = false
end
end
-
- def log_push_output
- if defined?(@push)
- filename = File.join('tmp', "push-output-#{project.name}")
- puts "Exception detected. Push output will be saved to #{filename}"
- IO.binwrite(filename, @push.output)
- end
- end
end
end
diff --git a/qa/qa/specs/features/repository/push_spec.rb b/qa/qa/specs/features/repository/push_spec.rb
index 16aaa2e6762..fc40b60d915 100644
--- a/qa/qa/specs/features/repository/push_spec.rb
+++ b/qa/qa/specs/features/repository/push_spec.rb
@@ -1,7 +1,7 @@
module QA
- feature 'push code to repository', :core do
+ describe 'push code to repository', :core do
context 'with regular account over http' do
- scenario 'user pushes code to the repository' do
+ it 'user pushes code to the repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
diff --git a/qa/spec/git/repository_spec.rb b/qa/spec/git/repository_spec.rb
index ee1f08da238..5c65128d10c 100644
--- a/qa/spec/git/repository_spec.rb
+++ b/qa/spec/git/repository_spec.rb
@@ -29,7 +29,7 @@ describe QA::Git::Repository do
def cd_empty_temp_directory
tmp_dir = 'tmp/git-repository-spec/'
- FileUtils.rm_r(tmp_dir) if File.exist?(tmp_dir)
+ FileUtils.rm_r(tmp_dir) if ::File.exist?(tmp_dir)
FileUtils.mkdir_p tmp_dir
FileUtils.cd tmp_dir
end
diff --git a/qa/spec/page/view_spec.rb b/qa/spec/page/view_spec.rb
index aedbc3863a7..34d2ff11447 100644
--- a/qa/spec/page/view_spec.rb
+++ b/qa/spec/page/view_spec.rb
@@ -32,7 +32,7 @@ describe QA::Page::View do
context 'when pattern is found' do
before do
- allow(File).to receive(:foreach)
+ allow(::File).to receive(:foreach)
.and_yield('some element').once
allow(element).to receive(:matches?)
.with('some element').and_return(true)
@@ -45,7 +45,7 @@ describe QA::Page::View do
context 'when pattern has not been found' do
before do
- allow(File).to receive(:foreach)
+ allow(::File).to receive(:foreach)
.and_yield('some element').once
allow(element).to receive(:matches?)
.with('some element').and_return(false)
diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb
index 2b6365dbc41..851026c71f0 100644
--- a/qa/spec/runtime/env_spec.rb
+++ b/qa/spec/runtime/env_spec.rb
@@ -76,4 +76,27 @@ describe QA::Runtime::Env do
expect { described_class.user_type }.to raise_error(ArgumentError)
end
end
+
+ describe '.github_access_token' do
+ it 'returns "" if GITHUB_ACCESS_TOKEN is not defined' do
+ expect(described_class.github_access_token).to eq('')
+ end
+
+ it 'returns stripped string if GITHUB_ACCESS_TOKEN is defined' do
+ stub_env('GITHUB_ACCESS_TOKEN', ' abc123 ')
+ expect(described_class.github_access_token).to eq('abc123')
+ end
+ end
+
+ describe '.require_github_access_token!' do
+ it 'raises ArgumentError if GITHUB_ACCESS_TOKEN is not defined' do
+ expect { described_class.require_github_access_token! }.to raise_error(ArgumentError)
+ end
+
+ it 'does not raise if GITHUB_ACCESS_TOKEN is defined' do
+ stub_env('GITHUB_ACCESS_TOKEN', ' abc123 ')
+
+ expect { described_class.require_github_access_token! }.not_to raise_error
+ end
+ end
end
diff --git a/qa/spec/scenario/test/instance_spec.rb b/qa/spec/scenario/test/instance_spec.rb
index a74a9538be8..0d0b534911f 100644
--- a/qa/spec/scenario/test/instance_spec.rb
+++ b/qa/spec/scenario/test/instance_spec.rb
@@ -30,7 +30,7 @@ describe QA::Scenario::Test::Instance do
subject.perform("test")
expect(runner).to have_received(:options=)
- .with(File.expand_path('../../../qa/specs/features', __dir__))
+ .with(::File.expand_path('../../../qa/specs/features', __dir__))
end
end
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index c2c6cf95406..8e6613cd688 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -1,6 +1,6 @@
require_relative '../qa'
-Dir[File.join(__dir__, 'support', '**', '*.rb')].each { |f| require f }
+Dir[::File.join(__dir__, 'support', '**', '*.rb')].each { |f| require f }
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
diff --git a/rubocop/cop/line_break_around_conditional_block.rb b/rubocop/cop/line_break_around_conditional_block.rb
index 8b6052fee1b..011f2bcf8bf 100644
--- a/rubocop/cop/line_break_around_conditional_block.rb
+++ b/rubocop/cop/line_break_around_conditional_block.rb
@@ -43,6 +43,8 @@ module RuboCop
# end
# end
class LineBreakAroundConditionalBlock < RuboCop::Cop::Cop
+ include RangeHelp
+
MSG = 'Add a line break around conditional blocks'
def on_if(node)
diff --git a/rubocop/cop/migration/add_reference.rb b/rubocop/cop/migration/add_reference.rb
new file mode 100644
index 00000000000..4b67270c97a
--- /dev/null
+++ b/rubocop/cop/migration/add_reference.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+require_relative '../../migration_helpers'
+
+module RuboCop
+ module Cop
+ module Migration
+ # Cop that checks if a foreign key constraint is added and require a index for it
+ class AddReference < RuboCop::Cop::Cop
+ include MigrationHelpers
+
+ MSG = '`add_reference` requires `index: true`'
+
+ def on_send(node)
+ return unless in_migration?(node)
+
+ name = node.children[1]
+
+ return unless name == :add_reference
+
+ opts = node.children.last
+
+ add_offense(node, location: :selector) unless opts && opts.type == :hash
+
+ index_present = false
+
+ opts.each_node(:pair) do |pair|
+ index_present ||= index_enabled?(pair)
+ end
+
+ add_offense(node, location: :selector) unless index_present
+ end
+
+ private
+
+ def index_enabled?(pair)
+ hash_key_type(pair) == :sym && hash_key_name(pair) == :index && pair.children[1].true_type?
+ end
+
+ def hash_key_type(pair)
+ pair.children[0].type
+ end
+
+ def hash_key_name(pair)
+ pair.children[0].children[0]
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index aa7ae601f75..a427208cdab 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -11,6 +11,7 @@ require_relative 'cop/migration/add_column'
require_relative 'cop/migration/add_concurrent_foreign_key'
require_relative 'cop/migration/add_concurrent_index'
require_relative 'cop/migration/add_index'
+require_relative 'cop/migration/add_reference'
require_relative 'cop/migration/add_timestamps'
require_relative 'cop/migration/datetime'
require_relative 'cop/migration/hash_index'
diff --git a/scripts/frontend/prettier.js b/scripts/frontend/prettier.js
index 6e4e36b9b2d..b66ba885701 100644
--- a/scripts/frontend/prettier.js
+++ b/scripts/frontend/prettier.js
@@ -49,7 +49,7 @@ const stagedFiles =
if (stagedFiles) {
if (!stagedFiles.length || (stagedFiles.length === 1 && !stagedFiles[0])) {
console.log('No matching staged files.');
- return;
+ process.exit(1);
}
console.log(`Matching staged Files : ${stagedFiles.length}`);
}
@@ -78,7 +78,7 @@ files = prettierIgnore.filter(files);
if (!files.length) {
console.log('No Files found to process with Prettier');
- return;
+ process.exit(1);
}
console.log(`${shouldSave ? 'Updating' : 'Checking'} ${files.length} file(s)`);
diff --git a/scripts/lint-rugged b/scripts/lint-rugged
index cabd083e9f9..d0c2c544c47 100755
--- a/scripts/lint-rugged
+++ b/scripts/lint-rugged
@@ -14,7 +14,10 @@ ALLOWED = [
'lib/tasks/gitlab/cleanup.rake',
# The only place where Rugged code is still allowed in production
- 'lib/gitlab/git/'
+ 'lib/gitlab/git/',
+
+ # Needed to avoid using the git binary to validate a branch name
+ 'lib/gitlab/git_ref_validator.rb'
].freeze
rugged_lines = IO.popen(%w[git grep -i -n rugged -- app config lib], &:read).lines
diff --git a/scripts/trigger-build-docs b/scripts/trigger-build-docs
index 2a0e7f4d76e..9ee35684509 100755
--- a/scripts/trigger-build-docs
+++ b/scripts/trigger-build-docs
@@ -16,18 +16,14 @@ end
GITLAB_DOCS_REPO = 'gitlab-com/gitlab-docs'.freeze
#
-# Truncate the remote docs branch name if it's more than 63 characters
-# otherwise we hit the filesystem limit and the directory name where
-# NGINX serves the site won't match the branch name.
+# Truncate the remote docs branch name otherwise we hit the filesystem
+# limit and the directory name where NGINX serves the site won't match
+# the branch name.
#
def docs_branch
# The maximum string length a file can have on a filesystem (ext4)
- # is 63 characters. Let's use something smaller to be 100% sure.
- max = 42
- # Prefix the remote branch with the slug of the project in order
- # to avoid name conflicts in the rare case the branch name already
- # exists in the docs repo and truncate to max length.
- "#{slug}-#{ENV["CI_ENVIRONMENT_SLUG"]}"[0...max]
+ # is 63 characters. CI_ENVIRONMENT_SLUG is limited to 24 characters.
+ ENV["CI_ENVIRONMENT_SLUG"]
end
#
diff --git a/spec/bin/changelog_spec.rb b/spec/bin/changelog_spec.rb
index f278043028f..9dc4edf97d1 100644
--- a/spec/bin/changelog_spec.rb
+++ b/spec/bin/changelog_spec.rb
@@ -3,6 +3,20 @@ require 'spec_helper'
load File.expand_path('../../bin/changelog', __dir__)
describe 'bin/changelog' do
+ let(:options) { OpenStruct.new(title: 'Test title', type: 'fixed', dry_run: true) }
+
+ describe ChangelogEntry do
+ it 'truncates the file path' do
+ entry = described_class.new(options)
+
+ allow(entry).to receive(:ee?).and_return(false)
+ allow(entry).to receive(:branch_name).and_return('long-branch-' * 100)
+
+ file_path = entry.send(:file_path)
+ expect(file_path.length).to eq(140)
+ end
+ end
+
describe ChangelogOptionParser do
describe '.parse' do
it 'parses --amend' do
diff --git a/spec/config/object_store_settings_spec.rb b/spec/config/object_store_settings_spec.rb
new file mode 100644
index 00000000000..b1ada3c99db
--- /dev/null
+++ b/spec/config/object_store_settings_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+require Rails.root.join('config', 'object_store_settings.rb')
+
+describe ObjectStoreSettings do
+ describe '.parse' do
+ it 'should set correct default values' do
+ settings = described_class.parse(nil)
+
+ expect(settings['enabled']).to be false
+ expect(settings['direct_upload']).to be false
+ expect(settings['background_upload']).to be true
+ expect(settings['remote_directory']).to be nil
+ end
+
+ it 'respects original values' do
+ original_settings = Settingslogic.new({
+ 'enabled' => true,
+ 'remote_directory' => 'artifacts'
+ })
+
+ settings = described_class.parse(original_settings)
+
+ expect(settings['enabled']).to be true
+ expect(settings['direct_upload']).to be false
+ expect(settings['background_upload']).to be true
+ expect(settings['remote_directory']).to eq 'artifacts'
+ end
+ end
+end
diff --git a/spec/controllers/admin/services_controller_spec.rb b/spec/controllers/admin/services_controller_spec.rb
index 701211c2586..4439ea4a533 100644
--- a/spec/controllers/admin/services_controller_spec.rb
+++ b/spec/controllers/admin/services_controller_spec.rb
@@ -13,7 +13,7 @@ describe Admin::ServicesController do
Service.available_services_names.each do |service_name|
context "#{service_name}" do
let!(:service) do
- service_template = service_name.concat("_service").camelize.constantize
+ service_template = "#{service_name}_service".camelize.constantize
service_template.where(template: true).first_or_create
end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 74f362fd7fc..421ab006792 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -56,7 +56,62 @@ describe ApplicationController do
end
end
+ describe '#add_gon_variables' do
+ before do
+ Gon.clear
+ sign_in user
+ end
+
+ let(:json_response) { JSON.parse(response.body) }
+
+ controller(described_class) do
+ def index
+ render json: Gon.all_variables
+ end
+ end
+
+ shared_examples 'setting gon variables' do
+ it 'sets gon variables' do
+ get :index, format: format
+
+ expect(json_response.size).not_to be_zero
+ end
+ end
+
+ shared_examples 'not setting gon variables' do
+ it 'does not set gon variables' do
+ get :index, format: format
+
+ expect(json_response.size).to be_zero
+ end
+ end
+
+ context 'with html format' do
+ let(:format) { :html }
+
+ it_behaves_like 'setting gon variables'
+
+ context 'for peek requests' do
+ before do
+ request.path = '/-/peek'
+ end
+
+ it_behaves_like 'not setting gon variables'
+ end
+ end
+
+ context 'with json format' do
+ let(:format) { :json }
+
+ it_behaves_like 'not setting gon variables'
+ end
+ end
+
describe "#authenticate_user_from_personal_access_token!" do
+ before do
+ stub_authentication_activity_metrics(debug: false)
+ end
+
controller(described_class) do
def index
render text: 'authenticated'
@@ -67,7 +122,13 @@ describe ApplicationController do
context "when the 'personal_access_token' param is populated with the personal access token" do
it "logs the user in" do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_sessionless_authentication_counter)
+
get :index, private_token: personal_access_token.token
+
expect(response).to have_gitlab_http_status(200)
expect(response.body).to eq('authenticated')
end
@@ -75,20 +136,56 @@ describe ApplicationController do
context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do
it "logs the user in" do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_sessionless_authentication_counter)
+
@request.headers["PRIVATE-TOKEN"] = personal_access_token.token
get :index
+
expect(response).to have_gitlab_http_status(200)
expect(response.body).to eq('authenticated')
end
end
it "doesn't log the user in otherwise" do
+ expect(authentication_metrics)
+ .to increment(:user_unauthenticated_counter)
+
get :index, private_token: "token"
+
expect(response.status).not_to eq(200)
expect(response.body).not_to eq('authenticated')
end
end
+ describe 'session expiration' do
+ controller(described_class) do
+ def index
+ render text: 'authenticated'
+ end
+ end
+
+ context 'authenticated user' do
+ it 'does not set the expire_after option' do
+ sign_in(create(:user))
+
+ get :index
+
+ expect(request.env['rack.session.options'][:expire_after]).to be_nil
+ end
+ end
+
+ context 'unauthenticated user' do
+ it 'sets the expire_after option' do
+ get :index
+
+ expect(request.env['rack.session.options'][:expire_after]).to eq(Settings.gitlab['unauthenticated_session_expire_delay'])
+ end
+ end
+ end
+
describe 'rescue from Gitlab::Git::Storage::Inaccessible' do
controller(described_class) do
def index
@@ -148,6 +245,10 @@ describe ApplicationController do
end
describe '#authenticate_sessionless_user!' do
+ before do
+ stub_authentication_activity_metrics(debug: false)
+ end
+
describe 'authenticating a user from a feed token' do
controller(described_class) do
def index
@@ -158,7 +259,13 @@ describe ApplicationController do
context "when the 'feed_token' param is populated with the feed token" do
context 'when the request format is atom' do
it "logs the user in" do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_sessionless_authentication_counter)
+
get :index, feed_token: user.feed_token, format: :atom
+
expect(response).to have_gitlab_http_status 200
expect(response.body).to eq 'authenticated'
end
@@ -166,7 +273,13 @@ describe ApplicationController do
context 'when the request format is ics' do
it "logs the user in" do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_sessionless_authentication_counter)
+
get :index, feed_token: user.feed_token, format: :ics
+
expect(response).to have_gitlab_http_status 200
expect(response.body).to eq 'authenticated'
end
@@ -174,7 +287,11 @@ describe ApplicationController do
context 'when the request format is neither atom nor ics' do
it "doesn't log the user in" do
+ expect(authentication_metrics)
+ .to increment(:user_unauthenticated_counter)
+
get :index, feed_token: user.feed_token
+
expect(response.status).not_to have_gitlab_http_status 200
expect(response.body).not_to eq 'authenticated'
end
@@ -183,7 +300,11 @@ describe ApplicationController do
context "when the 'feed_token' param is populated with an invalid feed token" do
it "doesn't log the user" do
+ expect(authentication_metrics)
+ .to increment(:user_unauthenticated_counter)
+
get :index, feed_token: 'token', format: :atom
+
expect(response.status).not_to eq 200
expect(response.body).not_to eq 'authenticated'
end
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index fb6d82d7de3..2c59d1929a1 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -228,12 +228,12 @@ describe AutocompleteController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'authorized projects' do
before do
- authorized_project.add_master(user)
+ authorized_project.add_maintainer(user)
end
describe 'GET #projects with project ID' do
@@ -253,8 +253,8 @@ describe AutocompleteController do
context 'authorized projects and search' do
before do
- authorized_project.add_master(user)
- authorized_search_project.add_master(user)
+ authorized_project.add_maintainer(user)
+ authorized_search_project.add_maintainer(user)
end
describe 'GET #projects with project ID and search' do
@@ -277,9 +277,9 @@ describe AutocompleteController do
authorized_project2 = create(:project)
authorized_project3 = create(:project)
- authorized_project.add_master(user)
- authorized_project2.add_master(user)
- authorized_project3.add_master(user)
+ authorized_project.add_maintainer(user)
+ authorized_project2.add_maintainer(user)
+ authorized_project3.add_maintainer(user)
stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
end
@@ -301,9 +301,9 @@ describe AutocompleteController do
authorized_project2 = create(:project)
authorized_project3 = create(:project)
- authorized_project.add_master(user)
- authorized_project2.add_master(user)
- authorized_project3.add_master(user)
+ authorized_project.add_maintainer(user)
+ authorized_project2.add_maintainer(user)
+ authorized_project3.add_maintainer(user)
end
describe 'GET #projects with project ID and offset_id' do
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index e47ff8661a2..d98e6ff0df8 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -13,7 +13,7 @@ describe Boards::IssuesController do
let!(:list2) { create(:list, board: board, label: development, position: 1) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_guest(guest)
end
@@ -42,7 +42,7 @@ describe Boards::IssuesController do
parsed_response = JSON.parse(response.body)
expect(response).to match_response_schema('issues')
- expect(parsed_response.length).to eq 2
+ expect(parsed_response['issues'].length).to eq 2
expect(development.issues.map(&:relative_position)).not_to include(nil)
end
@@ -80,7 +80,7 @@ describe Boards::IssuesController do
parsed_response = JSON.parse(response.body)
expect(response).to match_response_schema('issues')
- expect(parsed_response.length).to eq 2
+ expect(parsed_response['issues'].length).to eq 2
end
end
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb
index 57ccbf1d6b5..80631d2efb0 100644
--- a/spec/controllers/boards/lists_controller_spec.rb
+++ b/spec/controllers/boards/lists_controller_spec.rb
@@ -7,7 +7,7 @@ describe Boards::ListsController do
let(:guest) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_guest(guest)
end
diff --git a/spec/controllers/concerns/group_tree_spec.rb b/spec/controllers/concerns/group_tree_spec.rb
index ba84fbf8564..503eb416962 100644
--- a/spec/controllers/concerns/group_tree_spec.rb
+++ b/spec/controllers/concerns/group_tree_spec.rb
@@ -63,6 +63,17 @@ describe GroupTree do
expect(assigns(:groups)).to contain_exactly(parent, subgroup)
end
+
+ it 'preloads parents regardless of pagination' do
+ allow(Kaminari.config).to receive(:default_per_page).and_return(1)
+ group = create(:group, :public)
+ subgroup = create(:group, :public, parent: group)
+ search_result = create(:group, :public, name: 'result', parent: subgroup)
+
+ get :index, filter: 'resu', format: :json
+
+ expect(assigns(:groups)).to contain_exactly(group, subgroup, search_result)
+ end
end
context 'json content' do
diff --git a/spec/controllers/dashboard/groups_controller_spec.rb b/spec/controllers/dashboard/groups_controller_spec.rb
index 7f2eaf95165..9068c1a792e 100644
--- a/spec/controllers/dashboard/groups_controller_spec.rb
+++ b/spec/controllers/dashboard/groups_controller_spec.rb
@@ -28,8 +28,8 @@ describe Dashboard::GroupsController do
let!(:other_group) { create(:group, name: 'other') }
before do
- top_level_result.add_master(user)
- top_level_a.add_master(user)
+ top_level_result.add_maintainer(user)
+ top_level_a.add_maintainer(user)
end
it 'renders only groups the user is a member of when searching hierarchy correctly' do
diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb
index 60547db82b6..505c040b5d5 100644
--- a/spec/controllers/dashboard/milestones_controller_spec.rb
+++ b/spec/controllers/dashboard/milestones_controller_spec.rb
@@ -2,8 +2,10 @@ require 'spec_helper'
describe Dashboard::MilestonesController do
let(:project) { create(:project) }
- let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:user) { create(:user) }
let(:project_milestone) { create(:milestone, project: project) }
+ let(:group_milestone) { create(:milestone, group: group) }
let(:milestone) do
DashboardMilestone.build(
[project],
@@ -11,13 +13,17 @@ describe Dashboard::MilestonesController do
)
end
let(:issue) { create(:issue, project: project, milestone: project_milestone) }
+ let(:group_issue) { create(:issue, milestone: group_milestone) }
+
let!(:label) { create(:label, project: project, title: 'Issue Label', issues: [issue]) }
+ let!(:group_label) { create(:group_label, group: group, title: 'Group Issue Label', issues: [group_issue]) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: project_milestone) }
let(:milestone_path) { dashboard_milestone_path(milestone.safe_title, title: milestone.title) }
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
+ group.add_developer(user)
end
it_behaves_like 'milestone tabs'
@@ -35,4 +41,15 @@ describe Dashboard::MilestonesController do
expect(response).to have_gitlab_http_status(200)
end
end
+
+ describe "#index" do
+ it 'should contain group and project milestones' do
+ get :index, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to eq(2)
+ expect(json_response.map { |i| i["first_milestone"]["id"] }).to include(group_milestone.id, project_milestone.id)
+ expect(json_response.map { |i| i["group_name"] }).to include(group.name)
+ end
+ end
end
diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb
index 3458d679107..187542ba30c 100644
--- a/spec/controllers/dashboard_controller_spec.rb
+++ b/spec/controllers/dashboard_controller_spec.rb
@@ -5,7 +5,7 @@ describe DashboardController do
let(:project) { create(:project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb
index 0f5bde62006..bf41aa0706f 100644
--- a/spec/controllers/groups/boards_controller_spec.rb
+++ b/spec/controllers/groups/boards_controller_spec.rb
@@ -5,7 +5,7 @@ describe Groups::BoardsController do
let(:user) { create(:user) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index 733386500ca..f7068546093 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -28,7 +28,7 @@ describe Groups::MilestonesController do
before do
sign_in(user)
group.add_owner(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#index' do
diff --git a/spec/controllers/groups/runners_controller_spec.rb b/spec/controllers/groups/runners_controller_spec.rb
index 5770d15557c..598fb84552f 100644
--- a/spec/controllers/groups/runners_controller_spec.rb
+++ b/spec/controllers/groups/runners_controller_spec.rb
@@ -14,7 +14,7 @@ describe Groups::RunnersController do
before do
sign_in(user)
- group.add_master(user)
+ group.add_maintainer(user)
end
describe '#update' do
diff --git a/spec/controllers/groups/settings/ci_cd_controller_spec.rb b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
index e9f0924caba..ea18122e0c3 100644
--- a/spec/controllers/groups/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
@@ -5,7 +5,7 @@ describe Groups::Settings::CiCdController do
let(:user) { create(:user) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/groups/uploads_controller_spec.rb b/spec/controllers/groups/uploads_controller_spec.rb
index 6a1869d1a48..5a7281ed704 100644
--- a/spec/controllers/groups/uploads_controller_spec.rb
+++ b/spec/controllers/groups/uploads_controller_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Groups::UploadsController do
+ include WorkhorseHelpers
+
let(:model) { create(:group, :public) }
let(:params) do
{ group_id: model }
@@ -9,4 +11,10 @@ describe Groups::UploadsController do
it_behaves_like 'handle uploads' do
let(:uploader_class) { NamespaceFileUploader }
end
+
+ def post_authorize(verified: true)
+ request.headers.merge!(workhorse_internal_api_request_header) if verified
+
+ post :authorize, group_id: model.full_path, format: :json
+ end
end
diff --git a/spec/controllers/groups/variables_controller_spec.rb b/spec/controllers/groups/variables_controller_spec.rb
index 39a36b92bb4..e5ac5634f95 100644
--- a/spec/controllers/groups/variables_controller_spec.rb
+++ b/spec/controllers/groups/variables_controller_spec.rb
@@ -6,7 +6,7 @@ describe Groups::VariablesController do
before do
sign_in(user)
- group.add_master(user)
+ group.add_maintainer(user)
end
describe 'GET #show' do
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 8688fb33f0d..7a037828035 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -7,7 +7,7 @@ describe GroupsController do
let(:project) { create(:project, namespace: group) }
let!(:group_member) { create(:group_member, group: group, user: user) }
let!(:owner) { group.add_owner(create(:user)).user }
- let!(:master) { group.add_master(create(:user)).user }
+ let!(:maintainer) { group.add_maintainer(create(:user)).user }
let!(:developer) { group.add_developer(create(:user)).user }
let!(:guest) { group.add_guest(create(:user)).user }
@@ -62,7 +62,7 @@ describe GroupsController do
[true, false].each do |can_create_group_status|
context "and can_create_group is #{can_create_group_status}" do
before do
- User.where(id: [admin, owner, master, developer, guest]).update_all(can_create_group: can_create_group_status)
+ User.where(id: [admin, owner, maintainer, developer, guest]).update_all(can_create_group: can_create_group_status)
end
[:admin, :owner].each do |member_type|
@@ -73,7 +73,7 @@ describe GroupsController do
end
end
- [:guest, :developer, :master].each do |member_type|
+ [:guest, :developer, :maintainer].each do |member_type|
context "and logged in as #{member_type.capitalize}" do
it_behaves_like 'member without ability to create subgroups' do
let(:member) { send(member_type) }
diff --git a/spec/controllers/import/bitbucket_server_controller_spec.rb b/spec/controllers/import/bitbucket_server_controller_spec.rb
new file mode 100644
index 00000000000..5024ef71771
--- /dev/null
+++ b/spec/controllers/import/bitbucket_server_controller_spec.rb
@@ -0,0 +1,154 @@
+require 'spec_helper'
+
+describe Import::BitbucketServerController do
+ let(:user) { create(:user) }
+ let(:project_key) { 'test-project' }
+ let(:repo_slug) { 'some-repo' }
+ let(:client) { instance_double(BitbucketServer::Client) }
+
+ def assign_session_tokens
+ session[:bitbucket_server_url] = 'http://localhost:7990'
+ session[:bitbucket_server_username] = 'bitbucket'
+ session[:bitbucket_server_personal_access_token] = 'some-token'
+ end
+
+ before do
+ sign_in(user)
+ allow(controller).to receive(:bitbucket_server_import_enabled?).and_return(true)
+ end
+
+ describe 'GET new' do
+ render_views
+
+ it 'shows the input form' do
+ get :new
+
+ expect(response.body).to have_text('Bitbucket Server URL')
+ end
+ end
+
+ describe 'POST create' do
+ before do
+ allow(controller).to receive(:bitbucket_client).and_return(client)
+ repo = double(name: 'my-project')
+ allow(client).to receive(:repo).with(project_key, repo_slug).and_return(repo)
+ assign_session_tokens
+ end
+
+ set(:project) { create(:project) }
+
+ it 'returns the new project' do
+ allow(Gitlab::BitbucketServerImport::ProjectCreator)
+ .to receive(:new).with(project_key, repo_slug, anything, 'my-project', user.namespace, user, anything)
+ .and_return(double(execute: project))
+
+ post :create, project: project_key, repository: repo_slug, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'returns an error when an invalid project key is used' do
+ post :create, project: 'some&project'
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+
+ it 'returns an error when an invalid repository slug is used' do
+ post :create, project: 'some-project', repository: 'try*this'
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+
+ it 'returns an error when the project cannot be found' do
+ allow(client).to receive(:repo).with(project_key, repo_slug).and_return(nil)
+
+ post :create, project: project_key, repository: repo_slug, format: :json
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+
+ it 'returns an error when the project cannot be saved' do
+ allow(Gitlab::BitbucketServerImport::ProjectCreator)
+ .to receive(:new).with(project_key, repo_slug, anything, 'my-project', user.namespace, user, anything)
+ .and_return(double(execute: build(:project)))
+
+ post :create, project: project_key, repository: repo_slug, format: :json
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+
+ it "returns an error when the server can't be contacted" do
+ expect(client).to receive(:repo).with(project_key, repo_slug).and_raise(BitbucketServer::Client::ServerError)
+
+ post :create, project: project_key, repository: repo_slug, format: :json
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+ end
+
+ describe 'POST configure' do
+ let(:token) { 'token' }
+ let(:username) { 'bitbucket-user' }
+ let(:url) { 'http://localhost:7990/bitbucket' }
+
+ it 'clears out existing session' do
+ post :configure
+
+ expect(session[:bitbucket_server_url]).to be_nil
+ expect(session[:bitbucket_server_username]).to be_nil
+ expect(session[:bitbucket_server_personal_access_token]).to be_nil
+
+ expect(response).to have_gitlab_http_status(302)
+ expect(response).to redirect_to(status_import_bitbucket_server_path)
+ end
+
+ it 'sets the session variables' do
+ post :configure, personal_access_token: token, bitbucket_username: username, bitbucket_server_url: url
+
+ expect(session[:bitbucket_server_url]).to eq(url)
+ expect(session[:bitbucket_server_username]).to eq(username)
+ expect(session[:bitbucket_server_personal_access_token]).to eq(token)
+ expect(response).to have_gitlab_http_status(302)
+ expect(response).to redirect_to(status_import_bitbucket_server_path)
+ end
+ end
+
+ describe 'GET status' do
+ render_views
+
+ before do
+ allow(controller).to receive(:bitbucket_client).and_return(client)
+
+ @repo = double(slug: 'vim', project_key: 'asd', full_name: 'asd/vim', "valid?" => true, project_name: 'asd', browse_url: 'http://test', name: 'vim')
+ @invalid_repo = double(slug: 'invalid', project_key: 'foobar', full_name: 'asd/foobar', "valid?" => false, browse_url: 'http://bad-repo')
+ assign_session_tokens
+ end
+
+ it 'assigns repository categories' do
+ created_project = create(:project, import_type: 'bitbucket_server', creator_id: user.id, import_source: 'foo/bar', import_status: 'finished')
+ expect(client).to receive(:repos).and_return([@repo, @invalid_repo])
+
+ get :status
+
+ expect(assigns(:already_added_projects)).to eq([created_project])
+ expect(assigns(:repos)).to eq([@repo])
+ expect(assigns(:incompatible_repos)).to eq([@invalid_repo])
+ end
+ end
+
+ describe 'GET jobs' do
+ before do
+ assign_session_tokens
+ end
+
+ it 'returns a list of imported projects' do
+ created_project = create(:project, import_type: 'bitbucket_server', creator_id: user.id)
+
+ get :jobs
+
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['id']).to eq(created_project.id)
+ expect(json_response.first['import_status']).to eq('none')
+ end
+ end
+end
diff --git a/spec/controllers/instance_statistics/cohorts_controller_spec.rb b/spec/controllers/instance_statistics/cohorts_controller_spec.rb
new file mode 100644
index 00000000000..e4eedede93a
--- /dev/null
+++ b/spec/controllers/instance_statistics/cohorts_controller_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe InstanceStatistics::CohortsController do
+ it_behaves_like 'instance statistics availability'
+end
diff --git a/spec/controllers/instance_statistics/conversational_development_index_controller_spec.rb b/spec/controllers/instance_statistics/conversational_development_index_controller_spec.rb
new file mode 100644
index 00000000000..4935cb265bf
--- /dev/null
+++ b/spec/controllers/instance_statistics/conversational_development_index_controller_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe InstanceStatistics::ConversationalDevelopmentIndexController do
+ it_behaves_like 'instance statistics availability'
+end
diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb
index 7376841fac8..c7c83369d7c 100644
--- a/spec/controllers/metrics_controller_spec.rb
+++ b/spec/controllers/metrics_controller_spec.rb
@@ -15,55 +15,16 @@ describe MetricsController do
allow(Prometheus::Client.configuration).to receive(:multiprocess_files_dir).and_return(metrics_multiproc_dir)
allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(true)
allow(Settings.monitoring).to receive(:ip_whitelist).and_return([whitelisted_ip, whitelisted_ip_range])
+ allow_any_instance_of(MetricsService).to receive(:metrics_text).and_return("prometheus_counter 1")
end
describe '#index' do
shared_examples_for 'endpoint providing metrics' do
- it 'returns DB ping metrics' do
+ it 'returns prometheus metrics' do
get :index
- expect(response.body).to match(/^db_ping_timeout 0$/)
- expect(response.body).to match(/^db_ping_success 1$/)
- expect(response.body).to match(/^db_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns Redis ping metrics' do
- get :index
-
- expect(response.body).to match(/^redis_ping_timeout 0$/)
- expect(response.body).to match(/^redis_ping_success 1$/)
- expect(response.body).to match(/^redis_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns Caching ping metrics' do
- get :index
-
- expect(response.body).to match(/^redis_cache_ping_timeout 0$/)
- expect(response.body).to match(/^redis_cache_ping_success 1$/)
- expect(response.body).to match(/^redis_cache_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns Queues ping metrics' do
- get :index
-
- expect(response.body).to match(/^redis_queues_ping_timeout 0$/)
- expect(response.body).to match(/^redis_queues_ping_success 1$/)
- expect(response.body).to match(/^redis_queues_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns SharedState ping metrics' do
- get :index
-
- expect(response.body).to match(/^redis_shared_state_ping_timeout 0$/)
- expect(response.body).to match(/^redis_shared_state_ping_success 1$/)
- expect(response.body).to match(/^redis_shared_state_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns Gitaly metrics' do
- get :index
-
- expect(response.body).to match(/^gitaly_health_check_success{shard="default"} 1$/)
- expect(response.body).to match(/^gitaly_health_check_latency_seconds{shard="default"} [0-9\.]+$/)
+ expect(response.status).to eq(200)
+ expect(response.body).to match(/^prometheus_counter 1$/)
end
context 'prometheus metrics are disabled' do
@@ -101,7 +62,7 @@ describe MetricsController do
allow(Gitlab::RequestContext).to receive(:client_ip).and_return(not_whitelisted_ip)
end
- it 'returns proper response' do
+ it 'returns the expected error response' do
get :index
expect(response.status).to eq(404)
diff --git a/spec/controllers/profiles_controller_spec.rb b/spec/controllers/profiles_controller_spec.rb
index 4530a301d4d..360c536c667 100644
--- a/spec/controllers/profiles_controller_spec.rb
+++ b/spec/controllers/profiles_controller_spec.rb
@@ -78,6 +78,15 @@ describe ProfilesController, :request_store do
expect(ldap_user.name).not_to eq('John')
expect(ldap_user.location).to eq('City, Country')
end
+
+ it 'allows setting a user status' do
+ sign_in(user)
+
+ put :update, user: { status: { message: 'Working hard!' } }
+
+ expect(user.reload.status.message).to eq('Working hard!')
+ expect(response).to have_gitlab_http_status(302)
+ end
end
describe 'PUT update_username' do
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index acfa2730d94..17c9a61f339 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::AvatarsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/badges_controller_spec.rb b/spec/controllers/projects/badges_controller_spec.rb
index e7cddf8cfbf..dfe34171b55 100644
--- a/spec/controllers/projects/badges_controller_spec.rb
+++ b/spec/controllers/projects/badges_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::BadgesController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/blame_controller_spec.rb b/spec/controllers/projects/blame_controller_spec.rb
index 88d4f4e9cd0..fe4c4863717 100644
--- a/spec/controllers/projects/blame_controller_spec.rb
+++ b/spec/controllers/projects/blame_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::BlameController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 4dcb7dc6c87..32cd7c6e70a 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -108,7 +108,7 @@ describe Projects::BlobController do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -230,12 +230,12 @@ describe Projects::BlobController do
end
end
- context 'as master' do
- let(:master) { create(:user) }
+ context 'as maintainer' do
+ let(:maintainer) { create(:user) }
before do
- project.add_master(master)
- sign_in(master)
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
get :edit, default_params
end
@@ -263,7 +263,7 @@ describe Projects::BlobController do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb
index 509f19ed030..096efc1c7b2 100644
--- a/spec/controllers/projects/boards_controller_spec.rb
+++ b/spec/controllers/projects/boards_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::BoardsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 4860ea5dcce..31471cde420 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::BranchesController do
let(:developer) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user)
allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz'])
diff --git a/spec/controllers/projects/clusters/applications_controller_spec.rb b/spec/controllers/projects/clusters/applications_controller_spec.rb
index 99fdff5f846..9e17e392d3d 100644
--- a/spec/controllers/projects/clusters/applications_controller_spec.rb
+++ b/spec/controllers/projects/clusters/applications_controller_spec.rb
@@ -17,7 +17,7 @@ describe Projects::Clusters::ApplicationsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -70,7 +70,7 @@ describe Projects::Clusters::ApplicationsController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb
deleted file mode 100644
index 271ba37aed4..00000000000
--- a/spec/controllers/projects/clusters/gcp_controller_spec.rb
+++ /dev/null
@@ -1,182 +0,0 @@
-require 'spec_helper'
-
-describe Projects::Clusters::GcpController do
- include AccessMatchersForController
- include GoogleApi::CloudPlatformHelpers
-
- set(:project) { create(:project) }
-
- describe 'GET login' do
- describe 'functionality' do
- let(:user) { create(:user) }
-
- before do
- project.add_master(user)
- sign_in(user)
- end
-
- context 'when omniauth has been configured' do
- let(:key) { 'secret-key' }
- let(:session_key_for_redirect_uri) do
- GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key)
- end
-
- before do
- allow(SecureRandom).to receive(:hex).and_return(key)
- end
-
- it 'has authorize_url' do
- go
-
- expect(assigns(:authorize_url)).to include(key)
- expect(session[session_key_for_redirect_uri]).to eq(gcp_new_project_clusters_path(project))
- end
- end
-
- context 'when omniauth has not configured' do
- before do
- stub_omniauth_setting(providers: [])
- end
-
- it 'does not have authorize_url' do
- go
-
- expect(assigns(:authorize_url)).to be_nil
- end
- end
- end
-
- describe 'security' do
- it { expect { go }.to be_allowed_for(:admin) }
- it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
- it { expect { go }.to be_denied_for(:developer).of(project) }
- it { expect { go }.to be_denied_for(:reporter).of(project) }
- it { expect { go }.to be_denied_for(:guest).of(project) }
- it { expect { go }.to be_denied_for(:user) }
- it { expect { go }.to be_denied_for(:external) }
- end
-
- def go
- get :login, namespace_id: project.namespace, project_id: project
- end
- end
-
- describe 'GET new' do
- describe 'functionality' do
- let(:user) { create(:user) }
-
- before do
- project.add_master(user)
- sign_in(user)
- end
-
- context 'when access token is valid' do
- before do
- stub_google_api_validate_token
- end
-
- it 'has new object' do
- go
-
- expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster)
- end
- end
-
- context 'when access token is expired' do
- before do
- stub_google_api_expired_token
- end
-
- it { expect(go).to redirect_to(gcp_login_project_clusters_path(project)) }
- end
-
- context 'when access token is not stored in session' do
- it { expect(go).to redirect_to(gcp_login_project_clusters_path(project)) }
- end
- end
-
- describe 'security' do
- it { expect { go }.to be_allowed_for(:admin) }
- it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
- it { expect { go }.to be_denied_for(:developer).of(project) }
- it { expect { go }.to be_denied_for(:reporter).of(project) }
- it { expect { go }.to be_denied_for(:guest).of(project) }
- it { expect { go }.to be_denied_for(:user) }
- it { expect { go }.to be_denied_for(:external) }
- end
-
- def go
- get :new, namespace_id: project.namespace, project_id: project
- end
- end
-
- describe 'POST create' do
- let(:params) do
- {
- cluster: {
- name: 'new-cluster',
- provider_gcp_attributes: {
- gcp_project_id: '111'
- }
- }
- }
- end
-
- describe 'functionality' do
- let(:user) { create(:user) }
-
- before do
- project.add_master(user)
- sign_in(user)
- end
-
- context 'when access token is valid' do
- before do
- stub_google_api_validate_token
- end
-
- it 'creates a new cluster' do
- expect(ClusterProvisionWorker).to receive(:perform_async)
- expect { go }.to change { Clusters::Cluster.count }
- .and change { Clusters::Providers::Gcp.count }
- expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
- expect(project.clusters.first).to be_gcp
- expect(project.clusters.first).to be_kubernetes
- end
- end
-
- context 'when access token is expired' do
- before do
- stub_google_api_expired_token
- end
-
- it 'redirects to login page' do
- expect(go).to redirect_to(gcp_login_project_clusters_path(project))
- end
- end
-
- context 'when access token is not stored in session' do
- it 'redirects to login page' do
- expect(go).to redirect_to(gcp_login_project_clusters_path(project))
- end
- end
- end
-
- describe 'security' do
- it { expect { go }.to be_allowed_for(:admin) }
- it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
- it { expect { go }.to be_denied_for(:developer).of(project) }
- it { expect { go }.to be_denied_for(:reporter).of(project) }
- it { expect { go }.to be_denied_for(:guest).of(project) }
- it { expect { go }.to be_denied_for(:user) }
- it { expect { go }.to be_denied_for(:external) }
- end
-
- def go
- post :create, params.merge(namespace_id: project.namespace, project_id: project)
- end
- end
-end
diff --git a/spec/controllers/projects/clusters/user_controller_spec.rb b/spec/controllers/projects/clusters/user_controller_spec.rb
deleted file mode 100644
index 913976d187f..00000000000
--- a/spec/controllers/projects/clusters/user_controller_spec.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-require 'spec_helper'
-
-describe Projects::Clusters::UserController do
- include AccessMatchersForController
-
- set(:project) { create(:project) }
-
- describe 'GET new' do
- describe 'functionality' do
- let(:user) { create(:user) }
-
- before do
- project.add_master(user)
- sign_in(user)
- end
-
- it 'has new object' do
- go
-
- expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster)
- end
- end
-
- describe 'security' do
- it { expect { go }.to be_allowed_for(:admin) }
- it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
- it { expect { go }.to be_denied_for(:developer).of(project) }
- it { expect { go }.to be_denied_for(:reporter).of(project) }
- it { expect { go }.to be_denied_for(:guest).of(project) }
- it { expect { go }.to be_denied_for(:user) }
- it { expect { go }.to be_denied_for(:external) }
- end
-
- def go
- get :new, namespace_id: project.namespace, project_id: project
- end
- end
-
- describe 'POST create' do
- let(:params) do
- {
- cluster: {
- name: 'new-cluster',
- platform_kubernetes_attributes: {
- api_url: 'http://my-url',
- token: 'test',
- namespace: 'aaa'
- }
- }
- }
- end
-
- describe 'functionality' do
- let(:user) { create(:user) }
-
- before do
- project.add_master(user)
- sign_in(user)
- end
-
- context 'when creates a cluster' do
- it 'creates a new cluster' do
- expect(ClusterProvisionWorker).to receive(:perform_async)
- expect { go }.to change { Clusters::Cluster.count }
- .and change { Clusters::Platforms::Kubernetes.count }
- expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
- expect(project.clusters.first).to be_user
- expect(project.clusters.first).to be_kubernetes
- end
- end
- end
-
- describe 'security' do
- it { expect { go }.to be_allowed_for(:admin) }
- it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
- it { expect { go }.to be_denied_for(:developer).of(project) }
- it { expect { go }.to be_denied_for(:reporter).of(project) }
- it { expect { go }.to be_denied_for(:guest).of(project) }
- it { expect { go }.to be_denied_for(:user) }
- it { expect { go }.to be_denied_for(:external) }
- end
-
- def go
- post :create, params.merge(namespace_id: project.namespace, project_id: project)
- end
- end
-end
diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb
index 380e50c8cac..42917d0d505 100644
--- a/spec/controllers/projects/clusters_controller_spec.rb
+++ b/spec/controllers/projects/clusters_controller_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe Projects::ClustersController do
include AccessMatchersForController
+ include GoogleApi::CloudPlatformHelpers
set(:project) { create(:project) }
@@ -10,7 +11,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -60,7 +61,7 @@ describe Projects::ClustersController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -73,6 +74,231 @@ describe Projects::ClustersController do
end
end
+ describe 'GET new' do
+ describe 'functionality for new cluster' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context 'when omniauth has been configured' do
+ let(:key) { 'secret-key' }
+ let(:session_key_for_redirect_uri) do
+ GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key)
+ end
+
+ before do
+ allow(SecureRandom).to receive(:hex).and_return(key)
+ end
+
+ it 'has authorize_url' do
+ go
+
+ expect(assigns(:authorize_url)).to include(key)
+ expect(session[session_key_for_redirect_uri]).to eq(new_project_cluster_path(project))
+ end
+ end
+
+ context 'when omniauth has not configured' do
+ before do
+ stub_omniauth_setting(providers: [])
+ end
+
+ it 'does not have authorize_url' do
+ go
+
+ expect(assigns(:authorize_url)).to be_nil
+ end
+ end
+
+ context 'when access token is valid' do
+ before do
+ stub_google_api_validate_token
+ end
+
+ it 'has new object' do
+ go
+
+ expect(assigns(:gcp_cluster)).to be_an_instance_of(Clusters::Cluster)
+ end
+ end
+
+ context 'when access token is expired' do
+ before do
+ stub_google_api_expired_token
+ end
+
+ it { expect(@valid_gcp_token).to be_falsey }
+ end
+
+ context 'when access token is not stored in session' do
+ it { expect(@valid_gcp_token).to be_falsey }
+ end
+ end
+
+ describe 'functionality for existing cluster' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ it 'has new object' do
+ go
+
+ expect(assigns(:user_cluster)).to be_an_instance_of(Clusters::Cluster)
+ end
+ end
+
+ describe 'security' do
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
+ it { expect { go }.to be_denied_for(:developer).of(project) }
+ it { expect { go }.to be_denied_for(:reporter).of(project) }
+ it { expect { go }.to be_denied_for(:guest).of(project) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+
+ def go
+ get :new, namespace_id: project.namespace, project_id: project
+ end
+ end
+
+ describe 'POST create for new cluster' do
+ let(:params) do
+ {
+ cluster: {
+ name: 'new-cluster',
+ provider_gcp_attributes: {
+ gcp_project_id: 'gcp-project-12345'
+ }
+ }
+ }
+ end
+
+ describe 'functionality' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context 'when access token is valid' do
+ before do
+ stub_google_api_validate_token
+ end
+
+ it 'creates a new cluster' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Providers::Gcp.count }
+ expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
+ expect(project.clusters.first).to be_gcp
+ expect(project.clusters.first).to be_kubernetes
+ end
+ end
+
+ context 'when access token is expired' do
+ before do
+ stub_google_api_expired_token
+ end
+
+ it { expect(@valid_gcp_token).to be_falsey }
+ end
+
+ context 'when access token is not stored in session' do
+ it { expect(@valid_gcp_token).to be_falsey }
+ end
+ end
+
+ describe 'security' do
+ before do
+ allow_any_instance_of(described_class)
+ .to receive(:token_in_session).and_return('token')
+ allow_any_instance_of(described_class)
+ .to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
+ allow_any_instance_of(GoogleApi::CloudPlatform::Client)
+ .to receive(:projects_zones_clusters_create) do
+ OpenStruct.new(
+ self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
+ status: 'RUNNING'
+ )
+ end
+
+ allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
+ end
+
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
+ it { expect { go }.to be_denied_for(:developer).of(project) }
+ it { expect { go }.to be_denied_for(:reporter).of(project) }
+ it { expect { go }.to be_denied_for(:guest).of(project) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+
+ def go
+ post :create_gcp, params.merge(namespace_id: project.namespace, project_id: project)
+ end
+ end
+
+ describe 'POST create for existing cluster' do
+ let(:params) do
+ {
+ cluster: {
+ name: 'new-cluster',
+ platform_kubernetes_attributes: {
+ api_url: 'http://my-url',
+ token: 'test',
+ namespace: 'aaa'
+ }
+ }
+ }
+ end
+
+ describe 'functionality' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context 'when creates a cluster' do
+ it 'creates a new cluster' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Platforms::Kubernetes.count }
+ expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
+ expect(project.clusters.first).to be_user
+ expect(project.clusters.first).to be_kubernetes
+ end
+ end
+ end
+
+ describe 'security' do
+ it { expect { go }.to be_allowed_for(:admin) }
+ it { expect { go }.to be_allowed_for(:owner).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
+ it { expect { go }.to be_denied_for(:developer).of(project) }
+ it { expect { go }.to be_denied_for(:reporter).of(project) }
+ it { expect { go }.to be_denied_for(:guest).of(project) }
+ it { expect { go }.to be_denied_for(:user) }
+ it { expect { go }.to be_denied_for(:external) }
+ end
+
+ def go
+ post :create_user, params.merge(namespace_id: project.namespace, project_id: project)
+ end
+ end
+
describe 'GET status' do
let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
@@ -80,7 +306,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -101,7 +327,7 @@ describe Projects::ClustersController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -124,7 +350,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -139,7 +365,7 @@ describe Projects::ClustersController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -160,7 +386,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -211,7 +437,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -299,7 +525,7 @@ describe Projects::ClustersController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -326,7 +552,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -379,7 +605,7 @@ describe Projects::ClustersController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 003fec8ac68..916a4be2567 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -9,7 +9,7 @@ describe Projects::CommitController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET show' do
diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb
index 55ed276f96b..a43bdd3ea80 100644
--- a/spec/controllers/projects/commits_controller_spec.rb
+++ b/spec/controllers/projects/commits_controller_spec.rb
@@ -6,7 +6,19 @@ describe Projects::CommitsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
+ end
+
+ describe "GET commits_root" do
+ context "no ref is provided" do
+ it 'should redirect to the default branch of the project' do
+ get(:commits_root,
+ namespace_id: project.namespace,
+ project_id: project)
+
+ expect(response).to redirect_to project_commits_path(project)
+ end
+ end
end
describe "GET show" do
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index b15cde4314e..8695aa826bb 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::CompareController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET index' do
diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb
index 5516c95d044..5c79269e8f1 100644
--- a/spec/controllers/projects/cycle_analytics_controller_spec.rb
+++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::CycleAnalyticsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'cycle analytics not set up flag' do
diff --git a/spec/controllers/projects/deploy_keys_controller_spec.rb b/spec/controllers/projects/deploy_keys_controller_spec.rb
index 97db69427e9..73bf169085f 100644
--- a/spec/controllers/projects/deploy_keys_controller_spec.rb
+++ b/spec/controllers/projects/deploy_keys_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::DeployKeysController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -19,7 +19,7 @@ describe Projects::DeployKeysController do
it 'redirects to blob' do
get :index, params
- expect(response).to redirect_to(namespace_project_settings_repository_path(params))
+ expect(response).to redirect_to(project_settings_repository_path(project, anchor: 'js-deploy-keys-settings'))
end
end
diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb
index 6c67dfde63a..d1c960e895d 100644
--- a/spec/controllers/projects/deployments_controller_spec.rb
+++ b/spec/controllers/projects/deployments_controller_spec.rb
@@ -8,7 +8,7 @@ describe Projects::DeploymentsController do
let(:environment) { create(:environment, name: 'production', project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index 47d4942acbd..b86029a4baf 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -9,7 +9,7 @@ describe Projects::EnvironmentsController do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -277,6 +277,25 @@ describe Projects::EnvironmentsController do
end
end
+ describe 'GET #metrics_redirect' do
+ let(:project) { create(:project) }
+
+ it 'redirects to environment if it exists' do
+ environment = create(:environment, name: 'production', project: project)
+
+ get :metrics_redirect, namespace_id: project.namespace, project_id: project
+
+ expect(response).to redirect_to(environment_metrics_path(environment))
+ end
+
+ it 'redirects to empty page if no environment exists' do
+ get :metrics_redirect, namespace_id: project.namespace, project_id: project
+
+ expect(response).to be_ok
+ expect(response).to render_template 'empty'
+ end
+ end
+
describe 'GET #metrics' do
before do
allow(controller).to receive(:environment).and_return(environment)
diff --git a/spec/controllers/projects/find_file_controller_spec.rb b/spec/controllers/projects/find_file_controller_spec.rb
index 505fe82851a..66fe41108e2 100644
--- a/spec/controllers/projects/find_file_controller_spec.rb
+++ b/spec/controllers/projects/find_file_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::FindFileController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb
index e20623c0ac1..945b6142abf 100644
--- a/spec/controllers/projects/forks_controller_spec.rb
+++ b/spec/controllers/projects/forks_controller_spec.rb
@@ -31,7 +31,7 @@ describe Projects::ForksController do
context 'when fork is private' do
before do
- forked_project.update_attributes(visibility_level: Project::PRIVATE, group: group)
+ forked_project.update(visibility_level: Project::PRIVATE, group: group)
end
it 'is not be visible for non logged in users' do
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index c3605555fe7..da78592a6f6 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::GraphsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET languages' do
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
index 72f6af112b3..879aff26deb 100644
--- a/spec/controllers/projects/group_links_controller_spec.rb
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::GroupLinksController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -23,7 +23,7 @@ describe Projects::GroupLinksController do
context 'when project is not allowed to be shared with a group' do
before do
- group.update_attributes(share_with_group_lock: false)
+ group.update(share_with_group_lock: false)
end
include_context 'link project to group'
diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb
index 2d473d5bf52..0f3033b0933 100644
--- a/spec/controllers/projects/hooks_controller_spec.rb
+++ b/spec/controllers/projects/hooks_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::HooksController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb
index 812833cc86b..adf3c78ae51 100644
--- a/spec/controllers/projects/imports_controller_spec.rb
+++ b/spec/controllers/projects/imports_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::ImportsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET #show' do
@@ -29,7 +29,7 @@ describe Projects::ImportsController do
context 'when import is in progress' do
before do
- project.update_attributes(import_status: :started)
+ project.update(import_status: :started)
end
it 'renders template' do
@@ -47,7 +47,7 @@ describe Projects::ImportsController do
context 'when import failed' do
before do
- project.update_attributes(import_status: :failed)
+ project.update(import_status: :failed)
end
it 'redirects to new_namespace_project_import_path' do
@@ -59,7 +59,7 @@ describe Projects::ImportsController do
context 'when import finished' do
before do
- project.update_attributes(import_status: :finished)
+ project.update(import_status: :finished)
end
context 'when project is a fork' do
@@ -108,7 +108,7 @@ describe Projects::ImportsController do
context 'when import never happened' do
before do
- project.update_attributes(import_status: :none)
+ project.update(import_status: :none)
end
it 'redirects to namespace_project_path' do
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 3a41f0fc07a..5b347b1109a 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -695,7 +695,7 @@ describe Projects::IssuesController do
let(:project) { merge_request.source_project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
@@ -869,7 +869,7 @@ describe Projects::IssuesController do
def post_spam
admin = create(:admin)
create(:user_agent_detail, subject: issue)
- project.add_master(admin)
+ project.add_maintainer(admin)
sign_in(admin)
post :mark_as_spam, {
namespace_id: project.namespace,
@@ -993,6 +993,29 @@ describe Projects::IssuesController do
expect(json_response.first.keys).to match_array(%w[id reply_id expanded notes diff_discussion discussion_path individual_note resolvable resolved resolved_at resolved_by resolved_by_push commit_id for_commit project_id])
end
+ it 'renders the author status html if there is a status' do
+ create(:user_status, user: discussion.author)
+
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issue.iid
+
+ note_json = json_response.first['notes'].first
+
+ expect(note_json['author']['status_tooltip_html']).to be_present
+ end
+
+ it 'does not cause an extra query for the status' do
+ control = ActiveRecord::QueryRecorder.new do
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issue.iid
+ end
+
+ create(:user_status, user: discussion.author)
+ second_discussion = create(:discussion_note_on_issue, noteable: issue, project: issue.project, author: create(:user))
+ create(:user_status, user: second_discussion.author)
+
+ expect { get :discussions, namespace_id: project.namespace, project_id: project, id: issue.iid }
+ .not_to exceed_query_limit(control)
+ end
+
context 'with cross-reference system note', :request_store do
let(:new_issue) { create(:issue) }
let(:cross_reference) { "mentioned in #{new_issue.to_reference(issue.project)}" }
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 06c8a432561..1aca44c6e74 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -102,6 +102,8 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
describe 'GET show' do
let!(:job) { create(:ci_build, :failed, pipeline: pipeline) }
+ let!(:second_job) { create(:ci_build, :failed, pipeline: pipeline) }
+ let!(:third_job) { create(:ci_build, :failed) }
context 'when requesting HTML' do
context 'when job exists' do
@@ -113,6 +115,13 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:build).id).to eq(job.id)
end
+
+ it 'has the correct build collection' do
+ builds = assigns(:builds).map(&:id)
+
+ expect(builds).to include(job.id, second_job.id)
+ expect(builds).not_to include(third_job.id)
+ end
end
context 'when job does not exist' do
@@ -207,17 +216,19 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'when trace artifact is in ObjectStorage' do
+ let(:url) { 'http://object-storage/trace' }
+ let(:file_path) { expand_fixture_path('trace/sample_trace') }
let!(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) }
before do
allow_any_instance_of(JobArtifactUploader).to receive(:file_storage?) { false }
- allow_any_instance_of(JobArtifactUploader).to receive(:url) { remote_trace_url }
- allow_any_instance_of(JobArtifactUploader).to receive(:size) { remote_trace_size }
+ allow_any_instance_of(JobArtifactUploader).to receive(:url) { url }
+ allow_any_instance_of(JobArtifactUploader).to receive(:size) { File.size(file_path) }
end
context 'when there are no network issues' do
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
get_trace
end
@@ -232,11 +243,11 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'when there is a network issue' do
before do
- stub_remote_trace_500
+ stub_remote_url_500(url)
end
it 'returns a trace' do
- expect { get_trace }.to raise_error(Gitlab::Ci::Trace::HttpIO::FailedToGetChunkError)
+ expect { get_trace }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
end
end
end
@@ -420,7 +431,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
describe 'POST erase' do
- let(:role) { :master }
+ let(:role) { :maintainer }
before do
project.add_role(user, role)
@@ -553,4 +564,105 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
end
end
+
+ describe 'GET #terminal' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ context 'when job exists' do
+ context 'and it has a terminal' do
+ let!(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }
+
+ it 'has a job' do
+ get_terminal(id: job.id)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(assigns(:build).id).to eq(job.id)
+ end
+ end
+
+ context 'and does not have a terminal' do
+ let!(:job) { create(:ci_build, :running, pipeline: pipeline) }
+
+ it 'returns not_found' do
+ get_terminal(id: job.id)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'when job does not exist' do
+ it 'renders not_found' do
+ get_terminal(id: 1234)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ def get_terminal(**extra_params)
+ params = {
+ namespace_id: project.namespace.to_param,
+ project_id: project
+ }
+
+ get :terminal, params.merge(extra_params)
+ end
+ end
+
+ describe 'GET #terminal_websocket_authorize' do
+ let!(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ context 'with valid workhorse signature' do
+ before do
+ allow(Gitlab::Workhorse).to receive(:verify_api_request!).and_return(nil)
+ end
+
+ context 'and valid id' do
+ it 'returns the terminal for the job' do
+ expect(Gitlab::Workhorse)
+ .to receive(:terminal_websocket)
+ .and_return(workhorse: :response)
+
+ get_terminal_websocket(id: job.id)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.headers["Content-Type"]).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(response.body).to eq('{"workhorse":"response"}')
+ end
+ end
+
+ context 'and invalid id' do
+ it 'returns 404' do
+ get_terminal_websocket(id: 1234)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
+ context 'with invalid workhorse signature' do
+ it 'aborts with an exception' do
+ allow(Gitlab::Workhorse).to receive(:verify_api_request!).and_raise(JWT::DecodeError)
+
+ expect { get_terminal_websocket(id: job.id) }.to raise_error(JWT::DecodeError)
+ end
+ end
+
+ def get_terminal_websocket(**extra_params)
+ params = {
+ namespace_id: project.namespace.to_param,
+ project_id: project
+ }
+
+ get :terminal_websocket_authorize, params.merge(extra_params)
+ end
+ end
end
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
index 452d7e23983..e03d23bcdf6 100644
--- a/spec/controllers/projects/labels_controller_spec.rb
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::LabelsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -143,6 +143,14 @@ describe Projects::LabelsController do
expect(GroupLabel.find_by(title: promoted_label_name)).not_to be_nil
end
+ it 'renders label name without parsing it as HTML' do
+ label_1.update!(name: 'CCC&lt;img src=x onerror=alert(document.domain)&gt;')
+
+ post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param
+
+ expect(flash[:notice]).to eq("CCC&lt;img src=x onerror=alert(document.domain)&gt; promoted to <a href=\"#{group_labels_path(project.group)}\"><u>group label</u></a>.")
+ end
+
context 'service raising InvalidRecord' do
before do
expect_any_instance_of(Labels::PromoteService).to receive(:execute) do |label|
diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb
index c5ac0be27bb..c2a334a849c 100644
--- a/spec/controllers/projects/mattermosts_controller_spec.rb
+++ b/spec/controllers/projects/mattermosts_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::MattermostsController do
let!(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
index 00d76f3c39a..d8995f98575 100644
--- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
@@ -16,7 +16,7 @@ describe Projects::MergeRequests::CreationsController do
end
before do
- fork_project.add_master(user)
+ fork_project.add_maintainer(user)
Projects::ForkService.new(project, user).execute(fork_project)
sign_in(user)
end
@@ -94,7 +94,7 @@ describe Projects::MergeRequests::CreationsController do
let(:other_project) { create(:project, :repository) }
before do
- other_project.add_master(user)
+ other_project.add_maintainer(user)
end
context 'when the path exists in the diff' do
diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
index ec82b35f227..9dc06436c72 100644
--- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
@@ -140,7 +140,7 @@ describe Projects::MergeRequests::DiffsController do
let(:other_project) { create(:project) }
before do
- other_project.add_master(user)
+ other_project.add_maintainer(user)
diff_for_path(old_path: existing_path, new_path: existing_path, project_id: other_project)
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 7f5f0b76c51..d9bb3981539 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -53,6 +53,23 @@ describe Projects::MergeRequestsController do
it_behaves_like "loads labels", :show
describe 'as html' do
+ context 'when diff files were cleaned' do
+ render_views
+
+ it 'renders page when diff size is not persisted and diff_refs does not exist' do
+ diff = merge_request.merge_request_diff
+
+ diff.clean!
+ diff.update!(real_size: nil,
+ start_commit_sha: nil,
+ base_commit_sha: nil)
+
+ go(format: :html)
+
+ expect(response).to be_success
+ end
+ end
+
it "renders merge request page" do
go(format: :html)
@@ -315,7 +332,7 @@ describe Projects::MergeRequestsController do
context 'when the merge request is not mergeable' do
before do
- merge_request.update_attributes(title: "WIP: #{merge_request.title}")
+ merge_request.update(title: "WIP: #{merge_request.title}")
post :merge, base_params
end
@@ -563,6 +580,88 @@ describe Projects::MergeRequestsController do
end
end
+ describe 'GET test_reports' do
+ subject do
+ get :test_reports,
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: merge_request.iid,
+ format: :json
+ end
+
+ before do
+ allow_any_instance_of(MergeRequest)
+ .to receive(:compare_test_reports).and_return(comparison_status)
+ end
+
+ context 'when comparison is being processed' do
+ let(:comparison_status) { { 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 comparison is done' do
+ let(:comparison_status) { { status: :parsed, data: { summary: 1 } } }
+
+ it 'does not send polling interval' do
+ expect(Gitlab::PollingInterval).not_to receive(:set_header)
+
+ subject
+ end
+
+ it 'returns 200 HTTP status' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq({ 'summary' => 1 })
+ end
+ end
+
+ context 'when user created corrupted test reports' do
+ let(:comparison_status) { { status: :error, status_reason: 'Failed to parse test reports' } }
+
+ it 'does not send polling interval' do
+ expect(Gitlab::PollingInterval).not_to receive(:set_header)
+
+ subject
+ end
+
+ it 'returns 400 HTTP status' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response).to eq({ 'status_reason' => 'Failed to parse test reports' })
+ end
+ end
+
+ context 'when something went wrong on our system' do
+ let(:comparison_status) { {} }
+
+ it 'does not send polling interval' do
+ expect(Gitlab::PollingInterval).not_to receive(:set_header)
+
+ subject
+ end
+
+ it 'returns 500 HTTP status' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:internal_server_error)
+ expect(json_response).to eq({ 'status_reason' => 'Unknown error' })
+ 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 02b30f9bc6d..3190f1ce9d4 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -11,7 +11,7 @@ describe Projects::MilestonesController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
@@ -42,16 +42,45 @@ describe Projects::MilestonesController do
describe "#index" do
context "as html" do
- before do
- get :index, namespace_id: project.namespace.id, project_id: project.id
+ def render_index(project:, page:)
+ get :index, namespace_id: project.namespace.id,
+ project_id: project.id,
+ page: page
end
it "queries only projects milestones" do
+ render_index project: project, page: 1
+
milestones = assigns(:milestones)
expect(milestones.count).to eq(1)
expect(milestones.where(project_id: nil)).to be_empty
end
+
+ it 'renders paginated milestones without missing or duplicates' do
+ allow(Milestone).to receive(:default_per_page).and_return(2)
+ create_list(:milestone, 5, project: project)
+
+ render_index project: project, page: 1
+ page_1_milestones = assigns(:milestones)
+ expect(page_1_milestones.size).to eq(2)
+
+ render_index project: project, page: 2
+ page_2_milestones = assigns(:milestones)
+ expect(page_2_milestones.size).to eq(2)
+
+ render_index project: project, page: 3
+ page_3_milestones = assigns(:milestones)
+ expect(page_3_milestones.size).to eq(2)
+
+ rendered_milestone_ids =
+ page_1_milestones.pluck(:id) +
+ page_2_milestones.pluck(:id) +
+ page_3_milestones.pluck(:id)
+
+ expect(rendered_milestone_ids)
+ .to match_array(project.milestones.pluck(:id))
+ end
end
context "as json" do
@@ -124,9 +153,17 @@ describe Projects::MilestonesController do
it 'shows group milestone' do
post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid
- expect(flash[:notice]).to eq("#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\">group milestone</a>.")
+ expect(flash[:notice]).to eq("#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\"><u>group milestone</u></a>.")
expect(response).to redirect_to(project_milestones_path(project))
end
+
+ it 'renders milestone name without parsing it as HTML' do
+ milestone.update!(name: 'CCC&lt;img src=x onerror=alert(document.domain)&gt;')
+
+ post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid
+
+ expect(flash[:notice]).to eq("CCC promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\"><u>group milestone</u></a>.")
+ end
end
context 'promotion fails' do
diff --git a/spec/controllers/projects/mirrors_controller_spec.rb b/spec/controllers/projects/mirrors_controller_spec.rb
index 5d64f362252..6114eef7003 100644
--- a/spec/controllers/projects/mirrors_controller_spec.rb
+++ b/spec/controllers/projects/mirrors_controller_spec.rb
@@ -36,7 +36,7 @@ describe Projects::MirrorsController do
it 'processes a successful update' do
do_put(project, remote_mirrors_attributes: remote_mirror_attributes)
- expect(response).to redirect_to(project_settings_repository_path(project))
+ expect(response).to redirect_to(project_settings_repository_path(project, anchor: 'js-push-remote-settings'))
expect(flash[:notice]).to match(/successfully updated/)
end
@@ -53,7 +53,7 @@ describe Projects::MirrorsController do
it 'processes an unsuccessful update' do
do_put(project, remote_mirrors_attributes: remote_mirror_attributes)
- expect(response).to redirect_to(project_settings_repository_path(project))
+ expect(response).to redirect_to(project_settings_repository_path(project, anchor: 'js-push-remote-settings'))
expect(flash[:alert]).to match(/Only allowed protocols are/)
end
diff --git a/spec/controllers/projects/pages_controller_spec.rb b/spec/controllers/projects/pages_controller_spec.rb
index 8d2fa6a1740..927b6e0c473 100644
--- a/spec/controllers/projects/pages_controller_spec.rb
+++ b/spec/controllers/projects/pages_controller_spec.rb
@@ -14,7 +14,7 @@ describe Projects::PagesController do
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET show' do
diff --git a/spec/controllers/projects/pages_domains_controller_spec.rb b/spec/controllers/projects/pages_domains_controller_spec.rb
index d4058a5c515..75871eab1ab 100644
--- a/spec/controllers/projects/pages_domains_controller_spec.rb
+++ b/spec/controllers/projects/pages_domains_controller_spec.rb
@@ -19,7 +19,7 @@ describe Projects::PagesDomainsController do
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET show' do
diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
index 4cdaa54e0bc..7179423dde2 100644
--- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb
+++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
@@ -121,7 +121,7 @@ describe Projects::PipelineSchedulesController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_allowed_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -274,7 +274,7 @@ describe Projects::PipelineSchedulesController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_allowed_for(:developer).of(project).own(pipeline_schedule) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -292,19 +292,19 @@ describe Projects::PipelineSchedulesController do
it { expect { go }.to be_allowed_for(developer_1) }
it { expect { go }.to be_denied_for(:developer).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
end
- context 'when a master created a pipeline schedule' do
- let(:master_1) { create(:user) }
- let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: master_1) }
+ context 'when a maintainer created a pipeline schedule' do
+ let(:maintainer_1) { create(:user) }
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: maintainer_1) }
before do
- project.add_master(master_1)
+ project.add_maintainer(maintainer_1)
end
- it { expect { go }.to be_allowed_for(master_1) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(maintainer_1) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
end
end
@@ -331,7 +331,7 @@ describe Projects::PipelineSchedulesController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -346,7 +346,7 @@ describe Projects::PipelineSchedulesController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_allowed_for(:developer).of(project).own(pipeline_schedule) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -364,7 +364,7 @@ describe Projects::PipelineSchedulesController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_allowed_for(:developer).of(project).own(pipeline_schedule) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -453,9 +453,9 @@ describe Projects::PipelineSchedulesController do
end
end
- context 'when a master makes the request' do
+ context 'when a maintainer makes the request' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 9618a8417ec..d89716b1b50 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -4,7 +4,7 @@ describe Projects::PipelinesController do
include ApiHelpers
set(:user) { create(:user) }
- set(:project) { create(:project, :public, :repository) }
+ let(:project) { create(:project, :public, :repository) }
let(:feature) { ProjectFeature::DISABLED }
before do
@@ -55,10 +55,8 @@ describe Projects::PipelinesController do
stub_feature_flags(ci_pipeline_persisted_stages: false)
end
- it 'returns JSON with serialized pipelines', :request_store do
- queries = ActiveRecord::QueryRecorder.new do
- get_pipelines_index_json
- end
+ it 'returns JSON with serialized pipelines' do
+ get_pipelines_index_json
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('pipeline')
@@ -73,8 +71,14 @@ describe Projects::PipelinesController do
json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages|
expect(stages.count).to eq 3
end
+ end
- expect(queries.count).to be_within(3).of(30)
+ it 'does not execute N+1 queries' do
+ queries = ActiveRecord::QueryRecorder.new do
+ get_pipelines_index_json
+ end
+
+ expect(queries.count).to be <= 36
end
end
@@ -91,6 +95,24 @@ describe Projects::PipelinesController do
end
end
+ context 'when the project is private' do
+ let(:project) { create(:project, :private, :repository) }
+
+ it 'returns `not_found` when the user does not have access' do
+ sign_in(create(:user))
+
+ get_pipelines_index_json
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns the pipelines when the user has access' do
+ get_pipelines_index_json
+
+ expect(json_response['pipelines'].size).to eq(5)
+ end
+ end
+
def get_pipelines_index_json
get :index, namespace_id: project.namespace,
project_id: project,
diff --git a/spec/controllers/projects/pipelines_settings_controller_spec.rb b/spec/controllers/projects/pipelines_settings_controller_spec.rb
index 694896b6bcf..b1ba9f74e38 100644
--- a/spec/controllers/projects/pipelines_settings_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_settings_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::PipelinesSettingsController do
let(:project) { project_auto_devops.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index d84b31ad978..519af10d78c 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -37,7 +37,7 @@ describe Projects::ProjectMembersController do
context 'when user has enough rights' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'adds user to members' do
@@ -70,7 +70,7 @@ describe Projects::ProjectMembersController do
let(:requester) { create(:project_member, :access_request, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -121,7 +121,7 @@ describe Projects::ProjectMembersController do
context 'when user has enough rights' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it '[HTML] removes user from members' do
@@ -181,7 +181,7 @@ describe Projects::ProjectMembersController do
let(:project) { create(:project, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'cannot remove themselves from the project' do
@@ -263,7 +263,7 @@ describe Projects::ProjectMembersController do
context 'when user has enough rights' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'adds user to members' do
@@ -285,7 +285,7 @@ describe Projects::ProjectMembersController do
let(:member) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
another_project.add_guest(member)
sign_in(user)
end
@@ -332,7 +332,7 @@ describe Projects::ProjectMembersController do
context 'when creating owner' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -346,9 +346,9 @@ describe Projects::ProjectMembersController do
end
end
- context 'when create master' do
+ context 'when create maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -356,7 +356,7 @@ describe Projects::ProjectMembersController do
expect do
post :create, user_ids: stranger.id,
namespace_id: project.namespace,
- access_level: Member::MASTER,
+ access_level: Member::MAINTAINER,
project_id: project
end.to change { project.members.count }.by(1)
end
diff --git a/spec/controllers/projects/prometheus/metrics_controller_spec.rb b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
index 871dcf5c796..5c56a712245 100644
--- a/spec/controllers/projects/prometheus/metrics_controller_spec.rb
+++ b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::Prometheus::MetricsController do
let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/protected_branches_controller_spec.rb b/spec/controllers/projects/protected_branches_controller_spec.rb
index 096e29bc39f..ac812707e74 100644
--- a/spec/controllers/projects/protected_branches_controller_spec.rb
+++ b/spec/controllers/projects/protected_branches_controller_spec.rb
@@ -8,7 +8,7 @@ describe Projects::ProtectedBranchesController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "GET #index" do
@@ -20,10 +20,10 @@ describe Projects::ProtectedBranchesController do
end
describe "POST #create" do
- let(:master_access_level) { [{ access_level: Gitlab::Access::MASTER }] }
+ let(:maintainer_access_level) { [{ access_level: Gitlab::Access::MAINTAINER }] }
let(:access_level_params) do
- { merge_access_levels_attributes: master_access_level,
- push_access_levels_attributes: master_access_level }
+ { merge_access_levels_attributes: maintainer_access_level,
+ push_access_levels_attributes: maintainer_access_level }
end
let(:create_params) { attributes_for(:protected_branch).merge(access_level_params) }
diff --git a/spec/controllers/projects/protected_tags_controller_spec.rb b/spec/controllers/projects/protected_tags_controller_spec.rb
index b6de90039f3..20440c5a5d5 100644
--- a/spec/controllers/projects/protected_tags_controller_spec.rb
+++ b/spec/controllers/projects/protected_tags_controller_spec.rb
@@ -15,7 +15,7 @@ describe Projects::ProtectedTagsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/runners_controller_spec.rb b/spec/controllers/projects/runners_controller_spec.rb
index 2082dd2cff0..b1e0b496ede 100644
--- a/spec/controllers/projects/runners_controller_spec.rb
+++ b/spec/controllers/projects/runners_controller_spec.rb
@@ -15,7 +15,7 @@ describe Projects::RunnersController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#update' do
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb
index 61f35cf325b..45cea8c1351 100644
--- a/spec/controllers/projects/services_controller_spec.rb
+++ b/spec/controllers/projects/services_controller_spec.rb
@@ -9,7 +9,7 @@ describe Projects::ServicesController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#test' do
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index d53fe9bf734..1f14a0cc381 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::Settings::CiCdController do
let(:project) { project_auto_devops.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -27,7 +27,7 @@ describe Projects::Settings::CiCdController do
let!(:shared_runner) { create(:ci_runner, :instance) }
it 'sets assignable project runners only' do
- group.add_master(user)
+ group.add_maintainer(user)
get :show, namespace_id: project.namespace, project_id: project
@@ -40,7 +40,7 @@ describe Projects::Settings::CiCdController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(true)
end
diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb
index 77df9a6f567..a2484c04c7a 100644
--- a/spec/controllers/projects/settings/integrations_controller_spec.rb
+++ b/spec/controllers/projects/settings/integrations_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::Settings::IntegrationsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb
index 3a4014b7768..9cee40b7553 100644
--- a/spec/controllers/projects/settings/repository_controller_spec.rb
+++ b/spec/controllers/projects/settings/repository_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::Settings::RepositoryController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index e7c0b484ede..9c383bd7628 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -6,8 +6,8 @@ describe Projects::SnippetsController do
let(:user2) { create(:user) }
before do
- project.add_master(user)
- project.add_master(user2)
+ project.add_maintainer(user)
+ project.add_maintainer(user2)
end
describe 'GET #index' do
@@ -291,7 +291,7 @@ describe Projects::SnippetsController do
def mark_as_spam
admin = create(:admin)
create(:user_agent_detail, subject: snippet)
- project.add_master(admin)
+ project.add_maintainer(admin)
sign_in(admin)
post :mark_as_spam,
diff --git a/spec/controllers/projects/templates_controller_spec.rb b/spec/controllers/projects/templates_controller_spec.rb
index 8fcfa3c9ecd..d7f07aa2b01 100644
--- a/spec/controllers/projects/templates_controller_spec.rb
+++ b/spec/controllers/projects/templates_controller_spec.rb
@@ -13,7 +13,7 @@ describe Projects::TemplatesController do
end
before do
- project.add_user(user, Gitlab::Access::MASTER)
+ project.add_user(user, Gitlab::Access::MAINTAINER)
project.repository.create_file(user, file_path_1, 'something valid',
message: 'test 3', branch_name: 'master')
end
diff --git a/spec/controllers/projects/todos_controller_spec.rb b/spec/controllers/projects/todos_controller_spec.rb
index 1ce7e84bef9..58f2817c7cc 100644
--- a/spec/controllers/projects/todos_controller_spec.rb
+++ b/spec/controllers/projects/todos_controller_spec.rb
@@ -5,10 +5,29 @@ describe Projects::TodosController do
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:parent) { project }
+
+ shared_examples 'project todos actions' do
+ it_behaves_like 'todos actions'
+
+ context 'when not authorized for resource' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ sign_in(user)
+ end
+
+ it "doesn't create todo" do
+ expect { post_create }.not_to change { user.todos.count }
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
context 'Issues' do
describe 'POST create' do
- def go
+ def post_create
post :create,
namespace_id: project.namespace,
project_id: project,
@@ -17,66 +36,13 @@ describe Projects::TodosController do
format: 'html'
end
- context 'when authorized' do
- before do
- sign_in(user)
- project.add_developer(user)
- end
-
- it 'creates todo for issue' do
- expect do
- go
- end.to change { user.todos.count }.by(1)
-
- expect(response).to have_gitlab_http_status(200)
- end
-
- it 'returns todo path and pending count' do
- go
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['count']).to eq 1
- expect(json_response['delete_path']).to match(%r{/dashboard/todos/\d{1}})
- end
- end
-
- context 'when not authorized for project' do
- it 'does not create todo for issue that user has no access to' do
- sign_in(user)
- expect do
- go
- end.to change { user.todos.count }.by(0)
-
- expect(response).to have_gitlab_http_status(404)
- end
-
- it 'does not create todo for issue when user not logged in' do
- expect do
- go
- end.to change { user.todos.count }.by(0)
-
- expect(response).to have_gitlab_http_status(302)
- end
- end
-
- context 'when not authorized for issue' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
- sign_in(user)
- end
-
- it "doesn't create todo" do
- expect { go }.not_to change { user.todos.count }
- expect(response).to have_gitlab_http_status(404)
- end
- end
+ it_behaves_like 'project todos actions'
end
end
context 'Merge Requests' do
describe 'POST create' do
- def go
+ def post_create
post :create,
namespace_id: project.namespace,
project_id: project,
@@ -85,60 +51,7 @@ describe Projects::TodosController do
format: 'html'
end
- context 'when authorized' do
- before do
- sign_in(user)
- project.add_developer(user)
- end
-
- it 'creates todo for merge request' do
- expect do
- go
- end.to change { user.todos.count }.by(1)
-
- expect(response).to have_gitlab_http_status(200)
- end
-
- it 'returns todo path and pending count' do
- go
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['count']).to eq 1
- expect(json_response['delete_path']).to match(%r{/dashboard/todos/\d{1}})
- end
- end
-
- context 'when not authorized for project' do
- it 'does not create todo for merge request user has no access to' do
- sign_in(user)
- expect do
- go
- end.to change { user.todos.count }.by(0)
-
- expect(response).to have_gitlab_http_status(404)
- end
-
- it 'does not create todo for merge request user has no access to' do
- expect do
- go
- end.to change { user.todos.count }.by(0)
-
- expect(response).to have_gitlab_http_status(302)
- end
- end
-
- context 'when not authorized for merge_request' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
- sign_in(user)
- end
-
- it "doesn't create todo" do
- expect { go }.not_to change { user.todos.count }
- expect(response).to have_gitlab_http_status(404)
- end
- end
+ it_behaves_like 'project todos actions'
end
end
end
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index d3188f054cf..9982b49eebb 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::TreeController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb
index eca9baed9c9..325ee53aafb 100644
--- a/spec/controllers/projects/uploads_controller_spec.rb
+++ b/spec/controllers/projects/uploads_controller_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Projects::UploadsController do
+ include WorkhorseHelpers
+
let(:model) { create(:project, :public) }
let(:params) do
{ namespace_id: model.namespace.to_param, project_id: model }
@@ -15,4 +17,10 @@ describe Projects::UploadsController do
expect(response).to redirect_to(new_user_session_path)
end
end
+
+ def post_authorize(verified: true)
+ request.headers.merge!(workhorse_internal_api_request_header) if verified
+
+ post :authorize, namespace_id: model.namespace, project_id: model.path, format: :json
+ end
end
diff --git a/spec/controllers/projects/variables_controller_spec.rb b/spec/controllers/projects/variables_controller_spec.rb
index 68019743be0..9afd1f751c6 100644
--- a/spec/controllers/projects/variables_controller_spec.rb
+++ b/spec/controllers/projects/variables_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::VariablesController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET #show' do
diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb
index 92addf30307..30623b6cb3d 100644
--- a/spec/controllers/projects/wikis_controller_spec.rb
+++ b/spec/controllers/projects/wikis_controller_spec.rb
@@ -1,16 +1,132 @@
require 'spec_helper'
describe Projects::WikisController do
- let(:project) { create(:project_empty_repo, :public) }
- let(:user) { create(:user) }
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.owner }
+ let(:project_wiki) { ProjectWiki.new(project, user) }
+ let(:wiki) { project_wiki.wiki }
+ let(:wiki_title) { 'page-title-test' }
+
+ before do
+ create_page(wiki_title, 'hello world')
+
+ sign_in(user)
+ end
+
+ after do
+ destroy_page(wiki_title)
+ end
+
+ describe 'GET #show' do
+ render_views
+
+ subject { get :show, namespace_id: project.namespace, project_id: project, id: wiki_title }
+
+ context 'when page content encoding is invalid' do
+ it 'limits the retrieved pages for the sidebar' do
+ expect(controller).to receive(:load_wiki).and_return(project_wiki)
+
+ # empty? call
+ expect(project_wiki).to receive(:pages).with(limit: 1).and_call_original
+ # Sidebar entries
+ expect(project_wiki).to receive(:pages).with(limit: 15).and_call_original
+
+ subject
+
+ expect(response).to have_http_status(:ok)
+ expect(response.body).to include(wiki_title)
+ end
+ end
+
+ context 'when page content encoding is invalid' do
+ it 'sets flash error' do
+ allow(controller).to receive(:valid_encoding?).and_return(false)
+
+ subject
+
+ expect(response).to have_http_status(:ok)
+ expect(flash[:notice]).to eq 'The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.'
+ end
+ end
+ end
describe 'POST #preview_markdown' do
it 'renders json in a correct format' do
- sign_in(user)
-
post :preview_markdown, namespace_id: project.namespace, project_id: project, id: 'page/path', text: '*Markdown* text'
expect(JSON.parse(response.body).keys).to match_array(%w(body references))
end
end
+
+ describe 'GET #edit' do
+ subject { get(:edit, namespace_id: project.namespace, project_id: project, id: wiki_title) }
+
+ context 'when page content encoding is invalid' do
+ it 'redirects to show' do
+ allow(controller).to receive(:valid_encoding?).and_return(false)
+
+ subject
+
+ expect(response).to redirect_to(project_wiki_path(project, project_wiki.pages.first))
+ end
+ end
+
+ context 'when page content encoding is valid' do
+ render_views
+
+ it 'shows the edit page' do
+ subject
+
+ expect(response).to have_http_status(:ok)
+ expect(response.body).to include('Edit Page')
+ end
+ end
+ end
+
+ describe 'PATCH #update' do
+ let(:new_title) { 'New title' }
+ let(:new_content) { 'New content' }
+ subject do
+ patch(:update,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: wiki_title,
+ wiki: { title: new_title, content: new_content })
+ end
+
+ context 'when page content encoding is invalid' do
+ it 'redirects to show' do
+ allow(controller).to receive(:valid_encoding?).and_return(false)
+
+ subject
+ expect(response).to redirect_to(project_wiki_path(project, project_wiki.pages.first))
+ end
+ end
+
+ context 'when page content encoding is valid' do
+ render_views
+
+ it 'updates the page' do
+ subject
+
+ wiki_page = project_wiki.pages.first
+
+ expect(wiki_page.title).to eq new_title
+ expect(wiki_page.content).to eq new_content
+ end
+ end
+ end
+
+ def create_page(name, content)
+ wiki.write_page(name, :markdown, content, commit_details(name))
+ end
+
+ def commit_details(name)
+ Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "created page #{name}")
+ end
+
+ def destroy_page(title, dir = '')
+ page = wiki.page(title: title, dir: dir)
+ project_wiki.delete_page(page, "test commit")
+ end
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 27f04be3fdf..94644b1f9fd 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -166,7 +166,7 @@ describe ProjectsController do
User.project_views.keys.each do |project_view|
context "with #{project_view} view set" do
before do
- user.update_attributes(project_view: project_view)
+ user.update(project_view: project_view)
get :show, namespace_id: empty_project.namespace, id: empty_project
end
@@ -188,7 +188,7 @@ describe ProjectsController do
User.project_views.keys.each do |project_view|
context "with #{project_view} view set" do
before do
- user.update_attributes(project_view: project_view)
+ user.update(project_view: project_view)
get :show, namespace_id: empty_project.namespace, id: empty_project
end
@@ -616,13 +616,40 @@ describe ProjectsController do
end
describe 'POST #preview_markdown' do
- it 'renders json in a correct format' do
+ before do
sign_in(user)
+ end
+ it 'renders json in a correct format' do
post :preview_markdown, namespace_id: public_project.namespace, id: public_project, text: '*Markdown* text'
expect(JSON.parse(response.body).keys).to match_array(%w(body references))
end
+
+ context 'state filter on references' do
+ let(: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
+ post :preview_markdown, namespace_id: public_project.namespace,
+ id: public_project,
+ text: issue.to_reference
+
+ json_response = JSON.parse(response.body)
+
+ expect(json_response['body']).to match(/\##{issue.iid} \(closed\)/)
+ end
+
+ it 'renders JSON body with state filter for MRs' do
+ post :preview_markdown, namespace_id: public_project.namespace,
+ id: public_project,
+ text: merge_request.to_reference
+
+ json_response = JSON.parse(response.body)
+
+ expect(json_response['body']).to match(/\!#{merge_request.iid} \(closed\)/)
+ end
+ end
end
describe '#ensure_canonical_path' do
@@ -732,7 +759,7 @@ describe ProjectsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when project export is enabled' do
@@ -760,26 +787,58 @@ describe ProjectsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
- context 'when project export is enabled' do
- it 'returns 302' do
- get :download_export, namespace_id: project.namespace, id: project
+ context 'object storage disabled' do
+ before do
+ stub_feature_flags(import_export_object_storage: false)
+ end
- expect(response).to have_gitlab_http_status(302)
+ context 'when project export is enabled' do
+ it 'returns 302' do
+ get :download_export, namespace_id: project.namespace, id: project
+
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+
+ context 'when project export is disabled' do
+ before do
+ stub_application_setting(project_export_enabled?: false)
+ end
+
+ it 'returns 404' do
+ get :download_export, namespace_id: project.namespace, id: project
+
+ expect(response).to have_gitlab_http_status(404)
+ end
end
end
- context 'when project export is disabled' do
+ context 'object storage enabled' do
before do
- stub_application_setting(project_export_enabled?: false)
+ stub_feature_flags(import_export_object_storage: true)
end
- it 'returns 404' do
- get :download_export, namespace_id: project.namespace, id: project
+ context 'when project export is enabled' do
+ it 'returns 302' do
+ get :download_export, namespace_id: project.namespace, id: project
- expect(response).to have_gitlab_http_status(404)
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+
+ context 'when project export is disabled' do
+ before do
+ stub_application_setting(project_export_enabled?: false)
+ end
+
+ it 'returns 404' do
+ get :download_export, namespace_id: project.namespace, id: project
+
+ expect(response).to have_gitlab_http_status(404)
+ end
end
end
end
@@ -788,7 +847,7 @@ describe ProjectsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when project export is enabled' do
@@ -816,7 +875,7 @@ describe ProjectsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when project export is enabled' do
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index cdec26bd421..8e25b61e2f1 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -50,8 +50,6 @@ describe SessionsController do
end
context 'when using valid password', :clean_gitlab_redis_shared_state do
- include UserActivitiesHelpers
-
let(:user) { create(:user) }
let(:user_params) { { login: user.username, password: user.password } }
@@ -77,7 +75,7 @@ describe SessionsController do
it 'updates the user activity' do
expect do
post(:create, user: user_params)
- end.to change { user_activity(user) }
+ end.to change { user.reload.last_activity_on }.to(Date.today)
end
end
@@ -93,6 +91,12 @@ describe SessionsController do
it 'displays an error when the reCAPTCHA is not solved' do
# Without this, `verify_recaptcha` arbitraily returns true in test env
Recaptcha.configuration.skip_verify_env.delete('test')
+ counter = double(:counter)
+
+ expect(counter).to receive(:increment)
+ expect(Gitlab::Metrics).to receive(:counter)
+ .with(:failed_login_captcha_total, anything)
+ .and_return(counter)
post(:create, user: user_params)
@@ -104,6 +108,13 @@ describe SessionsController do
it 'successfully logs in a user when reCAPTCHA is solved' do
# Avoid test ordering issue and ensure `verify_recaptcha` returns true
Recaptcha.configuration.skip_verify_env << 'test'
+ counter = double(:counter)
+
+ expect(counter).to receive(:increment)
+ expect(Gitlab::Metrics).to receive(:counter)
+ .with(:successful_login_captcha_total, anything)
+ .and_return(counter)
+ expect(Gitlab::Metrics).to receive(:counter).and_call_original
post(:create, user: user_params)
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index eb94d395a9e..bcf289f36a9 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -269,13 +269,13 @@ describe UploadsController do
context "when the user has access to the project" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "when the user is blocked" do
before do
user.block
- project.add_master(user)
+ project.add_maintainer(user)
end
it "redirects to the sign in page" do
@@ -475,13 +475,13 @@ describe UploadsController do
context "when the user has access to the project" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "when the user is blocked" do
before do
user.block
- project.add_master(user)
+ project.add_maintainer(user)
end
it "redirects to the sign in page" do
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index b0acf4a49ac..071f96a729e 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
describe UsersController do
let(:user) { create(:user) }
+ let(:private_user) { create(:user, private_profile: true) }
+ let(:public_user) { create(:user) }
describe 'GET #show' do
context 'with rendered views' do
@@ -98,16 +100,47 @@ describe UsersController do
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 :show, username: private_user.username, format: :json
+
+ expect(assigns(:events)).to be_empty
+ end
end
end
describe 'GET #calendar' do
- it 'renders calendar' do
- sign_in(user)
+ context 'for user' do
+ let(:project) { create(:project) }
+
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ end
+
+ context 'with public profile' do
+ it 'renders calendar' do
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user)
+ EventCreateService.new.push(project, public_user, push_data)
+
+ get :calendar, username: public_user.username, format: :json
- get :calendar, username: user.username, format: :json
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ context 'with private profile' do
+ it 'does not render calendar' do
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user)
+ EventCreateService.new.push(project, private_user, push_data)
- expect(response).to have_gitlab_http_status(200)
+ get :calendar, username: private_user.username, format: :json
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
context 'forked project' do
@@ -150,9 +183,26 @@ describe UsersController do
expect(assigns(:calendar_date)).to eq(Date.parse('2014-07-31'))
end
- it 'renders calendar_activities' do
- get :calendar_activities, username: user.username
- expect(response).to render_template('calendar_activities')
+ context 'for user' do
+ context 'with public profile' do
+ it 'renders calendar_activities' do
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user)
+ EventCreateService.new.push(project, public_user, push_data)
+
+ get :calendar_activities, username: public_user.username
+ expect(assigns[:events]).not_to be_empty
+ end
+ end
+
+ context 'with private profile' do
+ it 'does not render calendar_activities' do
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user)
+ EventCreateService.new.push(project, private_user, push_data)
+
+ get :calendar_activities, username: private_user.username
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
end
diff --git a/spec/factories/ci/build_trace_chunks.rb b/spec/factories/ci/build_trace_chunks.rb
index c0b9a25bfe8..3e8e2736423 100644
--- a/spec/factories/ci/build_trace_chunks.rb
+++ b/spec/factories/ci/build_trace_chunks.rb
@@ -3,5 +3,53 @@ FactoryBot.define do
build factory: :ci_build
chunk_index 0
data_store :redis
+
+ trait :redis_with_data do
+ data_store :redis
+
+ transient do
+ initial_data 'test data'
+ end
+
+ after(:create) do |build_trace_chunk, evaluator|
+ Ci::BuildTraceChunks::Redis.new.set_data(build_trace_chunk, evaluator.initial_data)
+ end
+ end
+
+ trait :redis_without_data do
+ data_store :redis
+ end
+
+ trait :database_with_data do
+ data_store :database
+
+ transient do
+ initial_data 'test data'
+ end
+
+ after(:build) do |build_trace_chunk, evaluator|
+ Ci::BuildTraceChunks::Database.new.set_data(build_trace_chunk, evaluator.initial_data)
+ end
+ end
+
+ trait :database_without_data do
+ data_store :database
+ end
+
+ trait :fog_with_data do
+ data_store :fog
+
+ transient do
+ initial_data 'test data'
+ end
+
+ after(:create) do |build_trace_chunk, evaluator|
+ Ci::BuildTraceChunks::Fog.new.set_data(build_trace_chunk, evaluator.initial_data)
+ end
+ end
+
+ trait :fog_without_data do
+ data_store :fog
+ end
end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 4acc008ed38..9813190925b 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -187,6 +187,12 @@ FactoryBot.define do
end
end
+ trait :test_reports do
+ after(:build) do |build|
+ build.job_artifacts << create(:ci_job_artifact, :junit, job: build)
+ end
+ end
+
trait :expired do
artifacts_expire_at 1.minute.ago
end
@@ -248,5 +254,11 @@ FactoryBot.define do
failed
failure_reason 2
end
+
+ trait :with_runner_session do
+ after(:build) do |build|
+ build.build_runner_session(url: 'ws://localhost')
+ end
+ end
end
end
diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb
index 3d3287d8168..46aaaf6aa5d 100644
--- a/spec/factories/ci/job_artifacts.rb
+++ b/spec/factories/ci/job_artifacts.rb
@@ -4,6 +4,7 @@ FactoryBot.define do
factory :ci_job_artifact, class: Ci::JobArtifact do
job factory: :ci_build
file_type :archive
+ file_format :zip
trait :remote_store do
file_store JobArtifactUploader::Store::REMOTE
@@ -15,6 +16,7 @@ FactoryBot.define do
trait :archive do
file_type :archive
+ file_format :zip
after(:build) do |artifact, _|
artifact.file = fixture_file_upload(
@@ -24,6 +26,7 @@ FactoryBot.define do
trait :metadata do
file_type :metadata
+ file_format :gzip
after(:build) do |artifact, _|
artifact.file = fixture_file_upload(
@@ -33,6 +36,7 @@ FactoryBot.define do
trait :trace do
file_type :trace
+ file_format :raw
after(:build) do |artifact, evaluator|
artifact.file = fixture_file_upload(
@@ -40,6 +44,46 @@ FactoryBot.define do
end
end
+ trait :junit do
+ file_type :junit
+ file_format :gzip
+
+ after(:build) do |artifact, evaluator|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/junit/junit.xml.gz'), 'application/x-gzip')
+ end
+ end
+
+ trait :junit_with_ant do
+ file_type :junit
+ file_format :gzip
+
+ after(:build) do |artifact, evaluator|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/junit/junit_ant.xml.gz'), 'application/x-gzip')
+ end
+ end
+
+ trait :junit_with_three_testsuites do
+ file_type :junit
+ file_format :gzip
+
+ after(:build) do |artifact, evaluator|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/junit/junit_with_three_testsuites.xml.gz'), 'application/x-gzip')
+ end
+ end
+
+ trait :junit_with_corrupted_data do
+ file_type :junit
+ file_format :gzip
+
+ after(:build) do |artifact, evaluator|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/junit/junit_with_corrupted_data.xml.gz'), 'application/x-gzip')
+ end
+ end
+
trait :correct_checksum do
after(:build) do |artifact, evaluator|
artifact.file_sha256 = Digest::SHA256.file(artifact.file.path).hexdigest
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index 51a767e5b93..a6ff226fa75 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -58,6 +58,10 @@ FactoryBot.define do
status :success
end
+ trait :running do
+ status :running
+ end
+
trait :failed do
status :failed
end
@@ -65,6 +69,14 @@ FactoryBot.define do
trait :protected do
protected true
end
+
+ trait :with_test_reports do
+ status :success
+
+ after(:build) do |pipeline, evaluator|
+ pipeline.builds << build(:ci_build, :test_reports, pipeline: pipeline, project: pipeline.project)
+ end
+ end
end
end
end
diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb
index 6fb621b5e51..347e4f433e2 100644
--- a/spec/factories/ci/runners.rb
+++ b/spec/factories/ci/runners.rb
@@ -6,7 +6,6 @@ FactoryBot.define do
active true
access_level :not_protected
- is_shared true
runner_type :instance_type
trait :online do
@@ -14,12 +13,10 @@ FactoryBot.define do
end
trait :instance do
- is_shared true
runner_type :instance_type
end
trait :group do
- is_shared false
runner_type :group_type
after(:build) do |runner, evaluator|
@@ -28,7 +25,6 @@ FactoryBot.define do
end
trait :project do
- is_shared false
runner_type :project_type
after(:build) do |runner, evaluator|
diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb
index 3e4277e4ba6..7c4a440b9a9 100644
--- a/spec/factories/clusters/applications/helm.rb
+++ b/spec/factories/clusters/applications/helm.rb
@@ -32,11 +32,21 @@ FactoryBot.define do
updated_at ClusterWaitForAppInstallationWorker::TIMEOUT.ago
end
- factory :clusters_applications_ingress, class: Clusters::Applications::Ingress
- factory :clusters_applications_prometheus, class: Clusters::Applications::Prometheus
- factory :clusters_applications_runner, class: Clusters::Applications::Runner
+ factory :clusters_applications_ingress, class: Clusters::Applications::Ingress do
+ cluster factory: %i(cluster with_installed_helm provided_by_gcp)
+ end
+
+ factory :clusters_applications_prometheus, class: Clusters::Applications::Prometheus do
+ cluster factory: %i(cluster with_installed_helm provided_by_gcp)
+ end
+
+ factory :clusters_applications_runner, class: Clusters::Applications::Runner do
+ cluster factory: %i(cluster with_installed_helm provided_by_gcp)
+ end
+
factory :clusters_applications_jupyter, class: Clusters::Applications::Jupyter do
oauth_application factory: :oauth_application
+ cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
end
end
diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb
index 0430762c1ff..bbeba8ce8b9 100644
--- a/spec/factories/clusters/clusters.rb
+++ b/spec/factories/clusters/clusters.rb
@@ -36,5 +36,9 @@ FactoryBot.define do
trait :production_environment do
sequence(:environment_scope) { |n| "production#{n}/*" }
end
+
+ trait :with_installed_helm do
+ application_helm factory: %i(clusters_applications_helm installed)
+ end
end
end
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 1c2214e9481..47036560b9d 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -7,7 +7,7 @@ FactoryBot.define do
trait(:guest) { access_level GroupMember::GUEST }
trait(:reporter) { access_level GroupMember::REPORTER }
trait(:developer) { access_level GroupMember::DEVELOPER }
- trait(:master) { access_level GroupMember::MASTER }
+ trait(:maintainer) { access_level GroupMember::MAINTAINER }
trait(:owner) { access_level GroupMember::OWNER }
trait(:access_request) { requested_at Time.now }
diff --git a/spec/factories/import_export_uploads.rb b/spec/factories/import_export_uploads.rb
new file mode 100644
index 00000000000..7750d49b1d0
--- /dev/null
+++ b/spec/factories/import_export_uploads.rb
@@ -0,0 +1,5 @@
+FactoryBot.define do
+ factory :import_export_upload do
+ project { create(:project) }
+ end
+end
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 3a35bdd25de..4d4f74e101e 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -27,7 +27,7 @@ FactoryBot.define do
end
after(:create) do |issue, evaluator|
- issue.update_attributes(labels: evaluator.labels)
+ issue.update(labels: evaluator.labels)
end
end
end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 3441ce1b8cb..b8b089b069b 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -89,6 +89,18 @@ FactoryBot.define do
end
end
+ trait :with_test_reports do
+ after(:build) do |merge_request|
+ merge_request.head_pipeline = build(
+ :ci_pipeline,
+ :success,
+ :with_test_reports,
+ project: merge_request.source_project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha)
+ end
+ end
+
after(:build) do |merge_request|
target_project = merge_request.target_project
source_project = merge_request.source_project
@@ -100,6 +112,10 @@ FactoryBot.define do
end
end
+ after(:create) do |merge_request, evaluator|
+ merge_request.cache_merge_request_closes_issues!
+ end
+
factory :merged_merge_request, traits: [:merged]
factory :closed_merge_request, traits: [:closed]
factory :reopened_merge_request, traits: [:opened]
@@ -117,7 +133,7 @@ FactoryBot.define do
end
after(:create) do |merge_request, evaluator|
- merge_request.update_attributes(labels: evaluator.labels)
+ merge_request.update(labels: evaluator.labels)
end
end
end
diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb
index f95632e7187..019e4420212 100644
--- a/spec/factories/milestones.rb
+++ b/spec/factories/milestones.rb
@@ -18,6 +18,11 @@ FactoryBot.define do
state "closed"
end
+ trait :with_dates do
+ start_date { Date.new(2000, 1, 1) }
+ due_date { Date.new(2000, 1, 30) }
+ end
+
after(:build, :stub) do |milestone, evaluator|
if evaluator.group
milestone.group = evaluator.group
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 9fdc3e616a6..6844ed8aa4a 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -39,6 +39,7 @@ FactoryBot.define do
factory :legacy_diff_note_on_merge_request, traits: [:on_merge_request, :legacy_diff_note], class: LegacyDiffNote do
association :project, :repository
+ position ''
end
factory :diff_note_on_merge_request, traits: [:on_merge_request], class: DiffNote do
diff --git a/spec/factories/programming_languages.rb b/spec/factories/programming_languages.rb
new file mode 100644
index 00000000000..d3511d42b6c
--- /dev/null
+++ b/spec/factories/programming_languages.rb
@@ -0,0 +1,6 @@
+FactoryBot.define do
+ factory :programming_language do
+ name 'Ruby'
+ color '#123456'
+ end
+end
diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb
index 4260f52498d..22a8085ea45 100644
--- a/spec/factories/project_members.rb
+++ b/spec/factories/project_members.rb
@@ -2,12 +2,12 @@ FactoryBot.define do
factory :project_member do
user
project
- master
+ maintainer
trait(:guest) { access_level ProjectMember::GUEST }
trait(:reporter) { access_level ProjectMember::REPORTER }
trait(:developer) { access_level ProjectMember::DEVELOPER }
- trait(:master) { access_level ProjectMember::MASTER }
+ trait(:maintainer) { access_level ProjectMember::MAINTAINER }
trait(:access_request) { requested_at Time.now }
trait(:invited) do
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index f6b05bac0e8..1215b04913e 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -34,7 +34,7 @@ FactoryBot.define do
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
- project.project_feature.update_columns(
+ project.project_feature.update(
wiki_access_level: evaluator.wiki_access_level,
builds_access_level: builds_access_level,
snippets_access_level: evaluator.snippets_access_level,
@@ -47,7 +47,7 @@ FactoryBot.define do
# user have access to the project. Our specs don't use said service class,
# thus we must manually refresh things here.
unless project.group || project.pending_delete
- project.add_master(project.owner)
+ project.add_maintainer(project.owner)
end
project.group&.refresh_members_authorized_projects
@@ -103,6 +103,22 @@ FactoryBot.define do
end
trait :with_export do
+ before(:create) do |_project, _evaluator|
+ allow(Feature).to receive(:enabled?).with(:import_export_object_storage) { false }
+ allow(Feature).to receive(:enabled?).with('import_export_object_storage') { false }
+ end
+
+ after(:create) do |project, _evaluator|
+ ProjectExportWorker.new.perform(project.creator.id, project.id)
+ end
+ end
+
+ trait :with_object_export do
+ before(:create) do |_project, _evaluator|
+ allow(Feature).to receive(:enabled?).with(:import_export_object_storage) { true }
+ allow(Feature).to receive(:enabled?).with('import_export_object_storage') { true }
+ end
+
after(:create) do |project, evaluator|
ProjectExportWorker.new.perform(project.creator.id, project.id)
end
diff --git a/spec/factories/protected_branches.rb b/spec/factories/protected_branches.rb
index 60956511834..5457c0d2a8f 100644
--- a/spec/factories/protected_branches.rb
+++ b/spec/factories/protected_branches.rb
@@ -39,23 +39,23 @@ FactoryBot.define do
end
end
- trait :masters_can_push do
+ trait :maintainers_can_push do
transient do
default_push_level false
end
after(:build) do |protected_branch|
- protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
after(:build) do |protected_branch, evaluator|
if evaluator.default_access_level && evaluator.default_push_level
- protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
if evaluator.default_access_level && evaluator.default_merge_level
- protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
diff --git a/spec/factories/protected_tags.rb b/spec/factories/protected_tags.rb
index df9c8b3cb63..2b81d089549 100644
--- a/spec/factories/protected_tags.rb
+++ b/spec/factories/protected_tags.rb
@@ -27,19 +27,19 @@ FactoryBot.define do
end
end
- trait :masters_can_create do
+ trait :maintainers_can_create do
transient do
default_access_level false
end
after(:build) do |protected_tag|
- protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_tag.create_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
after(:build) do |protected_tag, evaluator|
if evaluator.default_access_level
- protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_tag.create_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
end
diff --git a/spec/factories/repository_languages.rb b/spec/factories/repository_languages.rb
new file mode 100644
index 00000000000..1757ba6766c
--- /dev/null
+++ b/spec/factories/repository_languages.rb
@@ -0,0 +1,7 @@
+FactoryBot.define do
+ factory :repository_language do
+ project
+ programming_language
+ share 98.5
+ end
+end
diff --git a/spec/factories/resource_label_events.rb b/spec/factories/resource_label_events.rb
new file mode 100644
index 00000000000..a67ad78c098
--- /dev/null
+++ b/spec/factories/resource_label_events.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :resource_label_event do
+ user { issue.project.creator }
+ action :add
+ label
+ issue
+ end
+end
diff --git a/spec/factories/site_statistics.rb b/spec/factories/site_statistics.rb
new file mode 100644
index 00000000000..dd8c795515a
--- /dev/null
+++ b/spec/factories/site_statistics.rb
@@ -0,0 +1,7 @@
+FactoryBot.define do
+ factory :site_statistics, class: 'SiteStatistic' do
+ id 1
+ repositories_count 999
+ wikis_count 555
+ end
+end
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index 94f8caedfa6..14486c80341 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -1,8 +1,8 @@
FactoryBot.define do
factory :todo do
project
- author { project.creator }
- user { project.creator }
+ author { project&.creator || user }
+ user { project&.creator || user }
target factory: :issue
action { Todo::ASSIGNED }
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index b45f6f30e40..a81b2169b89 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -28,6 +28,13 @@ FactoryBot.define do
secret SecureRandom.hex
end
+ trait :with_file do
+ after(:create) do |upload|
+ FileUtils.mkdir_p(File.dirname(upload.absolute_path))
+ FileUtils.touch(upload.absolute_path)
+ end
+ end
+
trait :object_storage do
store ObjectStorage::Store::REMOTE
end
diff --git a/spec/factories/user_statuses.rb b/spec/factories/user_statuses.rb
new file mode 100644
index 00000000000..9998ae9609c
--- /dev/null
+++ b/spec/factories/user_statuses.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :user_status do
+ user
+ emoji 'coffee'
+ message 'I crave coffee'
+ end
+end
diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb
index 091fdcec3db..eb12eebe8e6 100644
--- a/spec/features/abuse_report_spec.rb
+++ b/spec/features/abuse_report_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Abuse reports' do
+describe 'Abuse reports' do
let(:another_user) { create(:user) }
before do
sign_in(create(:user))
end
- scenario 'Report abuse' do
+ it 'Report abuse' do
visit user_path(another_user)
click_link 'Report abuse'
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index a5e0ac592b9..3c2ae5b3c6a 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
-feature 'Admin Appearance' do
+describe 'Admin Appearance' do
let!(:appearance) { create(:appearance) }
- scenario 'Create new appearance' do
+ it 'Create new appearance' do
sign_in(create(:admin))
visit admin_appearances_path
@@ -21,7 +21,7 @@ feature 'Admin Appearance' do
expect(page).to have_content 'Last edit'
end
- scenario 'Preview sign-in page appearance' do
+ it 'Preview sign-in page appearance' do
sign_in(create(:admin))
visit admin_appearances_path
@@ -30,7 +30,7 @@ feature 'Admin Appearance' do
expect_custom_sign_in_appearance(appearance)
end
- scenario 'Preview new project page appearance' do
+ it 'Preview new project page appearance' do
sign_in(create(:admin))
visit admin_appearances_path
@@ -39,20 +39,20 @@ feature 'Admin Appearance' do
expect_custom_new_project_appearance(appearance)
end
- scenario 'Custom sign-in page' do
+ it 'Custom sign-in page' do
visit new_user_session_path
expect_custom_sign_in_appearance(appearance)
end
- scenario 'Custom new project page' do
+ it 'Custom new project page' do
sign_in create(:user)
visit new_project_path
expect_custom_new_project_appearance(appearance)
end
- scenario 'Appearance logo' do
+ it 'Appearance logo' do
sign_in(create(:admin))
visit admin_appearances_path
@@ -64,7 +64,7 @@ feature 'Admin Appearance' do
expect(page).not_to have_css(logo_selector)
end
- scenario 'Header logos' do
+ it 'Header logos' do
sign_in(create(:admin))
visit admin_appearances_path
@@ -76,7 +76,7 @@ feature 'Admin Appearance' do
expect(page).not_to have_css(header_logo_selector)
end
- scenario 'Favicon' do
+ it 'Favicon' do
sign_in(create(:admin))
visit admin_appearances_path
diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb
index 430a8d22b0f..f6dc499df29 100644
--- a/spec/features/admin/admin_broadcast_messages_spec.rb
+++ b/spec/features/admin/admin_broadcast_messages_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
-feature 'Admin Broadcast Messages' do
+describe 'Admin Broadcast Messages' do
before do
sign_in(create(:admin))
create(:broadcast_message, :expired, message: 'Migration to new server')
visit admin_broadcast_messages_path
end
- scenario 'See broadcast messages list' do
+ it 'See broadcast messages list' do
expect(page).to have_content 'Migration to new server'
end
- scenario 'Create a customized broadcast message' do
+ it 'Create a customized broadcast message' do
fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**'
fill_in 'broadcast_message_color', with: '#f2dede'
fill_in 'broadcast_message_font', with: '#b94a48'
@@ -24,7 +24,7 @@ feature 'Admin Broadcast Messages' do
expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"])
end
- scenario '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'
@@ -33,14 +33,14 @@ feature 'Admin Broadcast Messages' do
expect(page).to have_content 'Application update RIGHT NOW'
end
- scenario '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
expect(page).not_to have_content 'Migration to new server'
end
- scenario 'Live preview a customized broadcast message', :js do
+ it 'Live preview a customized broadcast message', :js do
fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:"
page.within('.broadcast-message-preview') do
diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb
index 31d4142a8e9..4645fde7522 100644
--- a/spec/features/admin/admin_browse_spam_logs_spec.rb
+++ b/spec/features/admin/admin_browse_spam_logs_spec.rb
@@ -7,7 +7,7 @@ describe 'Admin browse spam logs' do
sign_in(create(:admin))
end
- scenario '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_cohorts_spec.rb b/spec/features/admin/admin_cohorts_spec.rb
deleted file mode 100644
index bca52bf674c..00000000000
--- a/spec/features/admin/admin_cohorts_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'rails_helper'
-
-feature 'Admin cohorts page' do
- before do
- sign_in(create(:admin))
- end
-
- scenario 'See users count per month' do
- 2.times { create(:user) }
-
- visit admin_cohorts_path
-
- expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
- end
-end
diff --git a/spec/features/admin/admin_conversational_development_index_spec.rb b/spec/features/admin/admin_conversational_development_index_spec.rb
deleted file mode 100644
index 2d2c7df5364..00000000000
--- a/spec/features/admin/admin_conversational_development_index_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require 'spec_helper'
-
-describe 'Admin Conversational Development Index' do
- before do
- sign_in(create(:admin))
- end
-
- context 'when usage ping is disabled' do
- it 'shows empty state' do
- stub_application_setting(usage_ping_enabled: false)
-
- visit admin_conversational_development_index_path
-
- expect(page).to have_content('Usage ping is not enabled')
- end
- end
-
- context 'when there is no data to display' do
- it 'shows empty state' do
- stub_application_setting(usage_ping_enabled: true)
-
- visit admin_conversational_development_index_path
-
- expect(page).to have_content('Data is still calculating')
- end
- end
-
- context 'when there is data to display' do
- it 'shows numbers for each metric' do
- stub_application_setting(usage_ping_enabled: true)
- create(:conversational_development_index_metric)
-
- visit admin_conversational_development_index_path
-
- expect(page).to have_content(
- 'Issues created per active user 1.2 You 9.3 Lead 13.3%'
- )
- end
- end
-end
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 9946cc77d1d..91c22e7ad82 100644
--- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb
+++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
@@ -1,22 +1,22 @@
require 'rails_helper'
-feature 'Admin disables Git access protocol' do
+describe 'Admin disables Git access protocol' do
include StubENV
let(:project) { create(:project, :empty_repo) }
let(:admin) { create(:admin) }
- background do
+ before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
end
context 'with HTTP disabled' do
- background do
+ before do
disable_http_protocol
end
- scenario 'shows only SSH url' do
+ it 'shows only SSH url' do
visit_project
expect(page).to have_content("git clone #{project.ssh_url_to_repo}")
@@ -25,11 +25,11 @@ feature 'Admin disables Git access protocol' do
end
context 'with SSH disabled' do
- background do
+ before do
disable_ssh_protocol
end
- scenario 'shows only HTTP url' do
+ it 'shows only HTTP url' do
visit_project
expect(page).to have_content("git clone #{project.http_url_to_repo}")
@@ -38,11 +38,11 @@ feature 'Admin disables Git access protocol' do
end
context 'with nothing disabled' do
- background do
+ before do
create(:personal_key, user: admin)
end
- scenario 'shows default SSH url and protocol selection dropdown' do
+ it 'shows default SSH url and protocol selection dropdown' do
visit_project
expect(page).to have_content("git clone #{project.ssh_url_to_repo}")
diff --git a/spec/features/admin/admin_disables_two_factor_spec.rb b/spec/features/admin/admin_disables_two_factor_spec.rb
index 2abdd3c9ef2..e41835b4f24 100644
--- a/spec/features/admin/admin_disables_two_factor_spec.rb
+++ b/spec/features/admin/admin_disables_two_factor_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
-feature 'Admin disables 2FA for a user' do
- scenario 'successfully', :js do
+describe 'Admin disables 2FA for a user' do
+ it 'successfully', :js do
sign_in(create(:admin))
user = create(:user, :two_factor)
@@ -16,7 +16,7 @@ feature 'Admin disables 2FA for a user' do
end
end
- scenario 'for a user without 2FA enabled' do
+ it 'for a user without 2FA enabled' do
sign_in(create(:admin))
user = create(:user)
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index a4226d7a682..96dfde2e08c 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Admin Groups' do
+describe 'Admin Groups' do
include Select2Helper
let(:internal) { Gitlab::VisibilityLevel::INTERNAL }
@@ -47,13 +47,13 @@ feature 'Admin Groups' do
expect(li_texts).to match group_description
end
- scenario 'shows the visibility level radio populated with the default value' do
+ it 'shows the visibility level radio populated with the default value' do
visit new_admin_group_path
expect_selected_visibility(internal)
end
- scenario 'when entered in group path, it auto filled the group name', :js do
+ it 'when entered in group path, it auto filled the group name', :js do
visit admin_groups_path
click_link "New group"
group_path = 'gitlab'
@@ -64,7 +64,7 @@ feature 'Admin Groups' do
end
describe 'show a group' do
- scenario 'shows the group' do
+ it 'shows the group' do
group = create(:group, :private)
visit admin_group_path(group)
@@ -74,7 +74,7 @@ feature 'Admin Groups' do
end
describe 'group edit' do
- scenario 'shows the visibility level radio populated with the group visibility_level value' do
+ it 'shows the visibility level radio populated with the group visibility_level value' do
group = create(:group, :private)
visit admin_group_edit_path(group)
@@ -82,7 +82,7 @@ feature 'Admin Groups' do
expect_selected_visibility(group.visibility_level)
end
- scenario 'edit group path does not change group name', :js do
+ it 'edit group path does not change group name', :js do
group = create(:group, :private)
visit admin_group_edit_path(group)
@@ -168,7 +168,7 @@ feature 'Admin Groups' do
it 'renders shared project' do
empty_project = create(:project)
empty_project.project_group_links.create!(
- group_access: Gitlab::Access::MASTER,
+ group_access: Gitlab::Access::MAINTAINER,
group: group
)
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
index 3693e5882f9..aaa3e8dc821 100644
--- a/spec/features/admin/admin_health_check_spec.rb
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature "Admin Health Check", :feature do
+describe "Admin Health Check", :feature do
include StubENV
before do
diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb
index 710822ac042..928f97b6d29 100644
--- a/spec/features/admin/admin_hook_logs_spec.rb
+++ b/spec/features/admin/admin_hook_logs_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Admin::HookLogs' do
+describe 'Admin::HookLogs' do
let(:project) { create(:project) }
let(:system_hook) { create(:system_hook) }
let(:hook_log) { create(:web_hook_log, web_hook: system_hook, internal_error_message: 'some error') }
@@ -9,7 +9,7 @@ feature 'Admin::HookLogs' do
sign_in(create(:admin))
end
- scenario 'show list of hook logs' do
+ it 'show list of hook logs' do
hook_log
visit edit_admin_hook_path(system_hook)
@@ -17,7 +17,7 @@ feature 'Admin::HookLogs' do
expect(page).to have_content(hook_log.url)
end
- scenario 'show hook log details' do
+ it 'show hook log details' do
hook_log
visit edit_admin_hook_path(system_hook)
click_link 'View details'
@@ -27,7 +27,7 @@ feature 'Admin::HookLogs' do
expect(page).to have_content('Resend Request')
end
- scenario 'retry hook log' do
+ it 'retry hook log' do
WebMock.stub_request(:post, system_hook.url)
hook_log
diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb
index de406d7d966..238ea2a25bd 100644
--- a/spec/features/admin/admin_labels_spec.rb
+++ b/spec/features/admin/admin_labels_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe 'admin issues labels' do
it 'deletes all labels', :js do
page.within '.labels' do
- page.all('.btn-remove').each do |remove|
+ page.all('.remove-row').each do |remove|
accept_confirm { remove.click }
wait_for_requests
end
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 328e8f25f89..d6ee256f5b5 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -88,7 +88,7 @@ describe "Admin::Projects" do
describe 'add admin himself to a project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'adds admin a to a project as developer', :js do
@@ -110,7 +110,7 @@ describe "Admin::Projects" do
describe 'admin remove himself from a project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(current_user)
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index f3ab4ff771a..af1c153dec8 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Admin updates settings' do
+describe 'Admin updates settings' do
include StubENV
include TermsHelper
@@ -12,7 +12,7 @@ feature 'Admin updates settings' do
visit admin_application_settings_path
end
- scenario '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'
@@ -21,7 +21,7 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario '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)
@@ -35,7 +35,7 @@ feature 'Admin updates settings' do
expect(find('#application_setting_visibility_level_20')).not_to be_checked
end
- scenario 'Modify import sources' do
+ it 'Modify import sources' do
expect(Gitlab::CurrentSettings.import_sources).not_to be_empty
page.within('.as-visibility-access') do
@@ -58,7 +58,7 @@ feature 'Admin updates settings' do
expect(Gitlab::CurrentSettings.import_sources).to eq(['git'])
end
- scenario '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'
@@ -68,7 +68,7 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario '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'
@@ -78,7 +78,7 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario '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'
@@ -88,7 +88,7 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario '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)
@@ -104,7 +104,7 @@ feature 'Admin updates settings' do
expect(page).to have_content 'Application settings saved successfully'
end
- scenario 'Modify oauth providers' do
+ it 'Modify oauth providers' do
expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to be_empty
page.within('.as-signin') do
@@ -124,7 +124,7 @@ feature 'Admin updates settings' do
expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).not_to include('google_oauth2')
end
- scenario '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(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to be_empty
page.within('.as-signin') do
@@ -147,7 +147,7 @@ feature 'Admin updates settings' do
expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2')
end
- scenario 'Change Help page' do
+ it 'Change Help page' do
page.within('.as-help-page') do
fill_in 'Help page text', with: 'Example text'
check 'Hide marketing-related entries from help'
@@ -161,7 +161,7 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario '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'
@@ -173,9 +173,9 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario 'Change CI/CD settings' do
+ it 'Change CI/CD settings' do
page.within('.as-ci-cd') do
- check 'Enabled Auto DevOps for projects by default'
+ check 'Default to Auto DevOps pipeline for all projects'
fill_in 'Auto devops domain', with: 'domain.com'
click_button 'Save changes'
end
@@ -185,7 +185,7 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario 'Change Influx settings' do
+ it 'Change Influx settings' do
page.within('.as-influx') do
check 'Enable InfluxDB Metrics'
click_button 'Save changes'
@@ -195,7 +195,7 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario 'Change Prometheus settings' do
+ it 'Change Prometheus settings' do
page.within('.as-prometheus') do
check 'Enable Prometheus Metrics'
click_button 'Save changes'
@@ -205,7 +205,7 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario 'Change Performance bar settings' do
+ it 'Change Performance bar settings' do
group = create(:group)
page.within('.as-performance-bar') do
@@ -228,7 +228,7 @@ feature 'Admin updates settings' do
expect(find_field('Allowed group').value).to be_nil
end
- scenario 'Change Background jobs settings' do
+ it 'Change Background jobs settings' do
page.within('.as-background') do
fill_in 'Throttling Factor', with: 1
click_button 'Save changes'
@@ -238,7 +238,7 @@ feature 'Admin updates settings' do
expect(page).to have_content "Application settings saved successfully"
end
- scenario 'Change Spam settings' do
+ it 'Change Spam settings' do
page.within('.as-spam') do
check 'Enable reCAPTCHA'
fill_in 'reCAPTCHA Site Key', with: 'key'
@@ -252,7 +252,7 @@ feature 'Admin updates settings' do
expect(Gitlab::CurrentSettings.unique_ips_limit_per_user).to eq(15)
end
- scenario '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'
@@ -262,7 +262,7 @@ feature 'Admin updates settings' do
expect(Gitlab::CurrentSettings.terminal_max_session_time).to eq(15)
end
- scenario 'Enable outbound requests' do
+ it 'Enable outbound requests' do
page.within('.as-outbound') do
check 'Allow requests to the local network from hooks and services'
click_button 'Save changes'
@@ -272,7 +272,17 @@ feature 'Admin updates settings' do
expect(Gitlab::CurrentSettings.allow_local_requests_from_hooks_and_services).to be true
end
- scenario 'Change Slack Notifications Service template settings' 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'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.hide_third_party_offers).to be true
+ end
+
+ it 'Change Slack Notifications Service template settings' do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
fill_in 'Webhook', with: 'http://localhost'
@@ -296,7 +306,7 @@ feature 'Admin updates settings' do
expect(find('#service_push_channel').value).to eq '#test_channel'
end
- scenario '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'
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 9e3221577c7..1db6c75b85b 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -68,10 +68,12 @@ describe "Admin::Users" do
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: "bang"
+ fill_in "user_username", with: user_username
fill_in "user_email", with: "bigbang@mail.com"
end
@@ -112,6 +114,17 @@ describe "Admin::Users" do
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
end
describe "GET /admin/users/:id" do
@@ -315,6 +328,40 @@ describe "Admin::Users" do
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
@@ -409,4 +456,8 @@ describe "Admin::Users" do
expect(page).not_to have_content('twitter')
end
end
+
+ def check_breadcrumb(content)
+ expect(find('.breadcrumbs-sub-title')).to have_content(content)
+ 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 7371a494d36..e658f1b6738 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Admin uses repository checks' do
+describe 'Admin uses repository checks' do
include StubENV
before do
@@ -8,7 +8,7 @@ feature 'Admin uses repository checks' do
sign_in(create(:admin))
end
- scenario 'to trigger a single check' do
+ it 'to trigger a single check' do
project = create(:project)
visit_admin_project_page(project)
@@ -19,7 +19,7 @@ feature 'Admin uses repository checks' do
expect(page).to have_content('Repository check was triggered')
end
- scenario 'to see a single failed repository check', :js do
+ it 'to see a single failed repository check', :js do
project = create(:project)
project.update_columns(
last_repository_check_failed: true,
@@ -32,7 +32,7 @@ feature 'Admin uses repository checks' do
end
end
- scenario 'to clear all repository checks', :js do
+ it 'to clear all repository checks', :js do
visit admin_application_settings_path
expect(RepositoryCheck::ClearWorker).to receive(:perform_async)
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index da7749b42d2..bd4c00d97b1 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -8,8 +8,8 @@ describe "Dashboard Issues Feed" do
let!(:project2) { create(:project) }
before do
- project1.add_master(user)
- project2.add_master(user)
+ project1.add_maintainer(user)
+ project2.add_maintainer(user)
end
describe "atom feed" do
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index 462eab07a75..86b3f88298f 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -26,7 +26,7 @@ describe "Dashboard Feed" do
let(:note) { create(:note, noteable: issue, author: user, note: 'Bug confirmed', project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
issue_event(issue, user)
note_event(note, user)
visit dashboard_projects_path(:atom, feed_token: user.feed_token)
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index eeaaa40fe21..8d7df346abb 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -47,7 +47,7 @@ describe "User Feed" do
let!(:push_event_payload) { create(:push_event_payload, event: push_event) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
issue_event(issue, user)
note_event(note, user)
merge_request_event(merge_request, user)
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index 7a14a441088..eebc987499d 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -12,7 +12,7 @@ describe 'Issue Boards add issue modal', :js do
let!(:issue2) { create(:issue, project: project, title: 'hij', description: 'klm') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index f6e0dee28c6..a0af2dea3ad 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -11,8 +11,8 @@ describe 'Issue Boards', :js do
let!(:user2) { create(:user) }
before do
- project.add_master(user)
- project.add_master(user2)
+ project.add_maintainer(user)
+ project.add_maintainer(user2)
set_cookie('sidebar_collapsed', 'true')
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index 32bd7b88840..ec0ca21450a 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -13,7 +13,7 @@ describe 'Issue Boards', :js do
let!(:issue3) { create(:labeled_issue, project: project, title: 'testing 3', labels: [label], relative_position: 1) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb
index be9c6a51c29..615223a2a88 100644
--- a/spec/features/boards/modal_filter_spec.rb
+++ b/spec/features/boards/modal_filter_spec.rb
@@ -10,7 +10,7 @@ describe 'Issue Boards add issue modal filtering', :js do
let!(:issue1) { create(:issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 7a95f5cf871..0bf1ecbc433 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -8,7 +8,7 @@ describe 'Issue Boards new issue', :js do
context 'authorized user' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index a03aa681827..ee38e756f9e 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -22,7 +22,7 @@ describe 'Issue Boards', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb
index 271c610dcc8..de2cb4c335e 100644
--- a/spec/features/boards/sub_group_project_spec.rb
+++ b/spec/features/boards/sub_group_project_spec.rb
@@ -11,7 +11,7 @@ describe 'Sub-group project issue boards', :js do
let!(:issue) { create(:labeled_issue, project: project, labels: [label]) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index 70faf28e09d..f08946b0593 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Contributions Calendar', :js do
+describe 'Contributions Calendar', :js do
let(:user) { create(:user) }
let(:contributed_project) { create(:project, :public, :repository) }
let(:issue_note) { create(:note, project: contributed_project) }
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 87fa3f60826..8989b2051bb 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -89,7 +89,7 @@ describe 'Commits' do
context 'Download artifacts' do
before do
- build.update_attributes(legacy_artifacts_file: artifacts_file)
+ build.update(legacy_artifacts_file: artifacts_file)
end
it do
@@ -146,7 +146,7 @@ describe 'Commits' do
context "when logged as reporter" do
before do
project.add_reporter(user)
- build.update_attributes(legacy_artifacts_file: artifacts_file)
+ build.update(legacy_artifacts_file: artifacts_file)
visit pipeline_path(pipeline)
end
@@ -168,7 +168,7 @@ describe 'Commits' do
project.update(
visibility_level: Gitlab::VisibilityLevel::INTERNAL,
public_builds: false)
- build.update_attributes(legacy_artifacts_file: artifacts_file)
+ build.update(legacy_artifacts_file: artifacts_file)
visit pipeline_path(pipeline)
end
@@ -188,7 +188,7 @@ describe 'Commits' do
let(:branch_name) { 'master' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_commits_path(project, branch_name)
end
diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb
index bef2aa9e0e5..9986206f619 100644
--- a/spec/features/container_registry_spec.rb
+++ b/spec/features/container_registry_spec.rb
@@ -16,7 +16,7 @@ describe "Container Registry", :js do
end
context 'when there are no image repositories' do
- scenario 'user visits container registry main page' do
+ it 'user visits container registry main page' do
visit_container_registry
expect(page).to have_content 'No container images'
@@ -29,13 +29,13 @@ describe "Container Registry", :js do
project.container_repositories << container_repository
end
- scenario 'user wants to see multi-level container repository' do
+ it 'user wants to see multi-level container repository' do
visit_container_registry
expect(page).to have_content('my/image')
end
- scenario 'user removes entire container repository' do
+ it 'user removes entire container repository' do
visit_container_registry
expect_any_instance_of(ContainerRepository)
@@ -44,7 +44,7 @@ describe "Container Registry", :js do
click_on(class: 'js-remove-repo')
end
- scenario 'user removes a specific tag from container repository' do
+ it 'user removes a specific tag from container repository' do
visit_container_registry
find('.js-toggle-repo').click
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index ef493db3f11..32c75cae0a1 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Cycle Analytics', :js do
+describe 'Cycle Analytics', :js do
let(:user) { create(:user) }
let(:guest) { create(:user) }
let(:project) { create(:project, :repository) }
@@ -12,7 +12,7 @@ feature 'Cycle Analytics', :js do
context 'as an allowed user' do
context 'when project is new' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
@@ -39,7 +39,7 @@ feature 'Cycle Analytics', :js do
context "when there's cycle analytics data" do
before do
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
- project.add_master(user)
+ project.add_maintainer(user)
@build = create_cycle(user, project, issue, mr, milestone, pipeline)
deploy_master(user, project)
@@ -95,7 +95,7 @@ feature 'Cycle Analytics', :js do
before do
user.update_attribute(:preferred_language, 'es')
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_cycle_analytics_path(project)
wait_for_requests
diff --git a/spec/features/dashboard/active_tab_spec.rb b/spec/features/dashboard/active_tab_spec.rb
index 8bab501134b..f4d0f82d248 100644
--- a/spec/features/dashboard/active_tab_spec.rb
+++ b/spec/features/dashboard/active_tab_spec.rb
@@ -7,32 +7,38 @@ RSpec.describe 'Dashboard Active Tab', :js do
shared_examples 'page has active tab' do |title|
it "#{title} tab" do
+ subject
+
expect(page).to have_selector('.navbar-sub-nav li.active', count: 1)
expect(find('.navbar-sub-nav li.active')).to have_content(title)
end
end
context 'on dashboard projects' do
- before do
- visit dashboard_projects_path
+ it_behaves_like 'page has active tab', 'Projects' do
+ subject { visit dashboard_projects_path }
end
-
- it_behaves_like 'page has active tab', 'Projects'
end
context 'on dashboard groups' do
- before do
- visit dashboard_groups_path
+ it_behaves_like 'page has active tab', 'Groups' do
+ subject { visit dashboard_groups_path }
end
-
- it_behaves_like 'page has active tab', 'Groups'
end
context 'on activity projects' do
- before do
- visit activity_dashboard_path
+ it_behaves_like 'page has active tab', 'Activity' do
+ subject { visit activity_dashboard_path }
end
+ end
- it_behaves_like 'page has active tab', 'Activity'
+ context 'on instance statistics' do
+ subject { visit instance_statistics_root_path }
+
+ it 'shows Instance Statistics` as active' do
+ subject
+
+ expect(find('.navbar-sub-nav li.active')).to have_link('Instance Statistics')
+ end
end
end
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index 941208fa244..bf91dc121d8 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard > Activity' do
+describe 'Dashboard > Activity' do
let(:user) { create(:user) }
before do
@@ -60,13 +60,13 @@ feature 'Dashboard > Activity' do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit activity_dashboard_path
wait_for_requests
end
- scenario 'user should see all events' do
+ it 'user should see all events' do
within '.content_list' do
expect(page).to have_content('pushed new branch')
expect(page).to have_content('joined')
@@ -77,7 +77,7 @@ feature 'Dashboard > Activity' do
end
end
- scenario 'user should see only pushed events' do
+ it 'user should see only pushed events' do
click_link('Push events')
wait_for_requests
@@ -90,7 +90,7 @@ feature 'Dashboard > Activity' do
end
end
- scenario 'user should see only merged events' do
+ it 'user should see only merged events' do
click_link('Merge events')
wait_for_requests
@@ -103,7 +103,7 @@ feature 'Dashboard > Activity' do
end
end
- scenario 'user should see only issues events' do
+ it 'user should see only issues events' do
click_link('Issue events')
wait_for_requests
@@ -117,7 +117,7 @@ feature 'Dashboard > Activity' do
end
end
- scenario 'user should see only comments events' do
+ it 'user should see only comments events' do
click_link('Comments')
wait_for_requests
@@ -130,7 +130,7 @@ feature 'Dashboard > Activity' do
end
end
- scenario 'user should see only joined events' do
+ it 'user should see only joined events' do
click_link('Team')
wait_for_requests
@@ -143,7 +143,7 @@ feature 'Dashboard > Activity' do
end
end
- scenario 'user see selected event after page reloading' do
+ it 'user see selected event after page reloading' do
click_link('Push events')
wait_for_requests
visit activity_dashboard_path
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index b36231fd78b..6a0cd848345 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -6,8 +6,8 @@ RSpec.describe 'Dashboard Archived Project' do
let(:archived_project) { create(:project, :archived) }
before do
- project.add_master(user)
- archived_project.add_master(user)
+ project.add_maintainer(user)
+ archived_project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index 089c388636d..d7234158fa1 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Tooltips on .timeago dates', :js do
+describe 'Tooltips on .timeago dates', :js do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:created_date) { Date.yesterday.to_time }
@@ -8,7 +8,7 @@ feature 'Tooltips on .timeago dates', :js do
context 'on the activity tab' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
Event.create( project: project, author_id: user.id, action: Event::JOINED,
updated_at: created_date, created_at: created_date)
@@ -27,7 +27,7 @@ feature 'Tooltips on .timeago dates', :js do
context 'on the snippets tab' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
create(:snippet, author: user, updated_at: created_date, created_at: created_date)
sign_in user
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index 29280bd6e06..eceb12e91cd 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard Groups page', :js do
+describe 'Dashboard Groups page', :js do
let(:user) { create :user }
let(:group) { create(:group) }
let(:nested_group) { create(:group, :nested) }
diff --git a/spec/features/dashboard/instance_statistics_spec.rb b/spec/features/dashboard/instance_statistics_spec.rb
new file mode 100644
index 00000000000..21ee2796bd8
--- /dev/null
+++ b/spec/features/dashboard/instance_statistics_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Showing instance statistics' do
+ before do
+ sign_in user if user
+ end
+
+ # Using a path that is publicly accessible
+ subject { visit explore_projects_path }
+
+ context 'for unauthenticated users' do
+ let(:user) { nil }
+
+ it 'does not show the instance statistics link' do
+ subject
+
+ expect(page).not_to have_link('Instance Statistics')
+ end
+ end
+
+ context 'for regular users' do
+ let(:user) { create(:user) }
+
+ context 'when instance statistics are publicly available' do
+ before do
+ stub_application_setting(instance_statistics_visibility_private: false)
+ end
+
+ it 'shows the instance statistics link' do
+ subject
+
+ expect(page).to have_link('Instance Statistics')
+ end
+ end
+
+ context 'when instance statistics are not publicly available' do
+ before do
+ stub_application_setting(instance_statistics_visibility_private: true)
+ end
+
+ it 'shows the instance statistics link' do
+ subject
+
+ expect(page).not_to have_link('Instance Statistics')
+ end
+ end
+ end
+
+ context 'for admins' do
+ let(:user) { create(:admin) }
+
+ it 'shows the instance statistics link' do
+ subject
+
+ expect(page).to have_link('Instance Statistics')
+ end
+ end
+end
diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb
index 8d0b0be1bd4..95e2610dd4a 100644
--- a/spec/features/dashboard/issues_filter_spec.rb
+++ b/spec/features/dashboard/issues_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard Issues filtering', :js do
+describe 'Dashboard Issues filtering', :js do
include Spec::Support::Helpers::Features::SortingHelpers
let(:user) { create(:user) }
@@ -11,7 +11,7 @@ feature 'Dashboard Issues filtering', :js do
let!(:issue2) { create(:issue, project: project, author: user, assignees: [user], milestone: milestone) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit_issues
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 3cc7b38550d..4ae062f242a 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Dashboard Issues' do
let!(:other_issue) { create :issue, project: project }
before do
- [project, project_with_issues_disabled].each { |project| project.add_master(current_user) }
+ [project, project_with_issues_disabled].each { |project| project.add_maintainer(current_user) }
sign_in(current_user)
visit issues_dashboard_path(assignee_id: current_user.id)
end
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 0965b745c03..f51142f5790 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard Merge Requests' do
+describe 'Dashboard Merge Requests' do
include Spec::Support::Helpers::Features::SortingHelpers
include FilterItemSelectHelper
include ProjectForksHelper
@@ -12,7 +12,7 @@ feature 'Dashboard Merge Requests' do
let(:forked_project) { fork_project(public_project, current_user, repository: true) }
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
sign_in(current_user)
end
@@ -20,7 +20,7 @@ feature 'Dashboard Merge Requests' do
let(:project_with_disabled_merge_requests) { create(:project, :merge_requests_disabled) }
before do
- project_with_disabled_merge_requests.add_master(current_user)
+ project_with_disabled_merge_requests.add_maintainer(current_user)
visit merge_requests_dashboard_path
end
diff --git a/spec/features/dashboard/milestone_filter_spec.rb b/spec/features/dashboard/milestone_filter_spec.rb
index 8cd57f4f327..00373050aeb 100644
--- a/spec/features/dashboard/milestone_filter_spec.rb
+++ b/spec/features/dashboard/milestone_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard > milestone filter', :js do
+describe 'Dashboard > milestone filter', :js do
include FilterItemSelectHelper
let(:user) { create(:user) }
diff --git a/spec/features/dashboard/milestone_tabs_spec.rb b/spec/features/dashboard/milestone_tabs_spec.rb
index 6fcde35f541..21de7c2f06f 100644
--- a/spec/features/dashboard/milestone_tabs_spec.rb
+++ b/spec/features/dashboard/milestone_tabs_spec.rb
@@ -14,7 +14,7 @@ describe 'Dashboard milestone tabs', :js do
let!(:merge_request) { create(:labeled_merge_request, source_project: project, target_project: project, milestone: project_milestone, labels: [label]) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit dashboard_milestone_path(milestone.safe_title, title: milestone.title)
diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb
index 7787772a958..0db69432702 100644
--- a/spec/features/dashboard/milestones_spec.rb
+++ b/spec/features/dashboard/milestones_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard > Milestones' do
+describe 'Dashboard > Milestones' do
describe 'as anonymous user' do
before do
visit dashboard_milestones_path
@@ -16,7 +16,7 @@ feature 'Dashboard > Milestones' do
let(:project) { create(:project, namespace: user.namespace) }
let!(:milestone) { create(:milestone, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit dashboard_milestones_path
end
diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb
index 6c3093607b0..498775acff3 100644
--- a/spec/features/dashboard/project_member_activity_index_spec.rb
+++ b/spec/features/dashboard/project_member_activity_index_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-feature 'Project member activity', :js do
+describe 'Project member activity', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public, name: 'x', namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
def visit_activities_and_wait_with_event(event_type)
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index ef2f0b5b31a..4daacc61d85 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard Projects' do
+describe 'Dashboard Projects' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, name: 'awesome stuff') }
let(:project2) { create(:project, :public, name: 'Community project') }
@@ -29,9 +29,37 @@ feature 'Dashboard Projects' do
end
end
+ context 'when user has access to the project' do
+ it 'shows role badge' do
+ visit dashboard_projects_path
+
+ page.within '.user-access-role' do
+ expect(page).to have_content('Developer')
+ end
+ end
+
+ context 'when role changes', :use_clean_rails_memory_store_fragment_caching do
+ it 'displays the right role' do
+ visit dashboard_projects_path
+
+ page.within '.user-access-role' do
+ expect(page).to have_content('Developer')
+ end
+
+ project.members.last.update(access_level: 40)
+
+ visit dashboard_projects_path
+
+ page.within '.user-access-role' do
+ expect(page).to have_content('Maintainer')
+ end
+ end
+ end
+ end
+
context 'when last_repository_updated_at, last_activity_at and update_at are present' do
it 'shows the last_repository_updated_at attribute as the update date' do
- project.update_attributes!(last_repository_updated_at: Time.now, last_activity_at: 1.hour.ago)
+ project.update!(last_repository_updated_at: Time.now, last_activity_at: 1.hour.ago)
visit dashboard_projects_path
@@ -39,7 +67,7 @@ feature 'Dashboard Projects' do
end
it 'shows the last_activity_at attribute as the update date' do
- project.update_attributes!(last_repository_updated_at: 1.hour.ago, last_activity_at: Time.now)
+ project.update!(last_repository_updated_at: 1.hour.ago, last_activity_at: Time.now)
visit dashboard_projects_path
@@ -49,7 +77,7 @@ feature 'Dashboard Projects' do
context 'when last_repository_updated_at and last_activity_at are missing' do
it 'shows the updated_at attribute as the update date' do
- project.update_attributes!(last_repository_updated_at: nil, last_activity_at: nil)
+ project.update!(last_repository_updated_at: nil, last_activity_at: nil)
project.touch
visit dashboard_projects_path
@@ -121,7 +149,7 @@ feature 'Dashboard Projects' do
visit dashboard_projects_path
end
- scenario 'shows "Create merge request" button' do
+ it 'shows "Create merge request" button' do
expect(page).to have_content 'You pushed to feature'
within('#content-body') do
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index e41bd7a8419..e5c5ab9c039 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Dashboard shortcuts', :js do
+describe 'Dashboard shortcuts', :js do
context 'logged in' do
before do
sign_in(create(:user))
visit root_dashboard_path
end
- scenario 'Navigate to tabs' do
+ it 'Navigate to tabs' do
find('body').send_keys([:shift, 'I'])
check_page_title('Issues')
@@ -31,7 +31,7 @@ feature 'Dashboard shortcuts', :js do
visit explore_root_path
end
- scenario '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/todos/target_state_spec.rb b/spec/features/dashboard/todos/target_state_spec.rb
index 030a86d1c01..d55c32b3082 100644
--- a/spec/features/dashboard/todos/target_state_spec.rb
+++ b/spec/features/dashboard/todos/target_state_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Dashboard > Todo target states' do
+describe 'Dashboard > Todo target states' do
let(:user) { create(:user) }
let(:author) { create(:user) }
let(:project) { create(:project, :public) }
@@ -9,7 +9,7 @@ feature 'Dashboard > Todo target states' do
sign_in(user)
end
- scenario 'on a closed issue todo has closed label' do
+ it 'on a closed issue todo has closed label' do
issue_closed = create(:issue, state: 'closed')
create_todo issue_closed
visit dashboard_todos_path
@@ -19,7 +19,7 @@ feature 'Dashboard > Todo target states' do
end
end
- scenario 'on an open issue todo does not have an open label' do
+ it 'on an open issue todo does not have an open label' do
issue_open = create(:issue)
create_todo issue_open
visit dashboard_todos_path
@@ -29,7 +29,7 @@ feature 'Dashboard > Todo target states' do
end
end
- scenario 'on a merged merge request todo has merged label' do
+ it 'on a merged merge request todo has merged label' do
mr_merged = create(:merge_request, :simple, :merged, author: user)
create_todo mr_merged
visit dashboard_todos_path
@@ -39,7 +39,7 @@ feature 'Dashboard > Todo target states' do
end
end
- scenario 'on a closed merge request todo has closed label' do
+ it 'on a closed merge request todo has closed label' do
mr_closed = create(:merge_request, :simple, :closed, author: user)
create_todo mr_closed
visit dashboard_todos_path
@@ -49,7 +49,7 @@ feature 'Dashboard > Todo target states' do
end
end
- scenario 'on an open merge request todo does not have an open label' do
+ it 'on an open merge request todo does not have an open label' do
mr_open = create(:merge_request, :simple, author: user)
create_todo mr_open
visit dashboard_todos_path
diff --git a/spec/features/dashboard/todos/todos_filtering_spec.rb b/spec/features/dashboard/todos/todos_filtering_spec.rb
index 7b359b0c651..85f865321cf 100644
--- a/spec/features/dashboard/todos/todos_filtering_spec.rb
+++ b/spec/features/dashboard/todos/todos_filtering_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard > User filters todos', :js do
+describe 'Dashboard > User filters todos', :js do
let(:user_1) { create(:user, username: 'user_1', name: 'user_1') }
let(:user_2) { create(:user, username: 'user_2', name: 'user_2') }
diff --git a/spec/features/dashboard/todos/todos_sorting_spec.rb b/spec/features/dashboard/todos/todos_sorting_spec.rb
index 10e3ad843fd..b87caaa1c07 100644
--- a/spec/features/dashboard/todos/todos_sorting_spec.rb
+++ b/spec/features/dashboard/todos/todos_sorting_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard > User sorts todos' do
+describe 'Dashboard > User sorts todos' do
let(:user) { create(:user) }
let(:project) { create(:project) }
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index 5ed20b02a6e..96b22a0f64b 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Dashboard Todos' do
+describe 'Dashboard Todos' do
let(:user) { create(:user) }
let(:author) { create(:user) }
let(:project) { create(:project, :public) }
diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb
index 92f4d4b854c..3746d37b9cd 100644
--- a/spec/features/dashboard/user_filters_projects_spec.rb
+++ b/spec/features/dashboard/user_filters_projects_spec.rb
@@ -7,7 +7,7 @@ describe 'Dashboard > User filters projects' do
let(:project2) { create(:project, name: 'Treasure', namespace: user2.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb
index 69d35cdbc72..7a3b1d7ed47 100644
--- a/spec/features/discussion_comments/commit_spec.rb
+++ b/spec/features/discussion_comments/commit_spec.rb
@@ -8,7 +8,7 @@ describe 'Discussion Comments Commit', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_commit_path(project, sample_commit.id)
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
index 9812eaf3420..5ec19460bbd 100644
--- a/spec/features/discussion_comments/issue_spec.rb
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -6,7 +6,7 @@ describe 'Discussion Comments Issue', :js do
let(:issue) { create(:issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb
index b0019c32189..f940e973923 100644
--- a/spec/features/discussion_comments/merge_request_spec.rb
+++ b/spec/features/discussion_comments/merge_request_spec.rb
@@ -6,7 +6,7 @@ describe 'Discussion Comments Merge Request', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb
index 4a236c4639b..d330e89505e 100644
--- a/spec/features/discussion_comments/snippets_spec.rb
+++ b/spec/features/discussion_comments/snippets_spec.rb
@@ -6,7 +6,7 @@ describe 'Discussion Comments Snippet', :js do
let(:snippet) { create(:project_snippet, :private, project: project, author: user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_snippet_path(project, snippet)
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
index 31862b2e8f4..8d801161148 100644
--- a/spec/features/expand_collapse_diffs_spec.rb
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Expand and collapse diffs', :js do
+describe 'Expand and collapse diffs', :js do
let(:branch) { 'expand-collapse-diffs' }
let(:project) { create(:project, :repository) }
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index ad02b454aee..8ed4051856e 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -53,14 +53,14 @@ describe 'Explore Groups page', :js do
expect(find('.js-groups-list-holder .content-list li:first-child .stats .number-projects')).to have_text("1")
# Archive project
- empty_project.archive!
+ ::Projects::UpdateService.new(empty_project, user, archived: true).execute
visit explore_groups_path
# Check project count
expect(find('.js-groups-list-holder .content-list li:first-child .stats .number-projects')).to have_text("0")
# Unarchive project
- empty_project.unarchive!
+ ::Projects::UpdateService.new(empty_project, user, archived: false).execute
visit explore_groups_path
# Check project count
diff --git a/spec/features/explore/new_menu_spec.rb b/spec/features/explore/new_menu_spec.rb
index 2516db5422f..0a88988ea09 100644
--- a/spec/features/explore/new_menu_spec.rb
+++ b/spec/features/explore/new_menu_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Top Plus Menu', :js do
+describe 'Top Plus Menu', :js do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
@@ -15,7 +15,7 @@ feature 'Top Plus Menu', :js do
sign_in(user)
end
- scenario 'click on New project shows new project page' do
+ it 'click on New project shows new project page' do
visit root_dashboard_path
click_topmenuitem("New project")
@@ -24,7 +24,7 @@ feature 'Top Plus Menu', :js do
expect(page).to have_content('Project name')
end
- scenario 'click on New group shows new group page' do
+ it 'click on New group shows new group page' do
visit root_dashboard_path
click_topmenuitem("New group")
@@ -33,7 +33,7 @@ feature 'Top Plus Menu', :js do
expect(page).to have_content('Group name')
end
- scenario 'click on New snippet shows new snippet page' do
+ it 'click on New snippet shows new snippet page' do
visit root_dashboard_path
click_topmenuitem("New snippet")
@@ -42,7 +42,7 @@ feature 'Top Plus Menu', :js do
expect(page).to have_content('Title')
end
- scenario 'click on New issue shows new issue page' do
+ it 'click on New issue shows new issue page' do
visit project_path(project)
click_topmenuitem("New issue")
@@ -51,7 +51,7 @@ feature 'Top Plus Menu', :js do
expect(page).to have_content('Title')
end
- scenario 'click on New merge request shows new merge request page' do
+ it 'click on New merge request shows new merge request page' do
visit project_path(project)
click_topmenuitem("New merge request")
@@ -61,7 +61,7 @@ feature 'Top Plus Menu', :js do
expect(page).to have_content('Target branch')
end
- scenario 'click on New project snippet shows new snippet page' do
+ it 'click on New project snippet shows new snippet page' do
visit project_path(project)
page.within '.header-content' do
@@ -74,7 +74,7 @@ feature 'Top Plus Menu', :js do
expect(page).to have_content('Title')
end
- scenario 'Click on New subgroup shows new group page', :nested_groups do
+ it 'Click on New subgroup shows new group page', :nested_groups do
visit group_path(group)
click_topmenuitem("New subgroup")
@@ -83,7 +83,7 @@ feature 'Top Plus Menu', :js do
expect(page).to have_content('Group name')
end
- scenario 'Click on New project in group shows new project page' do
+ it 'Click on New project in group shows new project page' do
visit group_path(group)
page.within '.header-content' do
@@ -107,7 +107,7 @@ feature 'Top Plus Menu', :js do
sign_in(guest_user)
end
- scenario 'click on New issue shows new issue page' do
+ it 'click on New issue shows new issue page' do
visit project_path(project)
click_topmenuitem("New issue")
@@ -116,37 +116,37 @@ feature 'Top Plus Menu', :js do
expect(page).to have_content('Title')
end
- scenario 'has no New merge request menu item' do
+ it 'has no New merge request menu item' do
visit project_path(project)
hasnot_topmenuitem("New merge request")
end
- scenario 'has no New project snippet menu item' do
+ it 'has no New project snippet menu item' do
visit project_path(project)
expect(find('.header-new.dropdown')).not_to have_selector('.header-new-project-snippet')
end
- scenario 'public project has no New merge request menu item' do
+ it 'public project has no New merge request menu item' do
visit project_path(public_project)
hasnot_topmenuitem("New merge request")
end
- scenario 'public project has no New project snippet menu item' do
+ it 'public project has no New project snippet menu item' do
visit project_path(public_project)
expect(find('.header-new.dropdown')).not_to have_selector('.header-new-project-snippet')
end
- scenario 'has no New subgroup menu item' do
+ it 'has no New subgroup menu item' do
visit group_path(group)
hasnot_topmenuitem("New subgroup")
end
- scenario 'has no New project for group menu item' do
+ it 'has no New project for group menu item' do
visit group_path(group)
expect(find('.header-new.dropdown')).not_to have_selector('.header-new-group-project')
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index f8c4db1403c..d7692181453 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-feature 'Global search' do
+describe 'Global search' do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/group_variables_spec.rb b/spec/features/group_variables_spec.rb
index f7863807572..89e0cdd8ed7 100644
--- a/spec/features/group_variables_spec.rb
+++ b/spec/features/group_variables_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Group variables', :js do
+describe 'Group variables', :js do
let(:user) { create(:user) }
let(:group) { create(:group) }
let!(:variable) { create(:ci_group_variable, key: 'test_key', value: 'test value', group: group) }
let(:page_path) { group_settings_ci_cd_path(group) }
- background do
- group.add_master(user)
+ before do
+ group.add_maintainer(user)
gitlab_sign_in(user)
visit page_path
diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb
index 0d7d3771071..88fc12ae1e4 100644
--- a/spec/features/groups/activity_spec.rb
+++ b/spec/features/groups/activity_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Group activity page' do
+describe 'Group activity page' do
let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
let(:group) { create(:group) }
let(:path) { activity_group_path(group) }
@@ -23,7 +23,7 @@ feature 'Group activity page' do
let(:project) { create(:project, :public, namespace: group) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit path
end
diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb
new file mode 100644
index 00000000000..86a4a016f3d
--- /dev/null
+++ b/spec/features/groups/board_spec.rb
@@ -0,0 +1,35 @@
+require 'rails_helper'
+
+describe 'Group Boards' do
+ let(:group) { create(:group) }
+ let!(:project) { create(:project_empty_repo, group: group) }
+ let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
+
+ before do
+ sign_in(user)
+ end
+
+ context 'Creates a an issue', :js do
+ before do
+ visit group_boards_path(group)
+ end
+
+ 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
+ expect(find('.board-new-issue-form')).to be_visible
+
+ fill_in 'issue_title', with: issue_title
+ find('.dropdown-menu-toggle').click
+
+ wait_for_requests
+
+ click_link(project.name)
+ click_button 'Submit issue'
+
+ expect(page).to have_content(issue_title)
+ end
+ end
+ end
+end
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index 04217fec06c..8f5ca781b2c 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Group empty states' do
+describe 'Group empty states' do
let(:group) { create(:group) }
let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
@@ -19,7 +19,7 @@ feature 'Group empty states' do
let(:project) { create(:project, namespace: group) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "the project has #{issuable_name}s" do
@@ -59,6 +59,18 @@ feature 'Group empty states' do
end
end
+ shared_examples "no projects" do
+ it 'displays an empty state' do
+ expect(page).to have_selector('.empty-state')
+ end
+
+ it "does not show a new #{issuable_name} button" do
+ within '.empty-state' do
+ expect(page).not_to have_link("create #{issuable_name}")
+ end
+ end
+ end
+
context 'group without a project' do
context 'group has a subgroup', :nested_groups do
let(:subgroup) { create(:group, parent: group) }
@@ -92,16 +104,18 @@ feature 'Group empty states' do
visit path
end
- it 'displays an empty state' do
- expect(page).to have_selector('.empty-state')
- end
+ it_behaves_like "no projects"
+ end
+ end
- it "shows a new #{issuable_name} button" do
- within '.empty-state' do
- expect(page).not_to have_link("create #{issuable_name}")
- end
- end
+ context 'group has only a project with issues disabled' do
+ let(:project_with_issues_disabled) { create(:empty_project, :issues_disabled, group: group) }
+
+ before do
+ visit path
end
+
+ it_behaves_like "no projects"
end
end
end
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index bf329b0bb94..59254ecc982 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -1,10 +1,10 @@
require 'spec_helper'
-feature 'Edit group settings' do
- given(:user) { create(:user) }
- given(:group) { create(:group, path: 'foo') }
+describe 'Edit group settings' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group, path: 'foo') }
- background do
+ before do
group.add_owner(user)
sign_in(user)
end
@@ -14,14 +14,14 @@ feature 'Edit group settings' do
let(:old_group_full_path) { "/#{group.path}" }
let(:new_group_full_path) { "/#{new_group_path}" }
- scenario 'the group is accessible via the new path' do
+ it 'the group is accessible via the new path' do
update_path(new_group_path)
visit new_group_full_path
expect(current_path).to eq(new_group_full_path)
expect(find('h1.group-title')).to have_content(group.name)
end
- scenario 'the old group path redirects to the new path' do
+ it 'the old group path redirects to the new path' do
update_path(new_group_path)
visit old_group_full_path
expect(current_path).to eq(new_group_full_path)
@@ -29,18 +29,18 @@ feature 'Edit group settings' do
end
context 'with a subgroup' do
- given!(:subgroup) { create(:group, parent: group, path: 'subgroup') }
- given(:old_subgroup_full_path) { "/#{group.path}/#{subgroup.path}" }
- given(:new_subgroup_full_path) { "/#{new_group_path}/#{subgroup.path}" }
+ let!(:subgroup) { create(:group, parent: group, path: 'subgroup') }
+ let(:old_subgroup_full_path) { "/#{group.path}/#{subgroup.path}" }
+ let(:new_subgroup_full_path) { "/#{new_group_path}/#{subgroup.path}" }
- scenario 'the subgroup is accessible via the new path' do
+ it 'the subgroup is accessible via the new path' do
update_path(new_group_path)
visit new_subgroup_full_path
expect(current_path).to eq(new_subgroup_full_path)
expect(find('h1.group-title')).to have_content(subgroup.name)
end
- scenario 'the old subgroup path redirects to the new path' do
+ it 'the old subgroup path redirects to the new path' do
update_path(new_group_path)
visit old_subgroup_full_path
expect(current_path).to eq(new_subgroup_full_path)
@@ -49,9 +49,9 @@ feature 'Edit group settings' do
end
context 'with a project' do
- given!(:project) { create(:project, group: group) }
- given(:old_project_full_path) { "/#{group.path}/#{project.path}" }
- given(:new_project_full_path) { "/#{new_group_path}/#{project.path}" }
+ let!(:project) { create(:project, group: group) }
+ let(:old_project_full_path) { "/#{group.path}/#{project.path}" }
+ let(:new_project_full_path) { "/#{new_group_path}/#{project.path}" }
before(:context) do
TestEnv.clean_test_path
@@ -61,14 +61,14 @@ feature 'Edit group settings' do
TestEnv.clean_test_path
end
- scenario 'the project is accessible via the new path' do
+ it 'the project is accessible via the new path' do
update_path(new_group_path)
visit new_project_full_path
expect(current_path).to eq(new_project_full_path)
expect(find('.breadcrumbs')).to have_content(project.path)
end
- scenario 'the old project path redirects to the new path' do
+ it 'the old project path redirects to the new path' do
update_path(new_group_path)
visit old_project_full_path
expect(current_path).to eq(new_project_full_path)
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 111a24c0d94..176f4a668ff 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -1,10 +1,11 @@
require 'spec_helper'
-feature 'Group issues page' do
+describe 'Group issues page' do
include FilteredSearchHelpers
let(:group) { create(:group) }
let(:project) { create(:project, :public, group: group)}
+ let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) }
let(:path) { issues_group_path(group) }
context 'with shared examples' do
@@ -51,6 +52,7 @@ feature 'Group issues page' do
context 'issues list', :nested_groups do
let(:subgroup) { create(:group, parent: group) }
let(:subgroup_project) { create(:project, :public, group: subgroup)}
+ let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
let!(:issue) { create(:issue, project: project, title: 'root group issue') }
let!(:subgroup_issue) { create(:issue, project: subgroup_project, title: 'subgroup issue') }
@@ -66,7 +68,7 @@ feature 'Group issues page' do
context 'when project is archived' do
before do
- project.archive!
+ ::Projects::UpdateService.new(project, user_in_group, archived: true).execute
end
it 'does not render issue' do
@@ -76,4 +78,25 @@ feature 'Group issues page' do
end
end
end
+
+ context 'projects with issues disabled' do
+ describe 'issue dropdown' do
+ let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
+
+ before do
+ [project, project_with_issues_disabled].each { |project| project.add_maintainer(user_in_group) }
+ sign_in(user_in_group)
+ visit issues_group_path(group)
+ end
+
+ it 'shows projects only with issues feature enabled', :js do
+ find('.new-project-item-link').click
+
+ page.within('.select2-results') do
+ expect(page).to have_content(project.full_name)
+ expect(page).not_to have_content(project_with_issues_disabled.full_name)
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/groups/labels/edit_spec.rb b/spec/features/groups/labels/edit_spec.rb
index fb338127861..7cfc27a8905 100644
--- a/spec/features/groups/labels/edit_spec.rb
+++ b/spec/features/groups/labels/edit_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
-feature 'Edit group label' do
- given(:user) { create(:user) }
- given(:group) { create(:group) }
- given(:label) { create(:group_label, group: group) }
+describe 'Edit group label' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:label) { create(:group_label, group: group) }
- background do
+ before do
group.add_owner(user)
sign_in(user)
visit edit_group_label_path(group, label)
end
- scenario 'update label with new title' do
+ it 'update label with new title' do
fill_in 'label_title', with: 'new label name'
click_button 'Save changes'
diff --git a/spec/features/groups/labels/index_spec.rb b/spec/features/groups/labels/index_spec.rb
index 6c1b43a9013..0ce7dad4040 100644
--- a/spec/features/groups/labels/index_spec.rb
+++ b/spec/features/groups/labels/index_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
-feature 'Group labels' do
+describe 'Group labels' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let!(:label) { create(:group_label, group: group) }
- background do
+ before do
group.add_owner(user)
sign_in(user)
visit group_labels_path(group)
end
- scenario 'label has edit button', :js do
+ it 'label has edit button', :js do
expect(page).to have_selector('.label-action.edit')
end
end
diff --git a/spec/features/groups/labels/subscription_spec.rb b/spec/features/groups/labels/subscription_spec.rb
index 2e06caf98f6..d9543bfa97f 100644
--- a/spec/features/groups/labels/subscription_spec.rb
+++ b/spec/features/groups/labels/subscription_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Labels subscription' do
+describe 'Labels subscription' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let!(:feature) { create(:group_label, group: group, title: 'feature') }
@@ -11,7 +11,7 @@ feature 'Labels subscription' do
gitlab_sign_in user
end
- scenario 'users can subscribe/unsubscribe to group labels', :js do
+ it 'users can subscribe/unsubscribe to group labels', :js do
visit group_labels_path(group)
expect(page).to have_content('feature')
diff --git a/spec/features/groups/labels/user_sees_links_to_issuables.rb b/spec/features/groups/labels/user_sees_links_to_issuables.rb
index 5d6290d2109..1fdba78fa6c 100644
--- a/spec/features/groups/labels/user_sees_links_to_issuables.rb
+++ b/spec/features/groups/labels/user_sees_links_to_issuables.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Groups > Labels > User sees links to issuables' do
+describe 'Groups > Labels > User sees links to issuables' do
set(:group) { create(:group, :public) }
before do
@@ -8,7 +8,7 @@ feature 'Groups > Labels > User sees links to issuables' do
visit group_labels_path(group)
end
- scenario 'shows links to MRs and issues' do
+ it 'shows links to MRs and issues' do
expect(page).to have_link('view merge requests')
expect(page).to have_link('view open issues')
end
diff --git a/spec/features/groups/members/filter_members_spec.rb b/spec/features/groups/members/filter_members_spec.rb
index 5ddb5894624..386d81546d7 100644
--- a/spec/features/groups/members/filter_members_spec.rb
+++ b/spec/features/groups/members/filter_members_spec.rb
@@ -1,18 +1,18 @@
require 'spec_helper'
-feature 'Groups > Members > Filter members' do
+describe 'Groups > Members > Filter members' do
let(:user) { create(:user) }
let(:user_with_2fa) { create(:user, :two_factor_via_otp) }
let(:group) { create(:group) }
- background do
+ before do
group.add_owner(user)
- group.add_master(user_with_2fa)
+ group.add_maintainer(user_with_2fa)
sign_in(user)
end
- scenario 'shows all members' do
+ it 'shows all members' do
visit_members_list
expect(first_member).to include(user.name)
@@ -20,7 +20,7 @@ feature 'Groups > Members > Filter members' do
expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Everyone')
end
- scenario 'shows only 2FA members' do
+ it 'shows only 2FA members' do
visit_members_list(two_factor: 'enabled')
expect(first_member).to include(user_with_2fa.name)
@@ -28,7 +28,7 @@ feature 'Groups > Members > Filter members' do
expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Enabled')
end
- scenario 'shows only non 2FA members' do
+ it 'shows only non 2FA members' do
visit_members_list(two_factor: 'disabled')
expect(first_member).to include(user.name)
diff --git a/spec/features/groups/members/leave_group_spec.rb b/spec/features/groups/members/leave_group_spec.rb
index 067a2dc850f..7a91c64d7db 100644
--- a/spec/features/groups/members/leave_group_spec.rb
+++ b/spec/features/groups/members/leave_group_spec.rb
@@ -1,15 +1,15 @@
require 'spec_helper'
-feature 'Groups > Members > Leave group' do
+describe 'Groups > Members > Leave group' do
let(:user) { create(:user) }
let(:other_user) { create(:user) }
let(:group) { create(:group) }
- background do
+ before do
gitlab_sign_in(user)
end
- scenario 'guest leaves the group' do
+ it 'guest leaves the group' do
group.add_guest(user)
group.add_owner(other_user)
@@ -21,7 +21,7 @@ feature 'Groups > Members > Leave group' do
expect(group.users).not_to include(user)
end
- scenario 'guest leaves the group as last member' do
+ it 'guest leaves the group as last member' do
group.add_guest(user)
visit group_path(group)
@@ -32,7 +32,7 @@ feature 'Groups > Members > Leave group' do
expect(group.users).not_to include(user)
end
- scenario 'owner leaves the group if they is not the last owner' do
+ it 'owner leaves the group if they is not the last owner' do
group.add_owner(user)
group.add_owner(other_user)
@@ -44,7 +44,7 @@ feature 'Groups > Members > Leave group' do
expect(group.users).not_to include(user)
end
- scenario 'owner can not leave the group if they is a last owner' do
+ it 'owner can not leave the group if they is a last owner' do
group.add_owner(user)
visit group_path(group)
diff --git a/spec/features/groups/members/list_members_spec.rb b/spec/features/groups/members/list_members_spec.rb
index 5c5d48c3623..e1587a8b6a5 100644
--- a/spec/features/groups/members/list_members_spec.rb
+++ b/spec/features/groups/members/list_members_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Groups > Members > List members' do
+describe 'Groups > Members > List members' do
include Select2Helper
let(:user1) { create(:user, name: 'John Doe') }
@@ -8,11 +8,11 @@ feature 'Groups > Members > List members' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
- background do
- gitlab_sign_in(user1)
+ before do
+ sign_in(user1)
end
- scenario 'show members from current group and parent', :nested_groups do
+ it 'show members from current group and parent', :nested_groups do
group.add_developer(user1)
nested_group.add_developer(user2)
@@ -22,7 +22,7 @@ feature 'Groups > Members > List members' do
expect(second_row.text).to include(user2.name)
end
- scenario 'show user once if member of both current group and parent', :nested_groups do
+ it 'show user once if member of both current group and parent', :nested_groups do
group.add_developer(user1)
nested_group.add_developer(user1)
@@ -32,6 +32,18 @@ feature 'Groups > Members > List members' do
expect(second_row).to be_blank
end
+ describe 'showing status of members' do
+ before do
+ group.add_developer(user2)
+ end
+
+ subject { visit group_group_members_path(group) }
+
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { user2 }
+ end
+ end
+
def first_row
page.all('ul.content-list > li')[0]
end
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index 21f7b4999ad..0eda2c7f26d 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
-feature 'Groups > Members > Manage members' do
+describe 'Groups > Members > Manage members' do
include Select2Helper
let(:user1) { create(:user, name: 'John Doe') }
let(:user2) { create(:user, name: 'Mary Jane') }
let(:group) { create(:group) }
- background do
+ before do
sign_in(user1)
end
- scenario 'update user to owner level', :js do
+ it 'update user to owner level', :js do
group.add_owner(user1)
group.add_developer(user2)
@@ -25,7 +25,7 @@ feature 'Groups > Members > Manage members' do
end
end
- scenario 'add user to group', :js do
+ it 'add user to group', :js do
group.add_owner(user1)
visit group_group_members_path(group)
@@ -38,7 +38,7 @@ feature 'Groups > Members > Manage members' do
end
end
- scenario 'do not disclose email addresses', :js do
+ it 'do not disclose email addresses', :js do
group.add_owner(user1)
create(:user, email: 'undisclosed_email@gitlab.com', name: "Jane 'invisible' Doe")
@@ -59,7 +59,7 @@ feature 'Groups > Members > Manage members' do
expect(page).to have_content("Jane 'invisible' Doe")
end
- scenario 'remove user from group', :js do
+ it 'remove user from group', :js do
group.add_owner(user1)
group.add_developer(user2)
@@ -75,7 +75,7 @@ feature 'Groups > Members > Manage members' do
expect(group.users).not_to include(user2)
end
- scenario 'add yourself to group when already an owner', :js do
+ it 'add yourself to group when already an owner', :js do
group.add_owner(user1)
visit group_group_members_path(group)
@@ -88,7 +88,7 @@ feature 'Groups > Members > Manage members' do
end
end
- scenario 'invite user to group', :js do
+ it 'invite user to group', :js do
group.add_owner(user1)
visit group_group_members_path(group)
@@ -102,7 +102,7 @@ feature 'Groups > Members > Manage members' do
end
end
- scenario 'guest can not manage other users' do
+ it 'guest can not manage other users' do
group.add_guest(user1)
group.add_developer(user2)
diff --git a/spec/features/groups/members/master_manages_access_requests_spec.rb b/spec/features/groups/members/master_manages_access_requests_spec.rb
index 2fd6d1ec599..bd615c99412 100644
--- a/spec/features/groups/members/master_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/master_manages_access_requests_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
-feature 'Groups > Members > Master manages access requests' do
- it_behaves_like 'Master manages access requests' do
+describe 'Groups > Members > Maintainer manages access requests' do
+ it_behaves_like 'Maintainer manages access requests' do
let(:entity) { create(:group, :public, :access_requestable) }
let(:members_page_path) { group_group_members_path(entity) }
end
diff --git a/spec/features/groups/members/request_access_spec.rb b/spec/features/groups/members/request_access_spec.rb
index 10389a74703..94510f917a3 100644
--- a/spec/features/groups/members/request_access_spec.rb
+++ b/spec/features/groups/members/request_access_spec.rb
@@ -1,25 +1,25 @@
require 'spec_helper'
-feature 'Groups > Members > Request access' do
+describe 'Groups > Members > Request access' do
let(:user) { create(:user) }
let(:owner) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) }
let!(:project) { create(:project, :private, namespace: group) }
- background do
+ before do
group.add_owner(owner)
sign_in(user)
visit group_path(group)
end
- scenario 'request access feature is disabled' do
- group.update_attributes(request_access_enabled: false)
+ it 'request access feature is disabled' do
+ group.update(request_access_enabled: false)
visit group_path(group)
expect(page).not_to have_content 'Request Access'
end
- scenario 'user can request access to a group' do
+ it 'user can request access to a group' do
perform_enqueued_jobs { click_link 'Request Access' }
expect(ActionMailer::Base.deliveries.last.to).to eq [owner.notification_email]
@@ -32,13 +32,13 @@ feature 'Groups > Members > Request access' do
expect(page).not_to have_content 'Leave group'
end
- scenario 'user does not see private projects' do
+ it 'user does not see private projects' do
perform_enqueued_jobs { click_link 'Request Access' }
expect(page).not_to have_content project.name
end
- scenario 'user does not see group in the Dashboard > Groups page' do
+ it 'user does not see group in the Dashboard > Groups page' do
perform_enqueued_jobs { click_link 'Request Access' }
visit dashboard_groups_path
@@ -46,7 +46,7 @@ feature 'Groups > Members > Request access' do
expect(page).not_to have_content group.name
end
- scenario 'user is not listed in the group members page' do
+ it 'user is not listed in the group members page' do
click_link 'Request Access'
expect(group.requesters.exists?(user_id: user)).to be_truthy
@@ -58,7 +58,7 @@ feature 'Groups > Members > Request access' do
end
end
- scenario 'user can withdraw its request for access' do
+ it 'user can withdraw its request for access' do
click_link 'Request Access'
expect(group.requesters.exists?(user_id: user)).to be_truthy
@@ -69,7 +69,7 @@ feature 'Groups > Members > Request access' do
expect(page).to have_content 'Your access request to the group has been withdrawn.'
end
- scenario 'member does not see the request access button' do
+ it 'member does not see the request access button' do
group.add_owner(user)
visit group_path(group)
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index e175ad04f86..ee32f6d77fe 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -1,18 +1,18 @@
require 'spec_helper'
-feature 'Groups > Members > Sort members' do
+describe 'Groups > Members > Sort members' do
let(:owner) { create(:user, name: 'John Doe') }
let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) }
let(:group) { create(:group) }
- background do
+ 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)
sign_in(owner)
end
- scenario 'sorts alphabetically by default' do
+ it 'sorts alphabetically by default' do
visit_members_list(sort: nil)
expect(first_member).to include(owner.name)
@@ -20,7 +20,7 @@ feature 'Groups > Members > Sort members' do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
- scenario 'sorts by access level ascending' do
+ it 'sorts by access level ascending' do
visit_members_list(sort: :access_level_asc)
expect(first_member).to include(developer.name)
@@ -28,7 +28,7 @@ feature 'Groups > Members > Sort members' do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
end
- scenario 'sorts by access level descending' do
+ it 'sorts by access level descending' do
visit_members_list(sort: :access_level_desc)
expect(first_member).to include(owner.name)
@@ -36,7 +36,7 @@ feature 'Groups > Members > Sort members' do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
end
- scenario 'sorts by last joined' do
+ it 'sorts by last joined' do
visit_members_list(sort: :last_joined)
expect(first_member).to include(developer.name)
@@ -44,7 +44,7 @@ feature 'Groups > Members > Sort members' do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
end
- scenario 'sorts by oldest joined' do
+ it 'sorts by oldest joined' do
visit_members_list(sort: :oldest_joined)
expect(first_member).to include(owner.name)
@@ -52,7 +52,7 @@ feature 'Groups > Members > Sort members' do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
end
- scenario 'sorts by name ascending' do
+ it 'sorts by name ascending' do
visit_members_list(sort: :name_asc)
expect(first_member).to include(owner.name)
@@ -60,7 +60,7 @@ feature 'Groups > Members > Sort members' do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
- scenario 'sorts by name descending' do
+ it 'sorts by name descending' do
visit_members_list(sort: :name_desc)
expect(first_member).to include(developer.name)
@@ -68,7 +68,7 @@ feature 'Groups > Members > Sort members' do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
- scenario 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
+ it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :recent_sign_in)
expect(first_member).to include(owner.name)
@@ -76,7 +76,7 @@ feature 'Groups > Members > Sort members' do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
- scenario 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
+ it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :oldest_sign_in)
expect(first_member).to include(developer.name)
diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb
index 672ae785c2d..54a8016c157 100644
--- a/spec/features/groups/merge_requests_spec.rb
+++ b/spec/features/groups/merge_requests_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Group merge requests page' do
+describe 'Group merge requests page' do
include FilteredSearchHelpers
let(:path) { merge_requests_group_path(group) }
@@ -56,4 +56,21 @@ feature 'Group merge requests page' do
expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name)
end
end
+
+ describe 'new merge request dropdown' do
+ let(:project_with_merge_requests_disabled) { create(:project, :merge_requests_disabled, group: group) }
+
+ before do
+ visit path
+ end
+
+ it 'shows projects only with merge requests feature enabled', :js do
+ find('.new-project-item-link').click
+
+ page.within('.select2-results') do
+ expect(page).to have_content(project.name_with_namespace)
+ expect(page).not_to have_content(project_with_merge_requests_disabled.name_with_namespace)
+ end
+ end
+ end
end
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 20337f1d3b0..80df0618a6a 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -1,9 +1,9 @@
require 'rails_helper'
-feature 'Group milestones' do
+describe 'Group milestones' do
let(:group) { create(:group) }
let!(:project) { create(:project_empty_repo, group: group) }
- let(:user) { create(:group_member, :master, user: create(:user), group: group ).user }
+ let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
around do |example|
Timecop.freeze { example.run }
@@ -107,19 +107,6 @@ feature 'Group milestones' do
expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1)
end
- it 'updates milestone' do
- page.within(".milestones #milestone_#{active_group_milestone.id}") do
- click_link('Edit')
- end
-
- page.within('.milestone-form') do
- fill_in 'milestone_title', with: 'new title'
- click_button('Update milestone')
- end
-
- expect(find('#content-body h2')).to have_content('new title')
- end
-
it 'shows milestone detail and supports its edit' do
page.within(".milestones #milestone_#{active_group_milestone.id}") do
click_link(active_group_milestone.title)
diff --git a/spec/features/groups/milestones_sorting_spec.rb b/spec/features/groups/milestones_sorting_spec.rb
index a0fe40cf1d3..bc226ff41c1 100644
--- a/spec/features/groups/milestones_sorting_spec.rb
+++ b/spec/features/groups/milestones_sorting_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Milestones sorting', :js do
+describe 'Milestones sorting', :js do
let(:group) { create(:group) }
let!(:project) { create(:project_empty_repo, group: group) }
let!(:other_project) { create(:project_empty_repo, group: group) }
@@ -9,13 +9,13 @@ feature 'Milestones sorting', :js do
let!(:project_milestone2) { create(:milestone, project: project, title: 'v2.0', due_date: 5.days.from_now) }
let!(:other_project_milestone2) { create(:milestone, project: other_project, title: 'v2.0', due_date: 5.days.from_now) }
let!(:group_milestone) { create(:milestone, group: group, title: 'v3.0', due_date: 7.days.from_now) }
- let(:user) { create(:group_member, :master, user: create(:user), group: group ).user }
+ let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
before do
sign_in(user)
end
- scenario 'visit group milestones and sort by due_date_asc' do
+ it 'visit group milestones and sort by due_date_asc' do
visit group_milestones_path(group)
expect(page).to have_button('Due soon')
diff --git a/spec/features/groups/settings/group_badges_spec.rb b/spec/features/groups/settings/group_badges_spec.rb
index a99da4a119b..070a4a31ffa 100644
--- a/spec/features/groups/settings/group_badges_spec.rb
+++ b/spec/features/groups/settings/group_badges_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Group Badges' do
+describe 'Group Badges' do
include WaitForRequests
let(:user) { create(:user) }
diff --git a/spec/features/groups/share_lock_spec.rb b/spec/features/groups/share_lock_spec.rb
index cefbc15e068..5bbe77019ca 100644
--- a/spec/features/groups/share_lock_spec.rb
+++ b/spec/features/groups/share_lock_spec.rb
@@ -1,19 +1,19 @@
require 'spec_helper'
-feature 'Group share with group lock' do
- given(:root_owner) { create(:user) }
- given(:root_group) { create(:group) }
+describe 'Group share with group lock' do
+ let(:root_owner) { create(:user) }
+ let(:root_group) { create(:group) }
- background do
+ before do
root_group.add_owner(root_owner)
sign_in(root_owner)
end
context 'with a subgroup', :nested_groups do
- given!(:subgroup) { create(:group, parent: root_group) }
+ let!(:subgroup) { create(:group, parent: root_group) }
context 'when enabling the parent group share with group lock' do
- scenario 'the subgroup share with group lock becomes enabled' do
+ it 'the subgroup share with group lock becomes enabled' do
visit edit_group_path(root_group)
enable_group_lock
@@ -23,14 +23,14 @@ feature 'Group share with group lock' do
end
context 'when disabling the parent group share with group lock (which was already enabled)' do
- background do
+ before do
visit edit_group_path(root_group)
enable_group_lock
end
context 'and the subgroup share with group lock is enabled' do
- scenario 'the subgroup share with group lock does not change' do
+ it 'the subgroup share with group lock does not change' do
visit edit_group_path(root_group)
disable_group_lock
@@ -40,13 +40,13 @@ feature 'Group share with group lock' do
end
context 'but the subgroup share with group lock is disabled' do
- background do
+ before do
visit edit_group_path(subgroup)
disable_group_lock
end
- scenario 'the subgroup share with group lock does not change' do
+ it 'the subgroup share with group lock does not change' do
visit edit_group_path(root_group)
disable_group_lock
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index b7a7aa0e174..ac961e98a61 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Group show page' do
+describe 'Group show page' do
let(:group) { create(:group) }
let(:path) { group_path(group) }
diff --git a/spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb b/spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb
index 5ed4f3ad2bc..6d6f206d761 100644
--- a/spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb
+++ b/spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Groups > User sees users dropdowns in issuables list' do
+describe 'Groups > User sees users dropdowns in issuables list' do
let(:entity) { create(:group) }
let(:user_in_dropdown) { create(:user) }
let!(:user_not_in_dropdown) { create(:user) }
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 236768b5d7f..e62bd6f8187 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Group' do
+describe 'Group' do
before do
sign_in(create(:admin))
end
@@ -154,6 +154,12 @@ feature 'Group' do
end
end
+ it 'focuses confirmation field on remove group' do
+ click_button('Remove group')
+
+ expect(page).to have_selector '#confirm_name_input:focus'
+ end
+
it 'removes group' do
expect { remove_with_confirm('Remove group', group.path) }.to change {Group.count}.by(-1)
expect(group.members.all.count).to be_zero
diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb
index a4d05c25a90..ea714934ae7 100644
--- a/spec/features/ics/dashboard_issues_spec.rb
+++ b/spec/features/ics/dashboard_issues_spec.rb
@@ -8,7 +8,7 @@ describe 'Dashboard Issues Calendar Feed' do
let(:milestone) { create(:milestone, project_id: project.id, title: 'v1.0') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when authenticated' do
diff --git a/spec/features/ide_spec.rb b/spec/features/ide_spec.rb
index b3f24c2966d..65989c36c1e 100644
--- a/spec/features/ide_spec.rb
+++ b/spec/features/ide_spec.rb
@@ -8,7 +8,7 @@ describe 'IDE', :js do
let(:subgroup_project) { create(:project, :repository, namespace: subgroup) }
before do
- subgroup_project.add_master(user)
+ subgroup_project.add_maintainer(user)
sign_in(user)
visit project_path(subgroup_project)
diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb
new file mode 100644
index 00000000000..e381d073804
--- /dev/null
+++ b/spec/features/import/manifest_import_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe 'Import multiple repositories by uploading a manifest file', :js, :postgresql do
+ include Select2Helper
+
+ let(:user) { create(:admin) }
+ let(:group) { create(:group) }
+
+ before do
+ sign_in(user)
+
+ group.add_owner(user)
+ end
+
+ it 'parses manifest file and list repositories' do
+ visit new_import_manifest_path
+
+ attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
+ click_on 'List available repositories'
+
+ expect(page).to have_button('Import all repositories')
+ expect(page).to have_content('https://android-review.googlesource.com/platform/build/blueprint')
+ end
+
+ it 'imports succesfully imports a project' do
+ visit new_import_manifest_path
+
+ attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
+ click_on 'List available repositories'
+
+ page.within(first_row) do
+ click_on 'Import'
+
+ expect(page).to have_content 'Done'
+ expect(page).to have_content("#{group.full_path}/build/make")
+ end
+ end
+
+ it 'renders an error if invalid file was provided' do
+ visit new_import_manifest_path
+
+ attach_file('manifest', Rails.root.join('spec/fixtures/banana_sample.gif'))
+ click_on 'List available repositories'
+
+ expect(page).to have_content 'The uploaded file is not a valid XML file.'
+ end
+
+ def first_row
+ page.all('table.import-jobs tbody tr')[0]
+ end
+end
diff --git a/spec/features/instance_statistics/cohorts_spec.rb b/spec/features/instance_statistics/cohorts_spec.rb
new file mode 100644
index 00000000000..81fc5eff980
--- /dev/null
+++ b/spec/features/instance_statistics/cohorts_spec.rb
@@ -0,0 +1,15 @@
+require 'rails_helper'
+
+describe 'Cohorts page' do
+ before do
+ sign_in(create(:admin))
+ end
+
+ it 'See users count per month' do
+ 2.times { create(:user) }
+
+ visit instance_statistics_cohorts_path
+
+ expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
+ end
+end
diff --git a/spec/features/instance_statistics/conversational_development_index_spec.rb b/spec/features/instance_statistics/conversational_development_index_spec.rb
new file mode 100644
index 00000000000..d441a7a5af9
--- /dev/null
+++ b/spec/features/instance_statistics/conversational_development_index_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe 'Conversational Development Index' do
+ before do
+ sign_in(create(:admin))
+ end
+
+ context 'when usage ping is disabled' do
+ it 'shows empty state' do
+ stub_application_setting(usage_ping_enabled: false)
+
+ visit instance_statistics_conversational_development_index_index_path
+
+ expect(page).to have_content('Usage ping is not enabled')
+ end
+ end
+
+ context 'when there is no data to display' do
+ it 'shows empty state' do
+ stub_application_setting(usage_ping_enabled: true)
+
+ visit instance_statistics_conversational_development_index_index_path
+
+ expect(page).to have_content('Data is still calculating')
+ end
+ end
+
+ context 'when there is data to display' do
+ it 'shows numbers for each metric' do
+ stub_application_setting(usage_ping_enabled: true)
+ create(:conversational_development_index_metric)
+
+ visit instance_statistics_conversational_development_index_index_path
+
+ expect(page).to have_content(
+ 'Issues created per active user 1.2 You 9.3 Lead 13.3%'
+ )
+ end
+ end
+end
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index a986ddc4abc..9e1a12a9c2a 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -8,7 +8,7 @@ describe 'Invites' do
let(:group_invite) { group.group_members.invite.last }
before do
- project.add_master(owner)
+ project.add_maintainer(owner)
group.add_user(owner, Gitlab::Access::OWNER)
group.add_developer('user@example.com', owner)
group_invite.generate_invite_token!
diff --git a/spec/features/issuables/close_reopen_report_toggle_spec.rb b/spec/features/issuables/close_reopen_report_toggle_spec.rb
index 3df77a104e8..de6f5fe1560 100644
--- a/spec/features/issuables/close_reopen_report_toggle_spec.rb
+++ b/spec/features/issuables/close_reopen_report_toggle_spec.rb
@@ -46,7 +46,7 @@ describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
login_as user
end
@@ -83,7 +83,7 @@ describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
login_as user
end
diff --git a/spec/features/issuables/shortcuts_issuable_spec.rb b/spec/features/issuables/shortcuts_issuable_spec.rb
index 0a19086ffbd..a0ae6720a9f 100644
--- a/spec/features/issuables/shortcuts_issuable_spec.rb
+++ b/spec/features/issuables/shortcuts_issuable_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Blob shortcuts', :js do
+describe 'Blob shortcuts', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:issue) { create(:issue, project: project, author: user) }
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 1131e1711bf..bf60b18873c 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -11,7 +11,7 @@ describe 'Awards Emoji' do
context 'authorized user' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/issues/award_spec.rb b/spec/features/issues/award_spec.rb
index ddb69d414da..e53a4ce49c7 100644
--- a/spec/features/issues/award_spec.rb
+++ b/spec/features/issues/award_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Issue awards', :js do
+describe 'Issue awards', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index cf283119f36..06cb2e36334 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Issues > Labels bulk assignment' do
+describe 'Issues > Labels bulk assignment' do
let(:user) { create(:user) }
let!(:project) { create(:project) }
let!(:issue1) { create(:issue, project: project, title: "Issue 1") }
@@ -11,7 +11,7 @@ feature 'Issues > Labels bulk assignment' do
context 'as an allowed user', :js do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
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 52962002c33..ada57285abf 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
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Resolving all open discussions in a merge request from an issue', :js do
+describe 'Resolving all open discussions in a merge request from an issue', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -14,7 +14,7 @@ feature 'Resolving all open discussions in a merge request from an issue', :js d
describe 'as a user with access to the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
visit project_merge_request_path(project, merge_request)
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 9170f9295f0..b20730bdb22 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
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Resolve an open discussion in a merge request by creating an issue', :js do
+describe 'Resolve an open discussion in a merge request by creating an issue', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: true) }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -14,7 +14,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue', :j
describe 'As a user with access to the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index c8115db9212..d011d2545bb 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -20,9 +20,9 @@ describe 'Dropdown assignee', :js do
end
before do
- project.add_master(user)
- project.add_master(user_john)
- project.add_master(user_jacob)
+ project.add_maintainer(user)
+ project.add_maintainer(user_john)
+ project.add_maintainer(user_jacob)
sign_in(user)
create(:issue, project: project)
@@ -224,7 +224,7 @@ describe 'Dropdown assignee', :js do
expect(initial_size).to be > 0
new_user = create(:user)
- project.add_master(new_user)
+ project.add_maintainer(new_user)
find('.filtered-search-box .clear-search').click
input_filtered_search('assignee:', submit: false, extra_space: false)
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index 70b4f11410d..50d819a6161 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -28,9 +28,9 @@ describe 'Dropdown author', :js do
end
before do
- project.add_master(user)
- project.add_master(user_john)
- project.add_master(user_jacob)
+ project.add_maintainer(user)
+ project.add_maintainer(user_john)
+ project.add_maintainer(user_jacob)
sign_in(user)
create(:issue, project: project)
@@ -195,7 +195,7 @@ describe 'Dropdown author', :js do
expect(initial_size).to be > 0
new_user = create(:user)
- project.add_master(new_user)
+ project.add_maintainer(new_user)
find('.filtered-search-box .clear-search').click
filtered_search.set('author')
send_keys_to_filtered_search(':')
diff --git a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
index 436625a6f7b..be229e8aa7d 100644
--- a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
@@ -28,7 +28,7 @@ describe 'Dropdown emoji', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
create_list(:award_emoji, 2, user: user, name: 'thumbsup')
create_list(:award_emoji, 1, user: user, name: 'thumbsdown')
create_list(:award_emoji, 3, user: user, name: 'star')
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index ef40dddfd3a..b99c5a7f4e3 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -13,7 +13,7 @@ describe 'Dropdown hint', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
create(:issue, project: project)
end
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index 18cdb199c70..ca5d506ab04 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -33,7 +33,7 @@ describe 'Dropdown label', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index 94710c2f71f..f76d30056da 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -29,7 +29,7 @@ describe 'Dropdown milestone', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index 8dca81a8627..d4949de3f27 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -24,7 +24,7 @@ describe 'Filter issues', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
create(:issue, project: project, author: user2, title: "Bug report 1")
create(:issue, project: project, author: user2, title: "Bug report 2")
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 268590da599..8abab3f35d6 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -8,7 +8,7 @@ describe 'Search bar', :js do
let(:filtered_search) { find('.filtered-search') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index 0ae70c855db..6ac7ccd00f7 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -22,8 +22,8 @@ describe 'Visual tokens', :js do
end
before do
- project.add_user(user, :master)
- project.add_user(user_rock, :master)
+ project.add_user(user, :maintainer)
+ project.add_user(user_rock, :maintainer)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 2cb3ae08b0e..1456a2f0375 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -13,8 +13,8 @@ describe 'New/edit issue', :js do
let!(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) }
before do
- project.add_master(user)
- project.add_master(user2)
+ project.add_maintainer(user)
+ project.add_maintainer(user2)
sign_in(user)
end
@@ -321,7 +321,7 @@ describe 'New/edit issue', :js do
let(:sub_group_project) { create(:project, group: nested_group_1) }
before do
- sub_group_project.add_master(user)
+ sub_group_project.add_maintainer(user)
visit new_project_issue_path(sub_group_project)
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index f2624f55c86..98e37d8011a 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -1,13 +1,13 @@
require 'rails_helper'
-feature 'GFM autocomplete', :js do
+describe 'GFM autocomplete', :js do
let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let(:project) { create(:project) }
let(:label) { create(:label, project: project, title: 'special+') }
let(:issue) { create(:issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb
index babb0285590..088ab114df3 100644
--- a/spec/features/issues/issue_detail_spec.rb
+++ b/spec/features/issues/issue_detail_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Issue Detail', :js do
+describe 'Issue Detail', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project, author: user) }
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 830c794376d..3050f23c130 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Issue Sidebar' do
+describe 'Issue Sidebar' do
include MobileHelpers
let(:group) { create(:group, :nested) }
@@ -112,7 +112,7 @@ feature 'Issue Sidebar' do
context 'editing issue labels', :js do
before do
- issue.update_attributes(labels: [label])
+ issue.update(labels: [label])
page.within('.block.labels') do
find('.edit-link').click
end
diff --git a/spec/features/issues/markdown_toolbar_spec.rb b/spec/features/issues/markdown_toolbar_spec.rb
index fee8fd9b365..042ecdb172a 100644
--- a/spec/features/issues/markdown_toolbar_spec.rb
+++ b/spec/features/issues/markdown_toolbar_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Issue markdown toolbar', :js do
+describe 'Issue markdown toolbar', :js do
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index 3c01ff345fc..2abc50b04e4 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'issue move to another project' do
+describe 'issue move to another project' do
let(:user) { create(:user) }
let(:old_project) { create(:project, :repository) }
let(:text) { 'Some issue description' }
@@ -9,16 +9,18 @@ feature 'issue move to another project' do
create(:issue, description: text, project: old_project, author: user)
end
- background { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'user does not have permission to move issue' do
- background do
+ before do
old_project.add_guest(user)
visit issue_path(issue)
end
- scenario 'moving issue to another project not allowed' do
+ it 'moving issue to another project not allowed' do
expect(page).to have_no_selector('.js-sidebar-move-issue-block')
end
end
@@ -30,14 +32,14 @@ feature 'issue move to another project' do
let(:text) { "Text with #{mr.to_reference}" }
let(:cross_reference) { old_project.to_reference(new_project) }
- background do
+ before do
old_project.add_reporter(user)
new_project.add_reporter(user)
visit issue_path(issue)
end
- scenario 'moving issue to another project', :js do
+ it 'moving issue to another project', :js do
find('.js-move-issue').click
wait_for_requests
all('.js-move-issue-dropdown-item')[0].click
@@ -49,7 +51,7 @@ feature 'issue move to another project' do
expect(page.current_path).to include project_path(new_project)
end
- scenario 'searching project dropdown', :js do
+ it 'searching project dropdown', :js do
new_project_search.add_reporter(user)
find('.js-move-issue').click
@@ -66,9 +68,11 @@ feature 'issue move to another project' do
context 'user does not have permission to move the issue to a project', :js do
let!(:private_project) { create(:project, :private) }
let(:another_project) { create(:project) }
- background { another_project.add_guest(user) }
+ before do
+ another_project.add_guest(user)
+ end
- scenario 'browsing projects in projects select' do
+ it 'browsing projects in projects select' do
find('.js-move-issue').click
wait_for_requests
@@ -84,7 +88,7 @@ feature 'issue move to another project' do
create(:issue, project: old_project, author: user, moved_to: new_issue)
end
- scenario 'user wants to move issue that has already been moved' do
+ it 'user wants to move issue that has already been moved' do
expect(page).to have_no_selector('#move_to_project_id')
end
end
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index 793572851da..3cd7ce6dada 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Issue notes polling', :js do
+describe 'Issue notes polling', :js do
include NoteInteractionHelpers
let(:project) { create(:project, :public) }
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
index 73022afbda2..7cce45ff206 100644
--- a/spec/features/issues/spam_issues_spec.rb
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -17,7 +17,7 @@ describe 'New issue', :js do
recaptcha_private_key: 'test private key'
)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index 4a44ec302fc..0114178b9be 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -1,12 +1,12 @@
require 'rails_helper'
-feature 'Manually create a todo item from issue', :js do
+describe 'Manually create a todo item from issue', :js do
let!(:project) { create(:project) }
let!(:issue) { create(:issue, project: project) }
let!(:user) { create(:user)}
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
end
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index 7d6edc171f8..fd8629ae504 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -1,12 +1,12 @@
require 'rails_helper'
-feature 'Multiple issue updating from issues#index', :js do
+describe 'Multiple issue updating from issues#index', :js do
let!(:project) { create(:project) }
let!(:issue) { create(:issue, project: project) }
let!(:user) { create(:user)}
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -49,7 +49,7 @@ feature 'Multiple issue updating from issues#index', :js do
click_update_issues_button
page.within('.issue .controls') do
- expect(find('.author_link')["title"]).to have_content(user.name)
+ expect(find('.author-link')["title"]).to have_content(user.name)
end
end
@@ -63,7 +63,7 @@ feature 'Multiple issue updating from issues#index', :js do
click_link 'Unassigned'
click_update_issues_button
- expect(find('.issue:first-child .controls')).not_to have_css('.author_link')
+ expect(find('.issue:first-child .controls')).not_to have_css('.author-link')
end
end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index 17818beb947..5926e442f24 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Issues > User uses quick actions', :js do
+describe 'Issues > User uses quick actions', :js do
include Spec::Support::Helpers::Features::NotesHelpers
it_behaves_like 'issuable record that supports quick actions in its description and notes', :issue do
@@ -12,7 +12,7 @@ feature 'Issues > User uses quick actions', :js do
let(:project) { create(:project, :public) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
end
@@ -196,7 +196,7 @@ feature 'Issues > User uses quick actions', :js do
let(:target_project) { create(:project, :public) }
before do
- target_project.add_master(user)
+ target_project.add_maintainer(user)
gitlab_sign_out
sign_in(user)
visit project_issue_path(project, issue)
@@ -258,7 +258,7 @@ feature 'Issues > User uses quick actions', :js do
let(:wontfix_target) { create(:label, project: target_project, title: 'wontfix') }
before do
- target_project.add_master(user)
+ target_project.add_maintainer(user)
gitlab_sign_out
sign_in(user)
visit project_issue_path(project, issue)
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index c6dcd97631d..4d9b8a10e04 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -398,7 +398,7 @@ describe 'Issues' do
before do
stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
- project1.add_master(user)
+ project1.add_maintainer(user)
visit namespace_project_issues_path(user.namespace, project1)
end
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 5573148f8bc..6f917f522bc 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Labels Hierarchy', :js, :nested_groups do
+describe 'Labels Hierarchy', :js, :nested_groups do
include FilteredSearchHelpers
let!(:user) { create(:user) }
diff --git a/spec/features/markdown/gitlab_flavored_markdown_spec.rb b/spec/features/markdown/gitlab_flavored_markdown_spec.rb
index 3c2186b3598..6997ca48427 100644
--- a/spec/features/markdown/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/markdown/gitlab_flavored_markdown_spec.rb
@@ -6,7 +6,7 @@ describe "GitLab Flavored Markdown" do
let(:issue) { create(:issue, project: project) }
let(:fred) do
create(:user, name: 'fred') do |user|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb
index 1808d0c0a0c..7839b97122c 100644
--- a/spec/features/merge_request/maintainer_edits_fork_spec.rb
+++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb
@@ -18,7 +18,7 @@ describe 'a maintainer edits files on a source-branch of an MR from a fork', :js
end
before do
- target_project.add_master(user)
+ target_project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(target_project, merge_request)
diff --git a/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb
index 0af37d76539..0ccab5b2fac 100644
--- a/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb
+++ b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb
@@ -71,7 +71,7 @@ describe 'create a merge request, allowing commits from members who can merge to
end
before do
- target_project.add_master(member)
+ target_project.add_maintainer(member)
sign_in(member)
end
diff --git a/spec/features/merge_request/user_cherry_picks_spec.rb b/spec/features/merge_request/user_cherry_picks_spec.rb
deleted file mode 100644
index 61d1bdaa95a..00000000000
--- a/spec/features/merge_request/user_cherry_picks_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-require 'rails_helper'
-
-describe 'Merge request > User cherry-picks', :js do
- let(:group) { create(:group) }
- let(:project) { create(:project, :repository, namespace: group) }
- let(:user) { project.creator }
- let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) }
-
- before do
- project.add_master(user)
- sign_in(user)
- end
-
- context 'Viewing a merged merge request' do
- before do
- service = MergeRequests::MergeService.new(project, user)
-
- perform_enqueued_jobs do
- service.execute(merge_request)
- end
- end
-
- # Fast-forward merge, or merged before GitLab 8.5.
- context 'Without a merge commit' do
- before do
- merge_request.merge_commit_sha = nil
- merge_request.save
- end
-
- it 'does not show a Cherry-pick button' do
- visit project_merge_request_path(project, merge_request)
-
- expect(page).not_to have_link 'Cherry-pick'
- end
- end
-
- context 'With a merge commit' do
- it 'shows a Cherry-pick button' do
- visit project_merge_request_path(project, merge_request)
-
- expect(page).to have_link 'Cherry-pick'
- end
-
- it 'hides the cherry pick button for an archived project' do
- project.update!(archived: true)
-
- visit project_merge_request_path(project, merge_request)
-
- expect(page).not_to have_link 'Cherry-pick'
- end
- end
- end
-end
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index 728e89db400..f0d38dc6a0c 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Merge request > User creates image diff notes', :js do
+describe 'Merge request > User creates image diff notes', :js do
include NoteInteractionHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
index e1e70b6d260..8d2d4279d3c 100644
--- a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
+++ b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
@@ -32,7 +32,7 @@ describe 'Merge request < User customizes merge commit message', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_merges_immediately_spec.rb b/spec/features/merge_request/user_merges_immediately_spec.rb
index b16fc9bfc89..ea61f9675bc 100644
--- a/spec/features/merge_request/user_merges_immediately_spec.rb
+++ b/spec/features/merge_request/user_merges_immediately_spec.rb
@@ -19,7 +19,7 @@ describe 'Merge requests > User merges immediately', :js do
context 'when there is active pipeline for merge request' do
before do
create(:ci_build, pipeline: pipeline)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
index a045791f6b4..8372b61f872 100644
--- a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
@@ -5,7 +5,7 @@ describe 'Merge request > User merges only if pipeline succeeds', :js do
let(:project) { merge_request.target_project }
before do
- project.add_master(merge_request.author)
+ project.add_maintainer(merge_request.author)
sign_in(merge_request.author)
end
diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
index db92a3504f3..53ed5d78598 100644
--- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
@@ -17,7 +17,7 @@ describe 'Merge request > User merges when pipeline succeeds', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when there is active pipeline for merge request' do
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 13cc5f256eb..77261f9375c 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -185,11 +185,11 @@ describe 'Merge request > User posts diff notes', :js do
end
describe 'posting a note' do
- xit 'adds as discussion' do
+ it 'adds as discussion' do
expect(page).to have_css('.js-temp-notes-holder', count: 2)
should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'), asset_form_reset: false)
- expect(page).to have_css('.notes_holder .note', count: 1)
+ expect(page).to have_css('.notes_holder .note.note-discussion', count: 1)
expect(page).to have_css('.js-temp-notes-holder', count: 1)
expect(page).to have_button('Reply...')
end
@@ -198,7 +198,7 @@ describe 'Merge request > User posts diff notes', :js do
context 'when the MR only supports legacy diff notes' do
before do
- merge_request.merge_request_diff.update_attributes(start_commit_sha: nil)
+ merge_request.merge_request_diff.update(start_commit_sha: nil)
visit diffs_project_merge_request_path(project, merge_request, view: 'inline')
end
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index fa819cbc385..260c5f9c28b 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -14,7 +14,7 @@ describe 'Merge request > User posts notes', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index a0b9d6cb059..2d268ecab58 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
@@ -19,7 +19,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do
context 'no discussions' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
note.destroy
visit_merge_request
@@ -33,7 +33,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do
context 'as authorized user' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit_merge_request
end
@@ -342,8 +342,9 @@ describe 'Merge request > User resolves diff notes and discussions', :js do
end
end
- it 'shows jump to next discussion button' do
- expect(page.all('.discussion-reply-holder', count: 2)).to all(have_selector('.discussion-next-btn'))
+ it 'shows jump to next discussion button, apart from the last one' do
+ expect(page).to have_selector('.discussion-reply-holder', count: 2)
+ expect(page).to have_selector('.discussion-reply-holder .discussion-next-btn', count: 1)
end
it 'displays next discussion even if hidden' do
diff --git a/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb
index fdf9a84e997..777464ef841 100644
--- a/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb
+++ b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Merge request > User resolves outdated diff discussions', :js do
+describe 'Merge request > User resolves outdated diff discussions', :js do
let(:project) { create(:project, :repository, :public) }
let(:merge_request) do
diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
index 0a8296bd722..428eb414274 100644
--- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
@@ -19,7 +19,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do
let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
set_cookie('sidebar_collapsed', 'true')
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 c40c720d168..86086a58f18 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
@@ -1,6 +1,6 @@
require 'rails_helper'
-describe 'Merge request > User sees Check out branch modal', :js do
+describe 'Merge request > User sees check out branch modal', :js do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -16,7 +16,7 @@ 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 model with Escape keypress' do
+ it 'closes the check out branch modal with escape keypress' do
find('#modal_merge_info').send_keys(:escape)
expect(page).not_to have_content('Check out, review, and merge locally')
diff --git a/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
new file mode 100644
index 00000000000..aa499493dbe
--- /dev/null
+++ b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
@@ -0,0 +1,71 @@
+require 'rails_helper'
+
+describe 'Merge request > User cherry-picks', :js do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :repository, namespace: group) }
+ let(:user) { project.creator }
+ let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context 'Viewing a merged merge request' do
+ before do
+ service = MergeRequests::MergeService.new(project, user)
+
+ perform_enqueued_jobs do
+ service.execute(merge_request)
+ end
+ end
+
+ # Fast-forward merge, or merged before GitLab 8.5.
+ context 'without a merge commit' do
+ before do
+ merge_request.merge_commit_sha = nil
+ merge_request.save
+ end
+
+ it 'does not show a Cherry-pick button' do
+ visit project_merge_request_path(project, merge_request)
+
+ expect(page).not_to have_link 'Cherry-pick'
+ end
+ end
+
+ context 'with a merge commit' do
+ it 'shows a Cherry-pick button' do
+ visit project_merge_request_path(project, merge_request)
+
+ expect(page).to have_link 'Cherry-pick'
+ end
+
+ it 'hides the cherry pick button for an archived project' do
+ project.update!(archived: true)
+
+ visit project_merge_request_path(project, merge_request)
+
+ expect(page).not_to have_link 'Cherry-pick'
+ end
+ end
+
+ context 'and seeing the cherry-pick modal' do
+ before do
+ visit project_merge_request_path(project, merge_request)
+
+ click_link('Cherry-pick')
+ end
+
+ it 'shows the cherry-pick modal' do
+ expect(page).to have_content('Cherry-pick this merge request')
+ end
+
+ it 'closes the cherry-pick modal with escape keypress' do
+ find('#modal-cherry-pick-commit').send_keys(:escape)
+
+ expect(page).not_to have_content('Start a new merge request with these changes')
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
index 726f35557a7..d7c784b14c5 100644
--- a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
+++ b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
@@ -18,7 +18,7 @@ describe 'Merge request > User sees closing issues message', :js do
let(:merge_request_title) { 'Merge Request Title' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
wait_for_requests
diff --git a/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb b/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
index 01115318370..46c21a2b155 100644
--- a/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
+++ b/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge request > User sees deleted target branch', :js do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
DeleteBranchService.new(project, user).execute('feature')
sign_in(user)
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/merge_request/user_sees_discussions_spec.rb b/spec/features/merge_request/user_sees_discussions_spec.rb
index 10390bd5864..7b8c3bacfe2 100644
--- a/spec/features/merge_request/user_sees_discussions_spec.rb
+++ b/spec/features/merge_request/user_sees_discussions_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge request > User sees discussions', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_sees_empty_state_spec.rb b/spec/features/merge_request/user_sees_empty_state_spec.rb
index a939c7e9001..482f31b02d4 100644
--- a/spec/features/merge_request/user_sees_empty_state_spec.rb
+++ b/spec/features/merge_request/user_sees_empty_state_spec.rb
@@ -5,7 +5,7 @@ describe 'Merge request > User sees empty state' do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
index 85df43df38e..f6b771facf8 100644
--- a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge request > User sees merge button depending on unresolved discuss
let!(:merge_request) { create(:merge_request_with_diff_notes, source_project: project, author: user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb
index 51a65407aec..b285cd7a7ac 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -2,6 +2,7 @@ require 'rails_helper'
describe 'Merge request > User sees merge widget', :js do
include ProjectForksHelper
+ include TestReportsHelper
let(:project) { create(:project, :repository) }
let(:project_only_mwps) { create(:project, :repository, only_allow_merge_if_pipeline_succeeds: true) }
@@ -10,8 +11,8 @@ describe 'Merge request > User sees merge widget', :js do
let(:merge_request_in_only_mwps_project) { create(:merge_request, source_project: project_only_mwps) }
before do
- project.add_master(user)
- project_only_mwps.add_master(user)
+ project.add_maintainer(user)
+ project_only_mwps.add_maintainer(user)
sign_in(user)
end
@@ -275,7 +276,7 @@ describe 'Merge request > User sees merge widget', :js do
let(:user2) { create(:user) }
before do
- project.add_master(user2)
+ project.add_maintainer(user2)
sign_out(:user)
sign_in(user2)
merge_request.update(target_project: fork_project)
@@ -325,4 +326,229 @@ describe 'Merge request > User sees merge widget', :js do
expect(page).to have_content('This merge request is in the process of being merged')
end
end
+
+ context 'when merge request has test reports' do
+ let!(:head_pipeline) do
+ create(:ci_pipeline,
+ :success,
+ project: project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha)
+ end
+
+ let!(:build) { create(:ci_build, :success, pipeline: head_pipeline, project: project) }
+
+ before do
+ merge_request.update!(head_pipeline_id: head_pipeline.id)
+ end
+
+ context 'when result has not been parsed yet' do
+ let!(:job_artifact) { create(:ci_job_artifact, :junit, job: build, project: project) }
+
+ before do
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'shows parsing status' do
+ expect(page).to have_content('Test summary results are being parsed')
+ end
+ end
+
+ context 'when result has already been parsed' do
+ context 'when JUnit xml is correctly formatted' do
+ let!(:job_artifact) { create(:ci_job_artifact, :junit, job: build, project: project) }
+
+ before do
+ allow_any_instance_of(MergeRequest).to receive(:compare_test_reports).and_return(compared_data)
+
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'shows parsed results' do
+ expect(page).to have_content('Test summary contained')
+ end
+ end
+
+ context 'when JUnit xml is corrupted' do
+ let!(:job_artifact) { create(:ci_job_artifact, :junit_with_corrupted_data, job: build, project: project) }
+
+ before do
+ allow_any_instance_of(MergeRequest).to receive(:compare_test_reports).and_return(compared_data)
+
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'shows the error state' do
+ expect(page).to have_content('Test summary failed loading results')
+ end
+ end
+
+ def compared_data
+ Ci::CompareTestReportsService.new(project).execute(nil, head_pipeline)
+ end
+ end
+
+ context 'when test reports have been parsed correctly' do
+ let(:serialized_data) do
+ {
+ status: :parsed,
+ data: TestReportsComparerSerializer
+ .new(project: project)
+ .represent(comparer)
+ }
+ end
+
+ before do
+ allow_any_instance_of(MergeRequest)
+ .to receive(:has_test_reports?).and_return(true)
+ allow_any_instance_of(MergeRequest)
+ .to receive(:compare_test_reports).and_return(serialized_data)
+
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ context 'when a new failures exists' do
+ let(:base_reports) do
+ Gitlab::Ci::Reports::TestReports.new.tap do |reports|
+ reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+ end
+
+ let(:head_reports) do
+ Gitlab::Ci::Reports::TestReports.new.tap do |reports|
+ reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+ end
+
+ it 'shows test reports summary which includes the new failure' do
+ within(".mr-section-container") do
+ click_button 'Expand'
+
+ expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests')
+ within(".js-report-section-container") do
+ expect(page).to have_content('rspec found no changed test results out of 1 total test')
+ expect(page).to have_content('junit found 1 failed test result out of 1 total test')
+ expect(page).to have_content('New')
+ expect(page).to have_content('subtractTest')
+ end
+ end
+ end
+
+ context 'when user clicks the new failure' do
+ it 'shows the test report detail' do
+ within(".mr-section-container") do
+ click_button 'Expand'
+
+ within(".js-report-section-container") do
+ click_button 'subtractTest'
+
+ expect(page).to have_content('6.66')
+ expect(page).to have_content(sample_java_failed_message)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when an existing failure exists' do
+ let(:base_reports) do
+ Gitlab::Ci::Reports::TestReports.new.tap do |reports|
+ reports.get_suite('rspec').add_test_case(create_test_case_rspec_failed)
+ reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+ end
+
+ let(:head_reports) do
+ Gitlab::Ci::Reports::TestReports.new.tap do |reports|
+ reports.get_suite('rspec').add_test_case(create_test_case_rspec_failed)
+ reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+ end
+
+ it 'shows test reports summary which includes the existing failure' do
+ within(".mr-section-container") do
+ click_button 'Expand'
+
+ expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests')
+ within(".js-report-section-container") do
+ expect(page).to have_content('rspec found 1 failed test result out of 1 total test')
+ expect(page).to have_content('junit found no changed test results out of 1 total test')
+ expect(page).not_to have_content('New')
+ expect(page).to have_content('Test#sum when a is 2 and b is 2 returns summary')
+ end
+ end
+ end
+
+ context 'when user clicks the existing failure' do
+ it 'shows test report detail of it' do
+ within(".mr-section-container") do
+ click_button 'Expand'
+
+ within(".js-report-section-container") do
+ click_button 'Test#sum when a is 2 and b is 2 returns summary'
+
+ expect(page).to have_content('2.22')
+ expect(page).to have_content(sample_rspec_failed_message)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when a resolved failure exists' do
+ let(:base_reports) do
+ Gitlab::Ci::Reports::TestReports.new.tap do |reports|
+ reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+ end
+
+ let(:head_reports) do
+ Gitlab::Ci::Reports::TestReports.new.tap do |reports|
+ reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ reports.get_suite('junit').add_test_case(create_test_case_java_resolved)
+ end
+ end
+
+ let(:create_test_case_java_resolved) do
+ create_test_case_java_failed.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ it 'shows test reports summary which includes the resolved failure' do
+ within(".mr-section-container") do
+ click_button 'Expand'
+
+ expect(page).to have_content('Test summary contained 1 fixed test result out of 2 total tests')
+ within(".js-report-section-container") do
+ expect(page).to have_content('rspec found no changed test results out of 1 total test')
+ expect(page).to have_content('junit found 1 fixed test result out of 1 total test')
+ expect(page).to have_content('subtractTest')
+ end
+ end
+ end
+
+ context 'when user clicks the resolved failure' do
+ it 'shows test report detail of it' do
+ within(".mr-section-container") do
+ click_button 'Expand'
+
+ within(".js-report-section-container") do
+ click_button 'subtractTest'
+
+ expect(page).to have_content('6.66')
+ end
+ end
+ end
+ end
+ end
+
+ def comparer
+ Gitlab::Ci::Reports::TestReportsComparer.new(base_reports, head_reports)
+ end
+ end
+ end
end
diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index a42c016392b..45cccbee63e 100644
--- a/spec/features/merge_request/user_sees_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -7,7 +7,7 @@ describe 'Merge request > User sees pipelines', :js do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -70,7 +70,7 @@ describe 'Merge request > User sees pipelines', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index 11e0806ba62..f42b4dcbb47 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -10,7 +10,7 @@ describe 'Merge request > User sees versions', :js do
let!(:params) { {} }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit diffs_project_merge_request_path(project, merge_request, params)
end
diff --git a/spec/features/merge_request/user_sees_wip_help_message_spec.rb b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
index bc25243244e..92cc73ddf1f 100644
--- a/spec/features/merge_request/user_sees_wip_help_message_spec.rb
+++ b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
@@ -5,7 +5,7 @@ describe 'Merge request > User sees WIP help message' do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
index ed6e29335d1..ae41cf90576 100644
--- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
+++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
@@ -11,7 +11,7 @@ describe 'Merge request > User selects branches for new MR', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
index 2e95a628013..dd860382daa 100644
--- a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
+++ b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge request > User toggles whitespace changes', :js do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit diffs_project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_uses_slash_commands_spec.rb b/spec/features/merge_request/user_uses_slash_commands_spec.rb
index 83ad4b45b5a..b81478a481f 100644
--- a/spec/features/merge_request/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_request/user_uses_slash_commands_spec.rb
@@ -21,7 +21,7 @@ describe 'Merge request > User uses quick actions', :js do
let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'time tracking' do
@@ -147,7 +147,7 @@ describe 'Merge request > User uses quick actions', :js do
let(:new_url_opts) { { merge_request: { source_branch: 'feature' } } }
before do
- another_project.add_master(user)
+ another_project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb
index 199ba7e87ad..cb6603d3f50 100644
--- a/spec/features/merge_requests/user_mass_updates_spec.rb
+++ b/spec/features/merge_requests/user_mass_updates_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge requests > User mass updates', :js do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -47,7 +47,7 @@ describe 'Merge requests > User mass updates', :js do
change_assignee(user.name)
page.within('.merge-request .controls') do
- expect(find('.author_link')["title"]).to have_content(user.name)
+ expect(find('.author-link')["title"]).to have_content(user.name)
end
end
end
@@ -62,7 +62,7 @@ describe 'Merge requests > User mass updates', :js do
it 'removes assignee from the merge request' do
change_assignee('Unassigned')
- expect(find('.merge-request .controls')).not_to have_css('.author_link')
+ expect(find('.merge-request .controls')).not_to have_css('.author-link')
end
end
end
diff --git a/spec/features/merge_requests/user_squashes_merge_request_spec.rb b/spec/features/merge_requests/user_squashes_merge_request_spec.rb
index 6c952791591..ec1153b7f7f 100644
--- a/spec/features/merge_requests/user_squashes_merge_request_spec.rb
+++ b/spec/features/merge_requests/user_squashes_merge_request_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'User squashes a merge request', :js do
+describe 'User squashes a merge request', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:source_branch) { 'csv' }
@@ -46,7 +46,7 @@ feature 'User squashes a merge request', :js do
# Prevent source branch from being removed so we can use be_merged_to_root_ref
# method to check if squash was performed or not
allow_any_instance_of(MergeRequest).to receive(:force_remove_source_branch?).and_return(false)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index b0db6870ddf..a0673b12738 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -1,18 +1,18 @@
require 'rails_helper'
-feature 'Milestone' do
+describe 'Milestone' do
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, namespace: group) }
let(:user) { create(:user) }
before do
create(:group_member, group: group, user: user)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
- feature 'Create a milestone' do
- scenario 'shows an informative message for a new milestone' do
+ describe 'Create a milestone' do
+ it 'shows an informative message for a new milestone' do
visit new_project_milestone_path(project)
page.within '.milestone-form' do
@@ -28,8 +28,8 @@ feature 'Milestone' do
end
end
- feature 'Open a milestone with closed issues' do
- scenario 'shows an informative message' do
+ describe 'Open a milestone with closed issues' do
+ it 'shows an informative message' do
milestone = create(:milestone, project: project, title: 8.7)
create(:issue, title: "Bugfix1", project: project, milestone: milestone, state: "closed")
@@ -39,8 +39,8 @@ feature 'Milestone' do
end
end
- feature 'Open a project milestone with an existing title' do
- scenario 'displays validation message when there is a project milestone with same title' do
+ describe 'Open a project milestone with an existing title' do
+ it 'displays validation message when there is a project milestone with same title' do
milestone = create(:milestone, project: project, title: 8.7)
visit new_project_milestone_path(project)
@@ -52,7 +52,7 @@ feature 'Milestone' do
expect(find('.alert-danger')).to have_content('already being used for another group or project milestone.')
end
- scenario 'displays validation message when there is a group milestone with same title' do
+ it 'displays validation message when there is a group milestone with same title' do
milestone = create(:milestone, project_id: nil, group: project.group, title: 8.7)
visit new_group_milestone_path(project.group)
@@ -66,8 +66,8 @@ feature 'Milestone' do
end
end
- feature 'Open a milestone', :js do
- scenario 'shows total issue time spent correctly when no time has been logged' do
+ describe 'Open a milestone', :js do
+ it 'shows total issue time spent correctly when no time has been logged' do
milestone = create(:milestone, project: project, title: 8.7)
visit project_milestone_path(project, milestone)
@@ -79,7 +79,7 @@ feature 'Milestone' do
end
end
- scenario 'shows total issue time spent' do
+ it 'shows total issue time spent' do
milestone = create(:milestone, project: project, title: 8.7)
issue1 = create(:issue, project: project, milestone: milestone)
issue2 = create(:issue, project: project, milestone: milestone)
@@ -98,8 +98,8 @@ feature 'Milestone' do
end
end
- feature 'Deleting a milestone' do
- scenario "The delete milestone button does not show for unauthorized users" do
+ describe 'Deleting a milestone' do
+ it "The delete milestone button does not show for unauthorized users" do
create(:milestone, project: project, title: 8.7)
sign_out(user)
@@ -109,7 +109,7 @@ feature 'Milestone' do
end
end
- feature 'deprecation popover', :js do
+ describe 'deprecation popover', :js do
it 'opens deprecation popover' do
milestone = create(:milestone, project: project)
diff --git a/spec/features/milestones/user_deletes_milestone_spec.rb b/spec/features/milestones/user_deletes_milestone_spec.rb
index 414702daba4..9d4a68239d3 100644
--- a/spec/features/milestones/user_deletes_milestone_spec.rb
+++ b/spec/features/milestones/user_deletes_milestone_spec.rb
@@ -13,6 +13,7 @@ describe "User deletes milestone", :js do
end
it "deletes milestone" do
+ click_link(milestone.title)
click_button("Delete")
click_button("Delete milestone")
diff --git a/spec/features/milestones/user_edits_milestone_spec.rb b/spec/features/milestones/user_edits_milestone_spec.rb
new file mode 100644
index 00000000000..077295f1cc0
--- /dev/null
+++ b/spec/features/milestones/user_edits_milestone_spec.rb
@@ -0,0 +1,22 @@
+require "rails_helper"
+
+describe "User edits milestone", :js do
+ set(:user) { create(:user) }
+ set(:project) { create(:project) }
+ set(:milestone) { create(:milestone, project: project, start_date: Date.today, due_date: 5.days.from_now) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+
+ visit(edit_project_milestone_path(project, milestone))
+ end
+
+ it "shows the right start date and due date" do
+ start_date = milestone.start_date.strftime("%F")
+ due_date = milestone.due_date.strftime("%F")
+
+ expect(page).to have_field(with: start_date)
+ expect(page).to have_field(with: due_date)
+ end
+end
diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb
index 013cdaa6479..f4105730402 100644
--- a/spec/features/oauth_login_spec.rb
+++ b/spec/features/oauth_login_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'OAuth Login', :js, :allow_forgery_protection do
+describe 'OAuth Login', :js, :allow_forgery_protection do
include DeviseHelpers
def enter_code(code)
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index b3bb8c48b4a..134731a4639 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Member autocomplete', :js do
+describe 'Member autocomplete', :js do
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
let(:author) { create(:user) }
diff --git a/spec/features/password_reset_spec.rb b/spec/features/password_reset_spec.rb
index 73a526c3d8a..dcc63dff9f5 100644
--- a/spec/features/password_reset_spec.rb
+++ b/spec/features/password_reset_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Password reset' do
+describe 'Password reset' do
describe 'throttling' do
it 'sends reset instructions when not previously sent' do
user = create(:user)
diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb
index 95947d2f111..90d0e9bb77c 100644
--- a/spec/features/profiles/account_spec.rb
+++ b/spec/features/profiles/account_spec.rb
@@ -1,25 +1,25 @@
require 'rails_helper'
-feature 'Profile > Account', :js do
- given(:user) { create(:user, username: 'foo') }
+describe 'Profile > Account', :js do
+ let(:user) { create(:user, username: 'foo') }
before do
sign_in(user)
end
describe 'Change username' do
- given(:new_username) { 'bar' }
- given(:new_user_path) { "/#{new_username}" }
- given(:old_user_path) { "/#{user.username}" }
+ let(:new_username) { 'bar' }
+ let(:new_user_path) { "/#{new_username}" }
+ let(:old_user_path) { "/#{user.username}" }
- scenario 'the user is accessible via the new path' do
+ it 'the user is accessible via the new path' do
update_username(new_username)
visit new_user_path
expect(current_path).to eq(new_user_path)
expect(find('.user-info')).to have_content(new_username)
end
- scenario 'the old user path redirects to the new path' do
+ it 'the old user path redirects to the new path' do
update_username(new_username)
visit old_user_path
expect(current_path).to eq(new_user_path)
@@ -27,9 +27,9 @@ feature 'Profile > Account', :js do
end
context 'with a project' do
- given!(:project) { create(:project, namespace: user.namespace) }
- given(:new_project_path) { "/#{new_username}/#{project.path}" }
- given(:old_project_path) { "/#{user.username}/#{project.path}" }
+ let!(:project) { create(:project, namespace: user.namespace) }
+ let(:new_project_path) { "/#{new_username}/#{project.path}" }
+ let(:old_project_path) { "/#{user.username}/#{project.path}" }
before(:context) do
TestEnv.clean_test_path
@@ -39,14 +39,14 @@ feature 'Profile > Account', :js do
TestEnv.clean_test_path
end
- scenario 'the project is accessible via the new path' do
+ it 'the project is accessible via the new path' 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')
end
- scenario 'the old project path redirects to the new path' do
+ 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)
diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb
index 4045cfd21c4..d3050760c06 100644
--- a/spec/features/profiles/active_sessions_spec.rb
+++ b/spec/features/profiles/active_sessions_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
+describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
let(:user) do
create(:user).tap do |user|
user.current_sign_in_at = Time.current
@@ -13,7 +13,7 @@ feature 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
end
end
- scenario 'User sees their active sessions' do
+ it 'User sees their active sessions' do
Capybara::Session.new(:session1)
Capybara::Session.new(:session2)
@@ -60,7 +60,7 @@ feature 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
end
end
- scenario 'User can revoke a session', :js, :redis_session_store do
+ it 'User can revoke a session', :js, :redis_session_store do
Capybara::Session.new(:session1)
Capybara::Session.new(:session2)
diff --git a/spec/features/profiles/chat_names_spec.rb b/spec/features/profiles/chat_names_spec.rb
index 5c959acbbc9..c72069f6262 100644
--- a/spec/features/profiles/chat_names_spec.rb
+++ b/spec/features/profiles/chat_names_spec.rb
@@ -1,19 +1,19 @@
require 'rails_helper'
-feature 'Profile > Chat' do
- given(:user) { create(:user) }
- given(:service) { create(:service) }
+describe 'Profile > Chat' do
+ let(:user) { create(:user) }
+ let(:service) { create(:service) }
before do
sign_in(user)
end
describe 'uses authorization link' do
- given(:params) do
+ let(:params) do
{ team_id: 'T00', team_domain: 'my_chat_team', user_id: 'U01', user_name: 'my_chat_user' }
end
- given!(:authorize_url) { ChatNames::AuthorizeUserService.new(service, params).execute }
- given(:authorize_path) { URI.parse(authorize_url).request_uri }
+ let!(:authorize_url) { ChatNames::AuthorizeUserService.new(service, params).execute }
+ let(:authorize_path) { URI.parse(authorize_url).request_uri }
before do
visit authorize_path
@@ -24,13 +24,13 @@ feature 'Profile > Chat' do
click_button 'Authorize'
end
- scenario 'goes to list of chat names and see chat account' do
+ it 'goes to list of chat names and see chat account' do
expect(page.current_path).to eq(profile_chat_names_path)
expect(page).to have_content('my_chat_team')
expect(page).to have_content('my_chat_user')
end
- scenario 'second use of link is denied' do
+ it 'second use of link is denied' do
visit authorize_path
expect(page).to have_gitlab_http_status(:not_found)
@@ -42,13 +42,13 @@ feature 'Profile > Chat' do
click_button 'Deny'
end
- scenario 'goes to list of chat names and do not see chat account' do
+ it 'goes to list of chat names and do not see chat account' do
expect(page.current_path).to eq(profile_chat_names_path)
expect(page).not_to have_content('my_chat_team')
expect(page).not_to have_content('my_chat_user')
end
- scenario 'second use of link is denied' do
+ it 'second use of link is denied' do
visit authorize_path
expect(page).to have_gitlab_http_status(:not_found)
@@ -57,18 +57,18 @@ feature 'Profile > Chat' do
end
describe 'visits chat accounts' do
- given!(:chat_name) { create(:chat_name, user: user, service: service) }
+ let!(:chat_name) { create(:chat_name, user: user, service: service) }
before do
visit profile_chat_names_path
end
- scenario 'sees chat user' do
+ it 'sees chat user' do
expect(page).to have_content(chat_name.team_domain)
expect(page).to have_content(chat_name.chat_name)
end
- scenario 'removes chat account' do
+ it 'removes chat account' do
click_link 'Remove'
expect(page).to have_content("You don't have any active chat names.")
diff --git a/spec/features/profiles/emails_spec.rb b/spec/features/profiles/emails_spec.rb
index 11cc8aae6f3..bc6d54b5ed7 100644
--- a/spec/features/profiles/emails_spec.rb
+++ b/spec/features/profiles/emails_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Profile > Emails' do
+describe 'Profile > Emails' do
let(:user) { create(:user) }
before do
@@ -12,7 +12,7 @@ feature 'Profile > Emails' do
visit profile_emails_path
end
- scenario 'saves the new email' do
+ it 'saves the new email' do
fill_in('Email', with: 'my@email.com')
click_button('Add email address')
@@ -21,7 +21,7 @@ feature 'Profile > Emails' do
expect(page).to have_content('Resend confirmation email')
end
- scenario 'does not add a duplicate email' do
+ it 'does not add a duplicate email' do
fill_in('Email', with: user.email)
click_button('Add email address')
@@ -31,7 +31,7 @@ feature 'Profile > Emails' do
end
end
- scenario '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")
@@ -40,7 +40,7 @@ feature 'Profile > Emails' do
expect(page).not_to have_content("my@email.com")
end
- scenario '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")
@@ -52,7 +52,7 @@ feature 'Profile > Emails' do
expect(page).to have_content("#{email.email} Verified")
end
- scenario '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
@@ -60,7 +60,7 @@ feature 'Profile > Emails' do
expect(page).to have_content("Confirmation email sent to #{email.email}")
end
- scenario 'old unconfirmed emails show Send Confirmation button' do
+ it 'old unconfirmed emails show Send Confirmation button' do
email = user.emails.create(email: 'my@email.com')
email.update_attribute(:confirmation_sent_at, nil)
visit profile_emails_path
diff --git a/spec/features/profiles/gpg_keys_spec.rb b/spec/features/profiles/gpg_keys_spec.rb
index 59233e92f93..ec3ec795b63 100644
--- a/spec/features/profiles/gpg_keys_spec.rb
+++ b/spec/features/profiles/gpg_keys_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Profile > GPG Keys' do
+describe 'Profile > GPG Keys' do
let(:user) { create(:user, email: GpgHelpers::User2.emails.first) }
before do
@@ -12,7 +12,7 @@ feature 'Profile > GPG Keys' do
visit profile_gpg_keys_path
end
- scenario 'saves the new key' do
+ it 'saves the new key' do
fill_in('Key', with: GpgHelpers::User2.public_key)
click_button('Add key')
@@ -21,7 +21,7 @@ feature 'Profile > GPG Keys' do
expect(page).to have_content(GpgHelpers::User2.fingerprint)
end
- scenario 'with multiple subkeys' do
+ it 'with multiple subkeys' do
fill_in('Key', with: GpgHelpers::User3.public_key)
click_button('Add key')
@@ -34,7 +34,7 @@ feature 'Profile > GPG Keys' do
end
end
- scenario '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
@@ -43,7 +43,7 @@ feature 'Profile > GPG Keys' do
expect(page).to have_content(GpgHelpers::User2.fingerprint)
end
- scenario '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
@@ -52,7 +52,7 @@ feature 'Profile > GPG Keys' do
expect(page).to have_content('Your GPG keys (0)')
end
- scenario '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 b04a5422fed..e6586fc8a0a 100644
--- a/spec/features/profiles/keys_spec.rb
+++ b/spec/features/profiles/keys_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Profile > SSH Keys' do
+describe 'Profile > SSH Keys' do
let(:user) { create(:user) }
before do
@@ -12,13 +12,13 @@ feature 'Profile > SSH Keys' do
visit profile_keys_path
end
- scenario 'auto-populates the title', :js do
+ it 'auto-populates the title', :js do
fill_in('Key', with: attributes_for(:key).fetch(:key))
expect(page).to have_field("Title", with: "dummy@gitlab.com")
end
- scenario 'saves the new key' do
+ it 'saves the new key' do
attrs = attributes_for(:key)
fill_in('Key', with: attrs[:key])
@@ -30,13 +30,27 @@ feature 'Profile > SSH Keys' do
expect(find('.breadcrumbs-sub-title')).to have_link(attrs[:title])
end
+ it 'shows a confirmable warning if the key does not start with ssh-' do
+ attrs = attributes_for(:key)
+
+ fill_in('Key', with: 'invalid-key')
+ fill_in('Title', with: attrs[:title])
+ click_button('Add key')
+
+ expect(page).to have_selector('.js-add-ssh-key-validation-warning')
+
+ find('.js-add-ssh-key-validation-confirm-submit').click
+
+ expect(page).to have_content('Key is invalid')
+ end
+
context 'when only DSA and ECDSA keys are allowed' do
before do
forbidden = ApplicationSetting::FORBIDDEN_KEY_VALUE
stub_application_setting(rsa_key_restriction: forbidden, ed25519_key_restriction: forbidden)
end
- scenario 'shows a validation error' do
+ it 'shows a validation error' do
attrs = attributes_for(:key)
fill_in('Key', with: attrs[:key])
@@ -48,14 +62,14 @@ feature 'Profile > SSH Keys' do
end
end
- scenario 'User sees their keys' do
+ it 'User sees their keys' do
key = create(:key, user: user)
visit profile_keys_path
expect(page).to have_content(key.title)
end
- scenario 'User removes a key via the key index' do
+ it 'User removes a key via the key index' do
create(:key, user: user)
visit profile_keys_path
@@ -64,7 +78,7 @@ feature 'Profile > SSH Keys' do
expect(page).to have_content('Your SSH keys (0)')
end
- scenario 'User removes a key via its details page' do
+ it 'User removes a key via its details page' do
key = create(:key, user: user)
visit profile_key_path(key)
diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb
index f9c6ff90ca1..5e3569e4beb 100644
--- a/spec/features/profiles/password_spec.rb
+++ b/spec/features/profiles/password_spec.rb
@@ -117,7 +117,7 @@ describe 'Profile > Password' do
before do
sign_in(user)
- user.update_attributes(password_expires_at: 1.hour.ago)
+ user.update(password_expires_at: 1.hour.ago)
user.identities.delete
expect(user.ldap_user?).to eq false
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 d5fe5bdffc5..f618bc330ea 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
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Profile > Notifications > User changes notified_of_own_activity setting', :js do
+describe 'Profile > Notifications > User changes notified_of_own_activity setting', :js do
let(:user) { create(:user) }
before do
sign_in(user)
end
- scenario '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]')
@@ -18,7 +18,7 @@ feature 'Profile > Notifications > User changes notified_of_own_activity setting
expect(page).to have_checked_field('user[notified_of_own_activity]')
end
- scenario '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 0b5eacbe916..9e60b4995bd 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -8,6 +8,10 @@ describe 'User edit profile' do
visit(profile_path)
end
+ def submit_settings
+ click_button 'Update profile settings'
+ end
+
it 'changes user profile' do
fill_in 'user_skype', with: 'testskype'
fill_in 'user_linkedin', with: 'testlinkedin'
@@ -16,7 +20,7 @@ describe 'User edit profile' do
fill_in 'user_location', with: 'Ukraine'
fill_in 'user_bio', with: 'I <3 GitLab'
fill_in 'user_organization', with: 'GitLab'
- click_button 'Update profile settings'
+ submit_settings
expect(user.reload).to have_attributes(
skype: 'testskype',
@@ -34,7 +38,7 @@ describe 'User edit profile' do
context 'user avatar' do
before do
attach_file(:user_avatar, Rails.root.join('spec', 'fixtures', 'banana_sample.gif'))
- click_button 'Update profile settings'
+ submit_settings
end
it 'changes user avatar' do
@@ -55,4 +59,76 @@ describe 'User edit profile' do
expect(page).to have_link('gravatar.com')
end
end
+
+ context 'user status', :js do
+ def select_emoji(emoji_name)
+ toggle_button = find('.js-toggle-emoji-menu')
+ toggle_button.click
+ emoji_button = find(%Q{.js-status-emoji-menu .js-emoji-btn gl-emoji[data-name="#{emoji_name}"]})
+ emoji_button.click
+ end
+
+ it 'shows the user status form' do
+ visit(profile_path)
+
+ expect(page).to have_content('Current status')
+ end
+
+ it 'adds emoji to user status' do
+ emoji = 'biohazard'
+ visit(profile_path)
+ select_emoji(emoji)
+ submit_settings
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ end
+ end
+
+ it 'adds message to user status' do
+ message = 'I have something to say'
+ visit(profile_path)
+ fill_in 'js-status-message-field', with: message
+ submit_settings
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji('speech_balloon')
+ expect(page).to have_content message
+ end
+ end
+
+ it 'adds message and emoji to user status' do
+ emoji = 'tanabata_tree'
+ message = 'Playing outside'
+ visit(profile_path)
+ select_emoji(emoji)
+ fill_in 'js-status-message-field', with: message
+ submit_settings
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(emoji)
+ expect(page).to have_content message
+ end
+ end
+
+ it 'clears the user status' do
+ user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+
+ visit user_path(user)
+ within('.cover-status') do
+ expect(page).to have_emoji(user_status.emoji)
+ expect(page).to have_content user_status.message
+ end
+
+ visit(profile_path)
+ click_button 'js-clear-user-status-button'
+ submit_settings
+
+ visit user_path(user)
+ expect(page).not_to have_selector '.cover-status'
+ end
+ end
end
diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb
index 95953fbcfac..db797bb586f 100644
--- a/spec/features/profiles/user_visits_notifications_tab_spec.rb
+++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-feature 'User visits the notifications tab', :js do
+describe 'User visits the notifications tab', :js do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(profile_notifications_path)
end
diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb
index 713112477c8..2dc4547b2d8 100644
--- a/spec/features/profiles/user_visits_profile_spec.rb
+++ b/spec/features/profiles/user_visits_profile_spec.rb
@@ -29,7 +29,7 @@ describe 'User visits their profile' do
let!(:project) do
create(:project, :repository, namespace: group) do |project|
create(:closed_issue_event, project: project)
- project.add_master(user)
+ project.add_maintainer(user)
end
end
diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb
index 0ba2224359a..a93df3696d2 100644
--- a/spec/features/project_variables_spec.rb
+++ b/spec/features/project_variables_spec.rb
@@ -8,7 +8,7 @@ describe 'Project variables', :js do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
project.variables << variable
visit page_path
diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb
index 4ac34adde0e..411134e7b8e 100644
--- a/spec/features/projects/activity/rss_spec.rb
+++ b/spec/features/projects/activity/rss_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Project Activity RSS' do
+describe 'Project Activity RSS' do
let(:project) { create(:project, :public) }
let(:user) { project.owner }
let(:path) { activity_project_path(project) }
diff --git a/spec/features/projects/activity/user_sees_activity_spec.rb b/spec/features/projects/activity/user_sees_activity_spec.rb
index 644a837dc14..e0248911b5f 100644
--- a/spec/features/projects/activity/user_sees_activity_spec.rb
+++ b/spec/features/projects/activity/user_sees_activity_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Projects > Activity > User sees activity' do
+describe 'Projects > Activity > User sees activity' do
let(:project) { create(:project, :repository, :public) }
let(:user) { project.creator }
diff --git a/spec/features/projects/actve_tabs_spec.rb b/spec/features/projects/actve_tabs_spec.rb
index ce5606b63ae..7c6110c533b 100644
--- a/spec/features/projects/actve_tabs_spec.rb
+++ b/spec/features/projects/actve_tabs_spec.rb
@@ -5,7 +5,7 @@ describe 'Project active tab' do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/artifacts/file_spec.rb b/spec/features/projects/artifacts/file_spec.rb
index df1d17bdcb7..993d0040434 100644
--- a/spec/features/projects/artifacts/file_spec.rb
+++ b/spec/features/projects/artifacts/file_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Artifact file', :js do
+describe 'Artifact file', :js do
let(:project) { create(:project, :public) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
diff --git a/spec/features/projects/artifacts/raw_spec.rb b/spec/features/projects/artifacts/raw_spec.rb
index 0bec6e9ad31..d8ee9adda6b 100644
--- a/spec/features/projects/artifacts/raw_spec.rb
+++ b/spec/features/projects/artifacts/raw_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Raw artifact', :js do
+describe 'Raw artifact', :js do
let(:project) { create(:project, :public) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
diff --git a/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb b/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
index 12e07647ecd..4d860893abe 100644
--- a/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
+++ b/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
@@ -6,7 +6,7 @@ describe 'User interacts with awards in an issue', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_issue_path(project, issue))
diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb
index f51001edcd7..8522ea747fa 100644
--- a/spec/features/projects/badges/coverage_spec.rb
+++ b/spec/features/projects/badges/coverage_spec.rb
@@ -1,16 +1,16 @@
require 'spec_helper'
-feature 'test coverage badge' do
- given!(:user) { create(:user) }
- given!(:project) { create(:project, :private) }
+describe 'test coverage badge' do
+ let!(:user) { create(:user) }
+ let!(:project) { create(:project, :private) }
context 'when user has access to view badge' do
- background do
+ before do
project.add_developer(user)
sign_in(user)
end
- scenario 'user requests coverage badge image for pipeline' do
+ it 'user requests coverage badge image for pipeline' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 100, name: 'test:1')
create_build(pipeline, coverage: 90, name: 'test:2')
@@ -21,7 +21,7 @@ feature 'test coverage badge' do
expect_coverage_badge('95.00%')
end
- scenario 'user requests coverage badge for specific job' do
+ it 'user requests coverage badge for specific job' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 50, name: 'test:1')
create_build(pipeline, coverage: 50, name: 'test:2')
@@ -33,7 +33,7 @@ feature 'test coverage badge' do
expect_coverage_badge('85.00%')
end
- scenario 'user requests coverage badge for pipeline without coverage' do
+ it 'user requests coverage badge for pipeline without coverage' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: nil, name: 'test')
end
@@ -45,9 +45,11 @@ feature 'test coverage badge' do
end
context 'when user does not have access to view badge' do
- background { sign_in(user) }
+ before do
+ sign_in(user)
+ end
- scenario 'user requests test coverage badge image' do
+ it 'user requests test coverage badge image' do
show_test_coverage_badge
expect(page).to have_gitlab_http_status(404)
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index 0abef4bc447..e30b908c60d 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -1,15 +1,15 @@
require 'spec_helper'
-feature 'list of badges' do
- background do
+describe 'list of badges' do
+ before do
user = create(:user)
project = create(:project, :repository)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_settings_ci_cd_path(project)
end
- scenario 'user wants to see build status badge' do
+ it 'user wants to see build status badge' do
page.within('.pipeline-status') do
expect(page).to have_content 'pipeline status'
expect(page).to have_content 'Markdown'
@@ -24,7 +24,7 @@ feature 'list of badges' do
end
end
- scenario 'user wants to see coverage report badge' do
+ it 'user wants to see coverage report badge' do
page.within('.coverage-report') do
expect(page).to have_content 'coverage report'
expect(page).to have_content 'Markdown'
@@ -39,7 +39,7 @@ feature 'list of badges' do
end
end
- scenario 'user changes current ref of build status badge', :js do
+ it 'user changes current ref of build status badge', :js do
page.within('.pipeline-status') do
first('.js-project-refs-dropdown').click
diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb
index b83ea8f4eaa..8c4488b2ca6 100644
--- a/spec/features/projects/badges/pipeline_badge_spec.rb
+++ b/spec/features/projects/badges/pipeline_badge_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Pipeline Badge' do
+describe 'Pipeline Badge' do
set(:project) { create(:project, :repository, :public) }
let(:ref) { project.default_branch }
diff --git a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
index c12e56d2c3f..96f514f4f04 100644
--- a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
+++ b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do
+describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do
include TreeHelper
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index e7b305925f7..1064f72c271 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'File blob', :js do
+describe 'File blob', :js do
include MobileHelpers
let(:project) { create(:project, :public, :repository) }
@@ -144,7 +144,7 @@ feature 'File blob', :js do
context 'Markdown file (stored in LFS)' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -237,7 +237,7 @@ feature 'File blob', :js do
context 'PDF file' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -350,7 +350,7 @@ feature 'File blob', :js do
context 'empty file' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -418,7 +418,7 @@ feature 'File blob', :js do
context '.gitlab-ci.yml' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -446,7 +446,7 @@ feature 'File blob', :js do
context '.gitlab/route-map.yml' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -494,7 +494,7 @@ feature 'File blob', :js do
context '*.gemspec' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -552,4 +552,33 @@ feature 'File blob', :js do
end
end
end
+
+ context 'for subgroups' do
+ let(:group) { create(:group) }
+ let(:subgroup) { create(:group, parent: group) }
+ let(:project) { create(:project, :public, :repository, group: subgroup) }
+
+ it 'renders tree table without errors' do
+ visit_blob('README.md')
+
+ expect(page).to have_selector('.file-content')
+ expect(page).not_to have_selector('.flash-alert')
+ end
+
+ it 'displays a GPG badge' do
+ visit_blob('CONTRIBUTING.md', ref: '33f3729a45c02fc67d00adb1b8bca394b0e761d9')
+
+ expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge'
+ expect(page).to have_selector '.gpg-status-box.invalid'
+ end
+ end
+
+ context 'on signed merge commit' do
+ it 'displays a GPG badge' do
+ visit_blob('conflicting-file.md', ref: '6101e87e575de14b38b4e1ce180519a813671e10')
+
+ expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge'
+ expect(page).to have_selector '.gpg-status-box.invalid'
+ end
+ end
end
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 89d3bd24b89..0e036b4ea68 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Editing file blob', :js do
+describe 'Editing file blob', :js do
include TreeHelper
let(:project) { create(:project, :public, :repository) }
@@ -134,11 +134,11 @@ feature 'Editing file blob', :js do
end
end
- context 'as master' do
+ context 'as maintainer' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_edit_blob_path(project, tree_join(branch, file_path))
end
diff --git a/spec/features/projects/blobs/shortcuts_blob_spec.rb b/spec/features/projects/blobs/shortcuts_blob_spec.rb
index 9f1fef80ab5..7203c5b1c21 100644
--- a/spec/features/projects/blobs/shortcuts_blob_spec.rb
+++ b/spec/features/projects/blobs/shortcuts_blob_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Blob shortcuts' do
+describe 'Blob shortcuts', :js do
include TreeHelper
let(:project) { create(:project, :public, :repository) }
let(:path) { project.repository.ls_files(project.repository.root_ref)[0] }
diff --git a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
index b7d063596c1..8a0b92190dd 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
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'User creates blob in new project', :js do
+describe 'User creates blob in new project', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :empty_repo) }
@@ -24,9 +24,9 @@ feature 'User creates blob in new project', :js do
end
end
- describe 'as a master' do
+ describe 'as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'creating a file'
diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb
index 605298ba8ab..c8dc72a34ec 100644
--- a/spec/features/projects/branches/download_buttons_spec.rb
+++ b/spec/features/projects/branches/download_buttons_spec.rb
@@ -1,12 +1,12 @@
require 'spec_helper'
-feature 'Download buttons in branches page' do
- given(:user) { create(:user) }
- given(:role) { :developer }
- given(:status) { 'success' }
- given(:project) { create(:project, :repository) }
+describe 'Download buttons in branches page' do
+ let(:user) { create(:user) }
+ let(:role) { :developer }
+ let(:status) { 'success' }
+ let(:project) { create(:project, :repository) }
- given(:pipeline) do
+ let(:pipeline) do
create(:ci_pipeline,
project: project,
sha: project.commit('binary-encoding').sha,
@@ -14,14 +14,14 @@ feature 'Download buttons in branches page' do
status: status)
end
- given!(:build) do
+ let!(:build) do
create(:ci_build, :success, :artifacts,
pipeline: pipeline,
status: pipeline.status,
name: 'build')
end
- background do
+ before do
sign_in(user)
project.add_role(user, role)
end
@@ -32,7 +32,7 @@ feature 'Download buttons in branches page' do
visit project_branches_filtered_path(project, state: 'all', search: 'binary-encoding')
end
- scenario 'shows download artifacts button' do
+ it 'shows download artifacts button' do
href = latest_succeeded_project_artifacts_path(project, 'binary-encoding/download', job: 'build')
expect(page).to have_link "Download '#{build.name}'", href: href
diff --git a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
index 0be434a567b..0faf73db7da 100644
--- a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
+++ b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
@@ -6,7 +6,7 @@ describe 'New Branch Ref Dropdown', :js do
let(:toggle) { find('.create-from .dropdown-menu-toggle') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit new_project_branch_path(project)
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index b7ce1b9993a..97757e8da92 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -182,10 +182,10 @@ describe 'Branches' do
end
end
- context 'logged in as master' do
+ context 'logged in as maintainer' do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'Initial branches page' do
diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb
index 7b2c57aa652..71d715237f5 100644
--- a/spec/features/projects/clusters/applications_spec.rb
+++ b/spec/features/projects/clusters/applications_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Clusters Applications', :js do
+describe 'Clusters Applications', :js do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -19,7 +19,7 @@ feature 'Clusters Applications', :js do
context 'when cluster is being created' do
let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project])}
- scenario 'user is unable to install applications' do
+ it 'user is unable to install applications' do
page.within('.js-cluster-application-row-helm') do
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install')
@@ -30,7 +30,7 @@ feature 'Clusters Applications', :js do
context 'when cluster is created' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project])}
- scenario 'user can install applications' do
+ it 'user can install applications' do
page.within('.js-cluster-application-row-helm') do
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to be_nil
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install')
@@ -46,12 +46,14 @@ feature 'Clusters Applications', :js do
end
end
- it 'he sees status transition' do
+ it 'they see status transition' do
page.within('.js-cluster-application-row-helm') do
# FE sends request and gets the response, then the buttons is "Install"
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install')
+ wait_until_helm_created!
+
Clusters::Cluster.last.application_helm.make_installing!
# FE starts polling and update the buttons to "Installing"
@@ -83,7 +85,7 @@ feature 'Clusters Applications', :js do
end
end
- it 'he sees status transition' do
+ it 'they see status transition' do
page.within('.js-cluster-application-row-ingress') do
# FE sends request and gets the response, then the buttons is "Install"
expect(page).to have_css('.js-cluster-application-install-button[disabled]')
@@ -116,4 +118,14 @@ feature 'Clusters Applications', :js do
end
end
end
+
+ def wait_until_helm_created!
+ retries = 0
+
+ while Clusters::Cluster.last.application_helm.nil?
+ raise "Timed out waiting for helm application to be created in DB" if (retries += 1) > 3
+
+ sleep(1)
+ end
+ end
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 3db384e5b65..31e3ebf675d 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Gcp Cluster', :js do
+describe 'Gcp Cluster', :js do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
gitlab_sign_in(user)
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
end
@@ -16,9 +16,9 @@ feature 'Gcp Cluster', :js do
let(:project_id) { 'test-project-1234' }
before do
- allow_any_instance_of(Projects::Clusters::GcpController)
+ allow_any_instance_of(Projects::ClustersController)
.to receive(:token_in_session).and_return('token')
- allow_any_instance_of(Projects::Clusters::GcpController)
+ allow_any_instance_of(Projects::ClustersController)
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
end
@@ -27,7 +27,7 @@ feature 'Gcp Cluster', :js do
visit project_clusters_path(project)
click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
+ click_link 'Create new Cluster on GKE'
end
context 'when user filled form with valid parameters' do
@@ -148,7 +148,7 @@ feature 'Gcp Cluster', :js do
visit project_clusters_path(project)
click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
+ click_link 'Create new Cluster on GKE'
end
it 'user sees a login page' do
@@ -162,7 +162,7 @@ feature 'Gcp Cluster', :js do
visit project_clusters_path(project)
click_link 'Add Kubernetes cluster'
- click_link 'Add an existing Kubernetes cluster'
+ click_link 'Add existing cluster'
end
it 'user does not see the "Environment scope" field' do
@@ -187,7 +187,7 @@ feature 'Gcp Cluster', :js do
it 'user sees offer on cluster GCP login page' do
click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
+ click_link 'Create new Cluster on GKE'
expect(page).to have_css('.gcp-signup-offer')
end
diff --git a/spec/features/projects/clusters/interchangeability_spec.rb b/spec/features/projects/clusters/interchangeability_spec.rb
index 3ddb35c755c..0033e12b6b1 100644
--- a/spec/features/projects/clusters/interchangeability_spec.rb
+++ b/spec/features/projects/clusters/interchangeability_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Interchangeability between KubernetesService and Platform::Kubernetes' do
+describe 'Interchangeability between KubernetesService and Platform::Kubernetes' do
EXCEPT_METHODS = %i[test title description help fields initialize_properties namespace namespace= api_url api_url= deprecated? deprecation_message].freeze
EXCEPT_METHODS_GREP_V = %w[_touched? _changed? _was].freeze
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 698b64a659c..babf47cc341 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'User Cluster', :js do
+describe 'User Cluster', :js do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
gitlab_sign_in(user)
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
end
@@ -17,7 +17,7 @@ feature 'User Cluster', :js do
visit project_clusters_path(project)
click_link 'Add Kubernetes cluster'
- click_link 'Add an existing Kubernetes cluster'
+ click_link 'Add existing cluster'
end
context 'when user filled form with valid parameters' do
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index a251a2f4e52..91eac9c8278 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Clusters', :js do
+describe 'Clusters', :js do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
gitlab_sign_in(user)
end
@@ -83,7 +83,7 @@ feature 'Clusters', :js do
visit project_clusters_path(project)
click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
+ click_link 'Create new Cluster on GKE'
end
it 'user sees a login page' do
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index 36a746ac83d..bd254caddfb 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -1,22 +1,22 @@
require 'spec_helper'
-feature 'project commit pipelines', :js do
- given(:project) { create(:project, :repository) }
+describe 'project commit pipelines', :js do
+ let(:project) { create(:project, :repository) }
- background do
+ before do
user = create(:user)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
context 'when no builds triggered yet' do
- background do
+ before do
create(:ci_pipeline, project: project,
sha: project.commit.sha,
ref: 'master')
end
- scenario 'user views commit pipelines page' do
+ it 'user views commit pipelines page' do
visit pipelines_project_commit_path(project, project.commit.sha)
page.within('.table-holder') do
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index 1df45865d6f..bc3c00dafe2 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -9,7 +9,7 @@ describe 'Cherry-pick Commits' do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
visit project_commit_path(project, master_pickable_commit.id)
end
diff --git a/spec/features/projects/commit/comments/user_adds_comment_spec.rb b/spec/features/projects/commit/comments/user_adds_comment_spec.rb
index 53866c32c69..6397df086a7 100644
--- a/spec/features/projects/commit/comments/user_adds_comment_spec.rb
+++ b/spec/features/projects/commit/comments/user_adds_comment_spec.rb
@@ -62,7 +62,7 @@ describe "User adds a comment on a commit", :js do
click_diff_line(sample_commit.line_code)
expect(page).to have_css(".js-temp-notes-holder form.new-note")
- .and have_css(".js-close-discussion-note-form", text: "Discard draft")
+ .and have_css(".js-close-discussion-note-form", text: "Cancel")
# The `Cancel` button closes the current form. The page should not have any open forms after that.
find(".js-close-discussion-note-form").click
diff --git a/spec/features/projects/commit/diff_notes_spec.rb b/spec/features/projects/commit/diff_notes_spec.rb
index 4dbfc6f6edf..e2aefa35fad 100644
--- a/spec/features/projects/commit/diff_notes_spec.rb
+++ b/spec/features/projects/commit/diff_notes_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Commit diff', :js do
+describe 'Commit diff', :js do
include RepoHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index 91282063a8d..19f6ebf2c1a 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Mini Pipeline Graph in Commit View', :js do
+describe 'Mini Pipeline Graph in Commit View', :js do
let(:project) { create(:project, :public, :repository) }
context 'when commit has pipelines' do
diff --git a/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb b/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb
new file mode 100644
index 00000000000..e78b7f7ae08
--- /dev/null
+++ b/spec/features/projects/commit/user_views_user_status_on_commit_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Project > Commit > View user status' do
+ include RepoHelpers
+
+ set(:project) { create(:project, :repository) }
+ set(:user) { create(:user) }
+ let(:commit_author) { create(:user, email: sample_commit.author_email) }
+
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ end
+
+ subject { visit(project_commit_path(project, sample_commit.id)) }
+
+ describe 'status for the commit author' do
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { commit_author }
+ end
+ end
+
+ describe 'status for a comment on the commit' do
+ let(:note) { create(:note, :on_commit, project: project) }
+
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { note.author }
+ end
+ end
+
+ describe 'status for a diff note on the commit' do
+ let(:note) { create(:diff_note_on_commit, project: project) }
+
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { note.author }
+ end
+ end
+end
diff --git a/spec/features/projects/commits/rss_spec.rb b/spec/features/projects/commits/rss_spec.rb
index 0bc207da970..cfc2637f1b2 100644
--- a/spec/features/projects/commits/rss_spec.rb
+++ b/spec/features/projects/commits/rss_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Project Commits RSS' do
+describe 'Project Commits RSS' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:path) { project_commits_path(project, :master) }
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 35ed6620548..534cfe1eb12 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -7,7 +7,7 @@ describe 'User browses commits' do
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -238,6 +238,5 @@ def check_author_link(email, author)
author_link = find('.commit-author-link')
expect(author_link['href']).to eq(user_path(author))
- expect(author_link['title']).to eq(email)
expect(find('.commit-author-name').text).to eq(author.name)
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index 7e863d9df32..69600884909 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -5,7 +5,7 @@ describe "Compare", :js do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 1552a3512dd..e12532e97fa 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -5,7 +5,7 @@ describe 'Project deploy keys', :js do
let(:project) { create(:project_empty_repo) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb
index 9bfcb1e816a..df05625d105 100644
--- a/spec/features/projects/diffs/diff_show_spec.rb
+++ b/spec/features/projects/diffs/diff_show_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Diff file viewer', :js do
+describe 'Diff file viewer', :js do
let(:project) { create(:project, :public, :repository) }
def visit_commit(sha, anchor: nil)
@@ -24,7 +24,7 @@ feature 'Diff file viewer', :js do
context 'Ruby file (stored in LFS)' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
@commit_id = Files::CreateService.new(
project,
diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb
index 82a722c5960..edbab14f7c1 100644
--- a/spec/features/projects/environments/environment_metrics_spec.rb
+++ b/spec/features/projects/environments/environment_metrics_spec.rb
@@ -1,16 +1,16 @@
require 'spec_helper'
-feature 'Environment > Metrics' do
+describe 'Environment > Metrics' do
include PrometheusHelpers
- given(:user) { create(:user) }
- given(:project) { create(:prometheus_project) }
- given(:pipeline) { create(:ci_pipeline, project: project) }
- given(:build) { create(:ci_build, pipeline: pipeline) }
- given(:environment) { create(:environment, project: project) }
- given(:current_time) { Time.now.utc }
+ let(:user) { create(:user) }
+ let(:project) { create(:prometheus_project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:environment) { create(:environment, project: project) }
+ let(:current_time) { Time.now.utc }
- background do
+ before do
project.add_developer(user)
create(:deployment, environment: environment, deployable: build)
stub_all_prometheus_requests(environment.slug)
@@ -24,7 +24,7 @@ feature 'Environment > Metrics' do
end
context 'with deployments and related deployable present' do
- scenario 'shows metrics' do
+ it 'shows metrics' do
click_link('See metrics')
expect(page).to have_css('div#prometheus-graphs')
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index b233af83eec..4c5dda29fee 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -1,42 +1,42 @@
require 'spec_helper'
-feature 'Environment' do
- given(:project) { create(:project) }
- given(:user) { create(:user) }
- given(:role) { :developer }
+describe 'Environment' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:role) { :developer }
- background do
+ before do
sign_in(user)
project.add_role(user, role)
end
- feature 'environment details page' do
- given!(:environment) { create(:environment, project: project) }
- given!(:permissions) { }
- given!(:deployment) { }
- given!(:action) { }
+ describe 'environment details page' do
+ let!(:environment) { create(:environment, project: project) }
+ let!(:permissions) { }
+ let!(:deployment) { }
+ let!(:action) { }
before do
visit_environment(environment)
end
- scenario 'shows environment name' do
+ it 'shows environment name' do
expect(page).to have_content(environment.name)
end
context 'without deployments' do
- scenario 'does show no deployments' do
+ it 'does show no deployments' do
expect(page).to have_content('You don\'t have any deployments right now.')
end
end
context 'with deployments' do
context 'when there is no related deployable' do
- given(:deployment) do
+ let(:deployment) do
create(:deployment, environment: environment, deployable: nil)
end
- scenario 'does show deployment SHA' do
+ it 'does show deployment SHA' do
expect(page).to have_link(deployment.short_sha)
expect(page).not_to have_link('Re-deploy')
expect(page).not_to have_terminal_button
@@ -44,27 +44,27 @@ feature 'Environment' do
end
context 'with related deployable present' do
- given(:pipeline) { create(:ci_pipeline, project: project) }
- given(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
- given(:deployment) do
+ let(:deployment) do
create(:deployment, environment: environment, deployable: build)
end
- scenario 'does show build name' do
+ it 'does show build name' do
expect(page).to have_link("#{build.name} (##{build.id})")
expect(page).to have_link('Re-deploy')
expect(page).not_to have_terminal_button
end
context 'with manual action' do
- given(:action) do
+ let(:action) do
create(:ci_build, :manual, pipeline: pipeline,
name: 'deploy to production')
end
context 'when user has ability to trigger deployment' do
- given(:permissions) do
+ let(:permissions) do
create(:protected_branch, :developers_can_merge,
name: action.ref, project: project)
end
@@ -91,21 +91,21 @@ feature 'Environment' do
end
context 'with external_url' do
- given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') }
- given(:build) { create(:ci_build, pipeline: pipeline) }
- given(:deployment) { create(:deployment, environment: environment, deployable: build) }
+ let(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:deployment) { create(:deployment, environment: environment, deployable: build) }
- scenario 'does show an external link button' do
+ it 'does show an external link button' do
expect(page).to have_link(nil, href: environment.external_url)
end
end
context 'with terminal' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
- context 'for project master' do
- let(:role) { :master }
+ context 'for project maintainer' do
+ let(:role) { :maintainer }
- scenario 'it shows the terminal button' do
+ it 'it shows the terminal button' do
expect(page).to have_terminal_button
end
@@ -126,7 +126,7 @@ feature 'Environment' do
context 'for developer' do
let(:role) { :developer }
- scenario 'does not show terminal button' do
+ it 'does not show terminal button' do
expect(page).not_to have_terminal_button
end
end
@@ -148,25 +148,26 @@ feature 'Environment' do
context 'when environment is available' do
context 'with stop action' do
- given(:action) do
+ let(:action) do
create(:ci_build, :manual, pipeline: pipeline,
name: 'close_app')
end
- given(:deployment) do
+ let(:deployment) do
create(:deployment, environment: environment,
deployable: build,
on_stop: 'close_app')
end
context 'when user has ability to stop environment' do
- given(:permissions) do
+ let(:permissions) do
create(:protected_branch, :developers_can_merge,
name: action.ref, project: project)
end
it 'allows to stop environment' do
- click_link('Stop')
+ click_button('Stop')
+ click_button('Stop environment') # confirm modal
expect(page).to have_content('close_app')
end
@@ -174,25 +175,25 @@ feature 'Environment' do
context 'when user has no ability to stop environment' do
it 'does not allow to stop environment' do
- expect(page).to have_no_link('Stop')
+ expect(page).not_to have_button('Stop')
end
end
context 'for reporter' do
let(:role) { :reporter }
- scenario 'does not show stop button' do
- expect(page).not_to have_link('Stop')
+ it 'does not show stop button' do
+ expect(page).not_to have_button('Stop')
end
end
end
end
context 'when environment is stopped' do
- given(:environment) { create(:environment, project: project, state: :stopped) }
+ let(:environment) { create(:environment, project: project, state: :stopped) }
- scenario 'does not show stop button' do
- expect(page).not_to have_link('Stop')
+ it 'does not show stop button' do
+ expect(page).not_to have_button('Stop')
end
end
end
@@ -200,7 +201,7 @@ feature 'Environment' do
end
end
- feature 'environment folders', :js do
+ describe 'environment folders', :js do
context 'when folder name contains special charaters' do
before do
create(:environment, project: project,
@@ -219,21 +220,21 @@ feature 'Environment' do
end
end
- feature 'auto-close environment when branch is deleted' do
- given(:project) { create(:project, :repository) }
+ describe 'auto-close environment when branch is deleted' do
+ let(:project) { create(:project, :repository) }
- given!(:environment) do
+ let!(:environment) do
create(:environment, :with_review_app, project: project,
ref: 'feature')
end
- scenario 'user visits environment page' do
+ it 'user visits environment page' do
visit_environment(environment)
- expect(page).to have_link('Stop')
+ expect(page).to have_button('Stop')
end
- scenario 'user deletes the branch with running environment' do
+ it 'user deletes the branch with running environment' do
visit project_branches_filtered_path(project, state: 'all', search: 'feature')
remove_branch_with_hooks(project, user, 'feature') do
@@ -242,7 +243,7 @@ feature 'Environment' do
visit_environment(environment)
- expect(page).to have_no_link('Stop')
+ expect(page).not_to have_button('Stop')
end
##
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index f9defa22d35..f0890018286 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -1,15 +1,19 @@
require 'spec_helper'
-feature 'Environments page', :js do
- given(:project) { create(:project) }
- given(:user) { create(:user) }
- given(:role) { :developer }
+describe 'Environments page', :js do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:role) { :developer }
- background do
+ before do
project.add_role(user, role)
sign_in(user)
end
+ def stop_button_selector
+ %q{button[data-original-title="Stop environment"]}
+ end
+
describe 'page tabs' do
it 'shows "Available" and "Stopped" tab with links' do
visit_environments(project)
@@ -99,7 +103,7 @@ feature 'Environments page', :js do
end
describe 'environments table' do
- given!(:environment) do
+ let!(:environment) do
create(:environment, project: project, state: :available)
end
@@ -120,14 +124,14 @@ feature 'Environments page', :js do
end
it 'does not show stip button when environment is not stoppable' do
- expect(page).not_to have_selector('.stop-env-link')
+ expect(page).not_to have_selector(stop_button_selector)
end
end
context 'when there are deployments' do
- given(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository) }
- given!(:deployment) do
+ let!(:deployment) do
create(:deployment, environment: environment,
sha: project.commit.id)
end
@@ -140,14 +144,14 @@ feature 'Environments page', :js do
end
context 'when builds and manual actions are present' do
- given!(:pipeline) { create(:ci_pipeline, project: project) }
- given!(:build) { create(:ci_build, pipeline: pipeline) }
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+ let!(:build) { create(:ci_build, pipeline: pipeline) }
- given!(:action) do
+ let!(:action) do
create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production')
end
- given!(:deployment) do
+ let!(:deployment) do
create(:deployment, environment: environment,
deployable: build,
sha: project.commit.id)
@@ -178,7 +182,7 @@ feature 'Environments page', :js do
end
it 'shows a stop button' do
- expect(page).not_to have_selector('.stop-env-link')
+ expect(page).not_to have_selector(stop_button_selector)
end
it 'does not show external link button' do
@@ -190,9 +194,9 @@ feature 'Environments page', :js do
end
context 'with external_url' do
- given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') }
- given(:build) { create(:ci_build, pipeline: pipeline) }
- given(:deployment) { create(:deployment, environment: environment, deployable: build) }
+ let(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:deployment) { create(:deployment, environment: environment, deployable: build) }
it 'shows an external link button' do
expect(page).to have_link(nil, href: environment.external_url)
@@ -200,33 +204,33 @@ feature 'Environments page', :js do
end
context 'with stop action' do
- given(:action) do
+ let(:action) do
create(:ci_build, :manual, pipeline: pipeline, name: 'close_app')
end
- given(:deployment) do
+ let(:deployment) do
create(:deployment, environment: environment,
deployable: build,
on_stop: 'close_app')
end
it 'shows a stop button' do
- expect(page).to have_selector('.stop-env-link')
+ expect(page).to have_selector(stop_button_selector)
end
context 'when user is a reporter' do
let(:role) { :reporter }
it 'does not show stop button' do
- expect(page).not_to have_selector('.stop-env-link')
+ expect(page).not_to have_selector(stop_button_selector)
end
end
end
context 'when kubernetes terminal is available' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
- context 'for project master' do
- let(:role) { :master }
+ context 'for project maintainer' do
+ let(:role) { :maintainer }
it 'shows the terminal button' do
expect(page).to have_terminal_button
@@ -271,9 +275,9 @@ feature 'Environments page', :js do
end
context 'user is a developer' do
- given(:role) { :developer }
+ let(:role) { :developer }
- scenario 'developer creates a new environment with a valid name' do
+ it 'developer creates a new environment with a valid name' do
within(".top-area") { click_link 'New environment' }
fill_in('Name', with: 'production')
click_on 'Save'
@@ -281,7 +285,7 @@ feature 'Environments page', :js do
expect(page).to have_content('production')
end
- scenario 'developer creates a new environmetn with invalid name' do
+ it 'developer creates a new environmetn with invalid name' do
within(".top-area") { click_link 'New environment' }
fill_in('Name', with: 'name,with,commas')
click_on 'Save'
@@ -291,9 +295,9 @@ feature 'Environments page', :js do
end
context 'user is a reporter' do
- given(:role) { :reporter }
+ let(:role) { :reporter }
- scenario 'reporters tries to create a new environment' do
+ it 'reporters tries to create a new environment' do
expect(page).not_to have_link('New environment')
end
end
@@ -309,7 +313,7 @@ feature 'Environments page', :js do
state: :available)
end
- scenario 'users unfurls an environment folder' do
+ it 'users unfurls an environment folder' do
visit_environments(project)
expect(page).not_to have_content 'review-1'
@@ -335,7 +339,7 @@ feature 'Environments page', :js do
state: :available)
end
- scenario 'user opens folder view' do
+ it 'user opens folder view' do
visit folder_project_environments_path(project, 'staging.review')
wait_for_requests
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index b0eb7c5b42a..ab16fdee883 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -8,7 +8,7 @@ describe 'Edit Project Settings' do
describe 'project features visibility selectors', :js do
before do
- project.add_master(member)
+ project.add_maintainer(member)
sign_in(member)
end
@@ -165,7 +165,7 @@ describe 'Edit Project Settings' do
describe 'repository visibility', :js do
before do
- project.add_master(member)
+ project.add_maintainer(member)
sign_in(member)
visit edit_project_path(project)
end
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index b410199fd1f..ac6c8c337fa 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -2,16 +2,16 @@ require 'spec_helper'
describe 'Projects > Files > Project owner creates a license file', :js do
let(:project) { create(:project, :repository) }
- let(:project_master) { project.owner }
+ let(:project_maintainer) { project.owner }
before do
- project.repository.delete_file(project_master, 'LICENSE',
+ project.repository.delete_file(project_maintainer, 'LICENSE',
message: 'Remove LICENSE', branch_name: 'master')
- sign_in(project_master)
+ sign_in(project_maintainer)
visit project_path(project)
end
- it 'project master creates a license file manually from a template' do
+ it 'project maintainer creates a license file manually from a template' do
visit project_tree_path(project, project.repository.root_ref)
find('.add-to-tree').click
click_link 'New file'
@@ -35,7 +35,7 @@ describe 'Projects > Files > Project owner creates a license file', :js do
expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end
- it 'project master creates a license file from the "Add license" link' do
+ it 'project maintainer creates a license file from the "Add license" link' do
click_link 'Add License'
expect(page).to have_content('New file')
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 53d8ace7c94..801291c1f77 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -2,13 +2,13 @@ require 'spec_helper'
describe 'Projects > Files > Project owner sees a link to create a license file in empty project', :js do
let(:project) { create(:project_empty_repo) }
- let(:project_master) { project.owner }
+ let(:project_maintainer) { project.owner }
before do
- sign_in(project_master)
+ sign_in(project_maintainer)
end
- it 'project master creates a license file from a template' do
+ it 'project maintainer creates a license file from a template' do
visit project_path(project)
click_on 'Add License'
expect(page).to have_content('New file')
diff --git a/spec/features/projects/files/template_selector_menu_spec.rb b/spec/features/projects/files/template_selector_menu_spec.rb
index b549a69ddf3..6b313824acd 100644
--- a/spec/features/projects/files/template_selector_menu_spec.rb
+++ b/spec/features/projects/files/template_selector_menu_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-feature 'Template selector menu', :js do
+describe 'Template selector menu', :js do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
@@ -14,7 +14,7 @@ feature 'Template selector menu', :js do
create_and_edit_file('README.md')
end
- scenario 'is not displayed' do
+ it 'is not displayed' do
check_template_selector_menu_display(false)
end
@@ -23,7 +23,7 @@ feature 'Template selector menu', :js do
click_link 'Preview'
end
- scenario 'template selector menu is not displayed' do
+ it 'template selector menu is not displayed' do
check_template_selector_menu_display(false)
click_link 'Write'
check_template_selector_menu_display(false)
@@ -36,7 +36,7 @@ feature 'Template selector menu', :js do
visit project_edit_blob_path(project, File.join(project.default_branch, 'LICENSE'))
end
- scenario 'is displayed' do
+ it 'is displayed' do
check_template_selector_menu_display(true)
end
@@ -45,7 +45,7 @@ feature 'Template selector menu', :js do
click_link 'Preview'
end
- scenario 'template selector menu is hidden and shown correctly' do
+ it 'template selector menu is hidden and shown correctly' do
check_template_selector_menu_display(false)
click_link 'Write'
check_template_selector_menu_display(true)
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 41f6c52fb8a..612722eeaad 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -147,11 +147,8 @@ describe "User browses files" do
page.within(".tree-table") do
click_link("README.md")
end
-
- # rubocop:disable Lint/Void
# Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
find("a", text: /^empty$/)["href"] == project_blob_url(project, "markdown/d/README.md")
- # rubocop:enable Lint/Void
end
it "shows correct content of directory" do
@@ -213,7 +210,7 @@ describe "User browses files" do
end
end
- context "when browsing a file content" do
+ context "when browsing a file content", :js do
before do
visit(tree_path_root_ref)
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index 208cc8d81f7..d4dda43c823 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -12,7 +12,7 @@ describe 'Projects > Files > User creates files' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/files/user_deletes_files_spec.rb b/spec/features/projects/files/user_deletes_files_spec.rb
index 36d3e001a64..5d37877ccb3 100644
--- a/spec/features/projects/files/user_deletes_files_spec.rb
+++ b/spec/features/projects/files/user_deletes_files_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Projects > Files > User deletes files' do
+describe 'Projects > Files > User deletes files', :js do
let(:fork_message) do
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
@@ -17,7 +17,7 @@ describe 'Projects > Files > User deletes files' do
context 'when an user has write access' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit(project_tree_path_root_ref)
end
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index dc6e4fd27cb..072dc5820c4 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Projects > Files > User edits files' do
+describe 'Projects > Files > User edits files', :js do
include ProjectForksHelper
let(:project) { create(:project, :repository, name: 'Shop') }
let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') }
@@ -31,7 +31,7 @@ describe 'Projects > Files > User edits files' do
context 'when an user has write access' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit(project_tree_path_root_ref)
end
diff --git a/spec/features/projects/files/user_find_file_spec.rb b/spec/features/projects/files/user_find_file_spec.rb
index df405e70dd4..e2d881b34d2 100644
--- a/spec/features/projects/files/user_find_file_spec.rb
+++ b/spec/features/projects/files/user_find_file_spec.rb
@@ -6,7 +6,7 @@ describe 'User find project file' do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
visit project_tree_path(project, project.repository.root_ref)
end
diff --git a/spec/features/projects/files/user_reads_pipeline_status_spec.rb b/spec/features/projects/files/user_reads_pipeline_status_spec.rb
index 2fb9da2f0a2..ff0aa933a3e 100644
--- a/spec/features/projects/files/user_reads_pipeline_status_spec.rb
+++ b/spec/features/projects/files/user_reads_pipeline_status_spec.rb
@@ -7,7 +7,7 @@ describe 'user reads pipeline status', :js do
let(:x110_pipeline) { create_pipeline('x1.1.0', 'failed') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.repository.add_tag(user, 'x1.1.0', 'v1.1.0')
v110_pipeline
@@ -17,7 +17,7 @@ describe 'user reads pipeline status', :js do
end
shared_examples 'visiting project tree' do
- scenario 'sees the correct pipeline status' do
+ it 'sees the correct pipeline status' do
visit project_tree_path(project, expected_pipeline.ref)
wait_for_requests
diff --git a/spec/features/projects/files/user_replaces_files_spec.rb b/spec/features/projects/files/user_replaces_files_spec.rb
index 9ac3417b671..3f973338305 100644
--- a/spec/features/projects/files/user_replaces_files_spec.rb
+++ b/spec/features/projects/files/user_replaces_files_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Projects > Files > User replaces files' do
+describe 'Projects > Files > User replaces files', :js do
include DropzoneHelper
let(:fork_message) do
@@ -19,7 +19,7 @@ describe 'Projects > Files > User replaces files' do
context 'when an user has write access' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit(project_tree_path_root_ref)
end
diff --git a/spec/features/projects/files/user_uploads_files_spec.rb b/spec/features/projects/files/user_uploads_files_spec.rb
index 8b212faa29d..af3fc528a20 100644
--- a/spec/features/projects/files/user_uploads_files_spec.rb
+++ b/spec/features/projects/files/user_uploads_files_spec.rb
@@ -14,7 +14,7 @@ describe 'Projects > Files > User uploads files' do
let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb
index 1743b1e083f..cd5fef8238e 100644
--- a/spec/features/projects/fork_spec.rb
+++ b/spec/features/projects/fork_spec.rb
@@ -129,11 +129,11 @@ describe 'Project fork' do
end
end
- context 'master in group' do
+ context 'maintainer in group' do
let(:group) { create(:group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'allows user to fork project to group or to user namespace' do
diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb
index 335174b7729..9665f1755d6 100644
--- a/spec/features/projects/graph_spec.rb
+++ b/spec/features/projects/graph_spec.rb
@@ -6,7 +6,7 @@ describe 'Project Graph', :js do
let(:branch_name) { 'master' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/hook_logs/user_reads_log_spec.rb b/spec/features/projects/hook_logs/user_reads_log_spec.rb
index 18e975fa653..086cd4b9f03 100644
--- a/spec/features/projects/hook_logs/user_reads_log_spec.rb
+++ b/spec/features/projects/hook_logs/user_reads_log_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
-feature 'Hook logs' do
- given(:web_hook_log) { create(:web_hook_log, response_body: '<script>') }
- given(:project) { web_hook_log.web_hook.project }
- given(:user) { create(:user) }
+describe 'Hook logs' do
+ let(:web_hook_log) { create(:web_hook_log, response_body: '<script>') }
+ let(:project) { web_hook_log.web_hook.project }
+ let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
- scenario 'user reads log without getting XSS' do
+ it 'user reads log without getting XSS' do
visit(
project_hook_hook_log_path(
project, web_hook_log.web_hook, web_hook_log))
diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb
index 6732cf61767..eb281cd2122 100644
--- a/spec/features/projects/import_export/export_file_spec.rb
+++ b/spec/features/projects/import_export/export_file_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
# It looks up for any sensitive word inside the JSON, so if a sensitive word is found
# we'll have to either include it adding the model that includes it to the +safe_list+
# or make sure the attribute is blacklisted in the +import_export.yml+ configuration
-feature 'Import/Export - project export integration test', :js do
+describe 'Import/Export - project export integration test', :js do
include Select2Helper
include ExportFileHelper
@@ -23,8 +23,9 @@ feature 'Import/Export - project export integration test', :js do
let(:project) { setup_project }
- background do
+ before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_feature_flags(import_export_object_storage: false)
end
after do
@@ -36,7 +37,7 @@ feature 'Import/Export - project export integration test', :js do
sign_in(user)
end
- scenario 'exports a project successfully' do
+ it 'exports a project successfully' do
visit edit_project_path(project)
expect(page).to have_content('Export project')
diff --git a/spec/features/projects/import_export/import_file_object_storage_spec.rb b/spec/features/projects/import_export/import_file_object_storage_spec.rb
new file mode 100644
index 00000000000..0d364543916
--- /dev/null
+++ b/spec/features/projects/import_export/import_file_object_storage_spec.rb
@@ -0,0 +1,103 @@
+require 'spec_helper'
+
+describe 'Import/Export - project import integration test', :js do
+ include Select2Helper
+
+ let(:user) { create(:user) }
+ let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
+ let(:export_path) { "#{Dir.tmpdir}/import_file_spec" }
+
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ gitlab_sign_in(user)
+ end
+
+ after do
+ FileUtils.rm_rf(export_path, secure: true)
+ end
+
+ context 'when selecting the namespace' do
+ let(:user) { create(:admin) }
+ let!(:namespace) { user.namespace }
+ let(:project_path) { 'test-project-path' + SecureRandom.hex }
+
+ context 'prefilled the path' do
+ it 'user imports an exported project successfully' do
+ visit new_project_path
+
+ select2(namespace.id, from: '#project_namespace_id')
+ fill_in :project_path, with: project_path, visible: true
+ click_import_project_tab
+ click_link 'GitLab export'
+
+ expect(page).to have_content('Import an exported GitLab project')
+ expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=#{project_path}")
+
+ 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(project_hook_exists?(project)).to be true
+ expect(wiki_exists?(project)).to be true
+ expect(project.import_state.status).to eq('finished')
+ end
+ end
+
+ context 'path is not prefilled' do
+ it 'user imports an exported project successfully' do
+ visit new_project_path
+ click_import_project_tab
+ click_link 'GitLab export'
+
+ fill_in :path, with: 'test-project-path', visible: true
+ attach_file('file', file)
+
+ expect { click_on 'Import project' }.to change { Project.count }.by(1)
+
+ project = Project.last
+ expect(project).not_to be_nil
+ expect(page).to have_content("Project 'test-project-path' is being imported")
+ end
+ end
+ end
+
+ it 'invalid project' do
+ project = create(:project, namespace: user.namespace)
+
+ visit new_project_path
+
+ select2(user.namespace.id, from: '#project_namespace_id')
+ fill_in :project_path, 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 project_hook_exists?(project)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository).exists?
+ end
+ end
+
+ def click_import_project_tab
+ find('#import-project-tab').click
+ end
+end
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index d0912e645bc..2d86115de12 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -1,13 +1,14 @@
require 'spec_helper'
-feature 'Import/Export - project import integration test', :js do
+describe 'Import/Export - project import integration test', :js do
include Select2Helper
let(:user) { create(:user) }
let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
let(:export_path) { "#{Dir.tmpdir}/import_file_spec" }
- background do
+ before do
+ stub_feature_flags(import_export_object_storage: false)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
gitlab_sign_in(user)
end
@@ -22,7 +23,7 @@ feature 'Import/Export - project import integration test', :js do
let(:project_path) { 'test-project-path' + SecureRandom.hex }
context 'prefilled the path' do
- scenario 'user imports an exported project successfully' do
+ it 'user imports an exported project successfully' do
visit new_project_path
select2(namespace.id, from: '#project_namespace_id')
@@ -51,7 +52,7 @@ feature 'Import/Export - project import integration test', :js do
end
context 'path is not prefilled' do
- scenario 'user imports an exported project successfully' do
+ it 'user imports an exported project successfully' do
visit new_project_path
click_import_project_tab
click_link 'GitLab export'
@@ -68,7 +69,7 @@ feature 'Import/Export - project import integration test', :js do
end
end
- scenario 'invalid project' do
+ it 'invalid project' do
project = create(:project, namespace: user.namespace)
visit new_project_path
diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb
index 7d056b0c140..9bb8a2063b5 100644
--- a/spec/features/projects/import_export/namespace_export_file_spec.rb
+++ b/spec/features/projects/import_export/namespace_export_file_spec.rb
@@ -5,6 +5,7 @@ describe 'Import/Export - Namespace export file cleanup', :js do
before do
allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_feature_flags(import_export_object_storage: false)
end
after do
diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz
index ceba4dfec57..3b5df47e0b6 100644
--- a/spec/features/projects/import_export/test_project_export.tar.gz
+++ b/spec/features/projects/import_export/test_project_export.tar.gz
Binary files differ
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index e26caf1f456..a57edc394f9 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'issuable templates', :js do
+describe 'issuable templates', :js do
include ProjectForksHelper
let(:user) { create(:user) }
@@ -8,7 +8,7 @@ feature 'issuable templates', :js do
let(:issue_form_location) { '#content-body .issuable-details .detail-page-description' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
@@ -18,7 +18,7 @@ feature 'issuable templates', :js do
let(:issue) { create(:issue, author: user, assignees: [user], project: project) }
let(:description_addition) { ' appending to description' }
- background do
+ before do
project.repository.create_file(
user,
'.gitlab/issue_templates/bug.md',
@@ -36,14 +36,14 @@ feature 'issuable templates', :js do
fill_in :'issuable-title', with: 'test issue title'
end
- scenario 'user selects "bug" template' do
+ it 'user selects "bug" template' do
select_template 'bug'
wait_for_requests
assert_template(page_part: issue_form_location)
save_changes
end
- scenario 'user selects "bug" template and then "no template"' do
+ it 'user selects "bug" template and then "no template"' do
select_template 'bug'
wait_for_requests
select_option 'No template'
@@ -51,7 +51,7 @@ feature 'issuable templates', :js do
save_changes('')
end
- scenario 'user selects "bug" template, edits description and then selects "reset template"' do
+ it 'user selects "bug" template, edits description and then selects "reset template"' do
select_template 'bug'
wait_for_requests
find_field('issue-description').send_keys(description_addition)
@@ -67,7 +67,7 @@ feature 'issuable templates', :js do
let(:template_content) { 'this is a test "bug" template' }
let(:issue) { create(:issue, author: user, assignees: [user], project: project) }
- background do
+ before do
project.repository.create_file(
user,
'.gitlab/issue_templates/bug.md',
@@ -80,7 +80,7 @@ feature 'issuable templates', :js do
fill_in :'issue-description', with: prior_description
end
- scenario 'user selects "bug" template' do
+ it 'user selects "bug" template' do
select_template 'bug'
wait_for_requests
assert_template(page_part: issue_form_location)
@@ -92,7 +92,7 @@ feature 'issuable templates', :js do
let(:template_content) { 'this is a test "feature-proposal" template' }
let(:merge_request) { create(:merge_request, :with_diffs, source_project: project) }
- background do
+ before do
project.repository.create_file(
user,
'.gitlab/merge_request_templates/feature-proposal.md',
@@ -103,7 +103,7 @@ feature 'issuable templates', :js do
fill_in :'merge_request[title]', with: 'test merge request title'
end
- scenario 'user selects "feature-proposal" template' do
+ it 'user selects "feature-proposal" template' do
select_template 'feature-proposal'
wait_for_requests
assert_template
@@ -117,7 +117,7 @@ feature 'issuable templates', :js do
let(:forked_project) { fork_project(project, fork_user, repository: true) }
let(:merge_request) { create(:merge_request, :with_diffs, source_project: forked_project, target_project: project) }
- background do
+ before do
sign_out(:user)
project.add_developer(fork_user)
@@ -136,7 +136,7 @@ feature 'issuable templates', :js do
context 'feature proposal template' do
context 'template exists in target project' do
- scenario 'user selects template' do
+ it 'user selects template' do
select_template 'feature-proposal'
wait_for_requests
assert_template
diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/projects/issues/rss_spec.rb
index 8b1f7d432ee..0e1383cd607 100644
--- a/spec/features/projects/issues/rss_spec.rb
+++ b/spec/features/projects/issues/rss_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Project Issues RSS' do
+describe 'Project Issues RSS' do
let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:path) { project_issues_path(project) }
diff --git a/spec/features/projects/issues/user_comments_on_issue_spec.rb b/spec/features/projects/issues/user_comments_on_issue_spec.rb
index 353f487485d..ba5b80ed04b 100644
--- a/spec/features/projects/issues/user_comments_on_issue_spec.rb
+++ b/spec/features/projects/issues/user_comments_on_issue_spec.rb
@@ -63,6 +63,14 @@ describe "User comments on issue", :js do
page.within(".current-note-edit-form") do
fill_in("note[note]", with: comment)
+ find('textarea').send_keys [:control, :shift, 'p']
+ expect(page).to have_selector('.current-note-edit-form .md-preview-holder')
+ expect(page.find('.current-note-edit-form .md-preview-holder p')).to have_content(comment)
+ end
+
+ expect(page).to have_selector('.new-note .note-textarea')
+
+ page.within(".current-note-edit-form") do
click_button("Save comment")
end
diff --git a/spec/features/projects/issues/user_creates_issue_spec.rb b/spec/features/projects/issues/user_creates_issue_spec.rb
index e76f7c5589d..5e8662100c5 100644
--- a/spec/features/projects/issues/user_creates_issue_spec.rb
+++ b/spec/features/projects/issues/user_creates_issue_spec.rb
@@ -17,6 +17,9 @@ describe "User creates issue" do
expect(page).to have_no_content("Assign to")
.and have_no_content("Labels")
.and have_no_content("Milestone")
+
+ expect(page.find('#issue_title')['placeholder']).to eq 'Title'
+ expect(page.find('#issue_description')['placeholder']).to eq 'Write a comment or drag your files here…'
end
issue_title = "500 error on profile"
diff --git a/spec/features/projects/issues/user_views_issue_spec.rb b/spec/features/projects/issues/user_views_issue_spec.rb
index 4093876c289..117e5986f29 100644
--- a/spec/features/projects/issues/user_views_issue_spec.rb
+++ b/spec/features/projects/issues/user_views_issue_spec.rb
@@ -29,4 +29,22 @@ describe "User views issue" do
expect(page).not_to have_link('Close issue')
end
end
+
+ describe 'user status' do
+ subject { visit(project_issue_path(project, issue)) }
+
+ describe 'showing status of the author of the issue' do
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { issue.author }
+ end
+ end
+
+ describe 'showing status of a user who commented on an issue', :js do
+ let!(:note) { create(:note, noteable: issue, project: project, author: user_with_status) }
+
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { create(:user) }
+ end
+ end
+ end
end
diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb
index e9588daf37d..e639f0cf82e 100644
--- a/spec/features/projects/jobs/permissions_spec.rb
+++ b/spec/features/projects/jobs/permissions_spec.rb
@@ -90,7 +90,7 @@ describe 'Project Jobs Permissions' do
before do
archive = fixture_file_upload('spec/fixtures/ci_build_artifacts.zip')
- job.update_attributes(legacy_artifacts_file: archive)
+ job.update(legacy_artifacts_file: archive)
end
context 'when public access for jobs is disabled' do
diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb
index ce0b38b7239..50e957bf12b 100644
--- a/spec/features/projects/jobs/user_browses_job_spec.rb
+++ b/spec/features/projects/jobs/user_browses_job_spec.rb
@@ -8,7 +8,7 @@ describe 'User browses a job', :js do
let!(:build) { create(:ci_build, :success, :trace_artifact, :coverage, pipeline: pipeline) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.enable_ci
sign_in(user)
diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb
index 786ec327b92..08786fe1630 100644
--- a/spec/features/projects/jobs/user_browses_jobs_spec.rb
+++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb
@@ -7,7 +7,7 @@ describe 'User browses jobs' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.enable_ci
project.update_attribute(:build_coverage_regex, /Coverage (\d+)%/)
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index d2aaf60e72c..83293c0ca7d 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require 'tempfile'
-feature 'Jobs', :clean_gitlab_redis_shared_state do
+describe 'Jobs', :clean_gitlab_redis_shared_state do
let(:user) { create(:user) }
let(:user_access_level) { :developer }
let(:project) { create(:project, :repository) }
@@ -135,6 +135,20 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
end
end
+ context 'sidebar' do
+ let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline, name: '<img src=x onerror=alert(document.domain)>') }
+
+ before do
+ visit project_job_path(project, job)
+ end
+
+ it 'renders escaped tooltip name' do
+ page.within('aside.right-sidebar') do
+ expect(find('.active.build-job a')['data-title']).to eq('<img src="x"> - passed')
+ end
+ end
+ end
+
context 'when job is not running', :js do
let(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) }
@@ -165,7 +179,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
it 'links to issues/new with the title and description filled in' do
button_title = "Job Failed ##{job.id}"
- job_url = project_job_path(project, job)
+ job_url = project_job_url(project, job, host: page.server.host, port: page.server.port)
options = { issue: { title: button_title, description: "Job [##{job.id}](#{job_url}) failed for #{job.sha}:\n" } }
href = new_project_issue_path(project, options)
@@ -187,7 +201,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
context "Download artifacts" do
before do
- job.update_attributes(legacy_artifacts_file: artifacts_file)
+ job.update(legacy_artifacts_file: artifacts_file)
visit project_job_path(project, job)
end
@@ -198,8 +212,8 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
context 'Artifacts expire date' do
before do
- job.update_attributes(legacy_artifacts_file: artifacts_file,
- artifacts_expire_at: expire_at)
+ job.update(legacy_artifacts_file: artifacts_file,
+ artifacts_expire_at: expire_at)
visit project_job_path(project, job)
end
@@ -259,7 +273,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- feature 'Raw trace' do
+ describe 'Raw trace' do
before do
job.run!
@@ -271,7 +285,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- feature 'HTML trace', :js do
+ describe 'HTML trace', :js do
before do
job.run!
@@ -291,7 +305,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
end
end
- feature 'Variables' do
+ describe 'Variables' do
let(:trigger_request) { create(:ci_trigger_request) }
let(:job) do
@@ -530,14 +544,14 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
describe "GET /:project/jobs/:id/download" do
before do
- job.update_attributes(legacy_artifacts_file: artifacts_file)
+ job.update(legacy_artifacts_file: artifacts_file)
visit project_job_path(project, job)
click_link 'Download'
end
context "Build from other project" do
before do
- job2.update_attributes(legacy_artifacts_file: artifacts_file)
+ job2.update(legacy_artifacts_file: artifacts_file)
visit download_project_job_artifacts_path(project, job2)
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 0292a3192d8..6178f11ded7 100644
--- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
+++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Issue prioritization' do
+describe 'Issue prioritization' do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
@@ -13,7 +13,7 @@ feature 'Issue prioritization' do
# According to https://gitlab.com/gitlab-org/gitlab-ce/issues/14189#note_4360653
context 'when issues have one label' do
- scenario '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)
@@ -43,7 +43,7 @@ feature 'Issue prioritization' do
end
context 'when issues have multiple labels' do
- scenario '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/labels/search_labels_spec.rb b/spec/features/projects/labels/search_labels_spec.rb
new file mode 100644
index 00000000000..2d5a138c3cc
--- /dev/null
+++ b/spec/features/projects/labels/search_labels_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Search for labels', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let!(:label1) { create(:label, title: 'Foo', description: 'Lorem ipsum', project: project) }
+ let!(:label2) { create(:label, title: 'Bar', description: 'Fusce consequat', project: project) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit project_labels_path(project)
+ end
+
+ it 'searches for label by title' do
+ fill_in 'label-search', with: 'Bar'
+ find('#label-search').native.send_keys(:enter)
+
+ expect(page).to have_content(label2.title)
+ expect(page).to have_content(label2.description)
+ expect(page).not_to have_content(label1.title)
+ expect(page).not_to have_content(label1.description)
+ end
+
+ it 'searches for label by title' do
+ fill_in 'label-search', with: 'Lorem'
+ find('#label-search').native.send_keys(:enter)
+
+ expect(page).to have_content(label1.title)
+ expect(page).to have_content(label1.description)
+ expect(page).not_to have_content(label2.title)
+ expect(page).not_to have_content(label2.description)
+ end
+
+ it 'shows nothing found message' do
+ fill_in 'label-search', with: 'nonexistent'
+ find('#label-search').native.send_keys(:enter)
+
+ expect(page).to have_content('No labels with such name or description')
+ expect(page).not_to have_content(label1.title)
+ expect(page).not_to have_content(label1.description)
+ expect(page).not_to have_content(label2.title)
+ expect(page).not_to have_content(label2.description)
+ end
+
+ context 'priority labels' do
+ let!(:label_priority) { create(:label_priority, label: label1, project: project) }
+
+ it 'searches for priority label' do
+ fill_in 'label-search', with: 'Foo'
+ find('#label-search').native.send_keys(:enter)
+
+ page.within('.prioritized-labels') do
+ expect(page).to have_content(label1.title)
+ expect(page).to have_content(label1.description)
+ end
+
+ page.within('.other-labels') do
+ expect(page).to have_content('No other labels with such name or description')
+ end
+ end
+
+ it 'searches for other label' do
+ fill_in 'label-search', with: 'Bar'
+ find('#label-search').native.send_keys(:enter)
+
+ page.within('.prioritized-labels') do
+ expect(page).to have_content('No prioritised labels with such name or description')
+ end
+
+ page.within('.other-labels') do
+ expect(page).to have_content(label2.title)
+ expect(page).to have_content(label2.description)
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb
index fafd338e448..49227eebf3d 100644
--- a/spec/features/projects/labels/subscription_spec.rb
+++ b/spec/features/projects/labels/subscription_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Labels subscription' do
+describe 'Labels subscription' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, :public, namespace: group) }
@@ -13,7 +13,7 @@ feature 'Labels subscription' do
sign_in user
end
- scenario 'users can subscribe/unsubscribe to labels', :js do
+ it 'users can subscribe/unsubscribe to labels', :js do
visit project_labels_path(project)
expect(page).to have_content('bug')
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index 359381c391c..996040fde02 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Prioritize labels' do
+describe 'Prioritize labels' do
include DragTo
let(:user) { create(:user) }
@@ -17,7 +17,7 @@ feature 'Prioritize labels' do
sign_in user
end
- scenario 'user can prioritize a group label', :js do
+ it 'user can prioritize a group label', :js do
visit project_labels_path(project)
expect(page).to have_content('Star labels to start sorting by priority')
@@ -34,7 +34,7 @@ feature 'Prioritize labels' do
end
end
- scenario 'user can unprioritize a group label', :js do
+ it 'user can unprioritize a group label', :js do
create(:label_priority, project: project, label: feature, priority: 1)
visit project_labels_path(project)
@@ -52,7 +52,7 @@ feature 'Prioritize labels' do
end
end
- scenario 'user can prioritize a project label', :js do
+ it 'user can prioritize a project label', :js do
visit project_labels_path(project)
expect(page).to have_content('Star labels to start sorting by priority')
@@ -69,7 +69,7 @@ feature 'Prioritize labels' do
end
end
- scenario 'user can unprioritize a project label', :js do
+ it 'user can unprioritize a project label', :js do
create(:label_priority, project: project, label: bug, priority: 1)
visit project_labels_path(project)
@@ -88,7 +88,7 @@ feature 'Prioritize labels' do
end
end
- scenario 'user can sort prioritized labels and persist across reloads', :js do
+ it 'user can sort prioritized labels and persist across reloads', :js do
create(:label_priority, project: project, label: bug, priority: 1)
create(:label_priority, project: project, label: feature, priority: 2)
diff --git a/spec/features/projects/labels/user_creates_labels_spec.rb b/spec/features/projects/labels/user_creates_labels_spec.rb
index 9fd7f3ee775..c71b04fea09 100644
--- a/spec/features/projects/labels/user_creates_labels_spec.rb
+++ b/spec/features/projects/labels/user_creates_labels_spec.rb
@@ -18,7 +18,7 @@ describe "User creates labels" do
context "in project" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(new_project_label_path(project))
@@ -69,7 +69,7 @@ describe "User creates labels" do
before do
create(:label, project: project, title: "bug") # Create label for `project` (not `another_project`) project.
- another_project.add_master(user)
+ another_project.add_maintainer(user)
sign_in(user)
visit(new_project_label_path(another_project))
diff --git a/spec/features/projects/labels/user_edits_labels_spec.rb b/spec/features/projects/labels/user_edits_labels_spec.rb
index d1041ff5c1e..0708bbd40ce 100644
--- a/spec/features/projects/labels/user_edits_labels_spec.rb
+++ b/spec/features/projects/labels/user_edits_labels_spec.rb
@@ -6,7 +6,7 @@ describe "User edits labels" do
set(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(edit_project_label_path(project, label))
diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb
index efa74015c6e..b0ce03a1c31 100644
--- a/spec/features/projects/labels/user_removes_labels_spec.rb
+++ b/spec/features/projects/labels/user_removes_labels_spec.rb
@@ -5,7 +5,7 @@ describe "User removes labels" do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/labels/user_sees_links_to_issuables.rb b/spec/features/projects/labels/user_sees_links_to_issuables.rb
index aa56fd7f74e..c404fc8d66f 100644
--- a/spec/features/projects/labels/user_sees_links_to_issuables.rb
+++ b/spec/features/projects/labels/user_sees_links_to_issuables.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Projects > Labels > User sees links to issuables' do
+describe 'Projects > Labels > User sees links to issuables' do
set(:user) { create(:user) }
before do
@@ -16,7 +16,7 @@ feature 'Projects > Labels > User sees links to issuables' do
context 'when merge requests and issues are enabled for the project' do
let(:project) { create(:project, :public) }
- scenario 'shows links to MRs and issues' do
+ it 'shows links to MRs and issues' do
expect(page).to have_link('view merge requests')
expect(page).to have_link('view open issues')
end
@@ -25,7 +25,7 @@ feature 'Projects > Labels > User sees links to issuables' do
context 'when issues are disabled for the project' do
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::DISABLED) }
- scenario 'shows links to MRs but not to issues' do
+ it 'shows links to MRs but not to issues' do
expect(page).to have_link('view merge requests')
expect(page).not_to have_link('view open issues')
end
@@ -34,7 +34,7 @@ feature 'Projects > Labels > User sees links to issuables' do
context 'when merge requests are disabled for the project' do
let(:project) { create(:project, :public, merge_requests_access_level: ProjectFeature::DISABLED) }
- scenario 'shows links to issues but not to MRs' do
+ it 'shows links to issues but not to MRs' do
expect(page).not_to have_link('view merge requests')
expect(page).to have_link('view open issues')
end
@@ -48,7 +48,7 @@ feature 'Projects > Labels > User sees links to issuables' do
context 'when merge requests and issues are enabled for the project' do
let(:project) { create(:project, :public, namespace: group) }
- scenario 'shows links to MRs and issues' do
+ it 'shows links to MRs and issues' do
expect(page).to have_link('view merge requests')
expect(page).to have_link('view open issues')
end
@@ -57,7 +57,7 @@ feature 'Projects > Labels > User sees links to issuables' do
context 'when issues are disabled for the project' do
let(:project) { create(:project, :public, namespace: group, issues_access_level: ProjectFeature::DISABLED) }
- scenario 'shows links to MRs and issues' do
+ it 'shows links to MRs and issues' do
expect(page).to have_link('view merge requests')
expect(page).to have_link('view open issues')
end
@@ -66,7 +66,7 @@ feature 'Projects > Labels > User sees links to issuables' do
context 'when merge requests are disabled for the project' do
let(:project) { create(:project, :public, namespace: group, merge_requests_access_level: ProjectFeature::DISABLED) }
- scenario 'shows links to MRs and issues' do
+ it 'shows links to MRs and issues' do
expect(page).to have_link('view merge requests')
expect(page).to have_link('view open issues')
end
diff --git a/spec/features/projects/members/anonymous_user_sees_members_spec.rb b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
index e2a48bfd1d4..b3ed725f602 100644
--- a/spec/features/projects/members/anonymous_user_sees_members_spec.rb
+++ b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
@@ -1,16 +1,16 @@
require 'spec_helper'
-feature 'Projects > Members > Anonymous user sees members' do
+describe 'Projects > Members > Anonymous user sees members' do
let(:user) { create(:user) }
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public) }
- background do
- project.add_master(user)
+ before do
+ project.add_maintainer(user)
create(:project_group_link, project: project, group: group)
end
- scenario "anonymous user visits the project's members page and sees the list of members" do
+ it "anonymous user visits the project's members page and sees the list of members" do
visit project_project_members_path(project)
expect(current_path).to eq(
diff --git a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
index 6b450fa4e45..0ab29660189 100644
--- a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
+++ b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
-feature 'Projects > Members > Group member cannot leave group project' do
+describe 'Projects > Members > Group member cannot leave group project' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
- background do
+ before do
group.add_developer(user)
sign_in(user)
visit project_path(project)
end
- scenario 'user does not see a "Leave project" link' do
+ it 'user does not see a "Leave project" link' do
expect(page).not_to have_content 'Leave project'
end
end
diff --git a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
index 296a80a3c60..bb475ea95e5 100644
--- a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
+++ b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
@@ -1,39 +1,39 @@
require 'spec_helper'
-feature 'Projects > Members > Group member cannot request access to his group project' do
+describe 'Projects > Members > Group member cannot request access to his group project' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
- scenario 'owner does not see the request access button' do
+ it 'owner does not see the request access button' do
group.add_owner(user)
login_and_visit_project_page(user)
expect(page).not_to have_content 'Request Access'
end
- scenario 'master does not see the request access button' do
- group.add_master(user)
+ it 'maintainer does not see the request access button' do
+ group.add_maintainer(user)
login_and_visit_project_page(user)
expect(page).not_to have_content 'Request Access'
end
- scenario 'developer does not see the request access button' do
+ it 'developer does not see the request access button' do
group.add_developer(user)
login_and_visit_project_page(user)
expect(page).not_to have_content 'Request Access'
end
- scenario 'reporter does not see the request access button' do
+ it 'reporter does not see the request access button' do
group.add_reporter(user)
login_and_visit_project_page(user)
expect(page).not_to have_content 'Request Access'
end
- scenario 'guest does not see the request access button' do
+ it 'guest does not see the request access button' do
group.add_guest(user)
login_and_visit_project_page(user)
diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb
index e22b6fa6c43..0b2cd13b8ec 100644
--- a/spec/features/projects/members/group_members_spec.rb
+++ b/spec/features/projects/members/group_members_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Projects members' do
+describe 'Projects members' do
let(:user) { create(:user) }
let(:developer) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) }
@@ -10,7 +10,7 @@ feature 'Projects members' do
let(:project_requester) { create(:user) }
let(:group_requester) { create(:user) }
- background do
+ before do
project.add_developer(developer)
group.add_owner(user)
sign_in(user)
@@ -22,7 +22,7 @@ feature 'Projects members' do
visit project_settings_members_path(project)
end
- scenario 'does not appear in the project members page' do
+ it 'does not appear in the project members page' do
page.within first('.content-list') do
expect(page).not_to have_content('test2@abc.com')
end
@@ -36,7 +36,7 @@ feature 'Projects members' do
visit project_settings_members_path(project)
end
- scenario 'shows the project invitee, the project developer, and the group owner' do
+ it 'shows the project invitee, the project developer, and the group owner' do
page.within first('.content-list') do
expect(page).to have_content('test1@abc.com')
expect(page).not_to have_content('test2@abc.com')
@@ -57,7 +57,7 @@ feature 'Projects members' do
visit project_settings_members_path(project)
end
- scenario 'does not appear in the project members page' do
+ it 'does not appear in the project members page' do
page.within first('.content-list') do
expect(page).not_to have_content(group_requester.name)
end
@@ -71,7 +71,7 @@ feature 'Projects members' do
visit project_settings_members_path(project)
end
- scenario 'shows the project requester, the project developer, and the group owner' do
+ it 'shows the project requester, the project developer, and the group owner' do
page.within first('.content-list') do
expect(page).to have_content(project_requester.name)
expect(page).not_to have_content(group_requester.name)
@@ -87,4 +87,12 @@ feature 'Projects members' do
end
end
end
+
+ describe 'showing status of members' do
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { developer }
+
+ subject { visit project_settings_members_path(project) }
+ end
+ end
end
diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
index 6d729f2f85f..ea3894c92bd 100644
--- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
+++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
@@ -1,12 +1,12 @@
require 'spec_helper'
-feature 'Projects > Members > Group requester cannot request access to project', :js do
+describe 'Projects > Members > Group requester cannot request access to project', :js do
let(:user) { create(:user) }
let(:owner) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) }
let(:project) { create(:project, :public, :access_requestable, namespace: group) }
- background do
+ before do
group.add_owner(owner)
sign_in(user)
visit group_path(group)
@@ -14,7 +14,7 @@ feature 'Projects > Members > Group requester cannot request access to project',
visit project_path(project)
end
- scenario 'group requester does not see the request access / withdraw access request button' do
+ it 'group requester does not see the request access / withdraw access request button' do
expect(page).not_to have_content 'Request Access'
expect(page).not_to have_content 'Withdraw Access Request'
end
diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb
index e6d0c6e00f8..c0b5d943e96 100644
--- a/spec/features/projects/members/groups_with_access_list_spec.rb
+++ b/spec/features/projects/members/groups_with_access_list_spec.rb
@@ -1,19 +1,19 @@
require 'spec_helper'
-feature 'Projects > Members > Groups with access list', :js do
+describe 'Projects > Members > Groups with access list', :js do
let(:user) { create(:user) }
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public) }
- background do
- project.add_master(user)
+ before do
+ project.add_maintainer(user)
@group_link = create(:project_group_link, project: project, group: group)
sign_in(user)
visit project_settings_members_path(project)
end
- scenario 'updates group access level' do
+ it 'updates group access level' do
click_button @group_link.human_access
page.within '.dropdown-menu' do
@@ -27,7 +27,7 @@ feature 'Projects > Members > Groups with access list', :js do
expect(first('.group_member')).to have_content('Guest')
end
- scenario 'updates expiry date' do
+ it 'updates expiry date' do
tomorrow = Date.today + 3
fill_in "member_expires_at_#{group.id}", with: tomorrow.strftime("%F")
@@ -39,7 +39,7 @@ feature 'Projects > Members > Groups with access list', :js do
end
end
- scenario 'deletes group link' do
+ it 'deletes group link' do
page.within(first('.group_member')) do
accept_confirm { find('.btn-remove').click }
end
@@ -49,7 +49,7 @@ feature 'Projects > Members > Groups with access list', :js do
end
context 'search in existing members (yes, this filters the groups list as well)' do
- scenario 'finds no results' do
+ it 'finds no results' do
page.within '.member-search-form' do
fill_in 'search', with: 'testing 123'
find('.member-search-btn').click
@@ -58,7 +58,7 @@ feature 'Projects > Members > Groups with access list', :js do
expect(page).not_to have_selector('.group_member')
end
- scenario 'finds results' do
+ it 'finds results' do
page.within '.member-search-form' do
fill_in 'search', with: group.name
find('.member-search-btn').click
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index 65b11a1d9e7..c2e980e75b8 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Project members list' do
+describe 'Project members list' do
include Select2Helper
let(:user1) { create(:user, name: 'John Doe') }
@@ -8,12 +8,12 @@ feature 'Project members list' do
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
- background do
+ before do
sign_in(user1)
group.add_owner(user1)
end
- scenario 'show members from project and group' do
+ it 'show members from project and group' do
project.add_developer(user2)
visit_members_page
@@ -22,7 +22,7 @@ feature 'Project members list' do
expect(second_row.text).to include(user2.name)
end
- scenario 'show user once if member of both group and project' do
+ it 'show user once if member of both group and project' do
project.add_developer(user1)
visit_members_page
@@ -31,7 +31,7 @@ feature 'Project members list' do
expect(second_row).to be_blank
end
- scenario 'update user acess level', :js do
+ it 'update user acess level', :js do
project.add_developer(user2)
visit_members_page
@@ -44,7 +44,7 @@ feature 'Project members list' do
end
end
- scenario 'add user to project', :js do
+ it 'add user to project', :js do
visit_members_page
add_user(user2.id, 'Reporter')
@@ -55,7 +55,7 @@ feature 'Project members list' do
end
end
- scenario 'remove user from project', :js do
+ it 'remove user from project', :js do
other_user = create(:user)
project.add_developer(other_user)
@@ -71,7 +71,7 @@ feature 'Project members list' do
expect(project.users).not_to include(other_user)
end
- scenario 'invite user to project', :js do
+ it 'invite user to project', :js do
visit_members_page
add_user('test@example.com', 'Reporter')
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index 8fe340d3bae..26de6fb33fd 100644
--- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
@@ -1,19 +1,19 @@
require 'spec_helper'
-feature 'Projects > Members > Master adds member with expiration date', :js do
+describe 'Projects > Members > Maintainer adds member with expiration date', :js do
include Select2Helper
include ActiveSupport::Testing::TimeHelpers
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:project) { create(:project) }
let!(:new_member) { create(:user) }
- background do
- project.add_master(master)
- sign_in(master)
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
end
- scenario 'expiration date is displayed in the members list' do
+ it 'expiration date is displayed in the members list' do
travel_to Time.zone.parse('2016-08-06 08:00') do
date = 4.days.from_now
visit project_project_members_path(project)
@@ -30,7 +30,7 @@ feature 'Projects > Members > Master adds member with expiration date', :js do
end
end
- scenario 'change expiration date' do
+ it 'change expiration date' do
travel_to Time.zone.parse('2016-08-06 08:00') do
date = 3.days.from_now
project.team.add_users([new_member.id], :developer, expires_at: Date.today.to_s(:medium))
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index 3ac6ca4fc86..adc8202cde7 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
-feature 'Projects > Members > Master manages access requests' do
- it_behaves_like 'Master manages access requests' do
+describe 'Projects > Members > Maintainer manages access requests' do
+ it_behaves_like 'Maintainer manages access requests' do
let(:entity) { create(:project, :public, :access_requestable) }
let(:members_page_path) { project_project_members_path(entity) }
end
diff --git a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
index 47911c32a72..f612ad8d551 100644
--- a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
@@ -1,16 +1,16 @@
require 'spec_helper'
-feature 'Projects > Members > Member cannot request access to his project' do
+describe 'Projects > Members > Member cannot request access to his project' do
let(:member) { create(:user) }
let(:project) { create(:project) }
- background do
+ before do
project.add_developer(member)
sign_in(member)
visit project_path(project)
end
- scenario 'member does not see the request access button' do
+ it 'member does not see the request access button' do
expect(page).not_to have_content 'Request Access'
end
end
diff --git a/spec/features/projects/members/member_leaves_project_spec.rb b/spec/features/projects/members/member_leaves_project_spec.rb
index e54c2c76975..94b29de4686 100644
--- a/spec/features/projects/members/member_leaves_project_spec.rb
+++ b/spec/features/projects/members/member_leaves_project_spec.rb
@@ -1,16 +1,16 @@
require 'spec_helper'
-feature 'Projects > Members > Member leaves project' do
+describe 'Projects > Members > Member leaves project' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
- background do
+ before do
project.add_developer(user)
sign_in(user)
visit project_path(project)
end
- scenario 'user leaves project' do
+ it 'user leaves project' do
click_link 'Leave project'
expect(current_path).to eq(dashboard_projects_path)
diff --git a/spec/features/projects/members/owner_cannot_leave_project_spec.rb b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
index 15162d01c44..0aa005adb4d 100644
--- a/spec/features/projects/members/owner_cannot_leave_project_spec.rb
+++ b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
@@ -1,14 +1,14 @@
require 'spec_helper'
-feature 'Projects > Members > Owner cannot leave project' do
+describe 'Projects > Members > Owner cannot leave project' do
let(:project) { create(:project) }
- background do
+ before do
sign_in(project.owner)
visit project_path(project)
end
- scenario 'user does not see a "Leave project" link' do
+ it 'user does not see a "Leave project" link' do
expect(page).not_to have_content 'Leave project'
end
end
diff --git a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
index c27925c8dc4..eb1b720af05 100644
--- a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
@@ -1,14 +1,14 @@
require 'spec_helper'
-feature 'Projects > Members > Owner cannot request access to his project' do
+describe 'Projects > Members > Owner cannot request access to his project' do
let(:project) { create(:project) }
- background do
+ before do
sign_in(project.owner)
visit project_path(project)
end
- scenario 'owner does not see the request access button' do
+ it 'owner does not see the request access button' do
expect(page).not_to have_content 'Request Access'
end
end
diff --git a/spec/features/projects/members/share_with_group_spec.rb b/spec/features/projects/members/share_with_group_spec.rb
index 134c8b8bc39..c6d85e5d22f 100644
--- a/spec/features/projects/members/share_with_group_spec.rb
+++ b/spec/features/projects/members/share_with_group_spec.rb
@@ -1,21 +1,21 @@
require 'spec_helper'
-feature 'Project > Members > Share with Group', :js do
+describe 'Project > Members > Share with Group', :js do
include Select2Helper
include ActionView::Helpers::DateHelper
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
describe 'Share with group lock' do
shared_examples 'the project can be shared with groups' do
- scenario 'the "Share with group" tab exists' do
+ it 'the "Share with group" tab exists' do
visit project_settings_members_path(project)
expect(page).to have_selector('#share-with-group-tab')
end
end
shared_examples 'the project cannot be shared with groups' do
- scenario 'the "Share with group" tab does not exist' do
+ it 'the "Share with group" tab does not exist' do
visit project_settings_members_path(project)
expect(page).to have_selector('#add-member-tab')
expect(page).not_to have_selector('#share-with-group-tab')
@@ -26,15 +26,15 @@ feature 'Project > Members > Share with Group', :js do
let!(:group_to_share_with) { create(:group) }
let(:project) { create(:project, namespace: create(:group)) }
- background do
- project.add_master(master)
- sign_in(master)
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
end
context 'when the group has "Share with group lock" disabled' do
it_behaves_like 'the project can be shared with groups'
- scenario 'the project can be shared with another group' do
+ it 'the project can be shared with another group' do
visit project_settings_members_path(project)
click_on 'share-with-group-tab'
@@ -64,9 +64,9 @@ feature 'Project > Members > Share with Group', :js do
let(:subgroup) { create(:group, parent: root_group) }
let(:project) { create(:project, namespace: subgroup) }
- background do
- project.add_master(master)
- sign_in(master)
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
end
context 'when the root_group has "Share with group lock" disabled' do
@@ -112,8 +112,8 @@ feature 'Project > Members > Share with Group', :js do
end
before do
- project.add_master(master)
- sign_in(master)
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
visit project_settings_members_path(project)
@@ -126,7 +126,7 @@ feature 'Project > Members > Share with Group', :js do
find('.btn-create').click
end
- scenario 'the group link shows the expiration time with a warning class' do
+ it 'the group link shows the expiration time with a warning class' do
page.within('.project-members-groups') do
# Using distance_of_time_in_words_to_now because it is not the same as
# subtraction, and this way avoids time zone issues as well
@@ -141,12 +141,12 @@ feature 'Project > Members > Share with Group', :js do
context 'with multiple groups to choose from' do
let(:project) { create(:project) }
- background do
- project.add_master(master)
- sign_in(master)
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
- create(:group).add_owner(master)
- create(:group).add_owner(master)
+ create(:group).add_owner(maintainer)
+ create(:group).add_owner(maintainer)
visit project_settings_members_path(project)
@@ -173,14 +173,14 @@ feature 'Project > Members > Share with Group', :js do
let!(:group_to_share_with) { create(:group) }
let!(:project) { create(:project, namespace: nested_group) }
- background do
- project.add_master(master)
- sign_in(master)
- group.add_master(master)
- group_to_share_with.add_master(master)
+ before do
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
+ group.add_maintainer(maintainer)
+ group_to_share_with.add_maintainer(maintainer)
end
- scenario 'the groups dropdown does not show ancestors', :nested_groups do
+ it 'the groups dropdown does not show ancestors', :nested_groups do
visit project_settings_members_path(project)
click_on 'share-with-group-tab'
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index afa173c59e5..220775b514d 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -1,85 +1,85 @@
require 'spec_helper'
-feature 'Projects > Members > Sorting' do
- let(:master) { create(:user, name: 'John Doe') }
+describe 'Projects > Members > Sorting' do
+ let(:maintainer) { create(:user, name: 'John Doe') }
let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) }
- let(:project) { create(:project, namespace: master.namespace, creator: master) }
+ let(:project) { create(:project, namespace: maintainer.namespace, creator: maintainer) }
- background do
+ before do
create(:project_member, :developer, user: developer, project: project, created_at: 3.days.ago)
- sign_in(master)
+ sign_in(maintainer)
end
- scenario 'sorts alphabetically by default' do
+ it 'sorts alphabetically by default' do
visit_members_list(sort: nil)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
- scenario 'sorts by access level ascending' do
+ it 'sorts by access level ascending' do
visit_members_list(sort: :access_level_asc)
expect(first_member).to include(developer.name)
- expect(second_member).to include(master.name)
+ expect(second_member).to include(maintainer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
end
- scenario 'sorts by access level descending' do
+ it 'sorts by access level descending' do
visit_members_list(sort: :access_level_desc)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
end
- scenario 'sorts by last joined' do
+ it 'sorts by last joined' do
visit_members_list(sort: :last_joined)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
end
- scenario 'sorts by oldest joined' do
+ it 'sorts by oldest joined' do
visit_members_list(sort: :oldest_joined)
expect(first_member).to include(developer.name)
- expect(second_member).to include(master.name)
+ expect(second_member).to include(maintainer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
end
- scenario 'sorts by name ascending' do
+ it 'sorts by name ascending' do
visit_members_list(sort: :name_asc)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
- scenario 'sorts by name descending' do
+ it 'sorts by name descending' do
visit_members_list(sort: :name_desc)
expect(first_member).to include(developer.name)
- expect(second_member).to include(master.name)
+ expect(second_member).to include(maintainer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
- scenario 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
+ it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :recent_sign_in)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
- scenario 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
+ it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :oldest_sign_in)
expect(first_member).to include(developer.name)
- expect(second_member).to include(master.name)
+ expect(second_member).to include(maintainer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
end
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index 672d5daa3d8..50ba67f0ffc 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -1,26 +1,26 @@
require 'spec_helper'
-feature 'Projects > Members > User requests access', :js do
+describe 'Projects > Members > User requests access', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :access_requestable, :repository) }
- let(:master) { project.owner }
+ let(:maintainer) { project.owner }
- background do
+ before do
sign_in(user)
visit project_path(project)
end
- scenario 'request access feature is disabled' do
- project.update_attributes(request_access_enabled: false)
+ it 'request access feature is disabled' do
+ project.update(request_access_enabled: false)
visit project_path(project)
expect(page).not_to have_content 'Request Access'
end
- scenario 'user can request access to a project' do
+ it 'user can request access to a project' do
perform_enqueued_jobs { click_link 'Request Access' }
- expect(ActionMailer::Base.deliveries.last.to).to eq [master.notification_email]
+ expect(ActionMailer::Base.deliveries.last.to).to eq [maintainer.notification_email]
expect(ActionMailer::Base.deliveries.last.subject).to eq "Request to join the #{project.full_name} project"
expect(project.requesters.exists?(user_id: user)).to be_truthy
@@ -31,7 +31,7 @@ feature 'Projects > Members > User requests access', :js do
end
context 'code access is restricted' do
- scenario 'user can request access' do
+ it 'user can request access' do
project.project_feature.update!(repository_access_level: ProjectFeature::PRIVATE,
builds_access_level: ProjectFeature::PRIVATE,
merge_requests_access_level: ProjectFeature::PRIVATE)
@@ -41,7 +41,7 @@ feature 'Projects > Members > User requests access', :js do
end
end
- scenario 'user is not listed in the project members page' do
+ it 'user is not listed in the project members page' do
click_link 'Request Access'
expect(project.requesters.exists?(user_id: user)).to be_truthy
@@ -55,7 +55,7 @@ feature 'Projects > Members > User requests access', :js do
end
end
- scenario 'user can withdraw its request for access' do
+ it 'user can withdraw its request for access' do
click_link 'Request Access'
expect(project.requesters.exists?(user_id: user)).to be_truthy
diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb
index b571d5a0e26..69561b4d733 100644
--- a/spec/features/projects/merge_request_button_spec.rb
+++ b/spec/features/projects/merge_request_button_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Merge Request button' do
+describe 'Merge Request button' do
shared_examples 'Merge request button only shown when allowed' do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb b/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb
index b257f447439..2d12d690151 100644
--- a/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb
@@ -6,7 +6,7 @@ describe 'User closes a merge requests', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
diff --git a/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb b/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb
index 0a952cfc2a9..8ea358bcc70 100644
--- a/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb
+++ b/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb
@@ -9,7 +9,7 @@ describe 'User comments on a commit', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_commit_path(project, sample_commit.id))
diff --git a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb b/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb
index 1828b60fec7..441b080bee5 100644
--- a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb
+++ b/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb
@@ -11,7 +11,7 @@ describe 'User comments on a diff', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(diffs_project_merge_request_path(project, merge_request))
@@ -31,7 +31,7 @@ describe 'User comments on a diff', :js do
page.within('.files > div:nth-child(3)') do
expect(page).to have_content('Line is wrong')
- find('.js-toggle-diff-comments').click
+ find('.js-btn-vue-toggle-comments').click
expect(page).not_to have_content('Line is wrong')
end
@@ -64,7 +64,7 @@ describe 'User comments on a diff', :js do
# Hide the comment.
page.within('.files > div:nth-child(3)') do
- find('.js-toggle-diff-comments').click
+ find('.js-btn-vue-toggle-comments').click
expect(page).not_to have_content('Line is wrong')
end
@@ -77,7 +77,7 @@ describe 'User comments on a diff', :js do
# Show the comment.
page.within('.files > div:nth-child(3)') do
- find('.js-toggle-diff-comments').click
+ find('.js-btn-vue-toggle-comments').click
end
# Now both the comments should be shown.
diff --git a/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb b/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb
index f90aaba3caf..69bdab85d81 100644
--- a/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb
@@ -8,7 +8,7 @@ describe 'User comments on a merge request', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
diff --git a/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb b/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb
index 1f21ef7b382..38b4e4a6d1b 100644
--- a/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb
@@ -8,7 +8,7 @@ describe "User creates a merge request", :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb b/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb
index 3d19a2923b9..7de0f9daac6 100644
--- a/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb
@@ -8,7 +8,7 @@ describe 'User edits a merge request', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(edit_project_merge_request_path(project, merge_request))
diff --git a/spec/features/projects/merge_requests/user_manages_subscription_spec.rb b/spec/features/projects/merge_requests/user_manages_subscription_spec.rb
index f55eb5c6664..68a835e7f77 100644
--- a/spec/features/projects/merge_requests/user_manages_subscription_spec.rb
+++ b/spec/features/projects/merge_requests/user_manages_subscription_spec.rb
@@ -6,7 +6,7 @@ describe 'User manages subscription', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
diff --git a/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb b/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb
index ba3c9789da1..745b4537e72 100644
--- a/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb
@@ -6,7 +6,7 @@ describe 'User reopens a merge requests', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
diff --git a/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb b/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
index 305658f1b5d..e401933aed2 100644
--- a/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
+++ b/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
@@ -9,7 +9,7 @@ describe 'User sorts merge requests' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_merge_requests_path(project))
diff --git a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb b/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
index 3aac93eaf7c..6ac495aa03d 100644
--- a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
@@ -31,7 +31,7 @@ describe 'User views an open merge request' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(edit_project_merge_request_path(project, merge_request))
diff --git a/spec/features/projects/merge_requests/user_views_user_status_on_merge_request_spec.rb b/spec/features/projects/merge_requests/user_views_user_status_on_merge_request_spec.rb
new file mode 100644
index 00000000000..78d9c6c6db1
--- /dev/null
+++ b/spec/features/projects/merge_requests/user_views_user_status_on_merge_request_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Project > Merge request > View user status' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:merge_request) do
+ create(:merge_request, source_project: project, target_project: project, author: create(:user))
+ end
+
+ subject { visit merge_request_path(merge_request) }
+
+ describe 'the status of the merge request author' do
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { merge_request.author }
+ end
+ end
+
+ context 'for notes', :js do
+ describe 'the status of the author of a note on a merge request' do
+ let(:note) { create(:note, noteable: merge_request, project: project, author: create(:user)) }
+
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { note.author }
+ end
+ end
+
+ describe 'the status of the author of a diff note on a merge request' do
+ let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, author: create(:user)) }
+
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { note.author }
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb
index 20a52d6011f..ff31092b910 100644
--- a/spec/features/projects/milestones/milestone_spec.rb
+++ b/spec/features/projects/milestones/milestone_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Project milestone' do
+describe 'Project milestone' do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:milestone) { create(:milestone, project: project) }
diff --git a/spec/features/projects/milestones/milestones_sorting_spec.rb b/spec/features/projects/milestones/milestones_sorting_spec.rb
index b64786d4eec..dc711377e6e 100644
--- a/spec/features/projects/milestones/milestones_sorting_spec.rb
+++ b/spec/features/projects/milestones/milestones_sorting_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Milestones sorting', :js do
+describe 'Milestones sorting', :js do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
@@ -17,7 +17,7 @@ feature 'Milestones sorting', :js do
sign_in(user)
end
- scenario 'visit project milestones and sort by due_date_asc' do
+ it 'visit project milestones and sort by due_date_asc' do
visit project_milestones_path(project)
expect(page).to have_button('Due soon')
diff --git a/spec/features/projects/milestones/new_spec.rb b/spec/features/projects/milestones/new_spec.rb
index f7900210fe6..0b5ab547dce 100644
--- a/spec/features/projects/milestones/new_spec.rb
+++ b/spec/features/projects/milestones/new_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Creating a new project milestone', :js do
+describe 'Creating a new project milestone', :js do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
@@ -9,9 +9,9 @@ feature 'Creating a new project milestone', :js do
visit new_project_milestone_path(project)
end
- it 'description has autocomplete' do
+ it 'description has emoji autocomplete' do
find('#milestone_description').native.send_keys('')
- fill_in 'milestone_description', with: '@'
+ fill_in 'milestone_description', with: ':'
expect(page).to have_selector('.atwho-view')
end
diff --git a/spec/features/projects/milestones/user_interacts_with_labels_spec.rb b/spec/features/projects/milestones/user_interacts_with_labels_spec.rb
index f6a82f80d65..a6d58be7b13 100644
--- a/spec/features/projects/milestones/user_interacts_with_labels_spec.rb
+++ b/spec/features/projects/milestones/user_interacts_with_labels_spec.rb
@@ -11,7 +11,7 @@ describe 'User interacts with labels' do
let(:label_enhancement) { create(:label, project: project, title: 'enhancement') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
issue1.labels << [label_bug, label_feature]
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index fee6287558e..bbe08ff83ff 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'New project' do
+describe 'New project' do
include Select2Helper
let(:user) { create(:admin) }
@@ -25,6 +25,22 @@ feature 'New project' do
expect(page).to have_link('GitLab export')
end
+ describe 'manifest import option' do
+ before do
+ visit new_project_path
+
+ find('#import-project-tab').click
+ end
+
+ context 'when using postgres', :postgresql do
+ it { expect(page).to have_link('Manifest file') }
+ end
+
+ context 'when using mysql', :mysql do
+ it { expect(page).not_to have_link('Manifest file') }
+ end
+ end
+
context 'Visibility level selector', :js do
Gitlab::VisibilityLevel.options.each do |key, level|
it "sets selector to #{key}" do
@@ -48,6 +64,15 @@ feature 'New project' do
end
end
+ context 'Readme selector' do
+ it 'shows the initialize with Readme checkbox' do
+ visit new_project_path
+
+ expect(page).to have_css('input#project_initialize_with_readme')
+ expect(page).to have_content('Initialize repository with a README')
+ end
+ end
+
context 'Namespace selector' do
context 'with user namespace' do
before do
@@ -85,7 +110,7 @@ feature 'New project' do
let(:subgroup) { create(:group, parent: group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
visit new_project_path(namespace_id: subgroup.id)
end
@@ -192,5 +217,16 @@ feature 'New project' do
expect(current_path).to eq new_import_google_code_path
end
end
+
+ context 'from manifest file', :postgresql do
+ before do
+ first('.import_manifest').click
+ end
+
+ it 'shows import instructions' do
+ expect(page).to have_content('Manifest file import')
+ expect(current_path).to eq new_import_manifest_path
+ end
+ end
end
end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index a2899ec0f48..831f22a0e69 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-feature 'Pages' do
- given(:project) { create(:project) }
- given(:user) { create(:user) }
- given(:role) { :master }
+describe 'Pages' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:role) { :maintainer }
- background do
+ before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
project.add_role(user, role)
@@ -14,7 +14,7 @@ feature 'Pages' do
end
shared_examples 'no pages deployed' do
- scenario 'does not see anything to destroy' do
+ it 'does not see anything to destroy' do
visit project_pages_path(project)
expect(page).to have_content('Configure pages')
@@ -24,16 +24,16 @@ feature 'Pages' do
end
context 'when user is the owner' do
- background do
+ before do
project.namespace.update(owner: user)
end
context 'when pages deployed' do
- background do
+ before do
allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
end
- scenario 'renders Access pages' do
+ it 'renders Access pages' do
visit project_pages_path(project)
expect(page).to have_content('Access pages')
@@ -48,7 +48,7 @@ feature 'Pages' do
end
context 'when pages are exposed on external HTTP address', :http_pages_enabled do
- given(:project) { create(:project, pages_https_only: false) }
+ let(:project) { create(:project, pages_https_only: false) }
shared_examples 'adds new domain' do
it 'adds new domain' do
@@ -210,11 +210,11 @@ feature 'Pages' do
context 'when the user is not the owner' do
context 'when pages deployed' do
- background do
+ before do
allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
end
- scenario 'sees "Only the project owner can remove pages" text' do
+ it 'sees "Only the project owner can remove pages" text' do
visit project_pages_path(project)
expect(page).to have_text('Only the project owner can remove pages')
@@ -225,13 +225,13 @@ feature 'Pages' do
end
describe 'HTTPS settings', :js, :https_pages_enabled do
- background do
+ before do
project.namespace.update(owner: user)
allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
end
- scenario 'tries to change the setting' do
+ it 'tries to change the setting' do
visit project_pages_path(project)
expect(page).to have_content("Force domains with SSL certificates to use HTTPS")
@@ -251,7 +251,7 @@ feature 'Pages' do
allow(service).to receive(:execute).and_return(status: :error)
end
- scenario 'tries to change the setting' do
+ it 'tries to change the setting' do
visit project_pages_path(project)
uncheck :project_pages_https_only
@@ -263,13 +263,13 @@ feature 'Pages' do
end
context 'non-HTTPS domain exists' do
- given(:project) { create(:project, pages_https_only: false) }
+ let(:project) { create(:project, pages_https_only: false) }
before do
create(:pages_domain, :without_key, :without_certificate, project: project)
end
- scenario 'the setting is disabled' do
+ it 'the setting is disabled' do
visit project_pages_path(project)
expect(page).to have_field(:project_pages_https_only, disabled: true)
@@ -278,7 +278,7 @@ feature 'Pages' do
end
context 'HTTPS pages are disabled', :https_pages_disabled do
- scenario 'the setting is unavailable' do
+ it 'the setting is unavailable' do
visit project_pages_path(project)
expect(page).not_to have_field(:project_pages_https_only)
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 065d00d51d4..ee6b67b2188 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Pipeline Schedules', :js do
+describe 'Pipeline Schedules', :js do
include PipelineSchedulesHelper
let!(:project) { create(:project, :repository) }
@@ -9,9 +9,9 @@ feature 'Pipeline Schedules', :js do
let(:scope) { nil }
let!(:user) { create(:user) }
- context 'logged in as master' do
+ context 'logged in as maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
gitlab_sign_in(user)
end
@@ -155,7 +155,7 @@ feature 'Pipeline Schedules', :js do
end
context 'when user creates a new pipeline schedule with variables' do
- background do
+ before do
visit_pipelines_schedules
click_link 'New schedule'
fill_in_schedule_form
@@ -166,7 +166,7 @@ feature 'Pipeline Schedules', :js do
save_pipeline_schedule
end
- scenario 'user sees the new variable in edit window' do
+ it 'user sees the new variable in edit window' do
find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click
page.within('.ci-variable-list') do
expect(find(".ci-variable-row:nth-child(1) .js-ci-variable-input-key").value).to eq('AAA')
@@ -178,7 +178,7 @@ feature 'Pipeline Schedules', :js do
end
context 'when user edits a variable of a pipeline schedule' do
- background do
+ before do
create(:ci_pipeline_schedule, project: project, owner: user).tap do |pipeline_schedule|
create(:ci_pipeline_schedule_variable, key: 'AAA', value: 'AAA123', pipeline_schedule: pipeline_schedule)
end
@@ -192,7 +192,7 @@ feature 'Pipeline Schedules', :js do
click_button 'Save pipeline schedule'
end
- scenario 'user sees the updated variable in edit window' do
+ it 'user sees the updated variable in edit window' do
find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click
page.within('.ci-variable-list') do
expect(find(".ci-variable-row:nth-child(1) .js-ci-variable-input-key").value).to eq('foo')
@@ -202,7 +202,7 @@ feature 'Pipeline Schedules', :js do
end
context 'when user removes a variable of a pipeline schedule' do
- background do
+ before do
create(:ci_pipeline_schedule, project: project, owner: user).tap do |pipeline_schedule|
create(:ci_pipeline_schedule_variable, key: 'AAA', value: 'AAA123', pipeline_schedule: pipeline_schedule)
end
@@ -213,7 +213,7 @@ feature 'Pipeline Schedules', :js do
click_button 'Save pipeline schedule'
end
- scenario 'user does not see the removed variable in edit window' do
+ it 'user does not see the removed variable in edit window' do
find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click
page.within('.ci-variable-list') do
expect(find(".ci-variable-row:nth-child(1) .js-ci-variable-input-key").value).to eq('')
@@ -223,13 +223,13 @@ feature 'Pipeline Schedules', :js do
end
context 'when active is true and next_run_at is NULL' do
- background do
+ before do
create(:ci_pipeline_schedule, project: project, owner: user).tap do |pipeline_schedule|
pipeline_schedule.update_attribute(:cron, nil) # Consequently next_run_at will be nil
end
end
- scenario 'user edit and recover the problematic pipeline schedule' do
+ it 'user edit and recover the problematic pipeline schedule' do
visit_pipelines_schedules
find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click
fill_in 'schedule_cron', with: '* 1 2 3 4'
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index ecc7cf84138..a84492ea5f1 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -63,6 +63,12 @@ describe 'Pipeline', :js do
expect(page).to have_css('#js-tab-pipeline.active')
end
+ it_behaves_like 'showing user status' do
+ let(:user_with_status) { pipeline.user }
+
+ subject { visit project_pipeline_path(project, pipeline) }
+ end
+
describe 'pipeline graph' do
context 'when pipeline has running builds' do
it 'shows a running icon and a cancel action for the running build' do
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 9c165b17704..4a83bcc3efb 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -595,7 +595,7 @@ describe 'Pipelines', :js do
before do
create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master')
- project.add_master(user)
+ project.add_maintainer(user)
visit project_pipelines_path(project)
end
@@ -606,7 +606,7 @@ describe 'Pipelines', :js do
describe 'user clicks the button' do
context 'when project already has jobs_cache_index' do
before do
- project.update_attributes(jobs_cache_index: 1)
+ project.update(jobs_cache_index: 1)
end
it 'increments jobs_cache_index' do
diff --git a/spec/features/projects/remote_mirror_spec.rb b/spec/features/projects/remote_mirror_spec.rb
index 81a6b613cc8..5259a8942dc 100644
--- a/spec/features/projects/remote_mirror_spec.rb
+++ b/spec/features/projects/remote_mirror_spec.rb
@@ -1,19 +1,19 @@
require 'spec_helper'
-feature 'Project remote mirror', :feature do
+describe 'Project remote mirror', :feature do
let(:project) { create(:project, :repository, :remote_mirror) }
let(:remote_mirror) { project.remote_mirrors.first }
let(:user) { create(:user) }
describe 'On a project', :js do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
context 'when last_error is present but last_update_at is not' do
it 'renders error message without timstamp' do
- remote_mirror.update_attributes(last_error: 'Some new error', last_update_at: nil)
+ remote_mirror.update(last_error: 'Some new error', last_update_at: nil)
visit project_mirror_path(project)
@@ -23,7 +23,7 @@ feature 'Project remote mirror', :feature do
context 'when last_error and last_update_at are present' do
it 'renders error message with timestamp' do
- remote_mirror.update_attributes(last_error: 'Some new error', last_update_at: Time.now - 5.minutes)
+ remote_mirror.update(last_error: 'Some new error', last_update_at: Time.now - 5.minutes)
visit project_mirror_path(project)
diff --git a/spec/features/projects/services/user_activates_asana_spec.rb b/spec/features/projects/services/user_activates_asana_spec.rb
index db836d2985c..c44e07dd3b4 100644
--- a/spec/features/projects/services/user_activates_asana_spec.rb
+++ b/spec/features/projects/services/user_activates_asana_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Asana' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_assembla_spec.rb b/spec/features/projects/services/user_activates_assembla_spec.rb
index f099b332785..9c3884a7c74 100644
--- a/spec/features/projects/services/user_activates_assembla_spec.rb
+++ b/spec/features/projects/services/user_activates_assembla_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Assembla' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
index a00c2e0ad99..19573565265 100644
--- a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
+++ b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Atlassian Bamboo CI' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_emails_on_push_spec.rb b/spec/features/projects/services/user_activates_emails_on_push_spec.rb
index 3769875b29c..cc55f7b2060 100644
--- a/spec/features/projects/services/user_activates_emails_on_push_spec.rb
+++ b/spec/features/projects/services/user_activates_emails_on_push_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Emails on push' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_flowdock_spec.rb b/spec/features/projects/services/user_activates_flowdock_spec.rb
index 5298d8acaf5..f981b7e9da9 100644
--- a/spec/features/projects/services/user_activates_flowdock_spec.rb
+++ b/spec/features/projects/services/user_activates_flowdock_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Flowdock' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_hipchat_spec.rb b/spec/features/projects/services/user_activates_hipchat_spec.rb
index a9bf16642c7..2f5313c91f9 100644
--- a/spec/features/projects/services/user_activates_hipchat_spec.rb
+++ b/spec/features/projects/services/user_activates_hipchat_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates HipChat' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_irker_spec.rb b/spec/features/projects/services/user_activates_irker_spec.rb
index 435663c818f..4c8e321b411 100644
--- a/spec/features/projects/services/user_activates_irker_spec.rb
+++ b/spec/features/projects/services/user_activates_irker_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Irker (IRC gateway)' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_issue_tracker_spec.rb b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
index e9502178bd7..7cd5b12802b 100644
--- a/spec/features/projects/services/user_activates_issue_tracker_spec.rb
+++ b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
@@ -15,7 +15,7 @@ describe 'User activates issue tracker', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_settings_integrations_path(project)
diff --git a/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb b/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb
index 1048803fde8..28d83a8b961 100644
--- a/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb
+++ b/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates JetBrains TeamCity CI' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb
index 429128ec096..08e1855d034 100644
--- a/spec/features/projects/services/user_activates_jira_spec.rb
+++ b/spec/features/projects/services/user_activates_jira_spec.rb
@@ -17,7 +17,7 @@ describe 'User activates Jira', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_settings_integrations_path(project)
diff --git a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
index fce41ce347f..25b74cc481d 100644
--- a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Setup Mattermost slash commands', :js do
+describe 'Setup Mattermost slash commands', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:service) { project.create_mattermost_slash_commands_service }
@@ -8,7 +8,7 @@ feature 'Setup Mattermost slash commands', :js do
before do
stub_mattermost_setting(enabled: mattermost_enabled)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit edit_project_service_path(project, service)
end
diff --git a/spec/features/projects/services/user_activates_packagist_spec.rb b/spec/features/projects/services/user_activates_packagist_spec.rb
index b0cc818f093..756e9b33c07 100644
--- a/spec/features/projects/services/user_activates_packagist_spec.rb
+++ b/spec/features/projects/services/user_activates_packagist_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Packagist' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_pivotaltracker_spec.rb b/spec/features/projects/services/user_activates_pivotaltracker_spec.rb
index d5d109ba48b..1d6b19e0b0c 100644
--- a/spec/features/projects/services/user_activates_pivotaltracker_spec.rb
+++ b/spec/features/projects/services/user_activates_pivotaltracker_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates PivotalTracker' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_prometheus_spec.rb b/spec/features/projects/services/user_activates_prometheus_spec.rb
index 33f884eb148..61361c8a2e3 100644
--- a/spec/features/projects/services/user_activates_prometheus_spec.rb
+++ b/spec/features/projects/services/user_activates_prometheus_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Prometheus' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_pushover_spec.rb b/spec/features/projects/services/user_activates_pushover_spec.rb
index 9b7e8d62792..24612ee1457 100644
--- a/spec/features/projects/services/user_activates_pushover_spec.rb
+++ b/spec/features/projects/services/user_activates_pushover_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Pushover' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_slack_notifications_spec.rb b/spec/features/projects/services/user_activates_slack_notifications_spec.rb
index fae9ebd1bd6..24b5d5259db 100644
--- a/spec/features/projects/services/user_activates_slack_notifications_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_notifications_spec.rb
@@ -6,7 +6,7 @@ describe 'User activates Slack notifications' do
let(:project) { create(:project, slack_service: service) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -29,7 +29,7 @@ describe 'User activates Slack notifications' do
context 'when service is already configured' do
before do
service.fields
- service.update_attributes(
+ service.update(
push_channel: 1,
issue_channel: 2,
merge_request_channel: 3,
diff --git a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
index 4a88654210c..08cfddf7993 100644
--- a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
@@ -1,12 +1,12 @@
require 'spec_helper'
-feature 'Slack slash commands' do
- given(:user) { create(:user) }
- given(:project) { create(:project) }
- given(:service) { project.create_slack_slash_commands_service }
+describe 'Slack slash commands' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:service) { project.create_slack_slash_commands_service }
- background do
- project.add_master(user)
+ before do
+ project.add_maintainer(user)
sign_in(user)
visit edit_project_service_path(project, service)
end
diff --git a/spec/features/projects/services/user_views_services_spec.rb b/spec/features/projects/services/user_views_services_spec.rb
index 5c5e8b66642..e9c8cf0fe34 100644
--- a/spec/features/projects/services/user_views_services_spec.rb
+++ b/spec/features/projects/services/user_views_services_spec.rb
@@ -5,7 +5,7 @@ describe 'User views services' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/settings/forked_project_settings_spec.rb b/spec/features/projects/settings/forked_project_settings_spec.rb
index a4d1b78b83b..df33d215602 100644
--- a/spec/features/projects/settings/forked_project_settings_spec.rb
+++ b/spec/features/projects/settings/forked_project_settings_spec.rb
@@ -7,8 +7,8 @@ describe 'Projects > Settings > For a forked project', :js do
let(:forked_project) { fork_project(original_project, user) }
before do
- original_project.add_master(user)
- forked_project.add_master(user)
+ original_project.add_maintainer(user)
+ forked_project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
index 5178d63050e..8745ff72df0 100644
--- a/spec/features/projects/settings/integration_settings_spec.rb
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -21,8 +21,8 @@ describe 'Projects > Settings > Integration settings' do
end
end
- context 'for master' do
- let(:role) { :master }
+ context 'for maintainer' do
+ let(:role) { :maintainer }
context 'Webhooks' do
let(:hook) { create(:project_hook, :all_events_enabled, enable_ssl_verification: true, project: project) }
diff --git a/spec/features/projects/settings/lfs_settings_spec.rb b/spec/features/projects/settings/lfs_settings_spec.rb
index 342be1d2a9d..befb306b48d 100644
--- a/spec/features/projects/settings/lfs_settings_spec.rb
+++ b/spec/features/projects/settings/lfs_settings_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
describe 'Projects > Settings > LFS settings' do
let(:project) { create(:project) }
let(:user) { create(:user) }
- let(:role) { :master }
+ let(:role) { :maintainer }
context 'LFS enabled setting' do
before do
@@ -13,8 +13,8 @@ describe 'Projects > Settings > LFS settings' do
project.add_role(user, role)
end
- context 'for master' do
- let(:role) { :master }
+ context 'for maintainer' do
+ let(:role) { :maintainer }
it 'displays the correct elements', :js do
visit edit_project_path(project)
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index cfdae246c09..30b0a5578ea 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -8,7 +8,6 @@ describe "Projects > Settings > Pipelines settings" do
before do
sign_in(user)
project.add_role(user, role)
- create(:project_auto_devops, project: project)
end
context 'for developer' do
@@ -21,8 +20,8 @@ describe "Projects > Settings > Pipelines settings" do
end
end
- context 'for master' do
- let(:role) { :master }
+ context 'for maintainer' do
+ let(:role) { :maintainer }
it 'be allowed to change' do
visit project_settings_ci_cd_path(project)
@@ -61,19 +60,58 @@ describe "Projects > Settings > Pipelines settings" do
end
describe 'Auto DevOps' do
- it 'update auto devops settings' do
- visit project_settings_ci_cd_path(project)
+ context 'when auto devops is turned on instance-wide' do
+ before do
+ stub_application_setting(auto_devops_enabled: true)
+ end
+
+ it 'auto devops is on by default and can be manually turned off' do
+ visit project_settings_ci_cd_path(project)
- page.within '#autodevops-settings' do
- fill_in('project_auto_devops_attributes_domain', with: 'test.com')
- page.choose('project_auto_devops_attributes_enabled_false')
- click_on 'Save changes'
+ page.within '#autodevops-settings' do
+ expect(find_field('project_auto_devops_attributes_enabled')).to be_checked
+ expect(page).to have_content('instance enabled')
+ uncheck 'Default to Auto DevOps pipeline'
+ click_on 'Save changes'
+ end
+
+ expect(page.status_code).to eq(200)
+ expect(project.auto_devops).to be_present
+ expect(project.auto_devops).not_to be_enabled
+
+ page.within '#autodevops-settings' do
+ expect(find_field('project_auto_devops_attributes_enabled')).not_to be_checked
+ expect(page).not_to have_content('instance enabled')
+ end
end
+ end
- expect(page.status_code).to eq(200)
- expect(project.auto_devops).to be_present
- expect(project.auto_devops).not_to be_enabled
- expect(project.auto_devops.domain).to eq('test.com')
+ context 'when auto devops is not turned on instance-wide' do
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+ end
+
+ it 'auto devops is off by default and can be manually turned on' do
+ visit project_settings_ci_cd_path(project)
+
+ page.within '#autodevops-settings' do
+ expect(page).not_to have_content('instance enabled')
+ expect(find_field('project_auto_devops_attributes_enabled')).not_to be_checked
+ check 'Default to Auto DevOps pipeline'
+ fill_in('project_auto_devops_attributes_domain', with: 'test.com')
+ click_on 'Save changes'
+ end
+
+ expect(page.status_code).to eq(200)
+ expect(project.auto_devops).to be_present
+ expect(project.auto_devops).to be_enabled
+ expect(project.auto_devops.domain).to eq('test.com')
+
+ page.within '#autodevops-settings' do
+ expect(find_field('project_auto_devops_attributes_enabled')).to be_checked
+ expect(page).not_to have_content('instance enabled')
+ end
+ end
end
context 'when there is a cluster with ingress and external_ip' do
diff --git a/spec/features/projects/settings/project_badges_spec.rb b/spec/features/projects/settings/project_badges_spec.rb
index 4893bef8884..2ec94274f80 100644
--- a/spec/features/projects/settings/project_badges_spec.rb
+++ b/spec/features/projects/settings/project_badges_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Project Badges' do
+describe 'Project Badges' do
include WaitForRequests
let(:user) { create(:user) }
@@ -12,7 +12,7 @@ feature 'Project Badges' do
let!(:group_badge) { create(:group_badge, group: group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
sign_in(user)
visit(project_settings_badges_path(project))
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 08b40653764..a0f5b234ebc 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -20,8 +20,8 @@ describe 'Projects > Settings > Repository settings' do
end
end
- context 'for master' do
- let(:role) { :master }
+ context 'for maintainer' do
+ let(:role) { :maintainer }
context 'Deploy Keys', :js do
let(:private_deploy_key) { create(:deploy_key, title: 'private_deploy_key', public: false) }
@@ -101,7 +101,7 @@ describe 'Projects > Settings > Repository settings' do
visit project_settings_repository_path(project)
end
- scenario 'view deploy tokens' do
+ it 'view deploy tokens' do
within('.deploy-tokens') do
expect(page).to have_content(deploy_token.name)
expect(page).to have_content('read_repository')
@@ -109,7 +109,7 @@ describe 'Projects > Settings > Repository settings' do
end
end
- scenario 'add a new deploy token' do
+ it 'add a new deploy token' do
fill_in 'deploy_token_name', with: 'new_deploy_key'
fill_in 'deploy_token_expires_at', with: (Date.today + 1.month).to_s
check 'deploy_token_read_repository'
@@ -124,7 +124,7 @@ describe 'Projects > Settings > Repository settings' do
let(:user2) { create(:user) }
before do
- project.add_master(user2)
+ project.add_maintainer(user2)
visit project_settings_repository_path(project)
end
diff --git a/spec/features/projects/settings/user_archives_project_spec.rb b/spec/features/projects/settings/user_archives_project_spec.rb
index 38c8a8c2468..5008eab4d39 100644
--- a/spec/features/projects/settings/user_archives_project_spec.rb
+++ b/spec/features/projects/settings/user_archives_project_spec.rb
@@ -4,7 +4,7 @@ describe 'Projects > Settings > User archives a project' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/projects/settings/user_changes_avatar_spec.rb b/spec/features/projects/settings/user_changes_avatar_spec.rb
index 2dcc79d8a12..64335163016 100644
--- a/spec/features/projects/settings/user_changes_avatar_spec.rb
+++ b/spec/features/projects/settings/user_changes_avatar_spec.rb
@@ -5,7 +5,7 @@ describe 'Projects > Settings > User changes avatar' do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
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 71a077039b7..ecfb49b9efe 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
@@ -50,7 +50,7 @@ describe "User interacts with deploy keys", :js do
before do
create(:deploy_keys_project, project: another_project, deploy_key: deploy_key)
- another_project.add_master(user)
+ another_project.add_maintainer(user)
end
it "shows deploy keys" do
@@ -110,7 +110,7 @@ describe "User interacts with deploy keys", :js do
before do
create(:deploy_keys_project, project: another_project, deploy_key: deploy_key)
- another_project.add_master(user)
+ another_project.add_maintainer(user)
end
it_behaves_like "attaches a key"
diff --git a/spec/features/projects/settings/user_manages_group_links_spec.rb b/spec/features/projects/settings/user_manages_group_links_spec.rb
index 92ce2ca83c7..2f1824d7849 100644
--- a/spec/features/projects/settings/user_manages_group_links_spec.rb
+++ b/spec/features/projects/settings/user_manages_group_links_spec.rb
@@ -9,10 +9,10 @@ describe 'Projects > Settings > User manages group links' do
let(:group_market) { create(:group, name: 'Market', path: 'market') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
- share_link = project.project_group_links.new(group_access: Gitlab::Access::MASTER)
+ share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
share_link.group_id = group_ops.id
share_link.save!
diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb
index d3003753ae6..b8ca11d53f0 100644
--- a/spec/features/projects/settings/user_manages_project_members_spec.rb
+++ b/spec/features/projects/settings/user_manages_project_members_spec.rb
@@ -9,7 +9,7 @@ describe 'Projects > Settings > User manages project members' do
let(:user_mike) { create(:user, name: 'Mike') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user_dmitriy)
sign_in(user)
end
@@ -30,7 +30,7 @@ describe 'Projects > Settings > User manages project members' do
end
it 'imports a team from another project' do
- project2.add_master(user)
+ project2.add_maintainer(user)
project2.add_reporter(user_mike)
visit(project_project_members_path(project))
@@ -54,7 +54,7 @@ describe 'Projects > Settings > User manages project members' do
group.add_owner(user)
group.add_developer(user_dmitriy)
- share_link = project.project_group_links.new(group_access: Gitlab::Access::MASTER)
+ share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
share_link.group_id = group.id
share_link.save!
diff --git a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb
new file mode 100644
index 00000000000..069704a1305
--- /dev/null
+++ b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb
@@ -0,0 +1,25 @@
+require 'rails_helper'
+
+describe 'Repository Settings > User sees revoke deploy token modal', :js do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:role) { :developer }
+ let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) }
+
+ before do
+ project.add_role(user, role)
+ sign_in(user)
+ visit(project_settings_repository_path(project))
+ click_link('Revoke')
+ end
+
+ it 'shows the revoke deploy token modal' do
+ expect(page).to have_content('You are about to revoke')
+ end
+
+ it 'closes the revoke deploy token modal with escape keypress' do
+ find('.modal.show').send_keys(:escape)
+
+ expect(page).not_to have_content('You are about to revoke')
+ end
+end
diff --git a/spec/features/projects/settings/user_transfers_a_project_spec.rb b/spec/features/projects/settings/user_transfers_a_project_spec.rb
index 96b7cf1f93b..2fdbc04fa62 100644
--- a/spec/features/projects/settings/user_transfers_a_project_spec.rb
+++ b/spec/features/projects/settings/user_transfers_a_project_spec.rb
@@ -10,7 +10,7 @@ describe 'Projects > Settings > User transfers a project', :js do
sign_in(user)
end
- def transfer_project(project, group)
+ def transfer_project(project, group, confirm: true)
visit edit_project_path(project)
page.within('.js-project-transfer-form') do
@@ -21,6 +21,8 @@ describe 'Projects > Settings > User transfers a project', :js do
click_button('Transfer project')
+ return unless confirm
+
fill_in 'confirm_name_input', with: project.name
click_button 'Confirm'
@@ -28,6 +30,11 @@ describe 'Projects > Settings > User transfers a project', :js do
wait_for_requests
end
+ it 'focuses on the confirmation field' do
+ transfer_project(project, group, confirm: false)
+ expect(page).to have_selector '#confirm_name_input:focus'
+ end
+
it 'allows transferring a project to a group' do
old_path = project_path(project)
transfer_project(project, group)
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index 2ec6990313f..1fbc108697f 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -59,12 +59,12 @@ describe 'Projects > Settings > Visibility settings', :js do
end
end
- context 'as master' do
- let(:master_user) { create(:user) }
+ context 'as maintainer' do
+ let(:maintainer_user) { create(:user) }
before do
- project.add_master(master_user)
- sign_in(master_user)
+ project.add_maintainer(maintainer_user)
+ sign_in(maintainer_user)
visit edit_project_path(project)
end
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
index 8803b5222be..227bdf524fe 100644
--- a/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb
+++ b/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb
@@ -1,23 +1,23 @@
require 'rails_helper'
-feature 'Projects > Show > Developer views empty project instructions' do
+describe 'Projects > Show > Developer views empty project instructions' do
let(:project) { create(:project, :empty_repo) }
let(:developer) { create(:user) }
- background do
+ before do
project.add_developer(developer)
sign_in(developer)
end
context 'without an SSH key' do
- scenario 'defaults to HTTP' do
+ it 'defaults to HTTP' do
visit_project
expect_instructions_for('http')
end
- scenario 'switches to SSH', :js do
+ it 'switches to SSH', :js do
visit_project
select_protocol('SSH')
@@ -27,17 +27,17 @@ feature 'Projects > Show > Developer views empty project instructions' do
end
context 'with an SSH key' do
- background do
+ before do
create(:personal_key, user: developer)
end
- scenario 'defaults to SSH' do
+ it 'defaults to SSH' do
visit_project
expect_instructions_for('ssh')
end
- scenario 'switches to HTTP', :js do
+ it 'switches to HTTP', :js do
visit_project
select_protocol('HTTP')
diff --git a/spec/features/projects/show/download_buttons_spec.rb b/spec/features/projects/show/download_buttons_spec.rb
index 254affd4a94..3a2dcc5aa55 100644
--- a/spec/features/projects/show/download_buttons_spec.rb
+++ b/spec/features/projects/show/download_buttons_spec.rb
@@ -1,12 +1,12 @@
require 'spec_helper'
-feature 'Projects > Show > Download buttons' do
- given(:user) { create(:user) }
- given(:role) { :developer }
- given(:status) { 'success' }
- given(:project) { create(:project, :repository) }
+describe 'Projects > Show > Download buttons' do
+ let(:user) { create(:user) }
+ let(:role) { :developer }
+ let(:status) { 'success' }
+ let(:project) { create(:project, :repository) }
- given(:pipeline) do
+ let(:pipeline) do
create(:ci_pipeline,
project: project,
sha: project.commit.sha,
@@ -14,14 +14,14 @@ feature 'Projects > Show > Download buttons' do
status: status)
end
- given!(:build) do
+ let!(:build) do
create(:ci_build, :success, :artifacts,
pipeline: pipeline,
status: pipeline.status,
name: 'build')
end
- background do
+ before do
sign_in(user)
project.add_role(user, role)
end
@@ -32,13 +32,13 @@ feature 'Projects > Show > Download buttons' do
visit project_path(project)
end
- scenario 'shows download artifacts button' do
+ it 'shows download artifacts button' do
href = latest_succeeded_project_artifacts_path(project, "#{project.default_branch}/download", job: 'build')
expect(page).to have_link "Download '#{build.name}'", href: href
end
- scenario 'download links have download attribute' do
+ it 'download links have download attribute' do
expect(page).to have_selector('a', text: 'Download')
page.all('a', text: 'Download').each do |link|
expect(link[:download]).to eq ''
diff --git a/spec/features/projects/show/no_password_spec.rb b/spec/features/projects/show/no_password_spec.rb
index b3b3212556c..8259d482fd9 100644
--- a/spec/features/projects/show/no_password_spec.rb
+++ b/spec/features/projects/show/no_password_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'No Password Alert' do
+describe 'No Password Alert' do
let(:project) { create(:project, :repository, namespace: user.namespace) }
context 'with internal auth enabled' do
diff --git a/spec/features/projects/show/rss_spec.rb b/spec/features/projects/show/rss_spec.rb
index 52164d30c40..4d9135b9677 100644
--- a/spec/features/projects/show/rss_spec.rb
+++ b/spec/features/projects/show/rss_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Projects > Show > RSS' do
+describe 'Projects > Show > RSS' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:path) { project_path(project) }
diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb
index 31b105229be..546619e88ec 100644
--- a/spec/features/projects/show/user_manages_notifications_spec.rb
+++ b/spec/features/projects/show/user_manages_notifications_spec.rb
@@ -16,4 +16,36 @@ describe 'Projects > Show > User manages notifications', :js do
expect(page).to have_content 'On mention'
end
end
+
+ context 'custom notification settings' do
+ let(:email_events) do
+ [
+ :new_note,
+ :new_issue,
+ :reopen_issue,
+ :close_issue,
+ :reassign_issue,
+ :issue_due,
+ :new_merge_request,
+ :push_to_merge_request,
+ :reopen_merge_request,
+ :close_merge_request,
+ :reassign_merge_request,
+ :merge_merge_request,
+ :failed_pipeline,
+ :success_pipeline
+ ]
+ end
+
+ it 'shows notification settings checkbox' do
+ first('.notifications-btn').click
+ page.find('a[data-notification-level="custom"]').click
+
+ page.within('.custom-notifications-form') do
+ email_events.each do |event_name|
+ expect(page).to have_selector("input[name='notification_setting[#{event_name}]']")
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb b/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb
index aa23bef6fd8..d9d57298929 100644
--- a/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb
+++ b/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb
@@ -8,7 +8,7 @@ describe 'Projects > Show > User sees a deletion failure message' do
end
it 'shows error message if deletion for project fails' do
- project.update_attributes(delete_error: "Something went wrong", pending_delete: false)
+ project.update(delete_error: "Something went wrong", pending_delete: false)
visit project_path(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 e44361fbe26..0405e21a0d7 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
@@ -5,6 +5,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
# see spec/features/projects/files/project_owner_creates_license_file_spec.rb
# see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+ include FakeBlobHelpers
+
let(:user) { create(:user) }
describe 'empty project' do
@@ -36,9 +38,9 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
end
- describe 'as a master' do
+ describe 'as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_path(project)
@@ -136,16 +138,62 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
end
- describe 'as a master' do
+ describe 'as a maintainer' do
before do
allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(false)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
+ end
- visit project_path(project)
+ context 'Readme button' do
+ before do
+ allow(Project).to receive(:find_by_full_path)
+ .with(project.full_path, follow_redirects: true)
+ .and_return(project)
+ end
+
+ context 'when the project has a populated Readme' do
+ it 'show the "Readme" anchor' do
+ visit project_path(project)
+
+ expect(project.repository.readme).not_to be_nil
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path)
+ expect(page).to have_link('Readme', href: presenter.readme_path)
+ end
+ end
+
+ context 'when the project has an empty Readme' do
+ it 'show the "Readme" anchor' do
+ allow(project.repository).to receive(:readme).and_return(fake_blob(path: 'README.md', data: '', size: 0))
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path)
+ expect(page).to have_link('Readme', href: presenter.readme_path)
+ end
+ end
+ end
+ end
+
+ context 'when the project does not have a Readme' do
+ it 'shows the "Add Readme" button' do
+ allow(project.repository).to receive(:readme).and_return(nil)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Add Readme', href: presenter.add_readme_path)
+ end
+ end
+ end
end
it 'no "Add Changelog" button if the project already has a changelog' do
+ visit project_path(project)
+
expect(project.repository.changelog).not_to be_nil
page.within('.project-stats') do
@@ -154,6 +202,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
it 'no "Add License" button if the project already has a license' do
+ visit project_path(project)
+
expect(project.repository.license_blob).not_to be_nil
page.within('.project-stats') do
@@ -162,6 +212,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
it 'no "Add Contribution guide" button if the project already has a contribution guide' do
+ visit project_path(project)
+
expect(project.repository.contribution_guide).not_to be_nil
page.within('.project-stats') do
@@ -171,6 +223,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
describe 'GitLab CI configuration button' do
it '"Set up CI/CD" button linked to new file populated for a .gitlab-ci.yml' do
+ visit project_path(project)
+
expect(project.repository.gitlab_ci_yml).to be_nil
page.within('.project-stats') do
@@ -211,6 +265,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
describe 'Auto DevOps button' do
it '"Enable Auto DevOps" button linked to settings page' do
+ visit project_path(project)
+
page.within('.project-stats') do
expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
@@ -263,6 +319,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
describe 'Kubernetes cluster button' do
it '"Add Kubernetes cluster" button linked to clusters page' do
+ visit project_path(project)
+
page.within('.project-stats') do
expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
end
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index 2388feeb980..6d8a72dd6a3 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -16,7 +16,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
context 'when a user is authenticated' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_snippets_path(project)
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
index 004ac55b656..f3dc13fb52f 100644
--- a/spec/features/projects/snippets/show_spec.rb
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Snippets > Project snippet', :js do
let(:snippet) { create(:project_snippet, project: project, file_name: file_name, content: content) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -141,4 +141,16 @@ describe 'Projects > Snippets > Project snippet', :js do
end
end
end
+
+ it_behaves_like 'showing user status' do
+ let(:file_name) { 'ruby-style-guide.md' }
+ let(:content) { project.repository.blob_at('master', 'files/markdown/ruby-style-guide.md').data }
+
+ let(:user_with_status) { snippet.author }
+
+ subject do
+ visit project_snippet_path(project, snippet)
+ wait_for_requests
+ end
+ end
end
diff --git a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
index 01cf9740d1f..d82e350e0f7 100644
--- a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Snippets > User comments on a snippet', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_snippet_path(project, snippet))
diff --git a/spec/features/projects/snippets/user_deletes_snippet_spec.rb b/spec/features/projects/snippets/user_deletes_snippet_spec.rb
index e64837ad59e..2bd8bb9d551 100644
--- a/spec/features/projects/snippets/user_deletes_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_deletes_snippet_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Snippets > User deletes a snippet' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_snippet_path(project, snippet))
diff --git a/spec/features/projects/snippets/user_updates_snippet_spec.rb b/spec/features/projects/snippets/user_updates_snippet_spec.rb
index eaedbbf32b6..33f77d55f89 100644
--- a/spec/features/projects/snippets/user_updates_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_updates_snippet_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Snippets > User updates a snippet' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_snippet_path(project, snippet))
diff --git a/spec/features/projects/snippets/user_views_snippets_spec.rb b/spec/features/projects/snippets/user_views_snippets_spec.rb
index 376b76e0001..1243db9d9f7 100644
--- a/spec/features/projects/snippets/user_views_snippets_spec.rb
+++ b/spec/features/projects/snippets/user_views_snippets_spec.rb
@@ -8,7 +8,7 @@ describe 'Projects > Snippets > User views snippets' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_snippets_path(project))
diff --git a/spec/features/projects/sub_group_issuables_spec.rb b/spec/features/projects/sub_group_issuables_spec.rb
index eb2d3ff50a0..50e7e934cf6 100644
--- a/spec/features/projects/sub_group_issuables_spec.rb
+++ b/spec/features/projects/sub_group_issuables_spec.rb
@@ -7,7 +7,7 @@ describe 'Subgroup Issuables', :js, :nested_groups do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb
index b62498194c4..fbfd8cee7aa 100644
--- a/spec/features/projects/tags/download_buttons_spec.rb
+++ b/spec/features/projects/tags/download_buttons_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Download buttons in tags page' do
- given(:user) { create(:user) }
- given(:role) { :developer }
- given(:status) { 'success' }
- given(:tag) { 'v1.0.0' }
- given(:project) { create(:project, :repository) }
+describe 'Download buttons in tags page' do
+ let(:user) { create(:user) }
+ let(:role) { :developer }
+ let(:status) { 'success' }
+ let(:tag) { 'v1.0.0' }
+ let(:project) { create(:project, :repository) }
- given(:pipeline) do
+ let(:pipeline) do
create(:ci_pipeline,
project: project,
sha: project.commit(tag).sha,
@@ -15,14 +15,14 @@ feature 'Download buttons in tags page' do
status: status)
end
- given!(:build) do
+ let!(:build) do
create(:ci_build, :success, :artifacts,
pipeline: pipeline,
status: pipeline.status,
name: 'build')
end
- background do
+ before do
sign_in(user)
project.add_role(user, role)
end
@@ -33,7 +33,7 @@ feature 'Download buttons in tags page' do
visit project_tags_path(project)
end
- scenario 'shows download artifacts button' do
+ it 'shows download artifacts button' do
href = latest_succeeded_project_artifacts_path(project, "#{tag}/download", job: 'build')
expect(page).to have_link "Download '#{build.name}'", href: href
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index 3017048e506..9e58280b868 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-feature 'Multi-file editor new directory', :js do
+describe 'Multi-file editor new directory', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_tree_path(project, :master)
@@ -22,9 +22,7 @@ feature 'Multi-file editor new directory', :js do
end
it 'creates directory in current directory' do
- find('.add-to-tree').click
-
- click_link('New directory')
+ all('.ide-tree-actions button').last.click
page.within('.modal') do
find('.form-control').set('folder name')
@@ -32,9 +30,7 @@ feature 'Multi-file editor new directory', :js do
click_button('Create directory')
end
- find('.add-to-tree').click
-
- click_link('New file')
+ first('.ide-tree-actions button').click
page.within('.modal-dialog') do
find('.form-control').set('file name')
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index 56471c8e7aa..a04d3566a7e 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-feature 'Multi-file editor new file', :js do
+describe 'Multi-file editor new file', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_path(project)
@@ -22,9 +22,7 @@ feature 'Multi-file editor new file', :js do
end
it 'creates file in current directory' do
- find('.add-to-tree').click
-
- click_link('New file')
+ first('.ide-tree-actions button').click
page.within('.modal') do
find('.form-control').set('file name')
diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb
index f52b3cc1d86..022167d9c5f 100644
--- a/spec/features/projects/tree/rss_spec.rb
+++ b/spec/features/projects/tree/rss_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Project Tree RSS' do
+describe 'Project Tree RSS' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:path) { project_tree_path(project, :master) }
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
index c4b3fb9d171..8ae036cd29f 100644
--- a/spec/features/projects/tree/tree_show_spec.rb
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -1,42 +1,86 @@
require 'spec_helper'
-feature 'Projects tree' do
+describe 'Projects tree', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
+ end
+ it 'renders tree table without errors' do
visit project_tree_path(project, 'master')
- end
+ wait_for_requests
- it 'renders tree table' do
expect(page).to have_selector('.tree-item')
expect(page).not_to have_selector('.label-lfs', text: 'LFS')
+ expect(page).not_to have_selector('.flash-alert')
end
- context 'LFS' do
- before do
- visit project_tree_path(project, File.join('master', 'files/lfs'))
+ context 'for signed commit' do
+ it 'displays a GPG badge' do
+ visit project_tree_path(project, '33f3729a45c02fc67d00adb1b8bca394b0e761d9')
+ wait_for_requests
+
+ expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge'
+ expect(page).to have_selector '.gpg-status-box.invalid'
end
+ context 'on a directory that has not changed recently' do
+ it 'displays a GPG badge' do
+ tree_path = File.join('eee736adc74341c5d3e26cd0438bc697f26a7575', 'subdir')
+ visit project_tree_path(project, tree_path)
+ wait_for_requests
+
+ expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge'
+ expect(page).to have_selector '.gpg-status-box.invalid'
+ end
+ end
+ end
+
+ context 'LFS' do
it 'renders LFS badge on blob item' do
+ visit project_tree_path(project, File.join('master', 'files/lfs'))
+
expect(page).to have_selector('.label-lfs', text: 'LFS')
end
end
- context 'web IDE', :js do
- before do
+ context 'web IDE' do
+ it 'opens folder in IDE' do
visit project_tree_path(project, File.join('master', 'bar'))
click_link 'Web IDE'
+ wait_for_requests
find('.ide-file-list')
+ wait_for_requests
+ expect(page).to have_selector('.is-open', text: 'bar')
end
+ end
- it 'opens folder in IDE' do
- expect(page).to have_selector('.is-open', text: 'bar')
+ context 'for subgroups' do
+ let(:group) { create(:group) }
+ let(:subgroup) { create(:group, parent: group) }
+ let(:project) { create(:project, :repository, group: subgroup) }
+
+ it 'renders tree table without errors' do
+ visit project_tree_path(project, 'master')
+ wait_for_requests
+
+ expect(page).to have_selector('.tree-item')
+ expect(page).not_to have_selector('.flash-alert')
+ end
+
+ context 'for signed commit' do
+ it 'displays a GPG badge' do
+ visit project_tree_path(project, '33f3729a45c02fc67d00adb1b8bca394b0e761d9')
+ wait_for_requests
+
+ expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge'
+ expect(page).to have_selector '.gpg-status-box.invalid'
+ end
end
end
end
diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb
index 4dfc325b37e..dcf7d314f8e 100644
--- a/spec/features/projects/tree/upload_file_spec.rb
+++ b/spec/features/projects/tree/upload_file_spec.rb
@@ -1,13 +1,13 @@
require 'spec_helper'
-feature 'Multi-file editor upload file', :js do
+describe 'Multi-file editor upload file', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:txt_file) { File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt') }
let(:img_file) { File.join(Rails.root, 'spec', 'fixtures', 'dk.png') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_tree_path(project, :master)
@@ -24,14 +24,10 @@ feature 'Multi-file editor upload file', :js do
end
it 'uploads text file' do
- find('.add-to-tree').click
-
# make the field visible so capybara can use it
execute_script('document.querySelector("#file-upload").classList.remove("hidden")')
attach_file('file-upload', txt_file)
- find('.add-to-tree').click
-
expect(page).to have_selector('.multi-file-tab', text: 'doc_sample.txt')
expect(find('.blob-editor-container .lines-content')['innerText']).to have_content(File.open(txt_file, &:readline))
end
diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb
index f95469ad070..83d18996f4e 100644
--- a/spec/features/projects/user_creates_project_spec.rb
+++ b/spec/features/projects/user_creates_project_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'User creates a project', :js do
+describe 'User creates a project', :js do
let(:user) { create(:user) }
before do
diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb
index 495a010b32c..64f9a4fcd39 100644
--- a/spec/features/projects/user_uses_shortcuts_spec.rb
+++ b/spec/features/projects/user_uses_shortcuts_spec.rb
@@ -5,10 +5,12 @@ describe 'User uses shortcuts', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_path(project))
+
+ wait_for_requests
end
context 'when navigating to the Project pages' do
@@ -110,6 +112,14 @@ describe 'User uses shortcuts', :js do
end
context 'when navigating to the Operations pages' do
+ it 'redirects to the Metrics page' do
+ find('body').native.send_key('g')
+ find('body').native.send_key('l')
+
+ expect(page).to have_active_navigation('Operations')
+ expect(page).to have_active_sub_navigation('Metrics')
+ end
+
it 'redirects to the Environments page' do
find('body').native.send_key('g')
find('body').native.send_key('e')
diff --git a/spec/features/projects/user_views_empty_project_spec.rb b/spec/features/projects/user_views_empty_project_spec.rb
index 7b982301ffc..b7c0834d33a 100644
--- a/spec/features/projects/user_views_empty_project_spec.rb
+++ b/spec/features/projects/user_views_empty_project_spec.rb
@@ -15,9 +15,9 @@ describe 'User views an empty project' do
end
end
- describe 'as a master' do
+ describe 'as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'allowing push to default branch'
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index 84ec32b3fac..a48ad94e9fa 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -7,7 +7,7 @@ describe 'View on environment', :js do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when the branch has a route map' do
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index bbdd98a7623..ed5f8105487 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Projects > Wiki > User previews markdown changes', :js do
+describe 'Projects > Wiki > User previews markdown changes', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:wiki_content) do
@@ -9,11 +9,12 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
[relative link 1](../relative)
[relative link 2](./relative)
[relative link 3](./e/f/relative)
+[spaced link](title with spaces)
HEREDOC
end
- background do
- project.add_master(user)
+ before do
+ project.add_maintainer(user)
sign_in(user)
@@ -42,6 +43,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
@@ -64,6 +66,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
@@ -86,6 +89,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
end
@@ -119,6 +123,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
@@ -136,6 +141,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
@@ -153,6 +159,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
end
diff --git a/spec/features/projects/wiki/shortcuts_spec.rb b/spec/features/projects/wiki/shortcuts_spec.rb
index 6178361082e..c01be1f14ed 100644
--- a/spec/features/projects/wiki/shortcuts_spec.rb
+++ b/spec/features/projects/wiki/shortcuts_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Wiki shortcuts', :js do
+describe 'Wiki shortcuts', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: 'Home page' }) }
@@ -10,7 +10,7 @@ feature 'Wiki shortcuts', :js do
visit project_wiki_path(project, wiki_page)
end
- scenario 'Visit edit wiki page using "e" keyboard shortcut' do
+ it 'Visit edit wiki page using "e" keyboard shortcut' do
find('body').native.send_key('e')
expect(find('.wiki-page-title')).to have_content('Edit Page')
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 706894f4b32..149eeb4f9ba 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -2,16 +2,22 @@ require "spec_helper"
describe "User creates wiki page" do
let(:user) { create(:user) }
+ let(:wiki) { ProjectWiki.new(project, user) }
+ let(:project) { create(:project) }
before do
- project.add_master(user)
- sign_in(user)
+ project.add_maintainer(user)
- visit(project_wikis_path(project))
- click_link "Create your first page"
+ sign_in(user)
end
context "when wiki is empty" do
+ before do
+ visit(project_wikis_path(project))
+
+ click_link "Create your first page"
+ end
+
context "in a user namespace" do
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
@@ -165,7 +171,9 @@ describe "User creates wiki page" do
context "when wiki is not empty", :js do
before do
- create(:wiki_page, wiki: create(:project, :wiki_repo, namespace: user.namespace).wiki, attrs: { title: "home", content: "Home page" })
+ create(:wiki_page, wiki: wiki, attrs: { title: 'home', content: 'Home page' })
+
+ visit(project_wikis_path(project))
end
context "in a user namespace" do
@@ -242,7 +250,7 @@ describe "User creates wiki page" do
end
end
- it "shows the autocompletion dropdown" do
+ it "shows the emoji autocompletion dropdown" do
click_link("New page")
page.within("#modal-new-wiki") do
@@ -254,7 +262,7 @@ describe "User creates wiki page" do
page.within(".wiki-form") do
find("#wiki_content").native.send_keys("")
- fill_in(:wiki_content, with: "@")
+ fill_in(:wiki_content, with: ":")
end
expect(page).to have_selector(".atwho-view")
@@ -290,4 +298,34 @@ describe "User creates wiki page" do
end
end
end
+
+ describe 'sidebar feature' do
+ context 'when there are some existing pages' do
+ before do
+ create(:wiki_page, wiki: wiki, attrs: { title: 'home', content: 'home' })
+ create(:wiki_page, wiki: wiki, attrs: { title: 'another', content: 'another' })
+ end
+
+ it 'renders a default sidebar when there is no customized sidebar' do
+ visit(project_wikis_path(project))
+
+ expect(page).to have_content('Another')
+ expect(page).to have_content('More Pages')
+ end
+
+ context 'when there is a customized sidebar' do
+ before do
+ create(:wiki_page, wiki: wiki, attrs: { title: '_sidebar', content: 'My customized sidebar' })
+ end
+
+ it 'renders my customized sidebar instead of the default one' do
+ visit(project_wikis_path(project))
+
+ expect(page).to have_content('My customized sidebar')
+ expect(page).to have_content('More Pages')
+ expect(page).not_to have_content('Another')
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb b/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb
index 2c67cec6b67..5007794cd77 100644
--- a/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'User deletes wiki page', :js do
+describe 'User deletes wiki page', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:wiki_page) { create(:wiki_page, wiki: project.wiki) }
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
index 823399ac3c3..db97d59e918 100644
--- a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
@@ -9,7 +9,7 @@ describe 'Projects > Wiki > User views Git access wiki page' do
sign_in(user)
end
- scenario 'Visit Wiki Page Current Commit' do
+ it 'Visit Wiki Page Current Commit' do
visit project_wiki_path(project, wiki_page)
click_link 'Clone repository'
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index 272dac127dd..2840d28cf30 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -4,7 +4,7 @@ describe 'User updates wiki page' do
shared_examples 'wiki page user update' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -96,11 +96,11 @@ describe 'User updates wiki page' do
expect(find('textarea#wiki_content').value).to eq('')
end
- it 'shows the autocompletion dropdown', :js do
+ it 'shows the emoji autocompletion dropdown', :js do
click_link('Edit')
find('#wiki_content').native.send_keys('')
- fill_in(:wiki_content, with: '@')
+ fill_in(:wiki_content, with: ':')
expect(page).to have_selector('.atwho-view')
end
diff --git a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
index 92b50169476..fb0ebe22bf7 100644
--- a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
@@ -4,7 +4,7 @@ describe 'Projects > Wiki > User views wiki in project page' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
index 1de7d9a56a8..760324adacc 100644
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
@@ -11,7 +11,7 @@ describe 'User views a wiki page' do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -137,6 +137,26 @@ describe 'User views a wiki page' do
end
end
+ context 'when page has invalid content encoding' do
+ let(:content) { 'whatever'.force_encoding('ISO-8859-1') }
+
+ before do
+ allow(Gitlab::EncodingHelper).to receive(:encode!).and_return(content)
+
+ visit(project_wiki_path(project, wiki_page))
+ end
+
+ it 'does not show "Edit" button' do
+ expect(page).not_to have_selector('a.btn', text: 'Edit')
+ end
+
+ it 'shows error' do
+ page.within(:css, '.flash-notice') do
+ expect(page).to have_content('The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.')
+ end
+ end
+ end
+
it 'opens a default wiki page', :js do
visit(project_path(project))
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index cfe979a8647..56ed0c936a6 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Project' do
+describe 'Project' do
include ProjectForksHelper
describe 'creating from template' do
@@ -151,10 +151,16 @@ feature 'Project' do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
visit edit_project_path(project)
end
+ it 'focuses on the confirmation field' do
+ click_button 'Remove project'
+
+ expect(page).to have_selector '#confirm_name_input:focus'
+ end
+
it 'removes a project' do
expect { remove_with_confirm('Remove project', project.path) }.to change { Project.count }.by(-1)
expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted."
@@ -169,7 +175,7 @@ feature 'Project' do
let(:project) { create(:forked_project_with_submodules) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
visit project_path(project)
end
@@ -191,6 +197,49 @@ feature 'Project' do
expect(page.status_code).to eq(200)
end
+
+ context 'for signed commit on default branch', :js do
+ before do
+ project.change_head('33f3729a45c02fc67d00adb1b8bca394b0e761d9')
+ end
+
+ it 'displays a GPG badge' do
+ visit project_path(project)
+ wait_for_requests
+
+ expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge'
+ expect(page).to have_selector '.gpg-status-box.invalid'
+ end
+ end
+
+ context 'for subgroups', :js do
+ let(:group) { create(:group) }
+ let(:subgroup) { create(:group, parent: group) }
+ let(:project) { create(:project, :repository, group: subgroup) }
+
+ it 'renders tree table without errors' do
+ wait_for_requests
+
+ expect(page).to have_selector('.tree-item')
+ expect(page).not_to have_selector('.flash-alert')
+ end
+
+ context 'for signed commit' do
+ before do
+ repository = project.repository
+ repository.write_ref("refs/heads/#{project.default_branch}", '33f3729a45c02fc67d00adb1b8bca394b0e761d9')
+ repository.expire_branches_cache
+ end
+
+ it 'displays a GPG badge' do
+ visit project_path(project)
+ wait_for_requests
+
+ expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge'
+ expect(page).to have_selector '.gpg-status-box.invalid'
+ end
+ end
+ end
end
describe 'activity view' do
@@ -198,7 +247,7 @@ feature 'Project' do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
visit project_path(project)
end
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 39bd4af6cd0..63c38a25f4b 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Protected Branches', :js do
+describe 'Protected Branches', :js do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, :repository) }
@@ -28,9 +28,9 @@ feature 'Protected Branches', :js do
end
end
- context 'logged in as master' do
+ context 'logged in as maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb
index efccaeaff6c..c8e92cd1c07 100644
--- a/spec/features/protected_tags_spec.rb
+++ b/spec/features/protected_tags_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Protected Tags', :js do
+describe 'Protected Tags', :js do
let(:user) { create(:user, :admin) }
let(:project) { create(:project, :repository) }
diff --git a/spec/features/raven_js_spec.rb b/spec/features/raven_js_spec.rb
index a9e815eaf4f..b0923b451ee 100644
--- a/spec/features/raven_js_spec.rb
+++ b/spec/features/raven_js_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'RavenJS' do
+describe 'RavenJS' do
let(:raven_path) { '/raven.chunk.js' }
it 'should not load raven if sentry is disabled' do
diff --git a/spec/features/reportable_note/commit_spec.rb b/spec/features/reportable_note/commit_spec.rb
index 9b6864eb90f..54ebda9dcab 100644
--- a/spec/features/reportable_note/commit_spec.rb
+++ b/spec/features/reportable_note/commit_spec.rb
@@ -7,7 +7,7 @@ describe 'Reportable note on commit', :js do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/reportable_note/issue_spec.rb b/spec/features/reportable_note/issue_spec.rb
index f5a1950e48e..bce1f7a3780 100644
--- a/spec/features/reportable_note/issue_spec.rb
+++ b/spec/features/reportable_note/issue_spec.rb
@@ -7,7 +7,7 @@ describe 'Reportable note on issue', :js do
let!(:note) { create(:note_on_issue, noteable: issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
diff --git a/spec/features/reportable_note/merge_request_spec.rb b/spec/features/reportable_note/merge_request_spec.rb
index 1f69257f7ed..d00324156c4 100644
--- a/spec/features/reportable_note/merge_request_spec.rb
+++ b/spec/features/reportable_note/merge_request_spec.rb
@@ -6,7 +6,7 @@ describe 'Reportable note on merge request', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb
index 98ef50b78de..06218d9b286 100644
--- a/spec/features/reportable_note/snippets_spec.rb
+++ b/spec/features/reportable_note/snippets_spec.rb
@@ -5,7 +5,7 @@ describe 'Reportable note on snippets', :js do
let(:project) { create(:project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index fe0b03a7e00..0c6cf3dc477 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -1,20 +1,20 @@
require 'spec_helper'
-feature 'Runners' do
- given(:user) { create(:user) }
+describe 'Runners' do
+ let(:user) { create(:user) }
- background do
+ before do
sign_in(user)
end
context 'when user opens runners page' do
- given(:project) { create(:project) }
+ let(:project) { create(:project) }
- background do
- project.add_master(user)
+ before do
+ project.add_maintainer(user)
end
- scenario 'user can see a button to install runners on kubernetes clusters' do
+ it 'user can see a button to install runners on kubernetes clusters' do
visit project_runners_path(project)
expect(page).to have_link('Install Runner on Kubernetes', href: project_clusters_path(project))
@@ -22,16 +22,16 @@ feature 'Runners' do
end
context 'when a project has enabled shared_runners' do
- given(:project) { create(:project) }
+ let(:project) { create(:project) }
- background do
- project.add_master(user)
+ before do
+ project.add_maintainer(user)
end
context 'when a project_type runner is activated on the project' do
- given!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
+ let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
- scenario 'user sees the specific runner' do
+ it 'user sees the specific runner' do
visit project_runners_path(project)
within '.activated-specific-runners' do
@@ -43,7 +43,7 @@ feature 'Runners' do
expect(page).to have_content(specific_runner.platform)
end
- scenario 'user can pause and resume the specific runner' do
+ it 'user can pause and resume the specific runner' do
visit project_runners_path(project)
within '.activated-specific-runners' do
@@ -63,7 +63,7 @@ feature 'Runners' do
end
end
- scenario 'user removes an activated specific runner if this is last project for that runners' do
+ it 'user removes an activated specific runner if this is last project for that runners' do
visit project_runners_path(project)
within '.activated-specific-runners' do
@@ -73,7 +73,7 @@ feature 'Runners' do
expect(page).not_to have_content(specific_runner.display_name)
end
- scenario 'user edits the runner to be protected' do
+ it 'user edits the runner to be protected' do
visit project_runners_path(project)
within '.activated-specific-runners' do
@@ -89,11 +89,11 @@ feature 'Runners' do
end
context 'when a runner has a tag' do
- background do
+ before do
specific_runner.update(tag_list: ['tag'])
end
- scenario 'user edits runner not to run untagged jobs' do
+ it 'user edits runner not to run untagged jobs' do
visit project_runners_path(project)
within '.activated-specific-runners' do
@@ -110,9 +110,9 @@ feature 'Runners' do
end
context 'when a shared runner is activated on the project' do
- given!(:shared_runner) { create(:ci_runner, :instance) }
+ let!(:shared_runner) { create(:ci_runner, :instance) }
- scenario 'user sees CI/CD setting page' do
+ it 'user sees CI/CD setting page' do
visit project_runners_path(project)
expect(page.find('.available-shared-runners')).to have_content(shared_runner.display_name)
@@ -121,14 +121,14 @@ feature 'Runners' do
end
context 'when a specific runner exists in another project' do
- given(:another_project) { create(:project) }
- given!(:specific_runner) { create(:ci_runner, :project, projects: [another_project]) }
+ let(:another_project) { create(:project) }
+ let!(:specific_runner) { create(:ci_runner, :project, projects: [another_project]) }
- background do
- another_project.add_master(user)
+ before do
+ another_project.add_maintainer(user)
end
- scenario 'user enables and disables a specific runner' do
+ it 'user enables and disables a specific runner' do
visit project_runners_path(project)
within '.available-specific-runners' do
@@ -146,14 +146,14 @@ feature 'Runners' do
end
context 'when application settings have shared_runners_text' do
- given(:shared_runners_text) { 'custom **shared** runners description' }
- given(:shared_runners_html) { 'custom shared runners description' }
+ let(:shared_runners_text) { 'custom **shared** runners description' }
+ let(:shared_runners_html) { 'custom shared runners description' }
- background do
+ before do
stub_application_setting(shared_runners_text: shared_runners_text)
end
- scenario 'user sees shared runners description' do
+ it 'user sees shared runners description' do
visit project_runners_path(project)
expect(page.find('.shared-runners-description')).to have_content(shared_runners_html)
@@ -162,13 +162,13 @@ feature 'Runners' do
end
context 'when a project has disabled shared_runners' do
- given(:project) { create(:project, shared_runners_enabled: false) }
+ let(:project) { create(:project, shared_runners_enabled: false) }
- background do
- project.add_master(user)
+ before do
+ project.add_maintainer(user)
end
- scenario 'user enables shared runners' do
+ it 'user enables shared runners' do
visit project_runners_path(project)
click_on 'Enable shared Runners'
@@ -178,21 +178,21 @@ feature 'Runners' do
end
context 'group runners in project settings' do
- background do
- project.add_master(user)
+ before do
+ project.add_maintainer(user)
end
- given(:group) { create :group }
+ let(:group) { create :group }
context 'as project and group maintainer' do
- background do
- group.add_master(user)
+ before do
+ group.add_maintainer(user)
end
context 'project with a group but no group runner' do
- given(:project) { create :project, group: group }
+ let(:project) { create :project, group: group }
- scenario 'group runners are not available' do
+ it 'group runners are not available' do
visit project_runners_path(project)
expect(page).to have_content 'This group does not provide any group Runners yet'
@@ -205,9 +205,9 @@ feature 'Runners' do
context 'as project maintainer' do
context 'project without a group' do
- given(:project) { create :project }
+ let(:project) { create :project }
- scenario 'group runners are not available' do
+ it 'group runners are not available' do
visit project_runners_path(project)
expect(page).to have_content 'This project does not belong to a group and can therefore not make use of group Runners.'
@@ -215,10 +215,10 @@ feature 'Runners' do
end
context 'project with a group but no group runner' do
- given(:group) { create(:group) }
- given(:project) { create(:project, group: group) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
- scenario 'group runners are not available' do
+ it 'group runners are not available' do
visit project_runners_path(project)
expect(page).to have_content 'This group does not provide any group Runners yet.'
@@ -229,18 +229,18 @@ feature 'Runners' do
end
context 'project with a group and a group runner' do
- given(:group) { create(:group) }
- given(:project) { create(:project, group: group) }
- given!(:ci_runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+ let!(:ci_runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
- scenario 'group runners are available' do
+ it 'group runners are available' do
visit project_runners_path(project)
expect(page).to have_content 'Available group Runners : 1'
expect(page).to have_content 'group-runner'
end
- scenario 'group runners may be disabled for a project' do
+ it 'group runners may be disabled for a project' do
visit project_runners_path(project)
click_on 'Disable group Runners'
@@ -258,13 +258,13 @@ feature 'Runners' do
end
context 'group runners in group settings' do
- given(:group) { create(:group) }
- background do
- group.add_master(user)
+ let(:group) { create(:group) }
+ before do
+ group.add_maintainer(user)
end
context 'group with no runners' do
- scenario 'there are no runners displayed' do
+ it 'there are no runners displayed' do
visit group_settings_ci_cd_path(group)
expect(page).to have_content 'This group does not provide any group Runners yet'
@@ -274,7 +274,7 @@ feature 'Runners' do
context 'group with a runner' do
let!(:runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
- scenario 'the runner is visible' do
+ it 'the runner is visible' do
visit group_settings_ci_cd_path(group)
expect(page).not_to have_content 'This group does not provide any group Runners yet'
@@ -282,7 +282,7 @@ feature 'Runners' do
expect(page).to have_content 'group-runner'
end
- scenario 'user can pause and resume the group runner' do
+ it 'user can pause and resume the group runner' do
visit group_settings_ci_cd_path(group)
expect(page).to have_content('Pause')
@@ -299,7 +299,7 @@ feature 'Runners' do
expect(page).not_to have_content('Resume')
end
- scenario 'user can view runner details' do
+ it 'user can view runner details' do
visit group_settings_ci_cd_path(group)
expect(page).to have_content(runner.display_name)
@@ -309,7 +309,7 @@ feature 'Runners' do
expect(page).to have_content(runner.platform)
end
- scenario 'user can remove a group runner' do
+ it 'user can remove a group runner' do
visit group_settings_ci_cd_path(group)
click_on 'Remove Runner'
@@ -317,7 +317,7 @@ feature 'Runners' do
expect(page).not_to have_content(runner.display_name)
end
- scenario 'user edits the runner to be protected' do
+ it 'user edits the runner to be protected' do
visit group_settings_ci_cd_path(group)
first('.edit-runner > a').click
@@ -331,11 +331,11 @@ feature 'Runners' do
end
context 'when a runner has a tag' do
- background do
+ before do
runner.update(tag_list: ['tag'])
end
- scenario 'user edits runner not to run untagged jobs' do
+ it 'user edits runner not to run untagged jobs' do
visit group_settings_ci_cd_path(group)
first('.edit-runner > a').click
diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb
index 9e089c5a6cb..ecec2f3e043 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -6,7 +6,7 @@ describe 'User searches for code' do
context 'when signed in' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb
index d6120ff8517..4bff269f89e 100644
--- a/spec/features/search/user_searches_for_issues_spec.rb
+++ b/spec/features/search/user_searches_for_issues_spec.rb
@@ -8,7 +8,7 @@ describe 'User searches for issues', :js do
context 'when signed in' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(search_path)
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 68e2f7a857d..75d44e413cb 100644
--- a/spec/features/search/user_searches_for_merge_requests_spec.rb
+++ b/spec/features/search/user_searches_for_merge_requests_spec.rb
@@ -7,7 +7,7 @@ describe 'User searches for merge requests', :js do
let!(:merge_request2) { create(:merge_request, :simple, title: 'Bar', source_project: project, target_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(search_path)
diff --git a/spec/features/search/user_searches_for_milestones_spec.rb b/spec/features/search/user_searches_for_milestones_spec.rb
index fc6cd81eb68..7d52c4c8bcc 100644
--- a/spec/features/search/user_searches_for_milestones_spec.rb
+++ b/spec/features/search/user_searches_for_milestones_spec.rb
@@ -7,7 +7,7 @@ describe 'User searches for milestones', :js do
let!(:milestone2) { create(:milestone, title: 'Bar', project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(search_path)
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 5098fb49ee1..3ee753b7d23 100644
--- a/spec/features/search/user_searches_for_wiki_pages_spec.rb
+++ b/spec/features/search/user_searches_for_wiki_pages_spec.rb
@@ -6,7 +6,7 @@ describe 'User searches for wiki pages', :js do
let!(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'test_wiki', content: 'Some Wiki content' }) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(search_path)
diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb
index a9128104b87..af38f77c0c6 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -62,10 +62,6 @@ describe 'User uses header search field' do
end
end
- it 'contains location badge' do
- expect(page).to have_selector('.has-location-badge')
- end
-
context 'when clicking the search field', :js do
before do
page.find('#search').click
diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb
index 5067f0b0a49..51b32ba6c03 100644
--- a/spec/features/security/group/internal_access_spec.rb
+++ b/spec/features/security/group/internal_access_spec.rb
@@ -23,7 +23,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -38,7 +38,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -54,7 +54,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -69,7 +69,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -84,7 +84,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_denied_for(:master).of(group) }
+ it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
it { is_expected.to be_denied_for(:reporter).of(group) }
it { is_expected.to be_denied_for(:guest).of(group) }
diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb
index ff32413dc7e..4705cd12d23 100644
--- a/spec/features/security/group/private_access_spec.rb
+++ b/spec/features/security/group/private_access_spec.rb
@@ -23,7 +23,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -38,7 +38,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -54,7 +54,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -69,7 +69,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -84,7 +84,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_denied_for(:master).of(group) }
+ it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
it { is_expected.to be_denied_for(:reporter).of(group) }
it { is_expected.to be_denied_for(:guest).of(group) }
diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb
index 16d114fb3f7..3a53c3c2bc7 100644
--- a/spec/features/security/group/public_access_spec.rb
+++ b/spec/features/security/group/public_access_spec.rb
@@ -23,7 +23,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -38,7 +38,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -54,7 +54,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -69,7 +69,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -84,7 +84,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_denied_for(:master).of(group) }
+ it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
it { is_expected.to be_denied_for(:reporter).of(group) }
it { is_expected.to be_denied_for(:guest).of(group) }
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index a7928857b7d..001e6c10eb2 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -17,7 +17,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -31,7 +31,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -45,7 +45,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -59,7 +59,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -73,7 +73,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -87,7 +87,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -101,7 +101,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -115,7 +115,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -130,7 +130,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -144,7 +144,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -158,7 +158,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -172,7 +172,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -187,7 +187,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -201,7 +201,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -215,7 +215,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -229,7 +229,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -243,7 +243,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -262,7 +262,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -281,7 +281,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -295,7 +295,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -309,7 +309,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -324,7 +324,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -343,7 +343,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -359,7 +359,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -381,7 +381,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -397,7 +397,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -419,7 +419,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -435,7 +435,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -450,7 +450,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -464,7 +464,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -479,7 +479,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -494,7 +494,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -508,7 +508,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -530,7 +530,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index a4396b20afd..c6618355eea 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -17,7 +17,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -31,7 +31,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -45,7 +45,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -59,7 +59,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -73,7 +73,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -87,7 +87,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -101,7 +101,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -115,7 +115,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -130,7 +130,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -144,7 +144,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -158,7 +158,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -172,7 +172,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -187,7 +187,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -201,7 +201,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -215,7 +215,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -234,7 +234,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -253,7 +253,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -267,7 +267,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -281,7 +281,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -308,7 +308,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -334,7 +334,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -362,7 +362,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -395,7 +395,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -425,7 +425,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -440,7 +440,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -455,7 +455,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -469,7 +469,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -483,7 +483,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -497,7 +497,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -511,7 +511,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -533,7 +533,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index fccdeb0e5b7..3717dc13f1e 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -17,7 +17,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -31,7 +31,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -45,7 +45,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -59,7 +59,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -73,7 +73,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -87,7 +87,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -101,7 +101,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -115,7 +115,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -129,7 +129,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -144,7 +144,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -163,7 +163,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -179,7 +179,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -201,7 +201,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -217,7 +217,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -239,7 +239,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -255,7 +255,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -270,7 +270,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -284,7 +284,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -299,7 +299,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -314,7 +314,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -328,7 +328,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -344,7 +344,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -357,7 +357,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -371,7 +371,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -385,7 +385,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -400,7 +400,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -414,7 +414,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -428,7 +428,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -442,7 +442,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -456,7 +456,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -475,7 +475,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -494,7 +494,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -508,7 +508,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -530,7 +530,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).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 d7dc99c0a57..b87eb86b88b 100644
--- a/spec/features/security/project/snippet/internal_access_spec.rb
+++ b/spec/features/security/project/snippet/internal_access_spec.rb
@@ -13,7 +13,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -27,7 +27,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -42,7 +42,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -56,7 +56,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -72,7 +72,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -86,7 +86,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).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 3ec1a388185..ead91d9a5fa 100644
--- a/spec/features/security/project/snippet/private_access_spec.rb
+++ b/spec/features/security/project/snippet/private_access_spec.rb
@@ -12,7 +12,7 @@ describe "Private Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -26,7 +26,7 @@ describe "Private Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -40,7 +40,7 @@ describe "Private Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -54,7 +54,7 @@ describe "Private Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).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 39b104bfe27..9bab3a474b8 100644
--- a/spec/features/security/project/snippet/public_access_spec.rb
+++ b/spec/features/security/project/snippet/public_access_spec.rb
@@ -14,7 +14,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -28,7 +28,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -43,7 +43,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -57,7 +57,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -71,7 +71,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -87,7 +87,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -101,7 +101,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -115,7 +115,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index db141ef7096..ef0e55a1468 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -1,19 +1,20 @@
require 'spec_helper'
describe 'GPG signed commits', :js do
+ set(:ref) { :'2d1096e3a0ecf1d2baf6dee036cc80775d4940ba' }
let(:project) { create(:project, :repository) }
it 'changes from unverified to verified when the user changes his email to match the gpg key' do
user = create :user, email: 'unrelated.user@example.org'
- project.add_master(user)
+ project.add_maintainer(user)
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user
end
sign_in(user)
- visit project_commits_path(project, :'signed-commits')
+ visit project_commits_path(project, ref)
within '#commits-list' do
expect(page).to have_content 'Unverified'
@@ -21,12 +22,12 @@ describe 'GPG signed commits', :js do
end
# user changes his email which makes the gpg key verified
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
user.skip_reconfirmation!
- user.update_attributes!(email: GpgHelpers::User1.emails.first)
+ user.update!(email: GpgHelpers::User1.emails.first)
end
- visit project_commits_path(project, :'signed-commits')
+ visit project_commits_path(project, ref)
within '#commits-list' do
expect(page).to have_content 'Unverified'
@@ -36,11 +37,11 @@ describe 'GPG signed commits', :js do
it 'changes from unverified to verified when the user adds the missing gpg key' do
user = create :user, email: GpgHelpers::User1.emails.first
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
- visit project_commits_path(project, :'signed-commits')
+ visit project_commits_path(project, ref)
within '#commits-list' do
expect(page).to have_content 'Unverified'
@@ -48,11 +49,11 @@ describe 'GPG signed commits', :js do
end
# user adds the gpg key which makes the signature valid
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user
end
- visit project_commits_path(project, :'signed-commits')
+ visit project_commits_path(project, ref)
within '#commits-list' do
expect(page).to have_content 'Unverified'
@@ -66,7 +67,7 @@ describe 'GPG signed commits', :js do
end
let(:user_1_key) do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user_1
end
end
@@ -79,20 +80,20 @@ describe 'GPG signed commits', :js do
end
let(:user_2_key) do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User2.public_key, user: user_2
end
end
before do
user = create :user
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
it 'unverified signature' do
- visit project_commits_path(project, :'signed-commits')
+ visit project_commits_path(project, ref)
within(find('.commit', text: 'signed commit by bette cartwright')) do
click_on 'Unverified'
@@ -107,7 +108,7 @@ describe 'GPG signed commits', :js do
it 'unverified signature: user email does not match the committer email, but is the same user' do
user_2_key
- visit project_commits_path(project, :'signed-commits')
+ visit project_commits_path(project, ref)
within(find('.commit', text: 'signed and authored commit by bette cartwright, different email')) do
click_on 'Unverified'
@@ -124,7 +125,7 @@ describe 'GPG signed commits', :js do
it 'unverified signature: user email does not match the committer email' do
user_2_key
- visit project_commits_path(project, :'signed-commits')
+ visit project_commits_path(project, ref)
within(find('.commit', text: 'signed commit by bette cartwright')) do
click_on 'Unverified'
@@ -141,7 +142,7 @@ describe 'GPG signed commits', :js do
it 'verified and the gpg user has a gitlab profile' do
user_1_key
- visit project_commits_path(project, :'signed-commits')
+ visit project_commits_path(project, ref)
within(find('.commit', text: 'signed and authored commit by nannie bernhard')) do
click_on 'Verified'
@@ -158,7 +159,7 @@ describe 'GPG signed commits', :js do
it "verified and the gpg user's profile doesn't exist anymore" do
user_1_key
- visit project_commits_path(project, :'signed-commits')
+ visit project_commits_path(project, ref)
# wait for the signature to get generated
within(find('.commit', text: 'signed and authored commit by nannie bernhard')) do
diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb
index 835fd90adc8..c08f25875f8 100644
--- a/spec/features/snippets/explore_spec.rb
+++ b/spec/features/snippets/explore_spec.rb
@@ -1,11 +1,11 @@
require 'rails_helper'
-feature 'Explore Snippets' do
+describe 'Explore Snippets' do
let!(:public_snippet) { create(:personal_snippet, :public) }
let!(:internal_snippet) { create(:personal_snippet, :internal) }
let!(:private_snippet) { create(:personal_snippet, :private) }
- scenario 'User should see snippets that are not private' do
+ it 'User should see snippets that are not private' do
sign_in create(:user)
visit explore_snippets_path
@@ -14,7 +14,7 @@ feature 'Explore Snippets' do
expect(page).not_to have_content(private_snippet.title)
end
- scenario 'External user should see only public snippets' do
+ it 'External user should see only public snippets' do
sign_in create(:user, :external)
visit explore_snippets_path
@@ -23,7 +23,7 @@ feature 'Explore Snippets' do
expect(page).not_to have_content(private_snippet.title)
end
- scenario 'Not authenticated user should see only public snippets' do
+ it 'Not authenticated user should see only public snippets' do
visit explore_snippets_path
expect(page).to have_content(public_snippet.title)
diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb
index 3a2768c424f..f6215b481dc 100644
--- a/spec/features/snippets/internal_snippet_spec.rb
+++ b/spec/features/snippets/internal_snippet_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Internal Snippets', :js do
+describe 'Internal Snippets', :js do
let(:internal_snippet) { create(:personal_snippet, :internal) }
describe 'normal user' do
@@ -8,13 +8,13 @@ feature 'Internal Snippets', :js do
sign_in(create(:user))
end
- scenario 'sees internal snippets' do
+ it 'sees internal snippets' do
visit snippet_path(internal_snippet)
expect(page).to have_content(internal_snippet.content)
end
- scenario 'sees raw internal snippets' do
+ it 'sees raw internal snippets' do
visit raw_snippet_path(internal_snippet)
expect(page).to have_content(internal_snippet.content)
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index 269351e55c9..1442e011d52 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -16,6 +16,8 @@ describe 'Comments on personal snippets', :js do
before do
sign_in user
visit snippet_path(snippet)
+
+ wait_for_requests
end
subject { page }
@@ -42,6 +44,15 @@ describe 'Comments on personal snippets', :js do
expect(page).to have_selector('.note-emoji-button')
end
end
+
+ it 'shows the status of a note author' do
+ status = create(:user_status, user: user)
+ visit snippet_path(snippet)
+
+ within("#note_#{snippet_notes[0].id}") do
+ expect(page).to show_user_status(status)
+ end
+ end
end
context 'when submitting a note' do
diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb
index bdeeca7187e..71c72b98fad 100644
--- a/spec/features/snippets/public_snippets_spec.rb
+++ b/spec/features/snippets/public_snippets_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
-feature 'Public Snippets', :js do
- scenario 'Unauthenticated user should see public snippets' do
+describe 'Public Snippets', :js do
+ it 'Unauthenticated user should see public snippets' do
public_snippet = create(:personal_snippet, :public)
visit snippet_path(public_snippet)
@@ -10,7 +10,7 @@ feature 'Public Snippets', :js do
expect(page).to have_content(public_snippet.content)
end
- scenario 'Unauthenticated user should see raw public snippets' do
+ it 'Unauthenticated user should see raw public snippets' do
public_snippet = create(:personal_snippet, :public)
visit raw_snippet_path(public_snippet)
diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb
index cd66a2cb20c..c137b0bcd96 100644
--- a/spec/features/snippets/search_snippets_spec.rb
+++ b/spec/features/snippets/search_snippets_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
-feature 'Search Snippets' do
- scenario 'User searches for snippets by title' do
+describe 'Search Snippets' 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')
@@ -19,7 +19,7 @@ feature 'Search Snippets' do
expect(page).to have_link(private_snippet.title)
end
- scenario 'User searches for snippet contents' do
+ it 'User searches for snippet contents' do
create(:personal_snippet,
:public,
title: 'Many lined snippet',
diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb
index 5a48f5774ca..3fe0b60b18f 100644
--- a/spec/features/snippets/show_spec.rb
+++ b/spec/features/snippets/show_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Snippet', :js do
+describe 'Snippet', :js do
let(:project) { create(:project, :repository) }
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content) }
@@ -68,6 +68,26 @@ feature 'Snippet', :js do
end
end
+ context 'with cached Redcarpet html' do
+ let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION) }
+ let(:file_name) { 'test.md' }
+ let(:content) { "1. one\n - sublist\n" }
+
+ it 'renders correctly' do
+ expect(page).to have_xpath("//ol//li//ul")
+ end
+ end
+
+ context 'with cached CommonMark html' do
+ let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
+ let(:file_name) { 'test.md' }
+ let(:content) { "1. one\n - sublist\n" }
+
+ it 'renders correctly' do
+ expect(page).not_to have_xpath("//ol//li//ul")
+ end
+ end
+
context 'switching to the simple viewer' do
before do
find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
@@ -135,4 +155,12 @@ feature 'Snippet', :js do
end
end
end
+
+ it_behaves_like 'showing user status' do
+ let(:file_name) { 'popen.rb' }
+ let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data }
+ let(:user_with_status) { snippet.author }
+
+ subject { visit snippet_path(snippet) }
+ end
end
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index 941765b7578..879c46d7c4e 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'User creates snippet', :js do
+describe 'User creates snippet', :js do
include DropzoneHelper
let(:user) { create(:user) }
@@ -18,7 +18,7 @@ feature 'User creates snippet', :js do
end
end
- scenario 'Authenticated user creates a snippet' do
+ it 'Authenticated user creates a snippet' do
fill_form
click_button('Create snippet')
@@ -32,7 +32,7 @@ feature 'User creates snippet', :js do
expect(page).to have_content('Hello World!')
end
- scenario 'previews a snippet with file' do
+ it 'previews a snippet with file' do
fill_in 'personal_snippet_description', with: 'My Snippet'
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
find('.js-md-preview-button').click
@@ -48,7 +48,7 @@ feature 'User creates snippet', :js do
end
end
- scenario 'uploads a file when dragging into textarea' do
+ it 'uploads a file when dragging into textarea' do
fill_form
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
@@ -65,7 +65,7 @@ feature 'User creates snippet', :js do
expect(reqs.first.status_code).to eq(200)
end
- scenario 'validation fails for the first time' do
+ it 'validation fails for the first time' do
fill_in 'personal_snippet_title', with: 'My Snippet Title'
click_button('Create snippet')
@@ -90,7 +90,7 @@ feature 'User creates snippet', :js do
expect(reqs.first.status_code).to eq(200)
end
- scenario 'Authenticated user creates a snippet with + in filename' do
+ it 'Authenticated user creates a snippet with + in filename' do
fill_in 'personal_snippet_title', with: 'My Snippet Title'
page.within('.file-editor') do
find(:xpath, "//input[@id='personal_snippet_file_name']").set 'snippet+file+name'
diff --git a/spec/features/snippets/user_deletes_snippet_spec.rb b/spec/features/snippets/user_deletes_snippet_spec.rb
index ae5b883c477..7bdccacb0fa 100644
--- a/spec/features/snippets/user_deletes_snippet_spec.rb
+++ b/spec/features/snippets/user_deletes_snippet_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'User deletes snippet' do
+describe 'User deletes snippet' do
let(:user) { create(:user) }
let(:content) { 'puts "test"' }
let(:snippet) { create(:personal_snippet, :public, content: content, author: user) }
diff --git a/spec/features/snippets/user_edits_snippet_spec.rb b/spec/features/snippets/user_edits_snippet_spec.rb
index 71de6b6bd1c..77f62990158 100644
--- a/spec/features/snippets/user_edits_snippet_spec.rb
+++ b/spec/features/snippets/user_edits_snippet_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'User edits snippet', :js do
+describe 'User edits snippet', :js do
include DropzoneHelper
let(:file_name) { 'test.rb' }
diff --git a/spec/features/snippets/user_snippets_spec.rb b/spec/features/snippets/user_snippets_spec.rb
index 7bc27486787..e3065a899cc 100644
--- a/spec/features/snippets/user_snippets_spec.rb
+++ b/spec/features/snippets/user_snippets_spec.rb
@@ -1,23 +1,23 @@
require 'rails_helper'
-feature 'User Snippets' do
+describe 'User Snippets' do
let(:author) { create(:user) }
let!(:public_snippet) { create(:personal_snippet, :public, author: author, title: "This is a public snippet") }
let!(:internal_snippet) { create(:personal_snippet, :internal, author: author, title: "This is an internal snippet") }
let!(:private_snippet) { create(:personal_snippet, :private, author: author, title: "This is a private snippet") }
- background do
+ before do
sign_in author
visit dashboard_snippets_path
end
- scenario 'View all of my snippets' do
+ it 'View all of my snippets' do
expect(page).to have_content(public_snippet.title)
expect(page).to have_content(internal_snippet.title)
expect(page).to have_content(private_snippet.title)
end
- scenario 'View my public snippets' do
+ it 'View my public snippets' do
page.within('.snippet-scope-menu') do
click_link "Public"
end
@@ -27,7 +27,7 @@ feature 'User Snippets' do
expect(page).not_to have_content(private_snippet.title)
end
- scenario 'View my internal snippets' do
+ it 'View my internal snippets' do
page.within('.snippet-scope-menu') do
click_link "Internal"
end
@@ -37,7 +37,7 @@ feature 'User Snippets' do
expect(page).not_to have_content(private_snippet.title)
end
- scenario '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/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb
index 8a8f6933fa5..db2970f3340 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/master_creates_tag_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-feature 'Master creates tag' do
+describe 'Maintainer creates tag' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -14,25 +14,25 @@ feature 'Master creates tag' do
visit project_tags_path(project)
end
- scenario 'with an invalid name displays an error' do
+ it 'with an invalid name displays an error' do
create_tag_in_form(tag: 'v 1.0', ref: 'master')
expect(page).to have_content 'Tag name invalid'
end
- scenario 'with an invalid reference displays an error' do
+ it 'with an invalid reference displays an error' do
create_tag_in_form(tag: 'v2.0', ref: 'foo')
expect(page).to have_content 'Target foo is invalid'
end
- scenario 'that already exists displays an error' do
+ it 'that already exists displays an error' do
create_tag_in_form(tag: 'v1.1.0', ref: 'master')
expect(page).to have_content 'Tag v1.1.0 already exists'
end
- scenario 'with multiline message displays the message in a <pre> block' do
+ it 'with multiline message displays the message in a <pre> block' do
create_tag_in_form(tag: 'v3.0', ref: 'master', message: "Awesome tag message\n\n- hello\n- world")
expect(current_path).to eq(
@@ -43,7 +43,7 @@ feature 'Master creates tag' do
end
end
- scenario 'with multiline release notes parses the release note as Markdown' do
+ it 'with multiline release notes parses the release note as Markdown' do
create_tag_in_form(tag: 'v4.0', ref: 'master', desc: "Awesome release notes\n\n- hello\n- world")
expect(current_path).to eq(
@@ -55,7 +55,7 @@ feature 'Master creates tag' do
end
end
- scenario 'opens dropdown for ref', :js do
+ it 'opens dropdown for ref', :js do
click_link 'New tag'
ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
page.within ref_row do
@@ -75,9 +75,9 @@ feature 'Master creates tag' do
visit new_project_tag_path(project)
end
- it 'description has autocomplete', :js do
+ it 'description has emoji autocomplete', :js do
find('#release_description').native.send_keys('')
- fill_in 'release_description', with: '@'
+ fill_in 'release_description', with: ':'
expect(page).to have_selector('.atwho-view')
end
diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb
index 9981bfa4609..8d567e925ef 100644
--- a/spec/features/tags/master_deletes_tag_spec.rb
+++ b/spec/features/tags/master_deletes_tag_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
-feature 'Master deletes tag' do
+describe 'Maintainer deletes tag' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_tags_path(project)
end
context 'from the tags list page', :js do
- scenario 'deletes the tag' do
+ it 'deletes the tag' do
expect(page).to have_content 'v1.1.0'
delete_first_tag
@@ -21,7 +21,7 @@ feature 'Master deletes tag' do
end
context 'from a specific tag page' do
- scenario 'deletes the tag' do
+ it 'deletes the tag' do
click_on 'v1.0.0'
expect(current_path).to eq(
project_tag_path(project, 'v1.0.0'))
@@ -35,30 +35,15 @@ feature 'Master deletes tag' do
end
context 'when pre-receive hook fails', :js do
- context 'when Gitaly operation_user_delete_tag feature is enabled' do
- before do
- allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag)
- .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
- end
-
- scenario 'shows the error message' do
- delete_first_tag
-
- expect(page).to have_content('Do not delete tags')
- end
+ before do
+ allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag)
+ .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
end
- context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do
- before do
- allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute)
- .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
- end
-
- scenario 'shows the error message' do
- delete_first_tag
+ it 'shows the error message' do
+ delete_first_tag
- expect(page).to have_content('Do not delete tags')
- end
+ expect(page).to have_content('Do not delete tags')
end
end
diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb
index 1c370a99b13..d8b5b3c4cc4 100644
--- a/spec/features/tags/master_updates_tag_spec.rb
+++ b/spec/features/tags/master_updates_tag_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
-feature 'Master updates tag' do
+describe 'Maintainer updates tag' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_tags_path(project)
end
context 'from the tags list page' do
- scenario 'updates the release notes' do
+ it 'updates the release notes' do
page.within(first('.content-list .controls')) do
click_link 'Edit release notes'
end
@@ -25,20 +25,20 @@ feature 'Master updates tag' do
expect(page).to have_content 'Awesome release notes'
end
- scenario 'description has autocomplete', :js do
+ it 'description has emoji autocomplete', :js do
page.within(first('.content-list .controls')) do
click_link 'Edit release notes'
end
find('#release_description').native.send_keys('')
- fill_in 'release_description', with: '@'
+ fill_in 'release_description', with: ':'
expect(page).to have_selector('.atwho-view')
end
end
context 'from a specific tag page' do
- scenario 'updates the release notes' do
+ it 'updates the release notes' do
click_on 'v1.1.0'
click_link 'Edit release notes'
fill_in 'release_description', with: 'Awesome release notes'
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb
index b625e7065cc..3f4fe549f3e 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/master_views_tags_spec.rb
@@ -1,10 +1,10 @@
require 'spec_helper'
-feature 'Master views tags' do
+describe 'Maintainer views tags' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -19,7 +19,7 @@ feature 'Master views tags' do
visit project_tags_path(project)
end
- scenario 'displays a specific message' do
+ it 'displays a specific message' do
expect(page).to have_content 'Repository has no tags yet.'
end
end
@@ -32,7 +32,7 @@ feature 'Master views tags' do
visit project_tags_path(project)
end
- scenario 'avoids a N+1 query in branches index' do
+ it 'avoids a N+1 query in branches index' do
control_count = ActiveRecord::QueryRecorder.new { visit project_tags_path(project) }.count
%w(one two three four five).each { |tag| repository.add_tag(user, tag, 'master', 'foo') }
@@ -40,11 +40,11 @@ feature 'Master views tags' do
expect { visit project_tags_path(project) }.not_to exceed_query_limit(control_count)
end
- scenario 'views the tags list page' do
+ it 'views the tags list page' do
expect(page).to have_content 'v1.0.0'
end
- scenario 'views a specific tag page' do
+ it 'views a specific tag page' do
click_on 'v1.0.0'
expect(current_path).to eq(
@@ -54,7 +54,7 @@ feature 'Master views tags' do
end
describe 'links on the tag page' do
- scenario 'has a button to browse files' do
+ it 'has a button to browse files' do
click_on 'v1.0.0'
expect(current_path).to eq(
@@ -66,7 +66,7 @@ feature 'Master views tags' do
project_tree_path(project, 'v1.0.0'))
end
- scenario 'has a button to browse commits' do
+ it 'has a button to browse commits' do
click_on 'v1.0.0'
expect(current_path).to eq(
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index f37d8998045..9c9127980a1 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Task Lists' do
+describe 'Task Lists' do
include Warden::Test::Helpers
let(:project) { create(:project, :repository) }
@@ -65,7 +65,7 @@ feature 'Task Lists' do
before do
Warden.test_mode!
- project.add_master(user)
+ project.add_maintainer(user)
project.add_guest(user2)
login_as(user)
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index 6be2606fd0d..919859c145a 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Triggers', :js do
+describe 'Triggers', :js do
let(:trigger_title) { 'trigger desc' }
let(:user) { create(:user) }
let(:user2) { create(:user) }
@@ -10,15 +10,15 @@ feature 'Triggers', :js do
sign_in(user)
@project = create(:project)
- @project.add_master(user)
- @project.add_master(user2)
+ @project.add_maintainer(user)
+ @project.add_maintainer(user2)
@project.add_guest(guest_user)
visit project_settings_ci_cd_path(@project)
end
describe 'create trigger workflow' do
- scenario 'prevents adding new trigger with no description' do
+ it 'prevents adding new trigger with no description' do
fill_in 'trigger_description', with: ''
click_button 'Add trigger'
@@ -26,7 +26,7 @@ feature 'Triggers', :js do
expect(page.find('form.gl-show-field-errors .gl-field-error')).to be_visible
end
- scenario 'adds new trigger with description' do
+ it 'adds new trigger with description' do
fill_in 'trigger_description', with: 'trigger desc'
click_button 'Add trigger'
@@ -40,7 +40,7 @@ feature 'Triggers', :js do
describe 'edit trigger workflow' do
let(:new_trigger_title) { 'new trigger' }
- scenario 'click on edit trigger opens edit trigger page' do
+ it 'click on edit trigger opens edit trigger page' do
create(:ci_trigger, owner: user, project: @project, description: trigger_title)
visit project_settings_ci_cd_path(@project)
@@ -49,7 +49,7 @@ feature 'Triggers', :js do
expect(page.find('#trigger_description').value).to have_content 'trigger desc'
end
- scenario 'edit trigger and save' do
+ it 'edit trigger and save' do
create(:ci_trigger, owner: user, project: @project, description: trigger_title)
visit project_settings_ci_cd_path(@project)
@@ -64,7 +64,7 @@ feature 'Triggers', :js do
expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
end
- scenario 'edit "legacy" trigger and save' do
+ it 'edit "legacy" trigger and save' do
# Create new trigger without owner association, i.e. Legacy trigger
create(:ci_trigger, owner: nil, project: @project)
visit project_settings_ci_cd_path(@project)
@@ -87,12 +87,12 @@ feature 'Triggers', :js do
visit project_settings_ci_cd_path(@project)
end
- scenario 'button "Take ownership" has correct alert' do
+ it 'button "Take ownership" has correct alert' do
expected_alert = 'By taking ownership you will bind this trigger to your user account. With this the trigger will have access to all your projects as if it was you. Are you sure?'
expect(page.find('a.btn-trigger-take-ownership')['data-confirm']).to eq expected_alert
end
- scenario 'take trigger ownership' do
+ it 'take trigger ownership' do
# See if "Take ownership" on trigger works post trigger creation
page.accept_confirm do
first(:link, "Take ownership").send_keys(:return)
@@ -110,12 +110,12 @@ feature 'Triggers', :js do
visit project_settings_ci_cd_path(@project)
end
- scenario 'button "Revoke" has correct alert' do
+ it 'button "Revoke" has correct alert' do
expected_alert = 'By revoking a trigger you will break any processes making use of it. Are you sure?'
expect(page.find('a.btn-trigger-revoke')['data-confirm']).to eq expected_alert
end
- scenario 'revoke trigger' do
+ it 'revoke trigger' do
# See if "Revoke" on trigger works post trigger creation
page.accept_confirm do
find('a.btn-trigger-revoke').send_keys(:return)
@@ -127,11 +127,11 @@ feature 'Triggers', :js do
end
describe 'show triggers workflow' do
- scenario 'contains trigger description placeholder' do
+ it 'contains trigger description placeholder' do
expect(page.find('#trigger_description')['placeholder']).to eq 'Trigger description'
end
- scenario 'show "legacy" badge for legacy trigger' do
+ it 'show "legacy" badge for legacy trigger' do
create(:ci_trigger, owner: nil, project: @project)
visit project_settings_ci_cd_path(@project)
@@ -140,7 +140,7 @@ feature 'Triggers', :js do
expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]')
end
- scenario 'show "invalid" badge for trigger with owner having insufficient permissions' do
+ it 'show "invalid" badge for trigger with owner having insufficient permissions' do
create(:ci_trigger, owner: guest_user, project: @project, description: trigger_title)
visit project_settings_ci_cd_path(@project)
@@ -149,7 +149,7 @@ feature 'Triggers', :js do
expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
end
- scenario 'do not show "Edit" or full token for not owned trigger' do
+ it 'do not show "Edit" or full token for not owned trigger' do
# Create trigger with user different from current_user
create(:ci_trigger, owner: user2, project: @project, description: trigger_title)
visit project_settings_ci_cd_path(@project)
@@ -163,7 +163,7 @@ feature 'Triggers', :js do
expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
end
- scenario 'show "Edit" and full token for owned trigger' do
+ it 'show "Edit" and full token for owned trigger' do
create(:ci_trigger, owner: user, project: @project, description: trigger_title)
visit project_settings_ci_cd_path(@project)
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index fb65b570dd6..f245c1ebbd9 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
+describe 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
def manage_two_factor_authentication
click_on 'Manage two-factor authentication'
expect(page).to have_content("Setup new U2F device")
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index c8db82a562f..a07edc42eae 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
-feature 'User uploads avatar to group' do
- scenario 'they see the new avatar' do
+describe 'User uploads avatar to group' do
+ it 'they see the new avatar' do
user = create(:user)
group = create(:group)
group.add_owner(user)
diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
index 766bb4f09cd..48f8b8bf77e 100644
--- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'User uploads avatar to profile' do
+describe 'User uploads avatar to profile' do
let!(:user) { create(:user) }
let(:avatar_file_path) { Rails.root.join('spec', 'fixtures', 'dk.png') }
@@ -9,19 +9,19 @@ feature 'User uploads avatar to profile' do
visit profile_path
end
- scenario 'they see their new avatar on their profile' do
+ it 'they see their new avatar on their profile' do
attach_file('user_avatar', avatar_file_path, visible: false)
click_button 'Update profile settings'
visit user_path(user)
- expect(page).to have_selector(%Q(img[data-src$="/uploads/-/system/user/avatar/#{user.id}/dk.png"]))
+ expect(page).to have_selector(%Q(img[data-src$="/uploads/-/system/user/avatar/#{user.id}/dk.png?width=90"]))
# Cheating here to verify something that isn't user-facing, but is important
expect(user.reload.avatar.file).to exist
end
- scenario 'their new avatar is immediately visible in the header', :js do
+ it 'their new avatar is immediately visible in the header', :js do
find('.js-user-avatar-input', visible: false).set(avatar_file_path)
click_button 'Set new profile picture'
diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb
index 972c10aaf23..24a00c86b0a 100644
--- a/spec/features/uploads/user_uploads_file_to_note_spec.rb
+++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'User uploads file to note' do
+describe 'User uploads file to note' do
include DropzoneHelper
let(:user) { create(:user) }
@@ -71,7 +71,7 @@ feature 'User uploads file to note' do
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
end
- scenario 'they see the attached file', :js do
+ it 'they see the attached file', :js do
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')])
click_button 'Comment'
wait_for_requests
diff --git a/spec/features/user_sees_revert_modal_spec.rb b/spec/features/user_sees_revert_modal_spec.rb
new file mode 100644
index 00000000000..3b48ea4786d
--- /dev/null
+++ b/spec/features/user_sees_revert_modal_spec.rb
@@ -0,0 +1,28 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees revert modal', :js do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ sign_in(user)
+ visit(project_merge_request_path(project, merge_request))
+ click_button('Merge')
+
+ wait_for_requests
+
+ visit(merge_request_path(merge_request))
+ click_link('Revert')
+ end
+
+ it 'shows the revert modal' do
+ expect(page).to have_content('Revert this merge request')
+ end
+
+ it 'closes the revert modal with escape keypress' do
+ find('#modal-revert-commit').send_keys(:escape)
+
+ expect(page).not_to have_content('Revert this merge request')
+ end
+end
diff --git a/spec/features/users/active_sessions_spec.rb b/spec/features/users/active_sessions_spec.rb
index 631d7e3bced..25349b5d036 100644
--- a/spec/features/users/active_sessions_spec.rb
+++ b/spec/features/users/active_sessions_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
-feature 'Active user sessions', :clean_gitlab_redis_shared_state do
- scenario 'Successful login adds a new active user login' do
+describe 'Active user sessions', :clean_gitlab_redis_shared_state 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)
@@ -24,7 +24,7 @@ feature 'Active user sessions', :clean_gitlab_redis_shared_state do
end
end
- scenario 'Successful login cleans up obsolete entries' do
+ it 'Successful login cleans up obsolete entries' do
user = create(:user)
Gitlab::Redis::SharedState.with do |redis|
@@ -38,7 +38,7 @@ feature 'Active user sessions', :clean_gitlab_redis_shared_state do
end
end
- scenario '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)
@@ -54,7 +54,7 @@ feature 'Active user sessions', :clean_gitlab_redis_shared_state do
end
end
- scenario '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/add_email_to_existing_account.rb b/spec/features/users/add_email_to_existing_account.rb
new file mode 100644
index 00000000000..4355f769429
--- /dev/null
+++ b/spec/features/users/add_email_to_existing_account.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe 'AdditionalEmailToExistingAccount' do
+ describe 'add secondary email associated with account' do
+ let(:user) { create(:user) }
+
+ it 'verifies confirmation of additional email' do
+ sign_in(user)
+
+ email = create(:email, user: user)
+ visit email_confirmation_path(confirmation_token: email.confirmation_token)
+ expect(page).to have_content 'Your email address has been successfully confirmed.'
+ end
+ end
+end
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 24a2c89f50b..44758f862a8 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -1,30 +1,42 @@
require 'spec_helper'
-feature 'Login' do
+describe 'Login' do
include TermsHelper
- scenario 'Successful user signin invalidates password reset token' do
- user = create(:user)
+ before do
+ stub_authentication_activity_metrics(debug: true)
+ end
+
+ describe 'password reset token after successful sign in' do
+ it 'invalidates password reset token' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ user = create(:user)
- expect(user.reset_password_token).to be_nil
+ expect(user.reset_password_token).to be_nil
- visit new_user_password_path
- fill_in 'user_email', with: user.email
- click_button 'Reset password'
+ visit new_user_password_path
+ fill_in 'user_email', with: user.email
+ click_button 'Reset password'
- user.reload
- expect(user.reset_password_token).not_to be_nil
+ user.reload
+ expect(user.reset_password_token).not_to be_nil
- find('a[href="#login-pane"]').click
- gitlab_sign_in(user)
- expect(current_path).to eq root_path
+ find('a[href="#login-pane"]').click
+ gitlab_sign_in(user)
+ expect(current_path).to eq root_path
- user.reload
- expect(user.reset_password_token).to be_nil
+ user.reload
+ expect(user.reset_password_token).to be_nil
+ end
end
describe 'initial login after setup' do
it 'allows the initial admin to create a password' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
# This behavior is dependent on there only being one user
User.delete_all
@@ -56,6 +68,11 @@ feature 'Login' do
describe 'with a blocked account' do
it 'prevents the user from logging in' do
+ expect(authentication_metrics)
+ .to increment(:user_blocked_counter)
+ .and increment(:user_unauthenticated_counter)
+ .and increment(:user_session_destroyed_counter).twice
+
user = create(:user, :blocked)
gitlab_sign_in(user)
@@ -64,6 +81,11 @@ feature 'Login' do
end
it 'does not update Devise trackable attributes', :clean_gitlab_redis_shared_state do
+ expect(authentication_metrics)
+ .to increment(:user_blocked_counter)
+ .and increment(:user_unauthenticated_counter)
+ .and increment(:user_session_destroyed_counter).twice
+
user = create(:user, :blocked)
expect { gitlab_sign_in(user) }.not_to change { user.reload.sign_in_count }
@@ -72,13 +94,22 @@ feature 'Login' do
describe 'with the ghost user' do
it 'disallows login' do
+ expect(authentication_metrics)
+ .to increment(:user_unauthenticated_counter)
+ .and increment(:user_password_invalid_counter)
+
gitlab_sign_in(User.ghost)
expect(page).to have_content('Invalid Login or password.')
end
it 'does not update Devise trackable attributes', :clean_gitlab_redis_shared_state do
- expect { gitlab_sign_in(User.ghost) }.not_to change { User.ghost.reload.sign_in_count }
+ expect(authentication_metrics)
+ .to increment(:user_unauthenticated_counter)
+ .and increment(:user_password_invalid_counter)
+
+ expect { gitlab_sign_in(User.ghost) }
+ .not_to change { User.ghost.reload.sign_in_count }
end
end
@@ -93,17 +124,30 @@ feature 'Login' do
before do
gitlab_sign_in(user, remember: true)
+
expect(page).to have_content('Two-Factor Authentication')
end
it 'does not show a "You are already signed in." error message' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_two_factor_authenticated_counter)
+
enter_code(user.current_otp)
+
expect(page).not_to have_content('You are already signed in.')
end
context 'using one-time code' do
it 'allows login with valid code' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_two_factor_authenticated_counter)
+
enter_code(user.current_otp)
+
expect(current_path).to eq root_path
end
@@ -114,11 +158,20 @@ feature 'Login' do
end
it 'blocks login with invalid code' do
+ # TODO invalid 2FA code does not generate any events
+ # See gitlab-org/gitlab-ce#49785
+
enter_code('foo')
+
expect(page).to have_content('Invalid two-factor code')
end
it 'allows login with invalid code, then valid code' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_two_factor_authenticated_counter)
+
enter_code('foo')
expect(page).to have_content('Invalid two-factor code')
@@ -139,16 +192,33 @@ feature 'Login' do
context 'with valid code' do
it 'allows login' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_two_factor_authenticated_counter)
+
enter_code(codes.sample)
+
expect(current_path).to eq root_path
end
it 'invalidates the used code' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_two_factor_authenticated_counter)
+
expect { enter_code(codes.sample) }
.to change { user.reload.otp_backup_codes.size }.by(-1)
end
it 'invalidates backup codes twice in a row' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter).twice
+ .and increment(:user_session_override_counter).twice
+ .and increment(:user_two_factor_authenticated_counter).twice
+ .and increment(:user_session_destroyed_counter)
+
random_code = codes.delete(codes.sample)
expect { enter_code(random_code) }
.to change { user.reload.otp_backup_codes.size }.by(-1)
@@ -163,6 +233,9 @@ feature 'Login' do
context 'with invalid code' do
it 'blocks login' do
+ # TODO, invalid two factor authentication does not increment
+ # metrics / counters, see gitlab-org/gitlab-ce#49785
+
code = codes.sample
expect(user.invalidate_otp_backup_code!(code)).to eq true
@@ -176,7 +249,7 @@ feature 'Login' do
end
end
- context 'logging in via OAuth' do
+ context 'when logging in via OAuth' do
let(:user) { create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml')}
let(:mock_saml_response) do
File.read('spec/fixtures/authentication/saml_response.xml')
@@ -185,49 +258,80 @@ feature 'Login' do
before do
stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'],
providers: [mock_saml_config_with_upstream_two_factor_authn_contexts])
- gitlab_sign_in_via('saml', user, 'my-uid', mock_saml_response)
end
context 'when authn_context is worth two factors' do
let(:mock_saml_response) do
File.read('spec/fixtures/authentication/saml_response.xml')
- .gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS')
+ .gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
+ 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS')
end
it 'signs user in without prompting for second factor' do
+ # TODO, OAuth authentication does not fire events,
+ # see gitlab-org/gitlab-ce#49786
+
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+
+ sign_in_using_saml!
+
expect(page).not_to have_content('Two-Factor Authentication')
expect(current_path).to eq root_path
end
end
- context 'when authn_context is not worth two factors' do
+ context 'when two factor authentication is required' do
it 'shows 2FA prompt after OAuth login' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_two_factor_authenticated_counter)
+
+ sign_in_using_saml!
+
expect(page).to have_content('Two-Factor Authentication')
+
enter_code(user.current_otp)
+
expect(current_path).to eq root_path
end
end
+
+ def sign_in_using_saml!
+ gitlab_sign_in_via('saml', user, 'my-uid', mock_saml_response)
+ end
end
end
describe 'without two-factor authentication' do
- let(:user) { create(:user) }
+ context 'with correct username and password' do
+ let(:user) { create(:user) }
- it 'allows basic login' do
- gitlab_sign_in(user)
- expect(current_path).to eq root_path
- end
+ it 'allows basic login' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
- it 'does not show a "You are already signed in." error message' do
- gitlab_sign_in(user)
- expect(page).not_to have_content('You are already signed in.')
+ gitlab_sign_in(user)
+
+ expect(current_path).to eq root_path
+ expect(page).not_to have_content('You are already signed in.')
+ end
end
- it 'blocks invalid login' do
- user = create(:user, password: 'not-the-default')
+ context 'with invalid username and password' do
+ let(:user) { create(:user, password: 'not-the-default') }
- gitlab_sign_in(user)
- expect(page).to have_content('Invalid Login or password.')
+ it 'blocks invalid login' do
+ expect(authentication_metrics)
+ .to increment(:user_unauthenticated_counter)
+ .and increment(:user_password_invalid_counter)
+
+ gitlab_sign_in(user)
+
+ expect(page).to have_content('Invalid Login or password.')
+ end
end
end
@@ -243,18 +347,26 @@ feature 'Login' do
context 'with grace period defined' do
before do
stub_application_setting(two_factor_grace_period: 48)
- gitlab_sign_in(user)
end
context 'within the grace period' do
it 'redirects to two-factor configuration page' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+
expect(current_path).to eq profile_two_factor_auth_path
expect(page).to have_content('The global settings require you to enable Two-Factor Authentication for your account. You need to do this before ')
end
it 'allows skipping two-factor configuration', :js do
- expect(current_path).to eq profile_two_factor_auth_path
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+ expect(current_path).to eq profile_two_factor_auth_path
click_link 'Configure it later'
expect(current_path).to eq root_path
end
@@ -264,6 +376,11 @@ feature 'Login' do
let(:user) { create(:user, otp_grace_period_started_at: 9999.hours.ago) }
it 'redirects to two-factor configuration page' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+
expect(current_path).to eq profile_two_factor_auth_path
expect(page).to have_content(
'The global settings require you to enable Two-Factor Authentication for your account.'
@@ -271,6 +388,11 @@ feature 'Login' do
end
it 'disallows skipping two-factor configuration', :js do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+
expect(current_path).to eq profile_two_factor_auth_path
expect(page).not_to have_link('Configure it later')
end
@@ -280,10 +402,14 @@ feature 'Login' do
context 'without grace period defined' do
before do
stub_application_setting(two_factor_grace_period: 0)
- gitlab_sign_in(user)
end
it 'redirects to two-factor configuration page' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+
expect(current_path).to eq profile_two_factor_auth_path
expect(page).to have_content(
'The global settings require you to enable Two-Factor Authentication for your account.'
@@ -303,11 +429,15 @@ feature 'Login' do
context 'with grace period defined' do
before do
stub_application_setting(two_factor_grace_period: 48)
- gitlab_sign_in(user)
end
context 'within the grace period' do
it 'redirects to two-factor configuration page' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+
expect(current_path).to eq profile_two_factor_auth_path
expect(page).to have_content(
'The group settings for Group 1 and Group 2 require you to enable ' \
@@ -316,8 +446,12 @@ feature 'Login' do
end
it 'allows skipping two-factor configuration', :js do
- expect(current_path).to eq profile_two_factor_auth_path
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ gitlab_sign_in(user)
+
+ expect(current_path).to eq profile_two_factor_auth_path
click_link 'Configure it later'
expect(current_path).to eq root_path
end
@@ -327,6 +461,11 @@ feature 'Login' do
let(:user) { create(:user, otp_grace_period_started_at: 9999.hours.ago) }
it 'redirects to two-factor configuration page' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+
expect(current_path).to eq profile_two_factor_auth_path
expect(page).to have_content(
'The group settings for Group 1 and Group 2 require you to enable ' \
@@ -335,6 +474,11 @@ feature 'Login' do
end
it 'disallows skipping two-factor configuration', :js do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+
expect(current_path).to eq profile_two_factor_auth_path
expect(page).not_to have_link('Configure it later')
end
@@ -344,10 +488,14 @@ feature 'Login' do
context 'without grace period defined' do
before do
stub_application_setting(two_factor_grace_period: 0)
- gitlab_sign_in(user)
end
it 'redirects to two-factor configuration page' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+
expect(current_path).to eq profile_two_factor_auth_path
expect(page).to have_content(
'The group settings for Group 1 and Group 2 require you to enable ' \
@@ -431,6 +579,9 @@ feature 'Login' do
end
it 'asks to accept the terms on first login' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
visit new_user_session_path
fill_in 'user_login', with: user.email
@@ -447,6 +598,9 @@ feature 'Login' do
end
it 'does not ask for terms when the user already accepted them' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
accept_terms(user)
visit new_user_session_path
@@ -467,6 +621,9 @@ feature 'Login' do
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)
+
visit new_user_session_path
fill_in 'user_login', with: user.email
@@ -495,6 +652,11 @@ feature 'Login' do
end
it 'asks the user to accept the terms' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_two_factor_authenticated_counter)
+
visit new_user_session_path
fill_in 'user_login', with: user.email
@@ -518,6 +680,9 @@ feature 'Login' do
end
it 'asks the user to accept the terms before setting a new password' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
visit new_user_session_path
fill_in 'user_login', with: user.email
@@ -546,6 +711,10 @@ feature 'Login' do
end
it 'asks the user to accept the terms before setting an email' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+
gitlab_sign_in_via('saml', user, 'my-uid')
expect_to_be_on_terms_page
diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb
index c3734b5c808..9af4447056e 100644
--- a/spec/features/users/rss_spec.rb
+++ b/spec/features/users/rss_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'User RSS' do
+describe 'User RSS' do
let(:user) { create(:user) }
let(:path) { user_path(create(:user)) }
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index b5bbb2c0ea5..bc07ab48c39 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -3,15 +3,85 @@ require 'spec_helper'
describe 'User page' do
let(:user) { create(:user) }
- it 'shows all the tabs' do
+ context 'with public profile' do
+ it 'shows all the tabs' do
+ visit(user_path(user))
+
+ page.within '.nav-links' do
+ expect(page).to have_link('Activity')
+ expect(page).to have_link('Groups')
+ expect(page).to have_link('Contributed projects')
+ expect(page).to have_link('Personal projects')
+ expect(page).to have_link('Snippets')
+ end
+ end
+
+ it 'does not show private profile message' do
+ visit(user_path(user))
+
+ expect(page).not_to have_content("This user has a private profile")
+ end
+ end
+
+ context 'with private profile' do
+ let(:user) { create(:user, private_profile: true) }
+
+ it 'shows no tab' do
+ visit(user_path(user))
+
+ expect(page).to have_css("div.profile-header")
+ expect(page).not_to have_css("ul.nav-links")
+ end
+
+ it 'shows private profile message' do
+ visit(user_path(user))
+
+ expect(page).to have_content("This user has a private profile")
+ end
+
+ it 'shows own tabs' do
+ sign_in(user)
+ visit(user_path(user))
+
+ page.within '.nav-links' do
+ expect(page).to have_link('Activity')
+ expect(page).to have_link('Groups')
+ expect(page).to have_link('Contributed projects')
+ expect(page).to have_link('Personal projects')
+ expect(page).to have_link('Snippets')
+ end
+ end
+ end
+
+ it 'shows the status if there was one' do
+ create(:user_status, user: user, message: "Working hard!")
+
visit(user_path(user))
- page.within '.nav-links' do
- expect(page).to have_link('Activity')
- expect(page).to have_link('Groups')
- expect(page).to have_link('Contributed projects')
- expect(page).to have_link('Personal projects')
- expect(page).to have_link('Snippets')
+ expect(page).to have_content("Working hard!")
+ end
+
+ context 'signup disabled' do
+ it 'shows the sign in link' do
+ stub_application_setting(signup_enabled: false)
+
+ visit(user_path(user))
+
+ page.within '.navbar-nav' do
+ expect(page).to have_link('Sign in')
+ end
+ end
+ end
+
+ context 'signup enabled' do
+ it 'shows the sign in and register link' do
+ stub_application_setting(signup_enabled: true)
+
+ visit(user_path(user))
+
+ page.within '.navbar-nav' do
+ expect(page).to have_link('Sign in / Register')
+ end
end
end
end
diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 5478e38ce70..6a9b281fb4c 100644
--- a/spec/features/users/user_browses_projects_on_user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -4,19 +4,19 @@ describe 'Users > User browses projects on user page', :js do
let!(:user) { create :user }
let!(:private_project) do
create :project, :private, name: 'private', namespace: user.namespace do |project|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
let!(:internal_project) do
create :project, :internal, name: 'internal', namespace: user.namespace do |project|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
let!(:public_project) do
create :project, :public, name: 'public', namespace: user.namespace do |project|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
diff --git a/spec/finders/access_requests_finder_spec.rb b/spec/finders/access_requests_finder_spec.rb
index 650f7229647..605777462bb 100644
--- a/spec/finders/access_requests_finder_spec.rb
+++ b/spec/finders/access_requests_finder_spec.rb
@@ -51,7 +51,7 @@ describe AccessRequestsFinder do
context 'when current user can see access requests' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
group.add_owner(user)
end
@@ -78,7 +78,7 @@ describe AccessRequestsFinder do
context 'when current user can see access requests' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
group.add_owner(user)
end
diff --git a/spec/finders/admin/projects_finder_spec.rb b/spec/finders/admin/projects_finder_spec.rb
index 7901d5fee28..44cc8debd04 100644
--- a/spec/finders/admin/projects_finder_spec.rb
+++ b/spec/finders/admin/projects_finder_spec.rb
@@ -54,7 +54,7 @@ describe Admin::ProjectsFinder do
context 'filter by visibility_level' do
before do
- private_project.add_master(user)
+ private_project.add_maintainer(user)
end
context 'private' do
diff --git a/spec/finders/concerns/finder_with_cross_project_access_spec.rb b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
index c784fb87972..1ff65a8101b 100644
--- a/spec/finders/concerns/finder_with_cross_project_access_spec.rb
+++ b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
@@ -25,7 +25,7 @@ describe FinderWithCrossProjectAccess do
let!(:result) { create(:issue) }
before do
- result.project.add_master(user)
+ result.project.add_maintainer(user)
end
def expect_access_check_on_result
diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb
index 60ea98e61c7..9155a8d6fe9 100644
--- a/spec/finders/contributed_projects_finder_spec.rb
+++ b/spec/finders/contributed_projects_finder_spec.rb
@@ -10,9 +10,9 @@ describe ContributedProjectsFinder do
let!(:private_project) { create(:project, :private) }
before do
- private_project.add_master(source_user)
+ private_project.add_maintainer(source_user)
private_project.add_developer(current_user)
- public_project.add_master(source_user)
+ public_project.add_maintainer(source_user)
create(:push_event, project: public_project, author: source_user)
create(:push_event, project: private_project, author: source_user)
diff --git a/spec/finders/environments_finder_spec.rb b/spec/finders/environments_finder_spec.rb
index 3a8a1e7de74..3cd421f22eb 100644
--- a/spec/finders/environments_finder_spec.rb
+++ b/spec/finders/environments_finder_spec.rb
@@ -7,7 +7,7 @@ describe EnvironmentsFinder do
let(:environment) { create(:environment, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'tagged deployment' do
diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb
index 63e15b365a4..f545da3aee4 100644
--- a/spec/finders/group_members_finder_spec.rb
+++ b/spec/finders/group_members_finder_spec.rb
@@ -9,9 +9,9 @@ describe GroupMembersFinder, '#execute' do
let(:user4) { create(:user) }
it 'returns members for top-level group' do
- member1 = group.add_master(user1)
- member2 = group.add_master(user2)
- member3 = group.add_master(user3)
+ member1 = group.add_maintainer(user1)
+ member2 = group.add_maintainer(user2)
+ member3 = group.add_maintainer(user3)
result = described_class.new(group).execute
@@ -19,11 +19,11 @@ describe GroupMembersFinder, '#execute' do
end
it 'returns members for nested group', :nested_groups do
- group.add_master(user2)
+ group.add_maintainer(user2)
nested_group.request_access(user4)
- member1 = group.add_master(user1)
- member3 = nested_group.add_master(user2)
- member4 = nested_group.add_master(user3)
+ member1 = group.add_maintainer(user1)
+ member3 = nested_group.add_maintainer(user2)
+ member4 = nested_group.add_maintainer(user3)
result = described_class.new(nested_group).execute
@@ -31,11 +31,11 @@ describe GroupMembersFinder, '#execute' do
end
it 'returns members for descendant groups if requested', :nested_groups do
- member1 = group.add_master(user2)
- member2 = group.add_master(user1)
- nested_group.add_master(user2)
- member3 = nested_group.add_master(user3)
- member4 = nested_group.add_master(user4)
+ member1 = group.add_maintainer(user2)
+ member2 = group.add_maintainer(user1)
+ nested_group.add_maintainer(user2)
+ member3 = nested_group.add_maintainer(user3)
+ member4 = nested_group.add_maintainer(user4)
result = described_class.new(group).execute(include_descendants: true)
diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb
index be80ee7d767..d6d95906f5e 100644
--- a/spec/finders/group_projects_finder_spec.rb
+++ b/spec/finders/group_projects_finder_spec.rb
@@ -17,16 +17,16 @@ describe GroupProjectsFinder do
let!(:subgroup_private_project) { create(:project, :private, path: '7', group: subgroup) }
before do
- shared_project_1.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
- shared_project_2.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
- shared_project_3.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
+ shared_project_1.project_group_links.create(group_access: Gitlab::Access::MAINTAINER, group: group)
+ shared_project_2.project_group_links.create(group_access: Gitlab::Access::MAINTAINER, group: group)
+ shared_project_3.project_group_links.create(group_access: Gitlab::Access::MAINTAINER, group: group)
end
subject { finder.execute }
describe 'with a group member current user' do
before do
- group.add_master(current_user)
+ group.add_maintainer(current_user)
end
context "only shared" do
@@ -68,7 +68,7 @@ describe GroupProjectsFinder do
describe 'without group member current_user' do
before do
- shared_project_2.add_master(current_user)
+ shared_project_2.add_maintainer(current_user)
current_user.reload
end
@@ -81,7 +81,7 @@ describe GroupProjectsFinder do
context "with external user" do
before do
- current_user.update_attributes(external: true)
+ current_user.update(external: true)
end
it { is_expected.to match_array([shared_project_2, shared_project_1]) }
@@ -93,8 +93,8 @@ describe GroupProjectsFinder do
context "without external user" do
before do
- private_project.add_master(current_user)
- subgroup_private_project.add_master(current_user)
+ private_project.add_maintainer(current_user)
+ subgroup_private_project.add_maintainer(current_user)
end
context 'with subgroups projects', :nested_groups do
@@ -112,7 +112,7 @@ describe GroupProjectsFinder do
context "with external user" do
before do
- current_user.update_attributes(external: true)
+ current_user.update(external: true)
end
context 'with subgroups projects', :nested_groups do
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 74e91b02f0f..07a2fa86dd7 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -26,7 +26,7 @@ describe IssuesFinder do
let(:issues) { described_class.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute }
before(:context) do
- project1.add_master(user)
+ project1.add_maintainer(user)
project2.add_developer(user)
project2.add_developer(user2)
project3.add_developer(user)
diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb
index 29a47e005a6..ae3e55f90f1 100644
--- a/spec/finders/joined_groups_finder_spec.rb
+++ b/spec/finders/joined_groups_finder_spec.rb
@@ -15,7 +15,7 @@ describe JoinedGroupsFinder do
context 'without a user' do
before do
- public_group.add_master(profile_owner)
+ public_group.add_maintainer(profile_owner)
end
it 'only shows public groups from profile owner' do
@@ -25,9 +25,9 @@ describe JoinedGroupsFinder do
context "with a user" do
before do
- private_group.add_master(profile_owner)
- internal_group.add_master(profile_owner)
- public_group.add_master(profile_owner)
+ private_group.add_maintainer(profile_owner)
+ internal_group.add_maintainer(profile_owner)
+ public_group.add_maintainer(profile_owner)
end
context "when the profile visitor is in the private group" do
@@ -53,7 +53,7 @@ describe JoinedGroupsFinder do
context 'external users' do
before do
- profile_visitor.update_attributes(external: true)
+ profile_visitor.update(external: true)
end
context 'if not a member' do
@@ -64,7 +64,7 @@ describe JoinedGroupsFinder do
context "if authorized" do
before do
- internal_group.add_master(profile_visitor)
+ internal_group.add_maintainer(profile_visitor)
end
it "shows internal groups if authorized" do
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index 899d0d22819..f5cec8e349a 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -14,7 +14,7 @@ describe LabelsFinder do
let(:project_4) { create(:project, :public) }
let(:project_5) { create(:project, namespace: group_1) }
- let!(:project_label_1) { create(:label, project: project_1, title: 'Label 1') }
+ let!(:project_label_1) { create(:label, project: project_1, title: 'Label 1', description: 'awesome label') }
let!(:project_label_2) { create(:label, project: project_2, title: 'Label 2') }
let!(:project_label_4) { create(:label, project: project_4, title: 'Label 4') }
let!(:project_label_5) { create(:label, project: project_5, title: 'Label 5') }
@@ -55,7 +55,7 @@ describe LabelsFinder do
context 'filtering by group_id' do
it 'returns labels available for any non-archived project within the group' do
group_1.add_developer(user)
- project_1.archive!
+ ::Projects::UpdateService.new(project_1, user, archived: true).execute
finder = described_class.new(user, group_id: group_1.id)
expect(finder.execute).to eq [group_label_2, group_label_1, project_label_5]
@@ -196,5 +196,19 @@ describe LabelsFinder do
expect(finder.execute).to be_empty
end
end
+
+ context 'search by title and description' do
+ it 'returns labels with a partially matching title' do
+ finder = described_class.new(user, search: '(group)')
+
+ expect(finder.execute).to eq [group_label_1]
+ end
+
+ it 'returns labels with a partially matching description' do
+ finder = described_class.new(user, search: 'awesome')
+
+ expect(finder.execute).to eq [project_label_1]
+ end
+ end
end
end
diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb
index 2fc5299b0f4..db48f00cd74 100644
--- a/spec/finders/members_finder_spec.rb
+++ b/spec/finders/members_finder_spec.rb
@@ -11,9 +11,9 @@ describe MembersFinder, '#execute' do
it 'returns members for project and parent groups', :nested_groups do
nested_group.request_access(user1)
- member1 = group.add_master(user2)
- member2 = nested_group.add_master(user3)
- member3 = project.add_master(user4)
+ member1 = group.add_maintainer(user2)
+ member2 = nested_group.add_maintainer(user3)
+ member3 = project.add_maintainer(user4)
result = described_class.new(project, user2).execute
@@ -23,9 +23,9 @@ describe MembersFinder, '#execute' do
it 'includes nested group members if asked', :nested_groups do
project = create(:project, namespace: group)
nested_group.request_access(user1)
- member1 = group.add_master(user2)
- member2 = nested_group.add_master(user3)
- member3 = project.add_master(user4)
+ member1 = group.add_maintainer(user2)
+ member2 = nested_group.add_maintainer(user3)
+ member3 = project.add_maintainer(user4)
result = described_class.new(project, user2).execute(include_descendants: true)
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index c8a43ddf410..35d0eeda8f6 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -19,12 +19,12 @@ describe MergeRequestsFinder do
let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
let!(:merge_request2) { create(:merge_request, :conflict, author: user, source_project: project2, target_project: project1, state: 'closed') }
- let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) }
+ let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, state: 'locked') }
let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) }
let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4) }
before do
- project1.add_master(user)
+ project1.add_maintainer(user)
project2.add_developer(user)
project3.add_developer(user)
project2.add_developer(user2)
@@ -35,7 +35,7 @@ describe MergeRequestsFinder do
it 'filters by scope' do
params = { scope: 'authored', state: 'opened' }
merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(4)
+ expect(merge_requests.size).to eq(3)
end
it 'filters by project' do
@@ -90,6 +90,14 @@ describe MergeRequestsFinder do
expect(merge_requests).to contain_exactly(merge_request2)
end
+ it 'filters by state' do
+ params = { state: 'locked' }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request3)
+ end
+
context 'filtering by group milestone' do
let!(:group) { create(:group, :public) }
let(:group_milestone) { create(:milestone, group: group) }
@@ -134,7 +142,7 @@ describe MergeRequestsFinder do
end
before do
- new_project.add_master(user)
+ new_project.add_maintainer(user)
end
it 'filters by created_after' do
@@ -199,7 +207,7 @@ describe MergeRequestsFinder do
it 'returns the number of rows for the default state' do
finder = described_class.new(user)
- expect(finder.row_count).to eq(4)
+ expect(finder.row_count).to eq(3)
end
it 'returns the number of rows for a given state' do
diff --git a/spec/finders/move_to_project_finder_spec.rb b/spec/finders/move_to_project_finder_spec.rb
index 74639d4147f..1b3f44cced1 100644
--- a/spec/finders/move_to_project_finder_spec.rb
+++ b/spec/finders/move_to_project_finder_spec.rb
@@ -8,7 +8,7 @@ describe MoveToProjectFinder do
let(:guest_project) { create(:project) }
let(:reporter_project) { create(:project) }
let(:developer_project) { create(:project) }
- let(:master_project) { create(:project) }
+ let(:maintainer_project) { create(:project) }
subject { described_class.new(user) }
@@ -23,9 +23,9 @@ describe MoveToProjectFinder do
it 'returns projects equal or above Gitlab::Access::REPORTER ordered by id in descending order' do
reporter_project.add_reporter(user)
developer_project.add_developer(user)
- master_project.add_master(user)
+ maintainer_project.add_maintainer(user)
- expect(subject.execute(project).to_a).to eq([master_project, developer_project, reporter_project])
+ expect(subject.execute(project).to_a).to eq([maintainer_project, developer_project, reporter_project])
end
it 'does not include the source project' do
@@ -36,7 +36,7 @@ describe MoveToProjectFinder do
it 'does not return archived projects' do
reporter_project.add_reporter(user)
- reporter_project.archive!
+ ::Projects::UpdateService.new(reporter_project, user, archived: true).execute
other_reporter_project = create(:project)
other_reporter_project.add_reporter(user)
@@ -45,7 +45,7 @@ describe MoveToProjectFinder do
it 'does not return projects for which issues are disabled' do
reporter_project.add_reporter(user)
- reporter_project.update_attributes(issues_enabled: false)
+ reporter_project.update(issues_enabled: false)
other_reporter_project = create(:project)
other_reporter_project.add_reporter(user)
@@ -57,9 +57,9 @@ describe MoveToProjectFinder do
reporter_project.add_reporter(user)
developer_project.add_developer(user)
- master_project.add_master(user)
+ maintainer_project.add_maintainer(user)
- expect(subject.execute(project).to_a).to eq([master_project, developer_project])
+ expect(subject.execute(project).to_a).to eq([maintainer_project, developer_project])
end
it 'returns projects after the given offset id' do
@@ -67,9 +67,9 @@ describe MoveToProjectFinder do
reporter_project.add_reporter(user)
developer_project.add_developer(user)
- master_project.add_master(user)
+ maintainer_project.add_maintainer(user)
- expect(subject.execute(project, search: nil, offset_id: master_project.id).to_a).to eq([developer_project, reporter_project])
+ expect(subject.execute(project, search: nil, offset_id: maintainer_project.id).to_a).to eq([developer_project, reporter_project])
expect(subject.execute(project, search: nil, offset_id: developer_project.id).to_a).to eq([reporter_project])
expect(subject.execute(project, search: nil, offset_id: reporter_project.id).to_a).to be_empty
end
@@ -84,10 +84,10 @@ describe MoveToProjectFinder do
it 'returns projects matching a search query' do
foo_project = create(:project)
- foo_project.add_master(user)
+ foo_project.add_maintainer(user)
wadus_project = create(:project, name: 'wadus')
- wadus_project.add_master(user)
+ wadus_project.add_maintainer(user)
expect(subject.execute(project).to_a).to eq([wadus_project, foo_project])
expect(subject.execute(project, search: 'wadus').to_a).to eq([wadus_project])
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 232f35c86f9..b776e9d856a 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -5,7 +5,7 @@ describe NotesFinder do
let(:project) { create(:project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb
index 00c551a1f65..ef7dd0cd4a8 100644
--- a/spec/finders/personal_projects_finder_spec.rb
+++ b/spec/finders/personal_projects_finder_spec.rb
@@ -35,7 +35,7 @@ describe PersonalProjectsFinder do
context 'external' do
before do
- current_user.update_attributes(external: true)
+ current_user.update(external: true)
end
it { is_expected.to eq([public_project, private_project]) }
diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb
index d6253b605b9..c6e832ad69b 100644
--- a/spec/finders/pipelines_finder_spec.rb
+++ b/spec/finders/pipelines_finder_spec.rb
@@ -1,9 +1,10 @@
require 'spec_helper'
describe PipelinesFinder do
- let(:project) { create(:project, :repository) }
-
- subject { described_class.new(project, params).execute }
+ let(:project) { create(:project, :public, :repository) }
+ let(:current_user) { nil }
+ let(:params) { {} }
+ subject { described_class.new(project, current_user, params).execute }
describe "#execute" do
context 'when params is empty' do
@@ -223,5 +224,27 @@ describe PipelinesFinder do
end
end
end
+
+ context 'when the project has limited access to piplines' do
+ let(:project) { create(:project, :private, :repository) }
+ let(:current_user) { create(:user) }
+ let!(:pipelines) { create_list(:ci_pipeline, 2, project: project) }
+
+ context 'when the user has access' do
+ before do
+ project.add_developer(current_user)
+ end
+
+ it 'is expected to return pipelines' do
+ is_expected.to contain_exactly(*pipelines)
+ end
+ end
+
+ context 'the user is not allowed to read pipelines' do
+ it 'returns empty' do
+ is_expected.to be_empty
+ end
+ end
+ end
end
end
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb
index 0dfe6ba9c32..7931ad9b9f0 100644
--- a/spec/finders/projects_finder_spec.rb
+++ b/spec/finders/projects_finder_spec.rb
@@ -41,7 +41,7 @@ describe ProjectsFinder do
describe 'with private projects' do
before do
- private_project.add_master(user)
+ private_project.add_maintainer(user)
end
it { is_expected.to match_array([public_project, internal_project, private_project]) }
@@ -56,7 +56,7 @@ describe ProjectsFinder do
describe 'filter by visibility_level' do
before do
- private_project.add_master(user)
+ private_project.add_maintainer(user)
end
context 'private' do
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index 9747b9402a7..7f7cfb2cb98 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -5,12 +5,50 @@ describe TodosFinder do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
+ let(:issue) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
let(:finder) { described_class }
before do
group.add_developer(user)
end
+ describe '#execute' do
+ context 'filtering' do
+ let!(:todo1) { create(:todo, user: user, project: project, target: issue) }
+ let!(:todo2) { create(:todo, user: user, group: group, target: merge_request) }
+
+ it 'returns correct todos when filtered by a project' do
+ todos = finder.new(user, { project_id: project.id }).execute
+
+ expect(todos).to match_array([todo1])
+ end
+
+ it 'returns correct todos when filtered by a group' do
+ todos = finder.new(user, { group_id: group.id }).execute
+
+ expect(todos).to match_array([todo1, todo2])
+ end
+
+ it 'returns correct todos when filtered by a type' do
+ todos = finder.new(user, { type: 'Issue' }).execute
+
+ expect(todos).to match_array([todo1])
+ end
+
+ context 'with subgroups', :nested_groups do
+ let(:subgroup) { create(:group, parent: group) }
+ let!(:todo3) { create(:todo, user: user, group: subgroup, target: issue) }
+
+ it 'returns todos from subgroups when filtered by a group' do
+ todos = finder.new(user, { group_id: group.id }).execute
+
+ expect(todos).to match_array([todo1, todo2, todo3])
+ end
+ end
+ end
+ end
+
describe '#sort' do
context 'by date' do
let!(:todo1) { create(:todo, user: user, project: project) }
diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb
index da043f94021..58470f4c84d 100644
--- a/spec/finders/user_recent_events_finder_spec.rb
+++ b/spec/finders/user_recent_events_finder_spec.rb
@@ -29,11 +29,22 @@ describe UserRecentEventsFinder do
public_project.add_developer(current_user)
end
- it 'returns all the events' do
- expect(finder.execute).to include(private_event, internal_event, public_event)
+ context 'when profile is public' do
+ it 'returns all the events' do
+ expect(finder.execute).to include(private_event, internal_event, public_event)
+ end
+ end
+
+ context 'when profile is private' do
+ it 'returns no event' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, project_owner).and_return(false)
+ expect(finder.execute).to be_empty
+ end
end
it 'does not include the events if the user cannot read cross project' do
+ expect(Ability).to receive(:allowed?).and_call_original
expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false }
expect(finder.execute).to be_empty
end
diff --git a/spec/fixtures/aosp_manifest.xml b/spec/fixtures/aosp_manifest.xml
new file mode 100644
index 00000000000..cfd0094b735
--- /dev/null
+++ b/spec/fixtures/aosp_manifest.xml
@@ -0,0 +1,685 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest>
+
+ <remote name="aosp"
+ fetch=".."
+ review="https://android-review.googlesource.com/" />
+ <default revision="master"
+ remote="aosp"
+ sync-j="4" />
+
+ <project path="build/make" name="platform/build" groups="pdk" >
+ <copyfile src="core/root.mk" dest="Makefile" />
+ <linkfile src="CleanSpec.mk" dest="build/CleanSpec.mk" />
+ <linkfile src="buildspec.mk.default" dest="build/buildspec.mk.default" />
+ <linkfile src="core" dest="build/core" />
+ <linkfile src="envsetup.sh" dest="build/envsetup.sh" />
+ <linkfile src="target" dest="build/target" />
+ <linkfile src="tools" dest="build/tools" />
+ </project>
+ <project path="build/blueprint" name="platform/build/blueprint" groups="pdk,tradefed" />
+ <project path="build/kati" name="platform/build/kati" groups="pdk,tradefed" />
+ <project path="build/soong" name="platform/build/soong" groups="pdk,tradefed" >
+ <linkfile src="root.bp" dest="Android.bp" />
+ <linkfile src="bootstrap.bash" dest="bootstrap.bash" />
+ </project>
+ <project path="art" name="platform/art" groups="pdk" />
+ <project path="bionic" name="platform/bionic" groups="pdk" />
+ <project path="bootable/recovery" name="platform/bootable/recovery" groups="pdk" />
+ <project path="compatibility/cdd" name="platform/compatibility/cdd" groups="pdk" />
+ <project path="cts" name="platform/cts" groups="cts,pdk-cw-fs,pdk-fs" />
+ <project path="dalvik" name="platform/dalvik" groups="pdk-cw-fs,pdk-fs" />
+ <project path="developers/build" name="platform/developers/build" groups="developers" />
+ <project path="developers/demos" name="platform/developers/demos" groups="developers" />
+ <project path="developers/samples/android" name="platform/developers/samples/android" groups="developers" />
+ <project path="development" name="platform/development" groups="developers,pdk-cw-fs,pdk-fs" />
+ <project path="device/asus/fugu" name="device/asus/fugu" groups="device,fugu,broadcom_pdk" />
+ <project path="device/asus/fugu-kernel" name="device/asus/fugu-kernel" groups="device,fugu,broadcom_pdk" clone-depth="1" />
+ <project path="device/common" name="device/common" groups="pdk-cw-fs,pdk" />
+ <project path="device/generic/arm64" name="device/generic/arm64" groups="pdk" />
+ <project path="device/generic/armv7-a-neon" name="device/generic/armv7-a-neon" groups="pdk" />
+ <project path="device/generic/car" name="device/generic/car" groups="pdk" />
+ <project path="device/generic/common" name="device/generic/common" groups="pdk" />
+ <project path="device/generic/goldfish" name="device/generic/goldfish" groups="pdk" />
+ <project path="device/generic/goldfish-opengl" name="device/generic/goldfish-opengl" groups="pdk" />
+ <project path="device/generic/mini-emulator-arm64" name="device/generic/mini-emulator-arm64" groups="pdk" />
+ <project path="device/generic/mini-emulator-armv7-a-neon" name="device/generic/mini-emulator-armv7-a-neon" groups="pdk" />
+ <project path="device/generic/mini-emulator-x86" name="device/generic/mini-emulator-x86" groups="pdk" />
+ <project path="device/generic/mini-emulator-x86_64" name="device/generic/mini-emulator-x86_64" groups="pdk" />
+ <project path="device/generic/qemu" name="device/generic/qemu" groups="pdk" />
+ <project path="device/generic/uml" name="device/generic/uml" groups="device,pdk" />
+ <project path="device/generic/x86" name="device/generic/x86" groups="pdk" />
+ <project path="device/generic/x86_64" name="device/generic/x86_64" groups="pdk" />
+ <project path="device/google/accessory/arduino" name="device/google/accessory/arduino" groups="device,pdk" />
+ <project path="device/google/accessory/demokit" name="device/google/accessory/demokit" groups="device,pdk" />
+ <project path="device/google/atv" name="device/google/atv" groups="device,broadcom_pdk,generic_fs,pdk" />
+ <project path="device/google/contexthub" name="device/google/contexthub" groups="device,marlin,pdk" />
+ <project path="device/google/cuttlefish" name="device/google/cuttlefish" groups="device" />
+ <project path="device/google/cuttlefish_common" name="device/google/cuttlefish_common" groups="device" />
+ <project path="device/google/cuttlefish_kernel" name="device/google/cuttlefish_kernel" groups="device" clone-depth="1" />
+ <project path="device/google/dragon" name="device/google/dragon" groups="device,dragon" />
+ <project path="device/google/dragon-kernel" name="device/google/dragon-kernel" groups="device,dragon" clone-depth="1" />
+ <project path="device/google/marlin" name="device/google/marlin" groups="device,marlin,pdk" />
+ <project path="device/google/marlin-kernel" name="device/google/marlin-kernel" groups="device,marlin,pdk" clone-depth="1" />
+ <project path="device/google/muskie" name="device/google/muskie" groups="device,muskie" />
+ <project path="device/google/taimen" name="device/google/taimen" groups="device,taimen" />
+ <project path="device/google/vrservices" name="device/google/vrservices" groups="pdk" clone-depth="1" />
+ <project path="device/google/wahoo" name="device/google/wahoo" groups="device,wahoo" />
+ <project path="device/google/wahoo-kernel" name="device/google/wahoo-kernel" groups="device,wahoo" clone-depth="1" />
+ <project path="device/huawei/angler" name="device/huawei/angler" groups="device,angler,broadcom_pdk" />
+ <project path="device/huawei/angler-kernel" name="device/huawei/angler-kernel" groups="device,angler,broadcom_pdk" clone-depth="1" />
+ <project path="device/lge/bullhead" name="device/lge/bullhead" groups="device,bullhead" />
+ <project path="device/lge/bullhead-kernel" name="device/lge/bullhead-kernel" groups="device,bullhead" clone-depth="1" />
+ <project path="device/linaro/bootloader/arm-trusted-firmware" name="device/linaro/bootloader/arm-trusted-firmware" />
+ <project path="device/linaro/bootloader/edk2" name="device/linaro/bootloader/edk2" />
+ <project path="device/linaro/bootloader/OpenPlatformPkg" name="device/linaro/bootloader/OpenPlatformPkg" />
+ <project path="device/linaro/hikey" name="device/linaro/hikey" groups="device,hikey,pdk" />
+ <project path="device/linaro/hikey-kernel" name="device/linaro/hikey-kernel" groups="device,hikey,pdk" clone-depth="1" />
+ <project path="device/sample" name="device/sample" groups="pdk" />
+ <project path="external/aac" name="platform/external/aac" groups="pdk" />
+ <project path="external/abi-compliance-checker" name="platform/external/abi-compliance-checker" groups="pdk" />
+ <project path="external/abi-dumper" name="platform/external/abi-dumper" groups="pdk" />
+ <project path="external/adt-infra" name="platform/external/adt-infra" groups="adt-infra,notdefault,pdk-fs" />
+ <project path="external/android-clat" name="platform/external/android-clat" groups="pdk" />
+ <project path="external/androidplot" name="platform/external/androidplot" groups="pdk" />
+ <project path="external/annotation-tools" name="platform/external/annotation-tools" groups="pdk" />
+ <project path="external/ant-glob" name="platform/external/ant-glob" groups="pdk" />
+ <project path="external/antlr" name="platform/external/antlr" groups="pdk" />
+ <project path="external/apache-commons-math" name="platform/external/apache-commons-math" groups="pdk" />
+ <project path="external/apache-harmony" name="platform/external/apache-harmony" groups="pdk" />
+ <project path="external/apache-http" name="platform/external/apache-http" groups="pdk" />
+ <project path="external/apache-xml" name="platform/external/apache-xml" groups="pdk" />
+ <project path="external/archive-patcher" name="platform/external/archive-patcher" groups="pdk" />
+ <project path="external/arm-neon-tests" name="platform/external/arm-neon-tests" groups="vendor" />
+ <project path="external/autotest" name="platform/external/autotest" groups="pdk-fs" />
+ <project path="external/avb" name="platform/external/avb" groups="pdk" />
+ <project path="external/bart" name="platform/external/bart" groups="pdk" />
+ <project path="external/blktrace" name="platform/external/blktrace" groups="pdk" />
+ <project path="external/boringssl" name="platform/external/boringssl" groups="pdk" />
+ <project path="external/bouncycastle" name="platform/external/bouncycastle" groups="pdk" />
+ <project path="external/brotli" name="platform/external/brotli" groups="pdk" />
+ <project path="external/bsdiff" name="platform/external/bsdiff" groups="pdk" />
+ <project path="external/bzip2" name="platform/external/bzip2" groups="pdk" />
+ <project path="external/caliper" name="platform/external/caliper" groups="pdk" />
+ <project path="external/cblas" name="platform/external/cblas" groups="pdk" />
+ <project path="external/chromium-libpac" name="platform/external/chromium-libpac" groups="pdk" />
+ <project path="external/chromium-trace" name="platform/external/chromium-trace" groups="pdk" />
+ <project path="external/chromium-webview" name="platform/external/chromium-webview" groups="pdk" clone-depth="1" />
+ <project path="external/clang" name="platform/external/clang" groups="pdk" />
+ <project path="external/cldr" name="platform/external/cldr" groups="pdk" />
+ <project path="external/cmockery" name="platform/external/cmockery" groups="pdk" />
+ <project path="external/cn-cbor" name="platform/external/cn-cbor" groups="pdk" />
+ <project path="external/compiler-rt" name="platform/external/compiler-rt" groups="pdk" />
+ <project path="external/conscrypt" name="platform/external/conscrypt" groups="pdk" />
+ <project path="external/crcalc" name="platform/external/crcalc" groups="pdk" />
+ <project path="external/cros/system_api" name="platform/external/cros/system_api" groups="pdk" />
+ <project path="external/curl" name="platform/external/curl" groups="pdk" />
+ <project path="external/dagger2" name="platform/external/dagger2" groups="pdk" />
+ <project path="external/deqp" name="platform/external/deqp" groups="pdk-fs" />
+ <project path="external/desugar" name="platform/external/desugar" groups="pdk" />
+ <project path="external/devlib" name="platform/external/devlib" groups="pdk" />
+ <project path="external/dexmaker" name="platform/external/dexmaker" groups="pdk" />
+ <project path="external/dhcpcd-6.8.2" name="platform/external/dhcpcd-6.8.2" groups="pdk" />
+ <project path="external/dlmalloc" name="platform/external/dlmalloc" groups="pdk" />
+ <project path="external/dng_sdk" name="platform/external/dng_sdk" groups="pdk" />
+ <project path="external/dnsmasq" name="platform/external/dnsmasq" groups="pdk" />
+ <project path="external/doclava" name="platform/external/doclava" groups="pdk" />
+ <project path="external/dokka" name="platform/external/dokka" groups="pdk" />
+ <project path="external/drm_hwcomposer" name="platform/external/drm_hwcomposer" groups="drm_hwcomposer,pdk-fs" />
+ <project path="external/droiddriver" name="platform/external/droiddriver" groups="pdk" />
+ <project path="external/drrickorang" name="platform/external/drrickorang" groups="pdk" />
+ <project path="external/dtc" name="platform/external/dtc" groups="pdk"/>
+ <project path="external/e2fsprogs" name="platform/external/e2fsprogs" groups="pdk" />
+ <project path="external/easymock" name="platform/external/easymock" groups="pdk" />
+ <project path="external/eigen" name="platform/external/eigen" groups="pdk" />
+ <project path="external/elfutils" name="platform/external/elfutils" groups="pdk" />
+ <project path="external/emma" name="platform/external/emma" groups="pdk" />
+ <project path="external/error_prone" name="platform/external/error_prone" groups="pdk" />
+ <project path="external/esd" name="platform/external/esd" groups="pdk" />
+ <project path="external/expat" name="platform/external/expat" groups="pdk" />
+ <project path="external/eyes-free" name="platform/external/eyes-free" groups="pdk" />
+ <project path="external/f2fs-tools" name="platform/external/f2fs-tools" groups="pdk" />
+ <project path="external/fdlibm" name="platform/external/fdlibm" groups="pdk" />
+ <project path="external/fec" name="platform/external/fec" groups="pdk" />
+ <project path="external/flac" name="platform/external/flac" groups="pdk" />
+ <project path="external/flatbuffers" name="platform/external/flatbuffers" groups="pdk" />
+ <project path="external/fonttools" name="platform/external/fonttools" groups="pdk" />
+ <project path="external/freetype" name="platform/external/freetype" groups="pdk" />
+ <project path="external/fsck_msdos" name="platform/external/fsck_msdos" groups="pdk" />
+ <project path="external/gemmlowp" name="platform/external/gemmlowp" groups="pdk" />
+ <project path="external/gflags" name="platform/external/gflags" groups="pdk" />
+ <project path="external/giflib" name="platform/external/giflib" groups="pdk,qcom_msm8x26" />
+ <project path="external/glide" name="platform/external/glide" groups="pdk" />
+ <project path="external/golang-protobuf" name="platform/external/golang-protobuf" groups="pdk" />
+ <project path="external/google-benchmark" name="platform/external/google-benchmark" groups="pdk" />
+ <project path="external/google-breakpad" name="platform/external/google-breakpad" groups="pdk-fs" />
+ <project path="external/google-fonts/carrois-gothic-sc" name="platform/external/google-fonts/carrois-gothic-sc" groups="pdk" />
+ <project path="external/google-fonts/coming-soon" name="platform/external/google-fonts/coming-soon" groups="pdk" />
+ <project path="external/google-fonts/cutive-mono" name="platform/external/google-fonts/cutive-mono" groups="pdk" />
+ <project path="external/google-fonts/dancing-script" name="platform/external/google-fonts/dancing-script" groups="pdk" />
+ <project path="external/google-styleguide" name="platform/external/google-styleguide" groups="pdk" />
+ <project path="external/google-tv-pairing-protocol" name="platform/external/google-tv-pairing-protocol" groups="pdk" />
+ <project path="external/googletest" name="platform/external/googletest" groups="pdk" />
+ <project path="external/gptfdisk" name="platform/external/gptfdisk" groups="pdk" />
+ <project path="external/guava" name="platform/external/guava" groups="pdk" />
+ <project path="external/guice" name="platform/external/guice" groups="pdk" />
+ <project path="external/hamcrest" name="platform/external/hamcrest" groups="pdk" />
+ <project path="external/harfbuzz_ng" name="platform/external/harfbuzz_ng" groups="pdk,qcom_msm8x26" />
+ <project path="external/hyphenation-patterns" name="platform/external/hyphenation-patterns" groups="pdk" />
+ <project path="external/icu" name="platform/external/icu" groups="pdk" />
+ <project path="external/ImageMagick" name="platform/external/ImageMagick" groups="pdk" />
+ <project path="external/ims" name="platform/external/ims" groups="pdk" />
+ <project path="external/iproute2" name="platform/external/iproute2" groups="pdk" />
+ <project path="external/ipsec-tools" name="platform/external/ipsec-tools" groups="pdk" />
+ <project path="external/iptables" name="platform/external/iptables" groups="pdk" />
+ <project path="external/iputils" name="platform/external/iputils" groups="pdk" />
+ <project path="external/iw" name="platform/external/iw" groups="pdk" />
+ <project path="external/jacoco" name="platform/external/jacoco" groups="pdk" />
+ <project path="external/jarjar" name="platform/external/jarjar" groups="pdk" />
+ <project path="external/javaparser" name="platform/external/javaparser" groups="pdk" />
+ <project path="external/javasqlite" name="platform/external/javasqlite" groups="pdk" />
+ <project path="external/javassist" name="platform/external/javassist" groups="pdk" />
+ <project path="external/jcommander" name="platform/external/jcommander" groups="pdk" />
+ <project path="external/jdiff" name="platform/external/jdiff" groups="pdk" />
+ <project path="external/jemalloc" name="platform/external/jemalloc" groups="pdk" />
+ <project path="external/jline" name="platform/external/jline" groups="pdk,tradefed,pdk-fs" />
+ <project path="external/jmdns" name="platform/external/jmdns" groups="pdk" />
+ <project path="external/jsilver" name="platform/external/jsilver" groups="pdk" />
+ <project path="external/jsmn" name="platform/external/jsmn" groups="pdk" />
+ <project path="external/jsoncpp" name="platform/external/jsoncpp" groups="pdk" />
+ <project path="external/jsr305" name="platform/external/jsr305" groups="pdk" />
+ <project path="external/jsr330" name="platform/external/jsr330" groups="pdk" />
+ <project path="external/junit" name="platform/external/junit" groups="pdk" />
+ <project path="external/junit-params" name="platform/external/junit-params" groups="pdk" />
+ <project path="external/kernel-headers" name="platform/external/kernel-headers" groups="pdk" />
+ <project path="external/kmod" name="platform/external/kmod" groups="pdk" />
+ <project path="external/kotlinc" name="platform/external/kotlinc" groups="pdk" />
+ <project path="external/ksoap2" name="platform/external/ksoap2" groups="pdk" />
+ <project path="external/libavc" name="platform/external/libavc" groups="pdk" />
+ <project path="external/libbackup" name="platform/external/libbackup" groups="pdk" />
+ <project path="external/libbrillo" name="platform/external/libbrillo" groups="pdk" />
+ <project path="external/libcap" name="platform/external/libcap" groups="pdk" />
+ <project path="external/libcap-ng" name="platform/external/libcap-ng" groups="pdk" />
+ <project path="external/libchrome" name="platform/external/libchrome" groups="pdk" />
+ <project path="external/libconstrainedcrypto" name="platform/external/libconstrainedcrypto" groups="pdk" />
+ <project path="external/libcups" name="platform/external/libcups" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/libcxx" name="platform/external/libcxx" groups="pdk" />
+ <project path="external/libcxxabi" name="platform/external/libcxxabi" groups="pdk" />
+ <project path="external/libdaemon" name="platform/external/libdaemon" groups="pdk" />
+ <project path="external/libdivsufsort" name="platform/external/libdivsufsort" groups="pdk" />
+ <project path="external/libdrm" name="platform/external/libdrm" groups="pdk" />
+ <project path="external/libedit" name="platform/external/libedit" groups="pdk" />
+ <project path="external/libese" name="platform/external/libese" groups="pdk" />
+ <project path="external/libevent" name="platform/external/libevent" groups="pdk" />
+ <project path="external/libexif" name="platform/external/libexif" groups="pdk" />
+ <project path="external/libgsm" name="platform/external/libgsm" groups="pdk" />
+ <project path="external/libhevc" name="platform/external/libhevc" groups="pdk" />
+ <project path="external/libjpeg-turbo" name="platform/external/libjpeg-turbo" groups="pdk" />
+ <project path="external/libldac" name="platform/external/libldac" groups="pdk" />
+ <project path="external/libmicrohttpd" name="platform/external/libmicrohttpd" groups="pdk" />
+ <project path="external/libmpeg2" name="platform/external/libmpeg2" groups="pdk" />
+ <project path="external/libmtp" name="platform/external/libmtp" groups="pdk" />
+ <project path="external/libnetfilter_conntrack" name="platform/external/libnetfilter_conntrack" groups="pdk" />
+ <project path="external/libnfnetlink" name="platform/external/libnfnetlink" groups="pdk" />
+ <project path="external/libnl" name="platform/external/libnl" groups="pdk" />
+ <project path="external/libogg" name="platform/external/libogg" groups="pdk" />
+ <project path="external/libopus" name="platform/external/libopus" groups="pdk" />
+ <project path="external/libpcap" name="platform/external/libpcap" groups="pdk" />
+ <project path="external/libphonenumber" name="platform/external/libphonenumber" groups="pdk" />
+ <project path="external/libpng" name="platform/external/libpng" groups="pdk" />
+ <project path="external/libtextclassifier" name="platform/external/libtextclassifier" groups="pdk" />
+ <project path="external/libunwind" name="platform/external/libunwind" groups="pdk" />
+ <project path="external/libunwind_llvm" name="platform/external/libunwind_llvm" groups="pdk" />
+ <project path="external/libusb" name="platform/external/libusb" groups="pdk" />
+ <project path="external/libusb-compat" name="platform/external/libusb-compat" groups="pdk" />
+ <project path="external/libvncserver" name="platform/external/libvncserver" groups="pdk" />
+ <project path="external/libvorbis" name="platform/external/libvorbis" groups="pdk" />
+ <project path="external/libvpx" name="platform/external/libvpx" groups="pdk" />
+ <project path="external/libvterm" name="platform/external/libvterm" groups="pdk" />
+ <project path="external/libxcam" name="platform/external/libxcam" groups="pdk" />
+ <project path="external/libxml2" name="platform/external/libxml2" groups="pdk,libxml2" />
+ <project path="external/libyuv" name="platform/external/libyuv" groups="pdk,libyuv" />
+ <project path="external/linux-kselftest" name="platform/external/linux-kselftest" groups="vts,pdk" />
+ <project path="external/lisa" name="platform/external/lisa" groups="pdk" />
+ <project path="external/llvm" name="platform/external/llvm" groups="pdk" />
+ <project path="external/lmfit" name="platform/external/lmfit" groups="pdk" />
+ <project path="external/ltp" name="platform/external/ltp" groups="vts,pdk" />
+ <project path="external/lz4" name="platform/external/lz4" groups="pdk" />
+ <project path="external/lzma" name="platform/external/lzma" groups="pdk" />
+ <project path="external/markdown" name="platform/external/markdown" groups="pdk" />
+ <project path="external/mdnsresponder" name="platform/external/mdnsresponder" groups="pdk" />
+ <project path="external/mesa3d" name="platform/external/mesa3d" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/Microsoft-GSL" name="platform/external/Microsoft-GSL" groups="pdk" />
+ <project path="external/minijail" name="platform/external/minijail" groups="pdk" />
+ <project path="external/mksh" name="platform/external/mksh" groups="pdk" />
+ <project path="external/mmc-utils" name="platform/external/mmc-utils" groups="pdk" />
+ <project path="external/mockftpserver" name="platform/external/mockftpserver" groups="pdk" />
+ <project path="external/mockito" name="platform/external/mockito" groups="pdk" />
+ <project path="external/mockwebserver" name="platform/external/mockwebserver" groups="pdk" />
+ <project path="external/modp_b64" name="platform/external/modp_b64" groups="pdk" />
+ <project path="external/mp4parser" name="platform/external/mp4parser" groups="pdk" />
+ <project path="external/mtpd" name="platform/external/mtpd" groups="pdk" />
+ <project path="external/nanohttpd" name="platform/external/nanohttpd" groups="pdk" />
+ <project path="external/nanopb-c" name="platform/external/nanopb-c" groups="pdk" />
+ <project path="external/naver-fonts" name="platform/external/naver-fonts" groups="pdk" />
+ <project path="external/neven" name="platform/external/neven" groups="pdk" />
+ <project path="external/nfacct" name="platform/external/nfacct" groups="pdk" />
+ <project path="external/nist-pkits" name="platform/external/nist-pkits" groups="pdk" />
+ <project path="external/nist-sip" name="platform/external/nist-sip" groups="pdk" />
+ <project path="external/noto-fonts" name="platform/external/noto-fonts" groups="pdk" />
+ <project path="external/oauth" name="platform/external/oauth" groups="pdk" />
+ <project path="external/objenesis" name="platform/external/objenesis" groups="pdk" />
+ <project path="external/oj-libjdwp" name="platform/external/oj-libjdwp" groups="pdk" />
+ <project path="external/okhttp" name="platform/external/okhttp" groups="pdk" />
+ <project path="external/one-true-awk" name="platform/external/one-true-awk" groups="pdk" />
+ <project path="external/opencv" name="platform/external/opencv" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/owasp/sanitizer" name="platform/external/owasp/sanitizer" groups="pdk" />
+ <project path="external/parameter-framework" name="platform/external/parameter-framework" groups="pdk" />
+ <project path="external/pcre" name="platform/external/pcre" groups="pdk" />
+ <project path="external/pdfium" name="platform/external/pdfium" groups="pdk" />
+ <project path="external/perf_data_converter" name="platform/external/perf_data_converter" groups="pdk" />
+ <project path="external/perfetto" name="platform/external/perfetto" groups="pdk" />
+ <project path="external/piex" name="platform/external/piex" groups="pdk" />
+ <project path="external/ply" name="platform/external/ply" groups="pdk" />
+ <project path="external/ppp" name="platform/external/ppp" groups="pdk" />
+ <project path="external/proguard" name="platform/external/proguard" groups="pdk" />
+ <project path="external/protobuf" name="platform/external/protobuf" groups="pdk" />
+ <project path="external/puffin" name="platform/external/puffin" groups="pdk" />
+ <project path="external/python/appdirs" name="platform/external/python/appdirs" groups="vts,pdk" />
+ <project path="external/python/cachetools" name="platform/external/python/cachetools" groups="vts,pdk" />
+ <project path="external/python/cpython2" name="platform/external/python/cpython2" groups="pdk" />
+ <project path="external/python/cpython3" name="platform/external/python/cpython3" groups="pdk" />
+ <project path="external/python/dateutil" name="platform/external/python/dateutil" groups="pdk" />
+ <project path="external/python/dill" name="platform/external/python/dill" groups="vts,pdk" />
+ <project path="external/python/enum" name="platform/external/python/enum" groups="vts,pdk" />
+ <project path="external/python/enum34" name="platform/external/python/enum34" groups="vts,pdk" />
+ <project path="external/python/future" name="platform/external/python/future" groups="vts,pdk" />
+ <project path="external/python/futures" name="platform/external/python/futures" groups="vts,pdk" />
+ <project path="external/python/gapic-google-cloud-pubsub-v1" name="platform/external/python/gapic-google-cloud-pubsub-v1" groups="vts,pdk" />
+ <project path="external/python/google-api-python-client" name="platform/external/python/google-api-python-client" groups="vts,pdk" />
+ <project path="external/python/google-auth" name="platform/external/python/google-auth" groups="vts,pdk" />
+ <project path="external/python/google-auth-httplib2" name="platform/external/python/google-auth-httplib2" groups="vts,pdk" />
+ <project path="external/python/google-cloud-core" name="platform/external/python/google-cloud-core" groups="vts,pdk" />
+ <project path="external/python/google-cloud-pubsub" name="platform/external/python/google-cloud-pubsub" groups="vts,pdk" />
+ <project path="external/python/google-gax" name="platform/external/python/google-gax" groups="vts,pdk" />
+ <project path="external/python/googleapis" name="platform/external/python/googleapis" groups="vts,pdk" />
+ <project path="external/python/grpc-google-iam-v1" name="platform/external/python/grpc-google-iam-v1" groups="vts,pdk" />
+ <project path="external/python/grpcio" name="platform/external/python/grpcio" groups="vts,pdk" />
+ <project path="external/python/httplib2" name="platform/external/python/httplib2" groups="vts,pdk" />
+ <project path="external/python/matplotlib" name="platform/external/python/matplotlib" groups="vts,pdk" />
+ <project path="external/python/numpy" name="platform/external/python/numpy" groups="vts,pdk" />
+ <project path="external/python/oauth2client" name="platform/external/python/oauth2client" groups="vts,pdk" />
+ <project path="external/python/olefile" name="platform/external/python/olefile" groups="vts,pdk" />
+ <project path="external/python/packaging" name="platform/external/python/packaging" groups="vts,pdk" />
+ <project path="external/python/parse" name="platform/external/python/parse" groups="vts,pdk" />
+ <project path="external/python/Pillow" name="platform/external/python/Pillow" groups="vts,pdk" />
+ <project path="external/python/ply" name="platform/external/python/ply" groups="vts,pdk" />
+ <project path="external/python/proto-google-cloud-pubsub-v1" name="platform/external/python/proto-google-cloud-pubsub-v1" groups="vts,pdk" />
+ <project path="external/python/protobuf" name="platform/external/python/protobuf" groups="vts,pdk" />
+ <project path="external/python/pyasn1" name="platform/external/python/pyasn1" groups="vts,pdk" />
+ <project path="external/python/pyasn1-modules" name="platform/external/python/pyasn1-modules" groups="vts,pdk" />
+ <project path="external/python/pyparsing" name="platform/external/python/pyparsing" groups="vts,pdk" />
+ <project path="external/python/requests" name="platform/external/python/requests" groups="vts,pdk" />
+ <project path="external/python/rsa" name="platform/external/python/rsa" groups="vts,pdk" />
+ <project path="external/python/scipy" name="platform/external/python/scipy" groups="vts,pdk" />
+ <project path="external/python/setuptools" name="platform/external/python/setuptools" groups="vts,pdk" />
+ <project path="external/python/six" name="platform/external/python/six" groups="vts,pdk" />
+ <project path="external/python/uritemplates" name="platform/external/python/uritemplates" groups="vts,pdk" />
+ <project path="external/replicaisland" name="platform/external/replicaisland" groups="pdk" />
+ <project path="external/rmi4utils" name="platform/external/rmi4utils" groups="pdk" />
+ <project path="external/robolectric" name="platform/external/robolectric" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/roboto-fonts" name="platform/external/roboto-fonts" groups="pdk" />
+ <project path="external/rootdev" name="platform/external/rootdev" groups="pdk" />
+ <project path="external/safe-iop" name="platform/external/safe-iop" groups="pdk" />
+ <project path="external/scapy" name="platform/external/scapy" groups="pdk-fs" />
+ <project path="external/scrypt" name="platform/external/scrypt" groups="pdk" />
+ <project path="external/seccomp-tests" name="platform/external/seccomp-tests" groups="pdk" />
+ <project path="external/selinux" name="platform/external/selinux" groups="pdk" />
+ <project path="external/sfntly" name="platform/external/sfntly" groups="pdk,qcom_msm8x26" />
+ <project path="external/shaderc/spirv-headers" name="platform/external/shaderc/spirv-headers" groups="pdk" />
+ <project path="external/shflags" name="platform/external/shflags" groups="pdk" />
+ <project path="external/skia" name="platform/external/skia" groups="pdk,qcom_msm8x26" />
+ <project path="external/sl4a" name="platform/external/sl4a" groups="pdk" />
+ <project path="external/slf4j" name="platform/external/slf4j" groups="pdk" />
+ <project path="external/smali" name="platform/external/smali" groups="pdk" />
+ <project path="external/snakeyaml" name="platform/external/snakeyaml" groups="pdk" />
+ <project path="external/sonic" name="platform/external/sonic" groups="pdk" />
+ <project path="external/sonivox" name="platform/external/sonivox" groups="pdk" />
+ <project path="external/speex" name="platform/external/speex" groups="pdk" />
+ <project path="external/spirv-llvm" name="platform/external/spirv-llvm" groups="pdk" />
+ <project path="external/sqlite" name="platform/external/sqlite" groups="pdk" />
+ <project path="external/squashfs-tools" name="platform/external/squashfs-tools" groups="pdk" />
+ <project path="external/strace" name="platform/external/strace" groups="pdk" />
+ <project path="external/stressapptest" name="platform/external/stressapptest" groups="pdk" />
+ <project path="external/subsampling-scale-image-view" name="platform/external/subsampling-scale-image-view" clone-depth="1" />
+ <project path="external/swiftshader" name="platform/external/swiftshader" groups="pdk" />
+ <project path="external/syslinux" name="platform/external/syslinux" groups="pdk" />
+ <project path="external/tagsoup" name="platform/external/tagsoup" groups="pdk" />
+ <project path="external/tcpdump" name="platform/external/tcpdump" groups="pdk" />
+ <project path="external/tensorflow" name="platform/external/tensorflow" groups="pdk" />
+ <project path="external/testng" name="platform/external/testng" groups="pdk" />
+ <project path="external/tinyalsa" name="platform/external/tinyalsa" groups="pdk" />
+ <project path="external/tinycompress" name="platform/external/tinycompress" groups="pdk" />
+ <project path="external/tinyxml" name="platform/external/tinyxml" groups="pdk" />
+ <project path="external/tinyxml2" name="platform/external/tinyxml2" groups="pdk" />
+ <project path="external/toolchain-utils" name="platform/external/toolchain-utils" />
+ <project path="external/toybox" name="platform/external/toybox" groups="pdk" />
+ <project path="external/tpm2" name="platform/external/tpm2" groups="pdk" />
+ <project path="external/trappy" name="platform/external/trappy" groups="pdk" />
+ <project path="external/tremolo" name="platform/external/tremolo" groups="pdk" />
+ <project path="external/turbine" name="platform/external/turbine" groups="pdk" />
+ <project path="external/unicode" name="platform/external/unicode" groups="pdk" />
+ <project path="external/universal-tween-engine" name="platform/external/universal-tween-engine" />
+ <project path="external/v4l2_codec2" name="platform/external/v4l2_codec2" groups="pdk" />
+ <project path="external/v8" name="platform/external/v8" groups="pdk" />
+ <project path="external/valgrind" name="platform/external/valgrind" groups="pdk" />
+ <project path="external/vboot_reference" name="platform/external/vboot_reference" groups="vboot,pdk-fs" />
+ <project path="external/vixl" name="platform/external/vixl" groups="pdk" />
+ <project path="external/vogar" name="platform/external/vogar" groups="pdk" />
+ <project path="external/volley" name="platform/external/volley" groups="pdk" />
+ <project path="external/vulkan-validation-layers" name="platform/external/vulkan-validation-layers" groups="pdk" />
+ <project path="external/walt" name="platform/external/walt" groups="pdk" />
+ <project path="external/webp" name="platform/external/webp" groups="pdk,qcom_msm8x26" />
+ <project path="external/webrtc" name="platform/external/webrtc" groups="pdk" />
+ <project path="external/webview_support_interfaces" name="platform/external/webview_support_interfaces" groups="pdk" />
+ <project path="external/wpa_supplicant_8" name="platform/external/wpa_supplicant_8" groups="pdk" />
+ <project path="external/wycheproof" name="platform/external/wycheproof" groups="pdk" />
+ <project path="external/x264" name="platform/external/x264" groups="pdk" />
+ <project path="external/xmlrpcpp" name="platform/external/xmlrpcpp" groups="pdk" />
+ <project path="external/xmp_toolkit" name="platform/external/xmp_toolkit" groups="pdk" />
+ <project path="external/xz-embedded" name="platform/external/xz-embedded" groups="pdk" />
+ <project path="external/zlib" name="platform/external/zlib" groups="pdk" />
+ <project path="external/zopfli" name="platform/external/zopfli" groups="pdk" />
+ <project path="external/zxing" name="platform/external/zxing" groups="pdk" />
+ <project path="frameworks/av" name="platform/frameworks/av" groups="pdk" />
+ <project path="frameworks/base" name="platform/frameworks/base" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/compile/libbcc" name="platform/frameworks/compile/libbcc" groups="pdk" />
+ <project path="frameworks/compile/mclinker" name="platform/frameworks/compile/mclinker" groups="pdk" />
+ <project path="frameworks/compile/slang" name="platform/frameworks/compile/slang" groups="pdk" />
+ <project path="frameworks/data-binding" name="platform/frameworks/data-binding" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/ex" name="platform/frameworks/ex" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/hardware/interfaces" name="platform/frameworks/hardware/interfaces" groups="pdk" />
+ <project path="frameworks/layoutlib" name="platform/frameworks/layoutlib" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/minikin" name="platform/frameworks/minikin" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/ml" name="platform/frameworks/ml" groups="pdk" />
+ <project path="frameworks/multidex" name="platform/frameworks/multidex" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/native" name="platform/frameworks/native" groups="pdk" />
+ <project path="frameworks/opt/bitmap" name="platform/frameworks/opt/bitmap" groups="pdk-fs" />
+ <project path="frameworks/opt/calendar" name="platform/frameworks/opt/calendar" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/car/services" name="platform/frameworks/opt/car/services" groups="pdk-fs" />
+ <project path="frameworks/opt/chips" name="platform/frameworks/opt/chips" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/colorpicker" name="platform/frameworks/opt/colorpicker" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/datetimepicker" name="platform/frameworks/opt/datetimepicker" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/inputmethodcommon" name="platform/frameworks/opt/inputmethodcommon" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/net/ethernet" name="platform/frameworks/opt/net/ethernet" groups="pdk-fs" />
+ <project path="frameworks/opt/net/ims" name="platform/frameworks/opt/net/ims" groups="frameworks_ims,pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/net/lowpan" name="platform/frameworks/opt/net/lowpan" groups="pdk-fs" />
+ <project path="frameworks/opt/net/voip" name="platform/frameworks/opt/net/voip" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/net/wifi" name="platform/frameworks/opt/net/wifi" groups="pdk" />
+ <project path="frameworks/opt/photoviewer" name="platform/frameworks/opt/photoviewer" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/setupwizard" name="platform/frameworks/opt/setupwizard" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/telephony" name="platform/frameworks/opt/telephony" groups="pdk" />
+ <project path="frameworks/opt/timezonepicker" name="platform/frameworks/opt/timezonepicker" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/vcard" name="platform/frameworks/opt/vcard" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/rs" name="platform/frameworks/rs" groups="pdk" />
+ <project path="frameworks/support" name="platform/frameworks/support" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/webview" name="platform/frameworks/webview" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/wilhelm" name="platform/frameworks/wilhelm" groups="pdk-cw-fs,pdk-fs" />
+ <project path="hardware/akm" name="platform/hardware/akm" groups="pdk" />
+ <project path="hardware/broadcom/libbt" name="platform/hardware/broadcom/libbt" groups="pdk" />
+ <project path="hardware/broadcom/wlan" name="platform/hardware/broadcom/wlan" groups="pdk,broadcom_wlan" />
+ <project path="hardware/google/apf" name="platform/hardware/google/apf" groups="pdk" />
+ <project path="hardware/google/easel" name="platform/hardware/google/easel" groups="pdk,easel" />
+ <project path="hardware/google/interfaces" name="platform/hardware/google/interfaces" groups="pdk" />
+ <project path="hardware/intel/audio_media" name="platform/hardware/intel/audio_media" groups="intel,pdk" />
+ <project path="hardware/intel/bootstub" name="platform/hardware/intel/bootstub" groups="intel,pdk" />
+ <project path="hardware/intel/common/libmix" name="platform/hardware/intel/common/libmix" groups="intel,pdk" />
+ <project path="hardware/intel/common/libstagefrighthw" name="platform/hardware/intel/common/libstagefrighthw" groups="intel,pdk" />
+ <project path="hardware/intel/common/libva" name="platform/hardware/intel/common/libva" groups="intel,pdk" />
+ <project path="hardware/intel/common/libwsbm" name="platform/hardware/intel/common/libwsbm" groups="intel,pdk" />
+ <project path="hardware/intel/common/omx-components" name="platform/hardware/intel/common/omx-components" groups="intel,pdk" />
+ <project path="hardware/intel/common/utils" name="platform/hardware/intel/common/utils" groups="intel,pdk" />
+ <project path="hardware/intel/common/wrs_omxil_core" name="platform/hardware/intel/common/wrs_omxil_core" groups="intel,pdk" />
+ <project path="hardware/intel/img/hwcomposer" name="platform/hardware/intel/img/hwcomposer" groups="intel,pdk" />
+ <project path="hardware/intel/img/psb_headers" name="platform/hardware/intel/img/psb_headers" groups="intel,pdk" />
+ <project path="hardware/intel/img/psb_video" name="platform/hardware/intel/img/psb_video" groups="intel,pdk" />
+ <project path="hardware/interfaces" name="platform/hardware/interfaces" groups="pdk" />
+ <project path="hardware/invensense" name="platform/hardware/invensense" groups="invensense,pdk" />
+ <project path="hardware/libhardware" name="platform/hardware/libhardware" groups="pdk" />
+ <project path="hardware/libhardware_legacy" name="platform/hardware/libhardware_legacy" groups="pdk" />
+ <project path="hardware/marvell/bt" name="platform/hardware/marvell/bt" groups="marvell_bt,pdk" />
+ <project path="hardware/nxp/nfc" name="platform/hardware/nxp/nfc" groups="pdk" />
+ <project path="hardware/nxp/secure_element" name="platform/hardware/nxp/secure_element" groups="pdk" />
+ <project path="hardware/qcom/audio" name="platform/hardware/qcom/audio" groups="qcom,qcom_audio,pdk" />
+ <project path="hardware/qcom/bootctrl" name="platform/hardware/qcom/bootctrl" groups="pdk" />
+ <project path="hardware/qcom/bt" name="platform/hardware/qcom/bt" groups="qcom,pdk" />
+ <project path="hardware/qcom/camera" name="platform/hardware/qcom/camera" groups="qcom_camera,pdk" />
+ <project path="hardware/qcom/data/ipacfg-mgr" name="platform/hardware/qcom/data/ipacfg-mgr" groups="qcom,pdk" />
+ <project path="hardware/qcom/display" name="platform/hardware/qcom/display" groups="pdk,qcom,qcom_display" />
+ <project path="hardware/qcom/gps" name="platform/hardware/qcom/gps" groups="qcom,qcom_gps,pdk" />
+ <project path="hardware/qcom/keymaster" name="platform/hardware/qcom/keymaster" groups="qcom,qcom_keymaster,pdk" />
+ <project path="hardware/qcom/media" name="platform/hardware/qcom/media" groups="qcom,pdk" />
+ <project path="hardware/qcom/msm8960" name="platform/hardware/qcom/msm8960" groups="qcom_msm8960,pdk" />
+ <project path="hardware/qcom/msm8994" name="platform/hardware/qcom/msm8994" groups="qcom_msm8994,pdk" />
+ <project path="hardware/qcom/msm8996" name="platform/hardware/qcom/msm8996" groups="qcom_msm8996,pdk" />
+ <project path="hardware/qcom/msm8998" name="platform/hardware/qcom/msm8998" groups="qcom_msm8998,pdk" />
+ <project path="hardware/qcom/msm8x09" name="platform/hardware/qcom/msm8x09" groups="qcom_msm8x09" />
+ <project path="hardware/qcom/msm8x26" name="platform/hardware/qcom/msm8x26" groups="qcom_msm8x26,pdk" />
+ <project path="hardware/qcom/msm8x27" name="platform/hardware/qcom/msm8x27" groups="qcom_msm8x27,pdk" />
+ <project path="hardware/qcom/msm8x84" name="platform/hardware/qcom/msm8x84" groups="qcom_msm8x84,pdk" />
+ <project path="hardware/qcom/neuralnetworks/hvxservice" name="platform/hardware/qcom/neuralnetworks/hvxservice" groups="wahoo" />
+ <project path="hardware/qcom/power" name="platform/hardware/qcom/power" groups="qcom,pdk" />
+ <project path="hardware/qcom/wlan" name="platform/hardware/qcom/wlan" groups="qcom_wlan,pdk" />
+ <project path="hardware/ril" name="platform/hardware/ril" groups="pdk" />
+ <project path="hardware/st/nfc" name="platform/hardware/st/nfc" groups="pdk" />
+ <project path="kernel/configs" name="kernel/configs" groups="vts,pdk" />
+ <project path="kernel/tests" name="kernel/tests" />
+ <project path="libcore" name="platform/libcore" groups="pdk" />
+ <project path="libnativehelper" name="platform/libnativehelper" groups="pdk" />
+ <project path="packages/apps/BasicSmsReceiver" name="platform/packages/apps/BasicSmsReceiver" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Bluetooth" name="platform/packages/apps/Bluetooth" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Browser2" name="platform/packages/apps/Browser2" groups="pdk-fs" />
+ <project path="packages/apps/Calendar" name="platform/packages/apps/Calendar" groups="pdk-fs" />
+ <project path="packages/apps/Camera2" name="platform/packages/apps/Camera2" groups="pdk-fs" />
+ <project path="packages/apps/Car/Dialer" name="platform/packages/apps/Car/Dialer" groups="pdk-fs" />
+ <project path="packages/apps/Car/Hvac" name="platform/packages/apps/Car/Hvac" groups="pdk-fs" />
+ <project path="packages/apps/Car/LatinIME" name="platform/packages/apps/Car/LatinIME" groups="pdk-fs" />
+ <project path="packages/apps/Car/Launcher" name="platform/packages/apps/Car/Launcher" groups="pdk-fs" />
+ <project path="packages/apps/Car/LensPicker" name="platform/packages/apps/Car/LensPicker" groups="pdk-fs" />
+ <project path="packages/apps/Car/libs" name="platform/packages/apps/Car/libs" groups="pdk-fs" />
+ <project path="packages/apps/Car/LocalMediaPlayer" name="platform/packages/apps/Car/LocalMediaPlayer" groups="pdk-fs" />
+ <project path="packages/apps/Car/Media" name="platform/packages/apps/Car/Media" groups="pdk-fs" />
+ <project path="packages/apps/Car/Messenger" name="platform/packages/apps/Car/Messenger" groups="pdk-fs" />
+ <project path="packages/apps/Car/Overview" name="platform/packages/apps/Car/Overview" groups="pdk-fs" />
+ <project path="packages/apps/Car/Radio" name="platform/packages/apps/Car/Radio" groups="pdk-fs" />
+ <project path="packages/apps/Car/Settings" name="platform/packages/apps/Car/Settings" groups="pdk-fs" />
+ <project path="packages/apps/Car/Stream" name="platform/packages/apps/Car/Stream" groups="pdk-fs" />
+ <project path="packages/apps/Car/SystemUpdater" name="platform/packages/apps/Car/SystemUpdater" groups="pdk-fs" />
+ <project path="packages/apps/CarrierConfig" name="platform/packages/apps/CarrierConfig" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/CellBroadcastReceiver" name="platform/packages/apps/CellBroadcastReceiver" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/CertInstaller" name="platform/packages/apps/CertInstaller" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Contacts" name="platform/packages/apps/Contacts" groups="pdk-fs" />
+ <project path="packages/apps/DeskClock" name="platform/packages/apps/DeskClock" groups="pdk-fs" />
+ <project path="packages/apps/DevCamera" name="platform/packages/apps/DevCamera" groups="pdk" />
+ <project path="packages/apps/Dialer" name="platform/packages/apps/Dialer" groups="pdk-fs" />
+ <project path="packages/apps/DocumentsUI" name="platform/packages/apps/DocumentsUI" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Email" name="platform/packages/apps/Email" groups="pdk-fs" />
+ <project path="packages/apps/EmergencyInfo" name="platform/packages/apps/EmergencyInfo" groups="pdk-fs" />
+ <project path="packages/apps/ExactCalculator" name="platform/packages/apps/ExactCalculator" groups="pdk-fs" />
+ <project path="packages/apps/Gallery" name="platform/packages/apps/Gallery" groups="pdk-fs" />
+ <project path="packages/apps/Gallery2" name="platform/packages/apps/Gallery2" groups="pdk-fs" />
+ <project path="packages/apps/HTMLViewer" name="platform/packages/apps/HTMLViewer" groups="pdk-fs" />
+ <project path="packages/apps/KeyChain" name="platform/packages/apps/KeyChain" groups="pdk-fs" />
+ <project path="packages/apps/Launcher2" name="platform/packages/apps/Launcher2" groups="pdk-fs" />
+ <project path="packages/apps/Launcher3" name="platform/packages/apps/Launcher3" groups="pdk-fs" />
+ <project path="packages/apps/LegacyCamera" name="platform/packages/apps/LegacyCamera" groups="pdk-fs" />
+ <project path="packages/apps/ManagedProvisioning" name="platform/packages/apps/ManagedProvisioning" groups="pdk-fs" />
+ <project path="packages/apps/Messaging" name="platform/packages/apps/Messaging" groups="pdk-fs" />
+ <project path="packages/apps/Music" name="platform/packages/apps/Music" groups="pdk-fs" />
+ <project path="packages/apps/MusicFX" name="platform/packages/apps/MusicFX" groups="pdk-fs" />
+ <project path="packages/apps/Nfc" name="platform/packages/apps/Nfc" groups="apps_nfc,pdk-fs" />
+ <project path="packages/apps/OneTimeInitializer" name="platform/packages/apps/OneTimeInitializer" groups="pdk-fs" />
+ <project path="packages/apps/PackageInstaller" name="platform/packages/apps/PackageInstaller" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/PhoneCommon" name="platform/packages/apps/PhoneCommon" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Protips" name="platform/packages/apps/Protips" groups="pdk-fs" />
+ <project path="packages/apps/Provision" name="platform/packages/apps/Provision" groups="pdk-fs" />
+ <project path="packages/apps/QuickSearchBox" name="platform/packages/apps/QuickSearchBox" groups="pdk-fs" />
+ <project path="packages/apps/SafetyRegulatoryInfo" name="platform/packages/apps/SafetyRegulatoryInfo" groups="pdk-fs" />
+ <project path="packages/apps/SecureElement" name="platform/packages/apps/SecureElement" groups="apps_se,pdk-fs" />
+ <project path="packages/apps/Settings" name="platform/packages/apps/Settings" groups="pdk-fs" />
+ <project path="packages/apps/SoundRecorder" name="platform/packages/apps/SoundRecorder" groups="pdk-fs" />
+ <project path="packages/apps/SpareParts" name="platform/packages/apps/SpareParts" groups="pdk-fs" />
+ <project path="packages/apps/Stk" name="platform/packages/apps/Stk" groups="apps_stk,pdk-fs" />
+ <project path="packages/apps/StorageManager" name="platform/packages/apps/StorageManager" groups="pdk-fs" />
+ <project path="packages/apps/Tag" name="platform/packages/apps/Tag" groups="pdk-fs" />
+ <project path="packages/apps/Terminal" name="platform/packages/apps/Terminal" groups="pdk-fs" />
+ <project path="packages/apps/Test/connectivity" name="platform/packages/apps/Test/connectivity" groups="pdk" />
+ <project path="packages/apps/TimeZoneData" name="platform/packages/apps/TimeZoneData" groups="pdk" />
+ <project path="packages/apps/TimeZoneUpdater" name="platform/packages/apps/TimeZoneUpdater" groups="pdk" />
+ <project path="packages/apps/Traceur" name="platform/packages/apps/Traceur" groups="pdk-fs" />
+ <project path="packages/apps/TvSettings" name="platform/packages/apps/TvSettings" groups="pdk-fs" />
+ <project path="packages/apps/TV" name="platform/packages/apps/TV" />
+ <project path="packages/apps/UnifiedEmail" name="platform/packages/apps/UnifiedEmail" groups="pdk-fs" />
+ <project path="packages/apps/WallpaperPicker" name="platform/packages/apps/WallpaperPicker" groups="pdk-fs" />
+ <project path="packages/experimental" name="platform/packages/experimental" />
+ <project path="packages/inputmethods/LatinIME" name="platform/packages/inputmethods/LatinIME" groups="pdk-fs" />
+ <project path="packages/inputmethods/OpenWnn" name="platform/packages/inputmethods/OpenWnn" groups="pdk-fs" />
+ <project path="packages/providers/ApplicationsProvider" name="platform/packages/providers/ApplicationsProvider" groups="pdk-fs" />
+ <project path="packages/providers/BlockedNumberProvider" name="platform/packages/providers/BlockedNumberProvider" groups="pdk-fs" />
+ <project path="packages/providers/BookmarkProvider" name="platform/packages/providers/BookmarkProvider" groups="pdk-fs" />
+ <project path="packages/providers/CalendarProvider" name="platform/packages/providers/CalendarProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/CallLogProvider" name="platform/packages/providers/CallLogProvider" groups="pdk-fs" />
+ <project path="packages/providers/ContactsProvider" name="platform/packages/providers/ContactsProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/DownloadProvider" name="platform/packages/providers/DownloadProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/MediaProvider" name="platform/packages/providers/MediaProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/PartnerBookmarksProvider" name="platform/packages/providers/PartnerBookmarksProvider" groups="pdk-fs" />
+ <project path="packages/providers/TelephonyProvider" name="platform/packages/providers/TelephonyProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/TvProvider" name="platform/packages/providers/TvProvider" groups="pdk-fs" />
+ <project path="packages/providers/UserDictionaryProvider" name="platform/packages/providers/UserDictionaryProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/screensavers/Basic" name="platform/packages/screensavers/Basic" groups="pdk-fs" />
+ <project path="packages/screensavers/PhotoTable" name="platform/packages/screensavers/PhotoTable" groups="pdk-fs" />
+ <project path="packages/screensavers/WebView" name="platform/packages/screensavers/WebView" groups="pdk-fs" />
+ <project path="packages/services/BuiltInPrintService" name="platform/packages/services/BuiltInPrintService" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/Car" name="platform/packages/services/Car" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/Mms" name="platform/packages/services/Mms" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/NetworkRecommendation" name="platform/packages/services/NetworkRecommendation" groups="pdk-fs" />
+ <project path="packages/services/Telecomm" name="platform/packages/services/Telecomm" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/Telephony" name="platform/packages/services/Telephony" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/wallpapers/LivePicker" name="platform/packages/wallpapers/LivePicker" groups="pdk-fs" />
+ <project path="pdk" name="platform/pdk" groups="pdk" />
+ <project path="platform_testing" name="platform/platform_testing" groups="pdk-fs,pdk-cw-fs,cts" />
+ <project path="prebuilts/abi-dumps/ndk" name="platform/prebuilts/abi-dumps/ndk" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/abi-dumps/vndk" name="platform/prebuilts/abi-dumps/vndk" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/android-emulator" name="platform/prebuilts/android-emulator" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/build-tools" name="platform/prebuilts/build-tools" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/checkstyle" name="platform/prebuilts/checkstyle" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/clang-tools" name="platform/prebuilts/clang-tools" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/clang/host/darwin-x86" name="platform/prebuilts/clang/host/darwin-x86" groups="pdk,darwin" clone-depth="1" />
+ <project path="prebuilts/clang/host/linux-x86" name="platform/prebuilts/clang/host/linux-x86" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/deqp" name="platform/prebuilts/deqp" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/devtools" name="platform/prebuilts/devtools" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/aarch64/aarch64-linux-android-4.9" name="platform/prebuilts/gcc/darwin-x86/aarch64/aarch64-linux-android-4.9" groups="pdk,darwin,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9" name="platform/prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9" groups="pdk,darwin,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/host/i686-apple-darwin-4.2.1" name="platform/prebuilts/gcc/darwin-x86/host/i686-apple-darwin-4.2.1" groups="pdk,darwin" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/mips/mips64el-linux-android-4.9" name="platform/prebuilts/gcc/darwin-x86/mips/mips64el-linux-android-4.9" groups="pdk,darwin,mips" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/x86/x86_64-linux-android-4.9" name="platform/prebuilts/gcc/darwin-x86/x86/x86_64-linux-android-4.9" groups="pdk,darwin,x86" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9" groups="pdk,linux,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9" groups="pdk,linux,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8" groups="pdk,linux" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8" name="platform/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9" name="platform/prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9" groups="pdk,linux,mips" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9" groups="pdk,linux,x86" clone-depth="1" />
+ <project path="prebuilts/gdb/darwin-x86" name="platform/prebuilts/gdb/darwin-x86" groups="darwin" clone-depth="1" />
+ <project path="prebuilts/gdb/linux-x86" name="platform/prebuilts/gdb/linux-x86" groups="linux" clone-depth="1" />
+ <project path="prebuilts/go/darwin-x86" name="platform/prebuilts/go/darwin-x86" groups="darwin,pdk,tradefed" clone-depth="1" />
+ <project path="prebuilts/go/linux-x86" name="platform/prebuilts/go/linux-x86" groups="linux,pdk,tradefed" clone-depth="1" />
+ <project path="prebuilts/gradle-plugin" name="platform/prebuilts/gradle-plugin" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/jdk/jdk8" name="platform/prebuilts/jdk/jdk8" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/jdk/jdk9" name="platform/prebuilts/jdk/jdk9" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/libs/libedit" name="platform/prebuilts/libs/libedit" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/maven_repo/android" name="platform/prebuilts/maven_repo/android" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/maven_repo/bumptech" name="platform/prebuilts/maven_repo/bumptech" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/maven_repo/google-play-service-client-libraries-3p" name="platform/prebuilts/maven_repo/google-play-service-client-libraries-3p" clone-depth="1" />
+ <project path="prebuilts/misc" name="platform/prebuilts/misc" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/ndk" name="platform/prebuilts/ndk" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/python/darwin-x86/2.7.5" name="platform/prebuilts/python/darwin-x86/2.7.5" groups="darwin,pdk,pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/python/linux-x86/2.7.5" name="platform/prebuilts/python/linux-x86/2.7.5" groups="linux,pdk,pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/qemu-kernel" name="platform/prebuilts/qemu-kernel" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/r8" name="platform/prebuilts/r8" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/sdk" name="platform/prebuilts/sdk" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/tools" name="platform/prebuilts/tools" groups="pdk,tools" clone-depth="1" />
+ <project path="sdk" name="platform/sdk" groups="pdk-cw-fs,pdk-fs" />
+ <project path="system/bt" name="platform/system/bt" groups="pdk" />
+ <project path="system/ca-certificates" name="platform/system/ca-certificates" groups="pdk" />
+ <project path="system/chre" name="platform/system/chre" groups="pdk" />
+ <project path="system/connectivity/wificond" name="platform/system/connectivity/wificond" groups="pdk" />
+ <project path="system/connectivity/wifilogd" name="platform/system/connectivity/wifilogd" groups="pdk" />
+ <project path="system/core" name="platform/system/core" groups="pdk" />
+ <project path="system/extras" name="platform/system/extras" groups="pdk" />
+ <project path="system/gatekeeper" name="platform/system/gatekeeper" groups="pdk" />
+ <project path="system/hardware/interfaces" name="platform/system/hardware/interfaces" groups="pdk" />
+ <project path="system/hwservicemanager" name="platform/system/hwservicemanager" groups="pdk" />
+ <project path="system/iot/attestation" name="platform/system/iot/attestation" groups="pdk" />
+ <project path="system/keymaster" name="platform/system/keymaster" groups="pdk" />
+ <project path="system/libfmq" name="platform/system/libfmq" groups="pdk" />
+ <project path="system/libhidl" name="platform/system/libhidl" groups="pdk" />
+ <project path="system/libhwbinder" name="platform/system/libhwbinder" groups="pdk" />
+ <project path="system/libufdt" name="platform/system/libufdt" groups="pdk" />
+ <project path="system/libvintf" name="platform/system/libvintf" groups="pdk" />
+ <project path="system/media" name="platform/system/media" groups="pdk" />
+ <project path="system/netd" name="platform/system/netd" groups="pdk" />
+ <project path="system/nfc" name="platform/system/nfc" groups="pdk" />
+ <project path="system/nvram" name="platform/system/nvram" groups="pdk" />
+ <project path="system/security" name="platform/system/security" groups="pdk" />
+ <project path="system/sepolicy" name="platform/system/sepolicy" groups="pdk" />
+ <project path="system/timezone" name="platform/system/timezone" groups="pdk" />
+ <project path="system/tools/aidl" name="platform/system/tools/aidl" groups="pdk-cw-fs,pdk-fs" />
+ <project path="system/tools/bpt" name="platform/system/tools/bpt" groups="pdk" />
+ <project path="system/tools/hidl" name="platform/system/tools/hidl" groups="pdk" />
+ <project path="system/update_engine" name="platform/system/update_engine" groups="pdk" />
+ <project path="system/vold" name="platform/system/vold" groups="pdk" />
+ <project path="test/framework" name="platform/test/framework" groups="vts,pdk" />
+ <project path="test/mlts/benchmark" name="platform/test/mlts/benchmark" groups="pdk" />
+ <project path="test/mlts/models" name="platform/test/mlts/models" groups="pdk" />
+ <project path="test/sts" name="platform/test/sts" groups="sts,pdk" />
+ <project path="test/vti/alert" name="platform/test/vti/alert" groups="vts,pdk" />
+ <project path="test/vti/dashboard" name="platform/test/vti/dashboard" groups="vts,pdk" />
+ <project path="test/vti/fuzz_test_serving" name="platform/test/vti/fuzz_test_serving" groups="vts,pdk" />
+ <project path="test/vti/test_serving" name="platform/test/vti/test_serving" groups="vts,pdk" />
+ <project path="test/vts" name="platform/test/vts" groups="vts,pdk" />
+ <project path="test/vts-testcase/fuzz" name="platform/test/vts-testcase/fuzz" groups="vts,pdk" />
+ <project path="test/vts-testcase/hal" name="platform/test/vts-testcase/hal" groups="vts,pdk" />
+ <project path="test/vts-testcase/hal-trace" name="platform/test/vts-testcase/hal-trace" groups="vts,pdk" />
+ <project path="test/vts-testcase/kernel" name="platform/test/vts-testcase/kernel" groups="vts,pdk" />
+ <project path="test/vts-testcase/nbu" name="platform/test/vts-testcase/nbu" groups="vts,pdk" />
+ <project path="test/vts-testcase/performance" name="platform/test/vts-testcase/performance" groups="vts,pdk" />
+ <project path="test/vts-testcase/security" name="platform/test/vts-testcase/security" groups="vts,pdk" />
+ <project path="test/vts-testcase/vndk" name="platform/test/vts-testcase/vndk" groups="vts,pdk" />
+ <project path="toolchain/benchmark" name="toolchain/benchmark" />
+ <project path="toolchain/binutils" name="toolchain/binutils" groups="pdk" />
+ <project path="toolchain/pgo-profiles" name="toolchain/pgo-profiles" groups="pdk" />
+ <project path="tools/acloud" name="platform/tools/acloud" groups="tools,vts,pdk,tradefed" />
+ <project path="tools/adt/idea" name="platform/tools/adt/idea" groups="notdefault,tools" />
+ <project path="tools/apksig" name="platform/tools/apksig" groups="pdk,tradefed" />
+ <project path="tools/apkzlib" name="platform/tools/apkzlib" groups="pdk,tradefed" />
+ <project path="tools/base" name="platform/tools/base" groups="notdefault,tools" />
+ <project path="tools/build" name="platform/tools/build" groups="notdefault,tools" />
+ <project path="tools/dexter" name="platform/tools/dexter" groups="tools,pdk-fs" />
+ <project path="tools/external/fat32lib" name="platform/tools/external/fat32lib" groups="tools" />
+ <project path="tools/external/gradle" name="platform/tools/external/gradle" groups="tools" clone-depth="1" />
+ <project path="tools/idea" name="platform/tools/idea" groups="notdefault,tools" />
+ <project path="tools/loganalysis" name="platform/tools/loganalysis" groups="nopresubmit,pdk,tradefed" />
+ <project path="tools/metalava" name="platform/tools/metalava" groups="tools" />
+ <project path="tools/motodev" name="platform/tools/motodev" groups="notdefault,motodev" />
+ <project path="tools/repohooks" name="platform/tools/repohooks" groups="adt-infra,cts,developers,motodev,pdk,tools,tradefed" />
+ <project path="tools/security" name="platform/tools/security" groups="pdk,tools" />
+ <project path="tools/studio/cloud" name="platform/tools/studio/cloud" groups="notdefault,tools" />
+ <project path="tools/swt" name="platform/tools/swt" groups="notdefault,tools" />
+ <project path="tools/test/connectivity" name="platform/tools/test/connectivity" groups="pdk" />
+ <project path="tools/test/graphicsbenchmark" name="platform/tools/test/graphicsbenchmark" groups="pdk" />
+ <project path="tools/tradefederation/core" name="platform/tools/tradefederation" groups="pdk,tradefed" />
+ <project path="tools/tradefederation/contrib" name="platform/tools/tradefederation/contrib" groups="pdk,tradefed" />
+
+ <repo-hooks in-project="platform/tools/repohooks" enabled-list="pre-upload" />
+
+</manifest>
diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index 38ce92a5dc7..c40977bc4ee 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -29,8 +29,10 @@
"merge_when_pipeline_succeeds": { "type": "boolean" },
"source_branch": { "type": "string" },
"source_project_id": { "type": "integer" },
+ "source_project_full_path": { "type": ["string", "null"]},
"target_branch": { "type": "string" },
"target_project_id": { "type": "integer" },
+ "target_project_full_path": { "type": ["string", "null"]},
"allow_collaboration": { "type": "boolean"},
"metrics": {
"oneOf": [
@@ -79,6 +81,7 @@
"can_revert_on_current_merge_request": { "type": ["boolean", "null"] },
"can_cherry_pick_on_current_merge_request": { "type": ["boolean", "null"] },
"can_create_note": { "type": "boolean" },
+ "can_create_issue": { "type": "boolean" },
"can_update": { "type": "boolean" }
},
"additionalProperties": false
@@ -114,7 +117,8 @@
"rebase_in_progress": { "type": "boolean" },
"can_push_to_source_branch": { "type": "boolean" },
"rebase_path": { "type": ["string", "null"] },
- "squash": { "type": "boolean" }
+ "squash": { "type": "boolean" },
+ "test_reports_path": { "type": ["string", "null"] }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/entities/test_case.json b/spec/fixtures/api/schemas/entities/test_case.json
new file mode 100644
index 00000000000..c9ba1f3ad18
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/test_case.json
@@ -0,0 +1,15 @@
+{
+ "type": "object",
+ "required" : [
+ "status",
+ "name"
+ ],
+ "properties": {
+ "status": { "type": "string" },
+ "name": { "type": "string" },
+ "execution_time": { "type": "float" },
+ "system_output": { "type": ["string", "null"] },
+ "stack_trace": { "type": ["string", "null"] }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/test_reports_comparer.json b/spec/fixtures/api/schemas/entities/test_reports_comparer.json
new file mode 100644
index 00000000000..d7880801c01
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/test_reports_comparer.json
@@ -0,0 +1,26 @@
+{
+ "type": "object",
+ "required" : [
+ "status",
+ "summary",
+ "suites"
+ ],
+ "properties": {
+ "status": { "type": "string" },
+ "summary": {
+ "type": "object",
+ "properties": {
+ "total": { "type": "integer" },
+ "resolved": { "type": "integer" },
+ "failed": { "type": "integer" }
+ },
+ "required": [
+ "total",
+ "resolved",
+ "failed"
+ ]
+ },
+ "suites": { "type": "array", "items": { "$ref": "test_suite_comparer.json" } }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/test_suite_comparer.json b/spec/fixtures/api/schemas/entities/test_suite_comparer.json
new file mode 100644
index 00000000000..d63fea1f0db
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/test_suite_comparer.json
@@ -0,0 +1,32 @@
+{
+ "type": "object",
+ "required": [
+ "name",
+ "status",
+ "summary",
+ "new_failures",
+ "resolved_failures",
+ "existing_failures"
+ ],
+ "properties": {
+ "name": { "type": "string" },
+ "status": { "type": "string" },
+ "summary": {
+ "type": "object",
+ "properties": {
+ "total": { "type": "integer" },
+ "resolved": { "type": "integer" },
+ "failed": { "type": "integer" }
+ },
+ "required": [
+ "total",
+ "resolved",
+ "failed"
+ ]
+ },
+ "new_failures": { "type": "array", "items": { "$ref": "test_case.json" } },
+ "resolved_failures": { "type": "array", "items": { "$ref": "test_case.json" } },
+ "existing_failures": { "type": "array", "items": { "$ref": "test_case.json" } }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/pipeline.json b/spec/fixtures/api/schemas/pipeline.json
index 55511d17b5e..b6e30c40f13 100644
--- a/spec/fixtures/api/schemas/pipeline.json
+++ b/spec/fixtures/api/schemas/pipeline.json
@@ -319,6 +319,10 @@
"id": "/properties/updated_at",
"type": "string"
},
+ "web_url": {
+ "id": "/properties/web_url",
+ "type": "string"
+ },
"user": {
"id": "/properties/user",
"properties": {
diff --git a/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json b/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json
index 0d127dc5297..56f86856dd4 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json
@@ -4,13 +4,15 @@
"id",
"sha",
"ref",
- "status"
+ "status",
+ "web_url"
],
"properties" : {
"id": { "type": "integer" },
"sha": { "type": "string" },
"ref": { "type": "string" },
- "status": { "type": "string" }
+ "status": { "type": "string" },
+ "web_url": { "type": "string" }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/projects.json b/spec/fixtures/api/schemas/public_api/v4/projects.json
index 17ad8d8c48d..af5670ebd33 100644
--- a/spec/fixtures/api/schemas/public_api/v4/projects.json
+++ b/spec/fixtures/api/schemas/public_api/v4/projects.json
@@ -24,13 +24,24 @@
"avatar_url": { "type": ["string", "null"] },
"star_count": { "type": "integer" },
"forks_count": { "type": "integer" },
- "last_activity_at": { "type": "date" }
+ "last_activity_at": { "type": "date" },
+ "namespace": {
+ "type": "object",
+ "properties" : {
+ "id": { "type": "integer" },
+ "name": { "type": "string" },
+ "path": { "type": "string" },
+ "kind": { "type": "string" },
+ "full_path": { "type": "string" },
+ "parent_id": { "type": ["integer", "null"] }
+ }
+ }
},
"required": [
"id", "name", "name_with_namespace", "description", "path",
"path_with_namespace", "created_at", "default_branch", "tag_list",
"ssh_url_to_repo", "http_url_to_repo", "web_url", "avatar_url",
- "star_count", "last_activity_at"
+ "star_count", "last_activity_at", "namespace"
],
"additionalProperties": false
}
diff --git a/spec/fixtures/emails/commands_in_reply.eml b/spec/fixtures/emails/commands_in_reply.eml
index 712f6f797b4..6a467ccbbc6 100644
--- a/spec/fixtures/emails/commands_in_reply.eml
+++ b/spec/fixtures/emails/commands_in_reply.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/commands_only_reply.eml b/spec/fixtures/emails/commands_only_reply.eml
index 2d2e2f94290..9b8d25c406e 100644
--- a/spec/fixtures/emails/commands_only_reply.eml
+++ b/spec/fixtures/emails/commands_only_reply.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
index 39d5cefbc2a..609d9d9e93d 100644
--- a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
+++ b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml
index 6823db0cfc8..7be1ed5bf44 100644
--- a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml
+++ b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>,<exchange@microsoft.com>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>,<exchange@microsoft.com>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/update_commands_only_reply.eml b/spec/fixtures/emails/update_commands_only_reply.eml
index bb0d2b0e03a..927a62f6475 100644
--- a/spec/fixtures/emails/update_commands_only_reply.eml
+++ b/spec/fixtures/emails/update_commands_only_reply.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/valid_reply.eml b/spec/fixtures/emails/valid_reply.eml
index 980e10a8812..5fbeebdb6b0 100644
--- a/spec/fixtures/emails/valid_reply.eml
+++ b/spec/fixtures/emails/valid_reply.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/importers/bitbucket_server/activities.json b/spec/fixtures/importers/bitbucket_server/activities.json
new file mode 100644
index 00000000000..09adfca9f31
--- /dev/null
+++ b/spec/fixtures/importers/bitbucket_server/activities.json
@@ -0,0 +1,1121 @@
+{
+ "isLastPage": true,
+ "limit": 25,
+ "size": 8,
+ "start": 0,
+ "values": [
+ {
+ "action": "COMMENTED",
+ "comment": {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [
+ {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [
+ {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [],
+ "createdDate": 1530164016725,
+ "id": 11,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [
+ {
+ "anchor": {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "createdDate": 1530164016725,
+ "id": 11,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "text": "Ok",
+ "type": "COMMENT",
+ "updatedDate": 1530164016725,
+ "version": 0
+ },
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "createdDate": 1530164026000,
+ "id": 1,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true,
+ "transitionable": true
+ },
+ "state": "OPEN",
+ "text": "here's a task"
+ }
+ ],
+ "text": "Ok",
+ "updatedDate": 1530164016725,
+ "version": 0
+ },
+ {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [],
+ "createdDate": 1530165543990,
+ "id": 12,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "hi",
+ "updatedDate": 1530165543990,
+ "version": 0
+ }
+ ],
+ "createdDate": 1530164013718,
+ "id": 10,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "Hello world",
+ "updatedDate": 1530164013718,
+ "version": 0
+ },
+ {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [],
+ "createdDate": 1530165549932,
+ "id": 13,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "hello",
+ "updatedDate": 1530165549932,
+ "version": 0
+ }
+ ],
+ "createdDate": 1530161499144,
+ "id": 9,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "is this a new line?",
+ "updatedDate": 1530161499144,
+ "version": 0
+ },
+ "commentAction": "ADDED",
+ "commentAnchor": {
+ "diffType": "EFFECTIVE",
+ "fileType": "TO",
+ "fromHash": "c5f4288162e2e6218180779c7f6ac1735bb56eab",
+ "line": 1,
+ "lineType": "ADDED",
+ "orphaned": false,
+ "path": "CHANGELOG.md",
+ "toHash": "a4c2164330f2549f67c13f36a93884cf66e976be"
+ },
+ "createdDate": 1530161499144,
+ "diff": {
+ "destination": {
+ "components": [
+ "CHANGELOG.md"
+ ],
+ "extension": "md",
+ "name": "CHANGELOG.md",
+ "parent": "",
+ "toString": "CHANGELOG.md"
+ },
+ "hunks": [
+ {
+ "destinationLine": 1,
+ "destinationSpan": 11,
+ "segments": [
+ {
+ "lines": [
+ {
+ "commentIds": [
+ 9
+ ],
+ "destination": 1,
+ "line": "# Edit 1",
+ "source": 1,
+ "truncated": false
+ },
+ {
+ "destination": 2,
+ "line": "",
+ "source": 1,
+ "truncated": false
+ }
+ ],
+ "truncated": false,
+ "type": "ADDED"
+ },
+ {
+ "lines": [
+ {
+ "destination": 3,
+ "line": "# ChangeLog",
+ "source": 1,
+ "truncated": false
+ },
+ {
+ "destination": 4,
+ "line": "",
+ "source": 2,
+ "truncated": false
+ },
+ {
+ "destination": 5,
+ "line": "This log summarizes the changes in each released version of rouge. The versioning scheme",
+ "source": 3,
+ "truncated": false
+ },
+ {
+ "destination": 6,
+ "line": "we use is semver, although we will often release new lexers in minor versions, as a",
+ "source": 4,
+ "truncated": false
+ },
+ {
+ "destination": 7,
+ "line": "practical matter.",
+ "source": 5,
+ "truncated": false
+ },
+ {
+ "destination": 8,
+ "line": "",
+ "source": 6,
+ "truncated": false
+ },
+ {
+ "destination": 9,
+ "line": "## version TBD: (unreleased)",
+ "source": 7,
+ "truncated": false
+ },
+ {
+ "destination": 10,
+ "line": "",
+ "source": 8,
+ "truncated": false
+ },
+ {
+ "destination": 11,
+ "line": "* General",
+ "source": 9,
+ "truncated": false
+ }
+ ],
+ "truncated": false,
+ "type": "CONTEXT"
+ }
+ ],
+ "sourceLine": 1,
+ "sourceSpan": 9,
+ "truncated": false
+ }
+ ],
+ "properties": {
+ "current": true,
+ "fromHash": "c5f4288162e2e6218180779c7f6ac1735bb56eab",
+ "toHash": "a4c2164330f2549f67c13f36a93884cf66e976be"
+ },
+ "source": null,
+ "truncated": false
+ },
+ "id": 19,
+ "user": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ }
+ },
+ {
+ "action": "COMMENTED",
+ "comment": {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [],
+ "createdDate": 1530053198463,
+ "id": 7,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "What about this line?",
+ "updatedDate": 1530053198463,
+ "version": 0
+ },
+ "commentAction": "ADDED",
+ "commentAnchor": {
+ "diffType": "EFFECTIVE",
+ "fileType": "FROM",
+ "fromHash": "c5f4288162e2e6218180779c7f6ac1735bb56eab",
+ "line": 9,
+ "lineType": "CONTEXT",
+ "orphaned": false,
+ "path": "CHANGELOG.md",
+ "toHash": "a4c2164330f2549f67c13f36a93884cf66e976be"
+ },
+ "createdDate": 1530053198463,
+ "diff": {
+ "destination": {
+ "components": [
+ "CHANGELOG.md"
+ ],
+ "extension": "md",
+ "name": "CHANGELOG.md",
+ "parent": "",
+ "toString": "CHANGELOG.md"
+ },
+ "hunks": [
+ {
+ "destinationLine": 1,
+ "destinationSpan": 12,
+ "segments": [
+ {
+ "lines": [
+ {
+ "destination": 1,
+ "line": "# Edit 1",
+ "source": 1,
+ "truncated": false
+ },
+ {
+ "destination": 2,
+ "line": "",
+ "source": 1,
+ "truncated": false
+ }
+ ],
+ "truncated": false,
+ "type": "ADDED"
+ },
+ {
+ "lines": [
+ {
+ "destination": 3,
+ "line": "# ChangeLog",
+ "source": 1,
+ "truncated": false
+ },
+ {
+ "destination": 4,
+ "line": "",
+ "source": 2,
+ "truncated": false
+ },
+ {
+ "destination": 5,
+ "line": "This log summarizes the changes in each released version of rouge. The versioning scheme",
+ "source": 3,
+ "truncated": false
+ },
+ {
+ "destination": 6,
+ "line": "we use is semver, although we will often release new lexers in minor versions, as a",
+ "source": 4,
+ "truncated": false
+ },
+ {
+ "destination": 7,
+ "line": "practical matter.",
+ "source": 5,
+ "truncated": false
+ },
+ {
+ "destination": 8,
+ "line": "",
+ "source": 6,
+ "truncated": false
+ },
+ {
+ "destination": 9,
+ "line": "## version TBD: (unreleased)",
+ "source": 7,
+ "truncated": false
+ },
+ {
+ "destination": 10,
+ "line": "",
+ "source": 8,
+ "truncated": false
+ },
+ {
+ "commentIds": [
+ 7
+ ],
+ "destination": 11,
+ "line": "* General",
+ "source": 9,
+ "truncated": false
+ },
+ {
+ "destination": 12,
+ "line": " * Load pastie theme ([#809](https://github.com/jneen/rouge/pull/809) by rramsden)",
+ "source": 10,
+ "truncated": false
+ }
+ ],
+ "truncated": false,
+ "type": "CONTEXT"
+ }
+ ],
+ "sourceLine": 1,
+ "sourceSpan": 10,
+ "truncated": false
+ }
+ ],
+ "properties": {
+ "current": true,
+ "fromHash": "c5f4288162e2e6218180779c7f6ac1735bb56eab",
+ "toHash": "a4c2164330f2549f67c13f36a93884cf66e976be"
+ },
+ "source": null,
+ "truncated": false
+ },
+ "id": 14,
+ "user": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ }
+ },
+ {
+ "action": "COMMENTED",
+ "comment": {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [
+ {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [
+ {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [],
+ "createdDate": 1530143330513,
+ "id": 8,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "How about this?",
+ "updatedDate": 1530143330513,
+ "version": 0
+ }
+ ],
+ "createdDate": 1530053193795,
+ "id": 6,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "It does.",
+ "updatedDate": 1530053193795,
+ "version": 0
+ }
+ ],
+ "createdDate": 1530053187904,
+ "id": 5,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "Does this line make sense?",
+ "updatedDate": 1530053187904,
+ "version": 0
+ },
+ "commentAction": "ADDED",
+ "commentAnchor": {
+ "diffType": "EFFECTIVE",
+ "fileType": "FROM",
+ "fromHash": "c5f4288162e2e6218180779c7f6ac1735bb56eab",
+ "line": 3,
+ "lineType": "CONTEXT",
+ "orphaned": false,
+ "path": "CHANGELOG.md",
+ "toHash": "a4c2164330f2549f67c13f36a93884cf66e976be"
+ },
+ "createdDate": 1530053187904,
+ "diff": {
+ "destination": {
+ "components": [
+ "CHANGELOG.md"
+ ],
+ "extension": "md",
+ "name": "CHANGELOG.md",
+ "parent": "",
+ "toString": "CHANGELOG.md"
+ },
+ "hunks": [
+ {
+ "destinationLine": 1,
+ "destinationSpan": 12,
+ "segments": [
+ {
+ "lines": [
+ {
+ "destination": 1,
+ "line": "# Edit 1",
+ "source": 1,
+ "truncated": false
+ },
+ {
+ "destination": 2,
+ "line": "",
+ "source": 1,
+ "truncated": false
+ }
+ ],
+ "truncated": false,
+ "type": "ADDED"
+ },
+ {
+ "lines": [
+ {
+ "destination": 3,
+ "line": "# ChangeLog",
+ "source": 1,
+ "truncated": false
+ },
+ {
+ "destination": 4,
+ "line": "",
+ "source": 2,
+ "truncated": false
+ },
+ {
+ "commentIds": [
+ 5
+ ],
+ "destination": 5,
+ "line": "This log summarizes the changes in each released version of rouge. The versioning scheme",
+ "source": 3,
+ "truncated": false
+ },
+ {
+ "destination": 6,
+ "line": "we use is semver, although we will often release new lexers in minor versions, as a",
+ "source": 4,
+ "truncated": false
+ },
+ {
+ "destination": 7,
+ "line": "practical matter.",
+ "source": 5,
+ "truncated": false
+ },
+ {
+ "destination": 8,
+ "line": "",
+ "source": 6,
+ "truncated": false
+ },
+ {
+ "destination": 9,
+ "line": "## version TBD: (unreleased)",
+ "source": 7,
+ "truncated": false
+ },
+ {
+ "destination": 10,
+ "line": "",
+ "source": 8,
+ "truncated": false
+ },
+ {
+ "destination": 11,
+ "line": "* General",
+ "source": 9,
+ "truncated": false
+ },
+ {
+ "destination": 12,
+ "line": " * Load pastie theme ([#809](https://github.com/jneen/rouge/pull/809) by rramsden)",
+ "source": 10,
+ "truncated": false
+ }
+ ],
+ "truncated": false,
+ "type": "CONTEXT"
+ }
+ ],
+ "sourceLine": 1,
+ "sourceSpan": 10,
+ "truncated": false
+ }
+ ],
+ "properties": {
+ "current": true,
+ "fromHash": "c5f4288162e2e6218180779c7f6ac1735bb56eab",
+ "toHash": "a4c2164330f2549f67c13f36a93884cf66e976be"
+ },
+ "source": null,
+ "truncated": false
+ },
+ "id": 12,
+ "user": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ }
+ },
+ {
+ "action": "COMMENTED",
+ "comment": {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [],
+ "createdDate": 1529813304164,
+ "id": 4,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "Hello world",
+ "updatedDate": 1529813304164,
+ "version": 0
+ },
+ "commentAction": "ADDED",
+ "createdDate": 1529813304164,
+ "id": 11,
+ "user": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ }
+ },
+ {
+ "action": "MERGED",
+ "commit": {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "authorTimestamp": 1529727872000,
+ "committer": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "committerTimestamp": 1529727872000,
+ "displayId": "839fa9a2d43",
+ "id": "839fa9a2d434eb697815b8fcafaecc51accfdbbc",
+ "message": "Merge pull request #1 in TEST/rouge from root/CHANGELOGmd-1529725646923 to master\n\n* commit '66fbe6a097803f0acb7342b19563f710657ce5a2':\n CHANGELOG.md edited online with Bitbucket",
+ "parents": [
+ {
+ "author": {
+ "emailAddress": "dblessing@users.noreply.github.com",
+ "name": "Drew Blessing"
+ },
+ "authorTimestamp": 1529604583000,
+ "committer": {
+ "emailAddress": "noreply@github.com",
+ "name": "GitHub"
+ },
+ "committerTimestamp": 1529604583000,
+ "displayId": "c5f4288162e",
+ "id": "c5f4288162e2e6218180779c7f6ac1735bb56eab",
+ "message": "Merge pull request #949 from jneen/dblessing-patch-1\n\nAdd 'obj-c', 'obj_c' as ObjectiveC aliases",
+ "parents": [
+ {
+ "displayId": "ea7675f741e",
+ "id": "ea7675f741ee28f3f177ff32a9bde192742ffc59"
+ },
+ {
+ "displayId": "386b95a977b",
+ "id": "386b95a977b331e267497aa5206861774656f0c5"
+ }
+ ]
+ },
+ {
+ "author": {
+ "emailAddress": "test.user@example.com",
+ "name": "root"
+ },
+ "authorTimestamp": 1529725651000,
+ "committer": {
+ "emailAddress": "test.user@example.com",
+ "name": "root"
+ },
+ "committerTimestamp": 1529725651000,
+ "displayId": "66fbe6a0978",
+ "id": "66fbe6a097803f0acb7342b19563f710657ce5a2",
+ "message": "CHANGELOG.md edited online with Bitbucket",
+ "parents": [
+ {
+ "displayId": "c5f4288162e",
+ "id": "c5f4288162e2e6218180779c7f6ac1735bb56eab"
+ }
+ ]
+ }
+ ]
+ },
+ "createdDate": 1529727872302,
+ "id": 7,
+ "user": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ }
+ },
+ {
+ "action": "COMMENTED",
+ "comment": {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [
+ {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [],
+ "createdDate": 1529813297478,
+ "id": 3,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "This is a thread",
+ "updatedDate": 1529813297478,
+ "version": 0
+ }
+ ],
+ "createdDate": 1529725692591,
+ "id": 2,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "What about this?",
+ "updatedDate": 1529725692591,
+ "version": 0
+ },
+ "commentAction": "ADDED",
+ "createdDate": 1529725692591,
+ "id": 6,
+ "user": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ }
+ },
+ {
+ "action": "COMMENTED",
+ "comment": {
+ "author": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ },
+ "comments": [],
+ "createdDate": 1529725685910,
+ "id": 1,
+ "permittedOperations": {
+ "deletable": true,
+ "editable": true
+ },
+ "properties": {
+ "repositoryId": 1
+ },
+ "tasks": [],
+ "text": "This is a test.\n\n[analyze.json](attachment:1/1f32f09d97%2Fanalyze.json)\n",
+ "updatedDate": 1529725685910,
+ "version": 0
+ },
+ "commentAction": "ADDED",
+ "createdDate": 1529725685910,
+ "id": 5,
+ "user": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ }
+ },
+ {
+ "action": "OPENED",
+ "createdDate": 1529725657542,
+ "id": 4,
+ "user": {
+ "active": true,
+ "displayName": "root",
+ "emailAddress": "test.user@example.com",
+ "id": 1,
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name": "root",
+ "slug": "root",
+ "type": "NORMAL"
+ }
+ }
+ ]
+}
diff --git a/spec/fixtures/importers/bitbucket_server/pull_request.json b/spec/fixtures/importers/bitbucket_server/pull_request.json
new file mode 100644
index 00000000000..6c7fcf3b04c
--- /dev/null
+++ b/spec/fixtures/importers/bitbucket_server/pull_request.json
@@ -0,0 +1,146 @@
+{
+ "author":{
+ "approved":false,
+ "role":"AUTHOR",
+ "status":"UNAPPROVED",
+ "user":{
+ "active":true,
+ "displayName":"root",
+ "emailAddress":"joe.montana@49ers.com",
+ "id":1,
+ "links":{
+ "self":[
+ {
+ "href":"http://localhost:7990/users/root"
+ }
+ ]
+ },
+ "name":"root",
+ "slug":"root",
+ "type":"NORMAL"
+ }
+ },
+ "closed":true,
+ "closedDate":1530600648850,
+ "createdDate":1530600635690,
+ "description":"Test",
+ "fromRef":{
+ "displayId":"root/CODE_OF_CONDUCTmd-1530600625006",
+ "id":"refs/heads/root/CODE_OF_CONDUCTmd-1530600625006",
+ "latestCommit":"074e2b4dddc5b99df1bf9d4a3f66cfc15481fdc8",
+ "repository":{
+ "forkable":true,
+ "id":1,
+ "links":{
+ "clone":[
+ {
+ "href":"http://root@localhost:7990/scm/test/rouge.git",
+ "name":"http"
+ },
+ {
+ "href":"ssh://git@localhost:7999/test/rouge.git",
+ "name":"ssh"
+ }
+ ],
+ "self":[
+ {
+ "href":"http://localhost:7990/projects/TEST/repos/rouge/browse"
+ }
+ ]
+ },
+ "name":"rouge",
+ "project":{
+ "description":"Test",
+ "id":1,
+ "key":"TEST",
+ "links":{
+ "self":[
+ {
+ "href":"http://localhost:7990/projects/TEST"
+ }
+ ]
+ },
+ "name":"test",
+ "public":false,
+ "type":"NORMAL"
+ },
+ "public":false,
+ "scmId":"git",
+ "slug":"rouge",
+ "state":"AVAILABLE",
+ "statusMessage":"Available"
+ }
+ },
+ "id":7,
+ "links":{
+ "self":[
+ {
+ "href":"http://localhost:7990/projects/TEST/repos/rouge/pull-requests/7"
+ }
+ ]
+ },
+ "locked":false,
+ "open":false,
+ "participants":[
+
+ ],
+ "properties":{
+ "commentCount":1,
+ "openTaskCount":0,
+ "resolvedTaskCount":0
+ },
+ "reviewers":[
+
+ ],
+ "state":"MERGED",
+ "title":"Added a new line",
+ "toRef":{
+ "displayId":"master",
+ "id":"refs/heads/master",
+ "latestCommit":"839fa9a2d434eb697815b8fcafaecc51accfdbbc",
+ "repository":{
+ "forkable":true,
+ "id":1,
+ "links":{
+ "clone":[
+ {
+ "href":"http://root@localhost:7990/scm/test/rouge.git",
+ "name":"http"
+ },
+ {
+ "href":"ssh://git@localhost:7999/test/rouge.git",
+ "name":"ssh"
+ }
+ ],
+ "self":[
+ {
+ "href":"http://localhost:7990/projects/TEST/repos/rouge/browse"
+ }
+ ]
+ },
+ "name":"rouge",
+ "project":{
+ "description":"Test",
+ "id":1,
+ "key":"TEST",
+ "links":{
+ "self":[
+ {
+ "href":"http://localhost:7990/projects/TEST"
+ }
+ ]
+ },
+ "name":"test",
+ "public":false,
+ "type":"NORMAL"
+ },
+ "public":false,
+ "scmId":"git",
+ "slug":"rouge",
+ "state":"AVAILABLE",
+ "statusMessage":"Available"
+ }
+ },
+ "updatedDate":1530600648850,
+ "version":2
+}
diff --git a/spec/fixtures/junit/junit.xml b/spec/fixtures/junit/junit.xml
new file mode 100644
index 00000000000..b826afdc3ae
--- /dev/null
+++ b/spec/fixtures/junit/junit.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<testsuite name="rspec" tests="4" skipped="0" failures="2" errors="0" time="0.011289" timestamp="2018-07-17T10:48:13+00:00" hostname="runner-400e3f62-project-15-concurrent-0">
+<properties>
+<property name="seed" value="404"/>
+</properties>
+<testcase classname="spec.test_spec" name="Test#sum when a is 1 and b is 2 returns summary" file="./spec/test_spec.rb" time="0.009292"><failure message="
+expected: 3
+ got: -1
+
+(compared using ==)
+" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: is_expected.to eq(3)
+
+ expected: 3
+ got: -1
+
+ (compared using ==)
+./spec/test_spec.rb:12:in `block (4 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#sum when a is 100 and b is 200 returns summary" file="./spec/test_spec.rb" time="0.000180"><failure message="
+expected: 300
+ got: -100
+
+(compared using ==)
+" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: is_expected.to eq(300)
+
+ expected: 300
+ got: -100
+
+ (compared using ==)
+./spec/test_spec.rb:21:in `block (4 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract when a is 1 and b is 2 raises an error" file="./spec/test_spec.rb" time="0.000748"></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract when a is 2 and b is 1 returns correct result" file="./spec/test_spec.rb" time="0.000064"></testcase>
+</testsuite>
diff --git a/spec/fixtures/junit/junit.xml.gz b/spec/fixtures/junit/junit.xml.gz
new file mode 100644
index 00000000000..88b7de6fa61
--- /dev/null
+++ b/spec/fixtures/junit/junit.xml.gz
Binary files differ
diff --git a/spec/fixtures/junit/junit_ant.xml.gz b/spec/fixtures/junit/junit_ant.xml.gz
new file mode 100644
index 00000000000..e9cca1b0c73
--- /dev/null
+++ b/spec/fixtures/junit/junit_ant.xml.gz
Binary files differ
diff --git a/spec/fixtures/junit/junit_with_corrupted_data.xml.gz b/spec/fixtures/junit/junit_with_corrupted_data.xml.gz
new file mode 100644
index 00000000000..e6d17e4595d
--- /dev/null
+++ b/spec/fixtures/junit/junit_with_corrupted_data.xml.gz
Binary files differ
diff --git a/spec/fixtures/junit/junit_with_three_testsuites.xml.gz b/spec/fixtures/junit/junit_with_three_testsuites.xml.gz
new file mode 100644
index 00000000000..aa4ad154de8
--- /dev/null
+++ b/spec/fixtures/junit/junit_with_three_testsuites.xml.gz
Binary files differ
diff --git a/spec/fixtures/junit/junit_with_three_testsuites_1.xml b/spec/fixtures/junit/junit_with_three_testsuites_1.xml
new file mode 100644
index 00000000000..5f31824042e
--- /dev/null
+++ b/spec/fixtures/junit/junit_with_three_testsuites_1.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<testsuite name="rspec" tests="2" skipped="0" failures="0" errors="0" time="0.001670" timestamp="2018-07-30T10:02:37+00:00" hostname="runner-7661726c-project-14-concurrent-0">
+<properties>
+<property name="seed" value="52549"/>
+</properties>
+<testcase classname="spec.hash_scan_spec" name="HashScan#scan when argument is hash returns the value" file="./spec/hash_scan_spec.rb" time="0.000287"></testcase>
+<testcase classname="spec.hash_scan_spec" name="HashScan#scan when argument is not hash raises and error" file="./spec/hash_scan_spec.rb" time="0.000686"></testcase>
+</testsuite>
diff --git a/spec/fixtures/junit/junit_with_three_testsuites_2.xml b/spec/fixtures/junit/junit_with_three_testsuites_2.xml
new file mode 100644
index 00000000000..8ae771978e0
--- /dev/null
+++ b/spec/fixtures/junit/junit_with_three_testsuites_2.xml
@@ -0,0 +1,6010 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<testsuite name="rspec" tests="1004" skipped="0" failures="1000" errors="0" time="0.202645" timestamp="2018-07-30T10:02:36+00:00" hostname="runner-7661726c-project-14-concurrent-0">
+<properties>
+<property name="seed" value="28152"/>
+</properties>
+<testcase classname="spec.test_spec" name="Test#sum when a is 1 and b is 2 returns summary" file="./spec/test_spec.rb" time="0.000368"></testcase>
+<testcase classname="spec.test_spec" name="Test#sum when a is 100 and b is 200 returns summary" file="./spec/test_spec.rb" time="0.000069"></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract when a is 1 and b is 2 raises an error" file="./spec/test_spec.rb" time="0.000734"></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract when a is 2 and b is 1 returns correct result" file="./spec/test_spec.rb" time="0.000066"></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract1 fails" file="./spec/test_spec.rb" time="0.009856"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:52:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract2 fails" file="./spec/test_spec.rb" time="0.000185"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:59:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract3 fails" file="./spec/test_spec.rb" time="0.000102"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:66:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract4 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:73:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract5 fails" file="./spec/test_spec.rb" time="0.000100"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:80:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract6 fails" file="./spec/test_spec.rb" time="0.000101"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:87:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract7 fails" file="./spec/test_spec.rb" time="0.000099"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:94:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract8 fails" file="./spec/test_spec.rb" time="0.000101"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:101:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract9 fails" file="./spec/test_spec.rb" time="0.000101"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:108:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract10 fails" file="./spec/test_spec.rb" time="0.013083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:115:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract11 fails" file="./spec/test_spec.rb" time="0.000117"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:122:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract12 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:129:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract13 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:136:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract14 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:143:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract15 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:150:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract16 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:157:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract17 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:164:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract18 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:171:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract19 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:178:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract20 fails" file="./spec/test_spec.rb" time="0.000099"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:185:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract21 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:192:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract22 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:199:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract23 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:206:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract24 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:213:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract25 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:220:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract26 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:227:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract27 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:234:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract28 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:241:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract29 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:248:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract30 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:255:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract31 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:262:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract32 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:269:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract33 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:276:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract34 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:283:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract35 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:290:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract36 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:297:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract37 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:304:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract38 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:311:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract39 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:318:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract40 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:325:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract41 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:332:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract42 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:339:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract43 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:346:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract44 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:353:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract45 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:360:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract46 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:367:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract47 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:374:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract48 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:381:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract49 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:388:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract50 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:395:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract51 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:402:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract52 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:409:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract53 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:416:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract54 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:423:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract55 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:430:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract56 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:437:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract57 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:444:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract58 fails" file="./spec/test_spec.rb" time="0.000110"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:451:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract59 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:458:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract60 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:465:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract61 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:472:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract62 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:479:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract63 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:486:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract64 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:493:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract65 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:500:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract66 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:507:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract67 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:514:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract68 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:521:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract69 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:528:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract70 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:535:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract71 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:542:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract72 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:549:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract73 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:556:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract74 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:563:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract75 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:570:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract76 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:577:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract77 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:584:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract78 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:591:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract79 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:598:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract80 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:605:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract81 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:612:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract82 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:619:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract83 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:626:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract84 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:633:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract85 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:640:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract86 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:647:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract87 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:654:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract88 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:661:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract89 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:668:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract90 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:675:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract91 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:682:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract92 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:689:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract93 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:696:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract94 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:703:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract95 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:710:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract96 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:717:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract97 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:724:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract98 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:731:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract99 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:738:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract100 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:745:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract101 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:752:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract102 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:759:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract103 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:766:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract104 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:773:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract105 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:780:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract106 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:787:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract107 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:794:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract108 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:801:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract109 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:808:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract110 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:815:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract111 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:822:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract112 fails" file="./spec/test_spec.rb" time="0.000099"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:829:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract113 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:836:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract114 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:843:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract115 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:850:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract116 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:857:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract117 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:864:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract118 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:871:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract119 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:878:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract120 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:885:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract121 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:892:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract122 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:899:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract123 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:906:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract124 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:913:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract125 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:920:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract126 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:927:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract127 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:934:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract128 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:941:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract129 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:948:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract130 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:955:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract131 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:962:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract132 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:969:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract133 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:976:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract134 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:983:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract135 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:990:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract136 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:997:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract137 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1004:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract138 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1011:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract139 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1018:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract140 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1025:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract141 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1032:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract142 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1039:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract143 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1046:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract144 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1053:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract145 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1060:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract146 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1067:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract147 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1074:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract148 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1081:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract149 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1088:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract150 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1095:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract151 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1102:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract152 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1109:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract153 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1116:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract154 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1123:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract155 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1130:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract156 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1137:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract157 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1144:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract158 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1151:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract159 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1158:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract160 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1165:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract161 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1172:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract162 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1179:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract163 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1186:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract164 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1193:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract165 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1200:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract166 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1207:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract167 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1214:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract168 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1221:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract169 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1228:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract170 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1235:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract171 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1242:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract172 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1249:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract173 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1256:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract174 fails" file="./spec/test_spec.rb" time="0.000101"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1263:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract175 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1270:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract176 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1277:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract177 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1284:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract178 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1291:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract179 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1298:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract180 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1305:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract181 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1312:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract182 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1319:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract183 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1326:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract184 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1333:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract185 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1340:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract186 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1347:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract187 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1354:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract188 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1361:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract189 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1368:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract190 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1375:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract191 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1382:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract192 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1389:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract193 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1396:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract194 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1403:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract195 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1410:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract196 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1417:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract197 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1424:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract198 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1431:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract199 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1438:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract200 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1445:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract201 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1452:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract202 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1459:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract203 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1466:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract204 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1473:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract205 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1480:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract206 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1487:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract207 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1494:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract208 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1501:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract209 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1508:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract210 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1515:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract211 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1522:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract212 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1529:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract213 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1536:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract214 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1543:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract215 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1550:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract216 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1557:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract217 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1564:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract218 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1571:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract219 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1578:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract220 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1585:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract221 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1592:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract222 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1599:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract223 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1606:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract224 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1613:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract225 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1620:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract226 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1627:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract227 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1634:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract228 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1641:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract229 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1648:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract230 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1655:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract231 fails" file="./spec/test_spec.rb" time="0.000102"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1662:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract232 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1669:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract233 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1676:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract234 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1683:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract235 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1690:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract236 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1697:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract237 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1704:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract238 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1711:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract239 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1718:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract240 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1725:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract241 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1732:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract242 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1739:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract243 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1746:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract244 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1753:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract245 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1760:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract246 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1767:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract247 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1774:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract248 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1781:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract249 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1788:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract250 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1795:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract251 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1802:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract252 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1809:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract253 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1816:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract254 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1823:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract255 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1830:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract256 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1837:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract257 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1844:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract258 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1851:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract259 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1858:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract260 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1865:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract261 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1872:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract262 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1879:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract263 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1886:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract264 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1893:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract265 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1900:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract266 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1907:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract267 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1914:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract268 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1921:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract269 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1928:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract270 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1935:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract271 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1942:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract272 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1949:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract273 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1956:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract274 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1963:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract275 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1970:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract276 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1977:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract277 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1984:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract278 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1991:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract279 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:1998:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract280 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2005:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract281 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2012:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract282 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2019:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract283 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2026:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract284 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2033:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract285 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2040:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract286 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2047:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract287 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2054:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract288 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2061:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract289 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2068:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract290 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2075:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract291 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2082:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract292 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2089:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract293 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2096:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract294 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2103:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract295 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2110:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract296 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2117:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract297 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2124:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract298 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2131:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract299 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2138:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract300 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2145:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract301 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2152:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract302 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2159:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract303 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2166:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract304 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2173:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract305 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2180:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract306 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2187:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract307 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2194:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract308 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2201:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract309 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2208:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract310 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2215:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract311 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2222:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract312 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2229:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract313 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2236:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract314 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2243:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract315 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2250:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract316 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2257:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract317 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2264:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract318 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2271:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract319 fails" file="./spec/test_spec.rb" time="0.000099"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2278:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract320 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2285:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract321 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2292:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract322 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2299:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract323 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2306:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract324 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2313:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract325 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2320:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract326 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2327:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract327 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2334:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract328 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2341:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract329 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2348:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract330 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2355:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract331 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2362:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract332 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2369:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract333 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2376:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract334 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2383:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract335 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2390:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract336 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2397:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract337 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2404:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract338 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2411:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract339 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2418:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract340 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2425:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract341 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2432:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract342 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2439:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract343 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2446:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract344 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2453:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract345 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2460:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract346 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2467:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract347 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2474:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract348 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2481:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract349 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2488:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract350 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2495:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract351 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2502:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract352 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2509:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract353 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2516:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract354 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2523:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract355 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2530:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract356 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2537:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract357 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2544:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract358 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2551:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract359 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2558:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract360 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2565:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract361 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2572:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract362 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2579:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract363 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2586:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract364 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2593:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract365 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2600:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract366 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2607:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract367 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2614:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract368 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2621:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract369 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2628:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract370 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2635:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract371 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2642:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract372 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2649:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract373 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2656:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract374 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2663:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract375 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2670:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract376 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2677:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract377 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2684:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract378 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2691:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract379 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2698:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract380 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2705:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract381 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2712:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract382 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2719:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract383 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2726:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract384 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2733:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract385 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2740:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract386 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2747:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract387 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2754:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract388 fails" file="./spec/test_spec.rb" time="0.006281"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2761:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract389 fails" file="./spec/test_spec.rb" time="0.000197"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2768:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract390 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2775:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract391 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2782:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract392 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2789:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract393 fails" file="./spec/test_spec.rb" time="0.000100"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2796:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract394 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2803:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract395 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2810:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract396 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2817:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract397 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2824:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract398 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2831:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract399 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2838:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract400 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2845:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract401 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2852:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract402 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2859:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract403 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2866:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract404 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2873:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract405 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2880:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract406 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2887:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract407 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2894:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract408 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2901:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract409 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2908:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract410 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2915:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract411 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2922:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract412 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2929:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract413 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2936:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract414 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2943:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract415 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2950:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract416 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2957:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract417 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2964:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract418 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2971:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract419 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2978:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract420 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2985:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract421 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2992:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract422 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:2999:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract423 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3006:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract424 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3013:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract425 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3020:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract426 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3027:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract427 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3034:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract428 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3041:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract429 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3048:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract430 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3055:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract431 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3062:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract432 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3069:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract433 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3076:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract434 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3083:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract435 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3090:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract436 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3097:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract437 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3104:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract438 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3111:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract439 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3118:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract440 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3125:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract441 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3132:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract442 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3139:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract443 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3146:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract444 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3153:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract445 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3160:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract446 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3167:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract447 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3174:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract448 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3181:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract449 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3188:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract450 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3195:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract451 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3202:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract452 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3209:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract453 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3216:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract454 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3223:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract455 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3230:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract456 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3237:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract457 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3244:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract458 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3251:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract459 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3258:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract460 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3265:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract461 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3272:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract462 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3279:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract463 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3286:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract464 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3293:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract465 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3300:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract466 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3307:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract467 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3314:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract468 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3321:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract469 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3328:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract470 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3335:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract471 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3342:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract472 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3349:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract473 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3356:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract474 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3363:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract475 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3370:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract476 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3377:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract477 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3384:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract478 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3391:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract479 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3398:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract480 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3405:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract481 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3412:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract482 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3419:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract483 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3426:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract484 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3433:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract485 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3440:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract486 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3447:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract487 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3454:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract488 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3461:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract489 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3468:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract490 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3475:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract491 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3482:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract492 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3489:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract493 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3496:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract494 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3503:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract495 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3510:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract496 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3517:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract497 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3524:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract498 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3531:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract499 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3538:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract500 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3545:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract501 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3552:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract502 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3559:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract503 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3566:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract504 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3573:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract505 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3580:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract506 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3587:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract507 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3594:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract508 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3601:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract509 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3608:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract510 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3615:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract511 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3622:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract512 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3629:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract513 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3636:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract514 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3643:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract515 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3650:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract516 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3657:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract517 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3664:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract518 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3671:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract519 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3678:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract520 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3685:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract521 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3692:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract522 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3699:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract523 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3706:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract524 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3713:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract525 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3720:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract526 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3727:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract527 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3734:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract528 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3741:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract529 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3748:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract530 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3755:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract531 fails" file="./spec/test_spec.rb" time="0.000099"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3762:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract532 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3769:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract533 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3776:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract534 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3783:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract535 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3790:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract536 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3797:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract537 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3804:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract538 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3811:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract539 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3818:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract540 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3825:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract541 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3832:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract542 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3839:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract543 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3846:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract544 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3853:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract545 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3860:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract546 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3867:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract547 fails" file="./spec/test_spec.rb" time="0.000100"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3874:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract548 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3881:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract549 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3888:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract550 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3895:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract551 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3902:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract552 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3909:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract553 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3916:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract554 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3923:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract555 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3930:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract556 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3937:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract557 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3944:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract558 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3951:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract559 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3958:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract560 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3965:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract561 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3972:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract562 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3979:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract563 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3986:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract564 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:3993:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract565 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4000:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract566 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4007:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract567 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4014:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract568 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4021:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract569 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4028:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract570 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4035:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract571 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4042:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract572 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4049:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract573 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4056:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract574 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4063:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract575 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4070:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract576 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4077:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract577 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4084:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract578 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4091:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract579 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4098:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract580 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4105:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract581 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4112:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract582 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4119:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract583 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4126:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract584 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4133:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract585 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4140:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract586 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4147:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract587 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4154:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract588 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4161:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract589 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4168:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract590 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4175:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract591 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4182:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract592 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4189:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract593 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4196:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract594 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4203:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract595 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4210:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract596 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4217:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract597 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4224:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract598 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4231:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract599 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4238:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract600 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4245:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract601 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4252:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract602 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4259:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract603 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4266:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract604 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4273:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract605 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4280:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract606 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4287:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract607 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4294:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract608 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4301:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract609 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4308:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract610 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4315:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract611 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4322:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract612 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4329:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract613 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4336:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract614 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4343:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract615 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4350:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract616 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4357:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract617 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4364:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract618 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4371:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract619 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4378:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract620 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4385:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract621 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4392:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract622 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4399:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract623 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4406:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract624 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4413:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract625 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4420:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract626 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4427:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract627 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4434:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract628 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4441:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract629 fails" file="./spec/test_spec.rb" time="0.000100"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4448:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract630 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4455:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract631 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4462:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract632 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4469:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract633 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4476:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract634 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4483:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract635 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4490:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract636 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4497:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract637 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4504:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract638 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4511:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract639 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4518:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract640 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4525:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract641 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4532:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract642 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4539:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract643 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4546:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract644 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4553:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract645 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4560:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract646 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4567:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract647 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4574:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract648 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4581:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract649 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4588:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract650 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4595:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract651 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4602:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract652 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4609:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract653 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4616:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract654 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4623:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract655 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4630:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract656 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4637:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract657 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4644:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract658 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4651:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract659 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4658:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract660 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4665:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract661 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4672:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract662 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4679:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract663 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4686:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract664 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4693:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract665 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4700:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract666 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4707:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract667 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4714:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract668 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4721:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract669 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4728:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract670 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4735:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract671 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4742:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract672 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4749:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract673 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4756:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract674 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4763:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract675 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4770:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract676 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4777:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract677 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4784:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract678 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4791:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract679 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4798:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract680 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4805:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract681 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4812:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract682 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4819:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract683 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4826:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract684 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4833:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract685 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4840:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract686 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4847:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract687 fails" file="./spec/test_spec.rb" time="0.000101"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4854:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract688 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4861:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract689 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4868:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract690 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4875:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract691 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4882:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract692 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4889:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract693 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4896:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract694 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4903:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract695 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4910:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract696 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4917:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract697 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4924:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract698 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4931:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract699 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4938:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract700 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4945:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract701 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4952:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract702 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4959:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract703 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4966:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract704 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4973:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract705 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4980:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract706 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4987:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract707 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:4994:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract708 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5001:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract709 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5008:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract710 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5015:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract711 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5022:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract712 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5029:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract713 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5036:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract714 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5043:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract715 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5050:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract716 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5057:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract717 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5064:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract718 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5071:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract719 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5078:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract720 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5085:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract721 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5092:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract722 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5099:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract723 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5106:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract724 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5113:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract725 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5120:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract726 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5127:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract727 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5134:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract728 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5141:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract729 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5148:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract730 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5155:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract731 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5162:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract732 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5169:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract733 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5176:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract734 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5183:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract735 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5190:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract736 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5197:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract737 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5204:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract738 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5211:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract739 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5218:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract740 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5225:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract741 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5232:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract742 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5239:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract743 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5246:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract744 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5253:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract745 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5260:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract746 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5267:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract747 fails" file="./spec/test_spec.rb" time="0.000083"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5274:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract748 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5281:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract749 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5288:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract750 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5295:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract751 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5302:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract752 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5309:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract753 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5316:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract754 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5323:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract755 fails" file="./spec/test_spec.rb" time="0.005906"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5330:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract756 fails" file="./spec/test_spec.rb" time="0.000117"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5337:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract757 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5344:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract758 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5351:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract759 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5358:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract760 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5365:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract761 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5372:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract762 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5379:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract763 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5386:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract764 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5393:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract765 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5400:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract766 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5407:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract767 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5414:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract768 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5421:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract769 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5428:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract770 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5435:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract771 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5442:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract772 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5449:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract773 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5456:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract774 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5463:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract775 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5470:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract776 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5477:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract777 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5484:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract778 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5491:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract779 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5498:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract780 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5505:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract781 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5512:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract782 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5519:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract783 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5526:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract784 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5533:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract785 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5540:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract786 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5547:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract787 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5554:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract788 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5561:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract789 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5568:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract790 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5575:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract791 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5582:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract792 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5589:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract793 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5596:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract794 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5603:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract795 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5610:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract796 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5617:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract797 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5624:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract798 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5631:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract799 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5638:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract800 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5645:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract801 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5652:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract802 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5659:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract803 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5666:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract804 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5673:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract805 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5680:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract806 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5687:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract807 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5694:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract808 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5701:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract809 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5708:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract810 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5715:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract811 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5722:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract812 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5729:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract813 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5736:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract814 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5743:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract815 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5750:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract816 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5757:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract817 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5764:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract818 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5771:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract819 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5778:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract820 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5785:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract821 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5792:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract822 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5799:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract823 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5806:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract824 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5813:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract825 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5820:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract826 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5827:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract827 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5834:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract828 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5841:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract829 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5848:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract830 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5855:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract831 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5862:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract832 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5869:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract833 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5876:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract834 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5883:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract835 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5890:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract836 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5897:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract837 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5904:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract838 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5911:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract839 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5918:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract840 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5925:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract841 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5932:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract842 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5939:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract843 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5946:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract844 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5953:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract845 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5960:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract846 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5967:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract847 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5974:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract848 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5981:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract849 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5988:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract850 fails" file="./spec/test_spec.rb" time="0.000100"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:5995:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract851 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6002:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract852 fails" file="./spec/test_spec.rb" time="0.000101"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6009:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract853 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6016:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract854 fails" file="./spec/test_spec.rb" time="0.000099"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6023:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract855 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6030:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract856 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6037:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract857 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6044:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract858 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6051:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract859 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6058:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract860 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6065:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract861 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6072:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract862 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6079:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract863 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6086:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract864 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6093:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract865 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6100:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract866 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6107:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract867 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6114:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract868 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6121:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract869 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6128:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract870 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6135:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract871 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6142:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract872 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6149:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract873 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6156:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract874 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6163:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract875 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6170:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract876 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6177:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract877 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6184:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract878 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6191:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract879 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6198:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract880 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6205:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract881 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6212:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract882 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6219:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract883 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6226:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract884 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6233:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract885 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6240:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract886 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6247:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract887 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6254:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract888 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6261:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract889 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6268:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract890 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6275:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract891 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6282:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract892 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6289:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract893 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6296:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract894 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6303:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract895 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6310:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract896 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6317:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract897 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6324:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract898 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6331:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract899 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6338:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract900 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6345:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract901 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6352:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract902 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6359:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract903 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6366:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract904 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6373:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract905 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6380:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract906 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6387:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract907 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6394:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract908 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6401:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract909 fails" file="./spec/test_spec.rb" time="0.000099"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6408:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract910 fails" file="./spec/test_spec.rb" time="0.000096"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6415:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract911 fails" file="./spec/test_spec.rb" time="0.000099"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6422:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract912 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6429:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract913 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6436:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract914 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6443:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract915 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6450:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract916 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6457:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract917 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6464:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract918 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6471:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract919 fails" file="./spec/test_spec.rb" time="0.000084"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6478:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract920 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6485:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract921 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6492:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract922 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6499:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract923 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6506:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract924 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6513:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract925 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6520:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract926 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6527:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract927 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6534:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract928 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6541:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract929 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6548:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract930 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6555:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract931 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6562:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract932 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6569:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract933 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6576:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract934 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6583:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract935 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6590:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract936 fails" file="./spec/test_spec.rb" time="0.000093"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6597:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract937 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6604:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract938 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6611:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract939 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6618:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract940 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6625:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract941 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6632:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract942 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6639:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract943 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6646:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract944 fails" file="./spec/test_spec.rb" time="0.000095"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6653:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract945 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6660:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract946 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6667:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract947 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6674:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract948 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6681:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract949 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6688:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract950 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6695:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract951 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6702:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract952 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6709:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract953 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6716:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract954 fails" file="./spec/test_spec.rb" time="0.000092"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6723:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract955 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6730:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract956 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6737:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract957 fails" file="./spec/test_spec.rb" time="0.000082"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6744:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract958 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6751:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract959 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6758:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract960 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6765:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract961 fails" file="./spec/test_spec.rb" time="0.000094"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6772:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract962 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6779:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract963 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6786:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract964 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6793:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract965 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6800:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract966 fails" file="./spec/test_spec.rb" time="0.000088"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6807:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract967 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6814:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract968 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6821:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract969 fails" file="./spec/test_spec.rb" time="0.000085"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6828:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract970 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6835:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract971 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6842:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract972 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6849:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract973 fails" file="./spec/test_spec.rb" time="0.000086"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6856:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract974 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6863:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract975 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6870:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract976 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6877:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract977 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6884:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract978 fails" file="./spec/test_spec.rb" time="0.000089"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6891:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract979 fails" file="./spec/test_spec.rb" time="0.000091"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6898:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract980 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6905:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract981 fails" file="./spec/test_spec.rb" time="0.000090"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6912:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract982 fails" file="./spec/test_spec.rb" time="0.000087"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6919:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract983 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6926:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract984 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6933:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract985 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6940:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract986 fails" file="./spec/test_spec.rb" time="0.000100"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6947:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract987 fails" file="./spec/test_spec.rb" time="0.000098"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6954:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract988 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6961:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract989 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6968:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract990 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6975:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract991 fails" file="./spec/test_spec.rb" time="0.000113"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6982:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract992 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6989:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract993 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:6996:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract994 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:7003:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract995 fails" file="./spec/test_spec.rb" time="0.000104"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:7010:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract996 fails" file="./spec/test_spec.rb" time="0.000079"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:7017:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract997 fails" file="./spec/test_spec.rb" time="0.000081"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:7024:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract998 fails" file="./spec/test_spec.rb" time="0.000080"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:7031:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract999 fails" file="./spec/test_spec.rb" time="0.000097"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:7038:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+<testcase classname="spec.test_spec" name="Test#subtract1000 fails" file="./spec/test_spec.rb" time="0.000078"><failure message="expected: falsey value
+ got: true" type="RSpec::Expectations::ExpectationNotMetError">Failure/Error: expect(true).to be_falsy
+
+ expected: falsey value
+ got: true
+./spec/test_spec.rb:7045:in `block (3 levels) in &lt;top (required)&gt;&apos;</failure></testcase>
+</testsuite>
diff --git a/spec/fixtures/junit/junit_with_three_testsuites_3.xml b/spec/fixtures/junit/junit_with_three_testsuites_3.xml
new file mode 100644
index 00000000000..9b3c287aec8
--- /dev/null
+++ b/spec/fixtures/junit/junit_with_three_testsuites_3.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<testsuite name="rspec" tests="2" skipped="0" failures="0" errors="0" time="0.001691" timestamp="2018-07-30T10:02:37+00:00" hostname="runner-7661726c-project-14-concurrent-0">
+<properties>
+<property name="seed" value="8528"/>
+</properties>
+<testcase classname="spec.string_helper_spec" name="StringHelper#concatenate when a is git and b is lab returns summary" file="./spec/string_helper_spec.rb" time="0.000287"></testcase>
+<testcase classname="spec.string_helper_spec" name="StringHelper#concatenate when a is git and b is 200 raises an error" file="./spec/string_helper_spec.rb" time="0.000706"></testcase>
+</testsuite>
diff --git a/spec/fixtures/project_export.tar.gz b/spec/fixtures/project_export.tar.gz
new file mode 100644
index 00000000000..72ab2d71f35
--- /dev/null
+++ b/spec/fixtures/project_export.tar.gz
Binary files differ
diff --git a/spec/fixtures/trace/sample_trace b/spec/fixtures/trace/sample_trace
index c65cf05d5ca..7bfe3f83b7b 100644
--- a/spec/fixtures/trace/sample_trace
+++ b/spec/fixtures/trace/sample_trace
@@ -41,7 +41,7 @@ From https://gitlab.com/gitlab-org/gitlab-ce
section_end:1522927113:get_sources
section_start:1522927113:restore_cache
Checking cache for ruby-2.3.6-with-yarn...
-Downloading cache.zip from http://runners-cache-5-internal.gitlab.com:444/runner/project/13083/ruby-2.3.6-with-yarn
+Downloading cache.zip from http://runners-cache-5-internal.gitlab.com:444/runner/project/13083/ruby-2.3.6-with-yarn
Successfully extracted cache
section_end:1522927128:restore_cache
section_start:1522927128:download_artifacts
@@ -51,7 +51,7 @@ Downloading artifacts from coordinator... ok  id=61303215 respon
Downloading artifacts from coordinator... ok  id=61303216 responseStatus=200 OK token=iy2yYbq8
Downloading artifacts for setup-test-env (61303217)...
Downloading artifacts from coordinator... ok  id=61303217 responseStatus=200 OK token=ur1g79-4
-WARNING: tmp/tests/gitlab-shell/.gitlab_shell_secret: chmod tmp/tests/gitlab-shell/.gitlab_shell_secret: no such file or directory (suppressing repeats)
+WARNING: tmp/tests/gitlab-shell/.gitlab_shell_secret: chmod tmp/tests/gitlab-shell/.gitlab_shell_secret: no such file or directory (suppressing repeats)
section_end:1522927141:download_artifacts
section_start:1522927141:build_script
$ bundle --version
@@ -1486,7 +1486,7 @@ Gitlab::ImportExport::ProjectTreeSaver
overrides the project description
group members
does not export group members if it has no permission
- does not export group members as master
+ does not export group members as maintainer
exports group members as group owner
as admin
exports group members as admin
@@ -1690,7 +1690,7 @@ GroupsController
and logged in as Developer
behaves like member without ability to create subgroups
renders the 404 page (PENDING: around hook at ./spec/spec_helper.rb:186 did not execute the example)
- and logged in as Master
+ and logged in as Maintainer
behaves like member without ability to create subgroups
renders the 404 page (PENDING: around hook at ./spec/spec_helper.rb:186 did not execute the example)
and can_create_group is false
@@ -1706,7 +1706,7 @@ GroupsController
and logged in as Developer
behaves like member without ability to create subgroups
renders the 404 page (PENDING: around hook at ./spec/spec_helper.rb:186 did not execute the example)
- and logged in as Master
+ and logged in as Maintainer
behaves like member without ability to create subgroups
renders the 404 page (PENDING: around hook at ./spec/spec_helper.rb:186 did not execute the example)
GET #activity
@@ -2324,7 +2324,7 @@ Editing file blob
shows blob editor with same branch
with protected branch
shows blob editor with patch branch
- as master
+ as maintainer
shows blob editor with same branch
Boards::Lists::MoveService
@@ -2880,7 +2880,7 @@ API::V3::Environments
won't update the external_url if only the name is passed
returns a 404 if the environment does not exist
DELETE /projects/:id/environments/:environment_id
- as a master
+ as a maintainer
returns a 200 for an existing environment
returns a 404 for non existing id
a non member
@@ -3001,11 +3001,11 @@ LfsFileLock
#can_be_unlocked_by?
when it's forced
can be unlocked by the author
- can be unlocked by a master
+ can be unlocked by a maintainer
can't be unlocked by other user
when it isn't forced
can be unlocked by the author
- can't be unlocked by a master
+ can't be unlocked by a maintainer
can't be unlocked by other user
Gitlab::Ci::Config::Entry::Boolean
@@ -3069,7 +3069,7 @@ Pending: (Failures listed here are expected and do not affect your suite's statu
# around hook at ./spec/spec_helper.rb:186 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:25
- 5) GroupsController GET #new when creating subgroups and can_create_group is true and logged in as Master behaves like member without ability to create subgroups renders the 404 page
+ 5) GroupsController GET #new when creating subgroups and can_create_group is true and logged in as Maintainer behaves like member without ability to create subgroups renders the 404 page
# around hook at ./spec/spec_helper.rb:186 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:25
@@ -3089,7 +3089,7 @@ Pending: (Failures listed here are expected and do not affect your suite's statu
# around hook at ./spec/spec_helper.rb:186 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:25
- 10) GroupsController GET #new when creating subgroups and can_create_group is false and logged in as Master behaves like member without ability to create subgroups renders the 404 page
+ 10) GroupsController GET #new when creating subgroups and can_create_group is false and logged in as Maintainer behaves like member without ability to create subgroups renders the 404 page
# around hook at ./spec/spec_helper.rb:186 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:25
@@ -3237,7 +3237,7 @@ Pending: (Failures listed here are expected and do not affect your suite's statu
# around hook at ./spec/spec_helper.rb:190 did not execute the example
# ./spec/services/groups/transfer_service_spec.rb:212
- 47) Groups::TransferService#execute when transferring a subgroup into another group when the group is allowed to be transferred should update parent group to the new parent
+ 47) Groups::TransferService#execute when transferring a subgroup into another group when the group is allowed to be transferred should update parent group to the new parent
# around hook at ./spec/spec_helper.rb:190 did not execute the example
# ./spec/services/groups/transfer_service_spec.rb:216
@@ -3435,10 +3435,10 @@ section_end:1522927515:after_script
section_end:1522927516:archive_cache
section_start:1522927516:upload_artifacts
Uploading artifacts...
-coverage/: found 5 matching files 
-knapsack/: found 5 matching files 
-rspec_flaky/: found 4 matching files 
-WARNING: tmp/capybara/: no matching files 
+coverage/: found 5 matching files 
+knapsack/: found 5 matching files 
+rspec_flaky/: found 4 matching files 
+WARNING: tmp/capybara/: no matching files 
Uploading artifacts to coordinator... ok  id=61303283 responseStatus=201 Created token=rusBKvxM
section_end:1522927520:upload_artifacts
Job succeeded
diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb
index b892f6b44ed..b9ddb427e85 100644
--- a/spec/graphql/gitlab_schema_spec.rb
+++ b/spec/graphql/gitlab_schema_spec.rb
@@ -18,8 +18,6 @@ describe GitlabSchema do
end
it 'has the base mutation' do
- pending('Adding an empty mutation breaks the documentation explorer')
-
expect(described_class.mutation).to eq(::Types::MutationType.to_graphql)
end
@@ -27,6 +25,12 @@ describe GitlabSchema do
expect(described_class.query).to eq(::Types::QueryType.to_graphql)
end
+ it 'paginates active record relations using `Gitlab::Graphql::Connections::KeysetConnection`' do
+ connection = GraphQL::Relay::BaseConnection::CONNECTION_IMPLEMENTATIONS[ActiveRecord::Relation.name]
+
+ expect(connection).to eq(Gitlab::Graphql::Connections::KeysetConnection)
+ end
+
def field_instrumenters
described_class.instrumenters[:field]
end
diff --git a/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb b/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb
new file mode 100644
index 00000000000..19f5a8907a2
--- /dev/null
+++ b/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Mutations::ResolvesProject do
+ let(:mutation_class) do
+ Class.new(Mutations::BaseMutation) do
+ include Mutations::ResolvesProject
+ end
+ end
+
+ let(:context) { double }
+ subject(:mutation) { mutation_class.new(object: nil, context: context) }
+
+ it 'uses the ProjectsResolver to resolve projects by path' do
+ project = create(:project)
+
+ expect(Resolvers::ProjectResolver).to receive(:new).with(object: nil, context: context).and_call_original
+ expect(mutation.resolve_project(full_path: project.full_path)).to eq(project)
+ end
+end
diff --git a/spec/graphql/mutations/merge_requests/set_wip_spec.rb b/spec/graphql/mutations/merge_requests/set_wip_spec.rb
new file mode 100644
index 00000000000..e600abf3941
--- /dev/null
+++ b/spec/graphql/mutations/merge_requests/set_wip_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe Mutations::MergeRequests::SetWip do
+ let(:merge_request) { create(:merge_request) }
+ let(:user) { create(:user) }
+ subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
+
+ describe '#resolve' do
+ let(:wip) { true }
+ let(:mutated_merge_request) { subject[:merge_request] }
+ subject { mutation.resolve(project_path: merge_request.project.full_path, iid: merge_request.iid, wip: wip) }
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+
+ context 'when the user can update the merge request' do
+ before do
+ merge_request.project.add_developer(user)
+ end
+
+ it 'returns the merge request as a wip' do
+ expect(mutated_merge_request).to eq(merge_request)
+ expect(mutated_merge_request).to be_work_in_progress
+ expect(subject[:errors]).to be_empty
+ end
+
+ it 'returns errors merge request could not be updated' do
+ # Make the merge request invalid
+ merge_request.allow_broken = true
+ merge_request.update!(source_project: nil)
+
+ expect(subject[:errors]).not_to be_empty
+ end
+
+ context 'when passing wip as false' do
+ let(:wip) { false }
+
+ it 'removes `wip` from the title' do
+ merge_request.update(title: "WIP: working on it")
+
+ expect(mutated_merge_request).not_to be_work_in_progress
+ end
+
+ it 'does not do anything if the title did not start with wip' do
+ expect(mutated_merge_request).not_to be_work_in_progress
+ end
+ end
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
new file mode 100644
index 00000000000..ea7159eacf9
--- /dev/null
+++ b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe ResolvesPipelines do
+ include GraphqlHelpers
+
+ subject(:resolver) do
+ Class.new(Resolvers::BaseResolver) do
+ include ResolvesPipelines
+
+ def resolve(**args)
+ resolve_pipelines(object, args)
+ end
+ end
+ end
+
+ let(:current_user) { create(:user) }
+ set(:project) { create(:project, :private) }
+ set(:pipeline) { create(:ci_pipeline, project: project) }
+ set(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) }
+ set(:ref_pipeline) { create(:ci_pipeline, project: project, ref: 'awesome-feature') }
+ set(:sha_pipeline) { create(:ci_pipeline, project: project, sha: 'deadbeef') }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ it { is_expected.to have_graphql_arguments(:status, :ref, :sha) }
+
+ it 'finds all pipelines' do
+ expect(resolve_pipelines).to contain_exactly(pipeline, failed_pipeline, ref_pipeline, sha_pipeline)
+ end
+
+ it 'allows filtering by status' do
+ expect(resolve_pipelines(status: 'failed')).to contain_exactly(failed_pipeline)
+ end
+
+ it 'allows filtering by ref' do
+ expect(resolve_pipelines(ref: 'awesome-feature')).to contain_exactly(ref_pipeline)
+ end
+
+ it 'allows filtering by sha' do
+ expect(resolve_pipelines(sha: 'deadbeef')).to contain_exactly(sha_pipeline)
+ end
+
+ it 'does not return any pipelines if the user does not have access' do
+ expect(resolve_pipelines({}, {})).to be_empty
+ end
+
+ def resolve_pipelines(args = {}, context = { current_user: current_user })
+ resolve(resolver, obj: project, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
new file mode 100644
index 00000000000..09b17bf6fc9
--- /dev/null
+++ b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+describe Resolvers::MergeRequestPipelinesResolver do
+ include GraphqlHelpers
+
+ set(:merge_request) { create(:merge_request) }
+ set(:pipeline) do
+ create(
+ :ci_pipeline,
+ project: merge_request.source_project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha
+ )
+ end
+ set(:other_project_pipeline) { create(:ci_pipeline, project: merge_request.source_project) }
+ set(:other_pipeline) { create(:ci_pipeline) }
+ let(:current_user) { create(:user) }
+
+ before do
+ merge_request.project.add_developer(current_user)
+ end
+
+ def resolve_pipelines
+ resolve(described_class, obj: merge_request, ctx: { current_user: current_user })
+ end
+
+ it 'resolves only MRs for the passed merge request' do
+ expect(resolve_pipelines).to contain_exactly(pipeline)
+ end
+end
diff --git a/spec/graphql/resolvers/project_pipelines_resolver_spec.rb b/spec/graphql/resolvers/project_pipelines_resolver_spec.rb
new file mode 100644
index 00000000000..407ca2f9d78
--- /dev/null
+++ b/spec/graphql/resolvers/project_pipelines_resolver_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe Resolvers::ProjectPipelinesResolver do
+ include GraphqlHelpers
+
+ set(:project) { create(:project) }
+ set(:pipeline) { create(:ci_pipeline, project: project) }
+ set(:other_pipeline) { create(:ci_pipeline) }
+ let(:current_user) { create(:user) }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ def resolve_pipelines
+ resolve(described_class, obj: project, ctx: { current_user: current_user })
+ end
+
+ it 'resolves only MRs for the passed merge request' do
+ expect(resolve_pipelines).to contain_exactly(pipeline)
+ end
+end
diff --git a/spec/graphql/types/ci/pipeline_type_spec.rb b/spec/graphql/types/ci/pipeline_type_spec.rb
new file mode 100644
index 00000000000..ec1c689a4be
--- /dev/null
+++ b/spec/graphql/types/ci/pipeline_type_spec.rb
@@ -0,0 +1,7 @@
+require 'spec_helper'
+
+describe Types::Ci::PipelineType do
+ it { expect(described_class.graphql_name).to eq('Pipeline') }
+
+ it { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Ci::Pipeline) }
+end
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index 6e57122867a..c369953e3ea 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -1,5 +1,16 @@
require 'spec_helper'
-describe Types::MergeRequestType do
+describe GitlabSchema.types['MergeRequest'] do
it { expect(described_class).to expose_permissions_using(Types::PermissionTypes::MergeRequest) }
+
+ describe 'head pipeline' do
+ it 'has a head pipeline field' do
+ expect(described_class).to have_graphql_field(:head_pipeline)
+ end
+
+ it 'authorizes the field' do
+ expect(described_class.fields['headPipeline'])
+ .to require_graphql_authorizations(:read_pipeline)
+ end
+ end
end
diff --git a/spec/graphql/types/mutation_type_spec.rb b/spec/graphql/types/mutation_type_spec.rb
new file mode 100644
index 00000000000..a67d83b1edf
--- /dev/null
+++ b/spec/graphql/types/mutation_type_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Types::MutationType do
+ it 'is expected to have the MergeRequestSetWip' do
+ expect(described_class).to have_graphql_mutation(Mutations::MergeRequests::SetWip)
+ end
+end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 7b5bc335511..49606c397b9 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -13,4 +13,6 @@ describe GitlabSchema.types['Project'] do
.to require_graphql_authorizations(:read_merge_request)
end
end
+
+ it { is_expected.to have_graphql_field(:pipelines) }
end
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index 5856bccb5b8..55ee87163f9 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -5,12 +5,67 @@ describe AvatarsHelper do
let(:user) { create(:user) }
- describe '#project_icon' do
- it 'returns an url for the avatar' do
- project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
+ describe '#project_icon & #group_icon' do
+ shared_examples 'resource with a default avatar' do |source_type|
+ it 'returns a default avatar div' do
+ expect(public_send("#{source_type}_icon", *helper_args))
+ .to match(%r{<div class="identicon bg\d+">F</div>})
+ end
+ end
+
+ shared_examples 'resource with a custom avatar' do |source_type|
+ it 'returns a custom avatar image' do
+ expect(public_send("#{source_type}_icon", *helper_args))
+ .to eq "<img src=\"#{resource.avatar.url}\" alt=\"Banana sample\" />"
+ end
+ end
+
+ context 'when providing a project' do
+ it_behaves_like 'resource with a default avatar', 'project' do
+ let(:resource) { create(:project, name: 'foo') }
+ let(:helper_args) { [resource] }
+ end
+
+ it_behaves_like 'resource with a custom avatar', 'project' do
+ let(:resource) { create(:project, :public, avatar: File.open(uploaded_image_temp_path)) }
+ let(:helper_args) { [resource] }
+ end
+ end
+
+ context 'when providing a project path' do
+ it_behaves_like 'resource with a default avatar', 'project' do
+ let(:resource) { create(:project, name: 'foo') }
+ let(:helper_args) { [resource.full_path] }
+ end
- expect(helper.project_icon(project.full_path).to_s)
- .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
+ it_behaves_like 'resource with a custom avatar', 'project' do
+ let(:resource) { create(:project, :public, avatar: File.open(uploaded_image_temp_path)) }
+ let(:helper_args) { [resource.full_path] }
+ end
+ end
+
+ context 'when providing a group' do
+ it_behaves_like 'resource with a default avatar', 'group' do
+ let(:resource) { create(:group, name: 'foo') }
+ let(:helper_args) { [resource] }
+ end
+
+ it_behaves_like 'resource with a custom avatar', 'group' do
+ let(:resource) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
+ let(:helper_args) { [resource] }
+ end
+ end
+
+ context 'when providing a group path' do
+ it_behaves_like 'resource with a default avatar', 'group' do
+ let(:resource) { create(:group, name: 'foo') }
+ let(:helper_args) { [resource.full_path] }
+ end
+
+ it_behaves_like 'resource with a custom avatar', 'group' do
+ let(:resource) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
+ let(:helper_args) { [resource.full_path] }
+ end
end
end
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index a3e010c3206..1c216b3fe97 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -65,9 +65,9 @@ describe BlobHelper do
describe "#sanitize_svg_data" do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
- let(:data) { open(input_svg_path).read }
+ let(:data) { File.read(input_svg_path) }
let(:expected_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') }
- let(:expected) { open(expected_svg_path).read }
+ let(:expected) { File.read(expected_svg_path) }
it 'retains essential elements' do
expect(sanitize_svg_data(data)).to eq(expected)
diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb
index fee8df10129..630f3eff258 100644
--- a/spec/helpers/button_helper_spec.rb
+++ b/spec/helpers/button_helper_spec.rb
@@ -121,6 +121,8 @@ describe ButtonHelper do
end
describe 'clipboard_button' do
+ include IconsHelper
+
let(:user) { create(:user) }
let(:project) { build_stubbed(:project) }
@@ -145,7 +147,7 @@ describe ButtonHelper do
expect(element.attr('data-clipboard-text')).to eq(nil)
expect(element.inner_text).to eq("")
- expect(element).to have_selector('.fa.fa-clipboard')
+ expect(element.to_html).to include sprite_icon('duplicate')
end
end
@@ -178,7 +180,7 @@ describe ButtonHelper do
context 'with `hide_button_icon` attribute provided' do
it 'shows copy to clipboard button without tooltip support' do
- expect(element(hide_button_icon: true)).not_to have_selector('.fa.fa-clipboard')
+ expect(element(hide_button_icon: true).to_html).not_to include sprite_icon('duplicate')
end
end
end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 6c94bd4e504..540a8674ec2 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -3,19 +3,6 @@ require 'spec_helper'
describe GroupsHelper do
include ApplicationHelper
- describe 'group_icon' do
- it 'returns an url for the avatar' do
- avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif')
-
- group = create(:group)
- group.avatar = fixture_file_upload(avatar_file_path)
- group.save!
-
- expect(helper.group_icon(group).to_s)
- .to eq "<img data-src=\"#{group.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
- end
- end
-
describe 'group_icon_url' do
it 'returns an url for the avatar' do
avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif')
@@ -206,8 +193,9 @@ describe GroupsHelper do
let(:group) { create(:group, :public) }
let(:user) { create(:user) }
before do
+ group.add_owner(user)
allow(helper).to receive(:current_user) { user }
- allow(helper).to receive(:can?) { true }
+ allow(helper).to receive(:can?) { |*args| Ability.allowed?(*args) }
helper.instance_variable_set(:@group, group)
end
@@ -231,7 +219,10 @@ describe GroupsHelper do
cross_project_features = [:activity, :issues, :labels, :milestones,
:merge_requests]
- expect(helper).to receive(:can?).with(user, :read_cross_project) { false }
+ allow(Ability).to receive(:allowed?).and_call_original
+ cross_project_features.each do |feature|
+ expect(Ability).to receive(:allowed?).with(user, "read_group_#{feature}".to_sym, group) { false }
+ end
expect(helper.group_sidebar_links).not_to include(*cross_project_features)
end
diff --git a/spec/helpers/icons_helper_spec.rb b/spec/helpers/icons_helper_spec.rb
index 93d8e672f8c..82f588d1a08 100644
--- a/spec/helpers/icons_helper_spec.rb
+++ b/spec/helpers/icons_helper_spec.rb
@@ -55,6 +55,29 @@ describe IconsHelper do
expect(sprite_icon(icon_name, size: 72, css_class: 'icon-danger').to_s)
.to eq "<svg class=\"s72 icon-danger\"><use xlink:href=\"#{icons_path}##{icon_name}\"></use></svg>"
end
+
+ describe 'non existing icon' do
+ non_existing = 'non_existing_icon_sprite'
+
+ it 'should raise in development mode' do
+ allow(Rails.env).to receive(:development?).and_return(true)
+
+ expect { sprite_icon(non_existing) }.to raise_error(ArgumentError, /is not a known icon/)
+ end
+
+ it 'should raise in test mode' do
+ allow(Rails.env).to receive(:test?).and_return(true)
+
+ expect { sprite_icon(non_existing) }.to raise_error(ArgumentError, /is not a known icon/)
+ end
+
+ it 'should not raise in production mode' do
+ allow(Rails.env).to receive(:test?).and_return(false)
+ allow(Rails.env).to receive(:development?).and_return(false)
+
+ expect { sprite_icon(non_existing) }.not_to raise_error
+ end
+ end
end
describe 'file_type_icon_class' do
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index cddb49b320f..f76ed4bfda4 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -21,6 +21,27 @@ describe IssuablesHelper do
end
end
+ describe '#group_dropdown_label' do
+ let(:group) { create(:group) }
+ let(:default) { 'default label' }
+
+ it 'returns default group label when group_id is nil' do
+ expect(group_dropdown_label(nil, default)).to eq('default label')
+ end
+
+ it 'returns "any group" when group_id is 0' do
+ expect(group_dropdown_label('0', default)).to eq('Any group')
+ end
+
+ it 'returns group full path when a group was found for the provided id' do
+ expect(group_dropdown_label(group.id, default)).to eq(group.full_name)
+ end
+
+ it 'returns default label when a group was not found for the provided id' do
+ expect(group_dropdown_label(9999, default)).to eq('default label')
+ end
+ end
+
describe '#issuable_labels_tooltip' do
it 'returns label text with no labels' do
expect(issuable_labels_tooltip([])).to eq("Labels")
@@ -163,6 +184,7 @@ describe IssuablesHelper do
issuableRef: "##{issue.iid}",
markdownPreviewPath: "/#{@project.full_path}/preview_markdown",
markdownDocsPath: '/help/user/markdown',
+ markdownVersion: 11,
issuableTemplates: [],
projectPath: @project.path,
projectNamespace: @project.namespace.path,
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 1a720aae55c..597648b064d 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -11,7 +11,7 @@ describe MarkupHelper do
before do
# Ensure the generated reference links aren't redacted
- project.add_master(user)
+ project.add_maintainer(user)
# Helper expects a @project instance variable
helper.instance_variable_set(:@project, project)
@@ -205,7 +205,9 @@ describe MarkupHelper do
it "uses Wiki pipeline for markdown files" do
allow(@wiki).to receive(:format).and_return(:markdown)
- expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", issuable_state_filter_enabled: true)
+ expect(helper).to receive(:markdown_unsafe).with('wiki content',
+ pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page",
+ issuable_state_filter_enabled: true, markdown_engine: :redcarpet)
helper.render_wiki_content(@wiki)
end
@@ -236,19 +238,32 @@ describe MarkupHelper do
expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8')
end
- it "delegates to #markdown_unsafe when file name corresponds to Markdown" do
+ it 'delegates to #markdown_unsafe when file name corresponds to Markdown' do
expect(helper).to receive(:gitlab_markdown?).with('foo.md').and_return(true)
expect(helper).to receive(:markdown_unsafe).and_return('NOEL')
expect(helper.markup('foo.md', content)).to eq('NOEL')
end
- it "delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc" do
+ it 'delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc' do
expect(helper).to receive(:asciidoc?).with('foo.adoc').and_return(true)
expect(helper).to receive(:asciidoc_unsafe).and_return('NOEL')
expect(helper.markup('foo.adoc', content)).to eq('NOEL')
end
+
+ it 'uses passed in rendered content' do
+ expect(helper).not_to receive(:gitlab_markdown?)
+ expect(helper).not_to receive(:markdown_unsafe)
+
+ expect(helper.markup('foo.md', content, rendered: '<p>NOEL</p>')).to eq('<p>NOEL</p>')
+ end
+
+ it 'defaults to Redcarpet' do
+ expect(helper).to receive(:markdown_unsafe).with(content, hash_including(markdown_engine: :redcarpet)).and_return('NOEL')
+
+ expect(helper.markup('foo.md', content)).to eq('NOEL')
+ end
end
describe '#first_line_in_markdown' do
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index 3008528e60c..885204062fe 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -54,7 +54,7 @@ describe MergeRequestsHelper do
let(:options) { { force_link: true } }
it 'removes the data-toggle attributes' do
- is_expected.not_to match(/data-toggle="tab"/)
+ is_expected.not_to match(/data-toggle="tabvue"/)
end
end
end
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
index 460d3b6a7e4..234690e742b 100644
--- a/spec/helpers/namespaces_helper_spec.rb
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -28,6 +28,54 @@ describe NamespacesHelper do
expect(options).not_to include(admin_group.name)
expect(options).to include(user_group.name)
+ expect(options).to include(user.name)
+ end
+
+ it 'avoids duplicate groups when extra_group is used' do
+ allow(helper).to receive(:current_user).and_return(admin)
+
+ options = helper.namespaces_options(user_group.id, display_path: true, extra_group: build(:group, name: admin_group.name))
+
+ expect(options.scan("data-name=\"#{admin_group.name}\"").count).to eq(1)
+ expect(options).to include(admin_group.name)
+ end
+
+ it 'selects existing group' do
+ allow(helper).to receive(:current_user).and_return(admin)
+
+ options = helper.namespaces_options(:extra_group, display_path: true, extra_group: user_group)
+
+ expect(options).to include("selected=\"selected\" value=\"#{user_group.id}\"")
+ expect(options).to include(admin_group.name)
+ end
+
+ it 'selects the new group by default' do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ options = helper.namespaces_options(:extra_group, display_path: true, extra_group: build(:group, name: 'new-group'))
+
+ expect(options).to include(user_group.name)
+ expect(options).not_to include(admin_group.name)
+ expect(options).to include("selected=\"selected\" value=\"-1\"")
+ end
+
+ it 'falls back to current user selection' do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ options = helper.namespaces_options(:extra_group, display_path: true, extra_group: build(:group, name: admin_group.name))
+
+ expect(options).to include(user_group.name)
+ expect(options).not_to include(admin_group.name)
+ expect(options).to include("selected=\"selected\" value=\"#{user.namespace.id}\"")
+ end
+
+ it 'returns only groups if groups_only option is true' do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ options = helper.namespaces_options(nil, groups_only: true)
+
+ expect(options).not_to include(user.name)
+ expect(options).to include(user_group.name)
end
context 'when nested groups are available', :nested_groups do
diff --git a/spec/helpers/notes_helper_spec.rb b/spec/helpers/notes_helper_spec.rb
index b992bdb4a5e..21461e46cf4 100644
--- a/spec/helpers/notes_helper_spec.rb
+++ b/spec/helpers/notes_helper_spec.rb
@@ -6,18 +6,18 @@ describe NotesHelper do
let(:owner) { create(:owner) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:owner_note) { create(:note, author: owner, project: project) }
- let(:master_note) { create(:note, author: master, project: project) }
+ let(:maintainer_note) { create(:note, author: maintainer, project: project) }
let(:reporter_note) { create(:note, author: reporter, project: project) }
- let!(:notes) { [owner_note, master_note, reporter_note] }
+ let!(:notes) { [owner_note, maintainer_note, reporter_note] }
before do
group.add_owner(owner)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
end
@@ -25,16 +25,16 @@ describe NotesHelper do
describe "#notes_max_access_for_users" do
it 'returns access levels' do
expect(helper.note_max_access_for_user(owner_note)).to eq(Gitlab::Access::OWNER)
- expect(helper.note_max_access_for_user(master_note)).to eq(Gitlab::Access::MASTER)
+ expect(helper.note_max_access_for_user(maintainer_note)).to eq(Gitlab::Access::MAINTAINER)
expect(helper.note_max_access_for_user(reporter_note)).to eq(Gitlab::Access::REPORTER)
end
it 'handles access in different projects' do
second_project = create(:project)
- second_project.add_reporter(master)
- other_note = create(:note, author: master, project: second_project)
+ second_project.add_reporter(maintainer)
+ other_note = create(:note, author: maintainer, project: second_project)
- expect(helper.note_max_access_for_user(master_note)).to eq(Gitlab::Access::MASTER)
+ expect(helper.note_max_access_for_user(maintainer_note)).to eq(Gitlab::Access::MAINTAINER)
expect(helper.note_max_access_for_user(other_note)).to eq(Gitlab::Access::REPORTER)
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 80147b13739..cbd4ff0fb4a 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -80,6 +80,7 @@ describe ProjectsHelper do
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).with(user, :read_cross_project) { true }
+ allow(user).to receive(:max_member_access_for_project).and_return(40)
end
it "includes the route" do
@@ -125,6 +126,10 @@ describe ProjectsHelper do
expect(helper.project_list_cache_key(project)).to include("pipeline-status/#{project.commit.sha}-success")
end
+
+ it "includes the user max member access" do
+ expect(helper.project_list_cache_key(project)).to include('access:40')
+ end
end
describe '#load_pipeline_status' do
@@ -285,33 +290,6 @@ describe ProjectsHelper do
end
end
- describe '#sanitizerepo_repo_path' do
- let(:project) { create(:project, :repository) }
- let(:storage_path) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages.default.legacy_disk_path
- end
- end
-
- before do
- allow(Settings.shared).to receive(:[]).with('path').and_return('/base/repo/export/path')
- end
-
- it 'removes the repo path' do
- repo = File.join(storage_path, 'namespace/test.git')
- import_error = "Could not clone #{repo}\n"
-
- expect(sanitize_repo_path(project, import_error)).to eq('Could not clone [REPOS PATH]/namespace/test.git')
- end
-
- it 'removes the temporary repo path used for uploads/exports' do
- repo = '/base/repo/export/path/tmp/project_exports/uploads/test.tar.gz'
- import_error = "Unable to decompress #{repo}\n"
-
- expect(sanitize_repo_path(project, import_error)).to eq('Unable to decompress [REPO EXPORT PATH]/uploads/test.tar.gz')
- end
- end
-
describe '#last_push_event' do
let(:user) { double(:user, fork_of: nil) }
let(:project) { double(:project, id: 1) }
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 6c9a7febf14..8bfd520528f 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -55,6 +55,20 @@ describe SearchHelper do
expect(search_autocomplete_opts(project.name).size).to eq(1)
end
+ it "includes the required project attrs" do
+ project = create(:project, namespace: create(:namespace, owner: user))
+ result = search_autocomplete_opts(project.name).first
+
+ expect(result.keys).to match_array(%i[category id value label url avatar_url])
+ end
+
+ it "includes the required group attrs" do
+ create(:group).add_owner(user)
+ result = search_autocomplete_opts("gro").first
+
+ expect(result.keys).to match_array(%i[category id label url avatar_url])
+ end
+
it "does not include the public group" do
group = create(:group)
expect(search_autocomplete_opts(group.name).size).to eq(0)
diff --git a/spec/helpers/snippets_helper_spec.rb b/spec/helpers/snippets_helper_spec.rb
index 0323ffb641c..ce5e037f88d 100644
--- a/spec/helpers/snippets_helper_spec.rb
+++ b/spec/helpers/snippets_helper_spec.rb
@@ -7,13 +7,13 @@ describe SnippetsHelper do
it 'gives view raw button of embedded snippets for project snippets' do
@snippet = create(:project_snippet, :public)
- expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_project_snippet_url(@snippet.project, @snippet)}\">#{external_snippet_icon('doc_code')}</a>")
+ expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_project_snippet_url(@snippet.project, @snippet)}\">#{external_snippet_icon('doc-code')}</a>")
end
it 'gives view raw button of embedded snippets for personal snippets' do
@snippet = create(:personal_snippet, :public)
- expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_snippet_url(@snippet)}\">#{external_snippet_icon('doc_code')}</a>")
+ expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_snippet_url(@snippet)}\">#{external_snippet_icon('doc-code')}</a>")
end
end
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index 5a2e4b34069..a64f8a11ef2 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -92,11 +92,10 @@ describe SubmoduleHelper do
context 'in-repository submodule' do
let(:group) { create(:group, name: "Master Project", path: "master-project") }
let(:project) { create(:project, group: group) }
- before do
- self.instance_variable_set(:@project, project)
- end
it 'in-repository' do
+ allow(repo).to receive(:project).and_return(project)
+
stub_url('./')
expect(submodule_links(submodule_item)).to eq(["/master-project/#{project.path}", "/master-project/#{project.path}/tree/hash"])
end
@@ -167,32 +166,28 @@ describe SubmoduleHelper do
let(:project) { create(:project, group: group) }
let(:commit_id) { sample_commit[:id] }
- before do
- self.instance_variable_set(:@project, project)
- end
-
it 'one level down' do
- result = relative_self_links('../test.git', commit_id)
+ result = relative_self_links('../test.git', commit_id, project)
expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
end
it 'with trailing whitespace' do
- result = relative_self_links('../test.git ', commit_id)
+ result = relative_self_links('../test.git ', commit_id, project)
expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
end
it 'two levels down' do
- result = relative_self_links('../../test.git', commit_id)
+ result = relative_self_links('../../test.git', commit_id, project)
expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
end
it 'one level down with namespace and repo' do
- result = relative_self_links('../foobar/test.git', commit_id)
+ result = relative_self_links('../foobar/test.git', commit_id, project)
expect(result).to eq(["/foobar/test", "/foobar/test/tree/#{commit_id}"])
end
it 'two levels down with namespace and repo' do
- result = relative_self_links('../foobar/baz/test.git', commit_id)
+ result = relative_self_links('../foobar/baz/test.git', commit_id, project)
expect(result).to eq(["/baz/test", "/baz/test/tree/#{commit_id}"])
end
@@ -201,7 +196,7 @@ describe SubmoduleHelper do
let(:project) { create(:project, namespace: user.namespace) }
it 'one level down with personal project' do
- result = relative_self_links('../test.git', commit_id)
+ result = relative_self_links('../test.git', commit_id, project)
expect(result).to eq(["/#{user.username}/test", "/#{user.username}/test/tree/#{commit_id}"])
end
end
diff --git a/spec/helpers/time_helper_spec.rb b/spec/helpers/time_helper_spec.rb
index 21f35585367..0b371d69ecf 100644
--- a/spec/helpers/time_helper_spec.rb
+++ b/spec/helpers/time_helper_spec.rb
@@ -4,10 +4,12 @@ describe TimeHelper do
describe "#time_interval_in_words" do
it "returns minutes and seconds" do
intervals_in_words = {
- 100 => "1 minute 40 seconds",
- 100.32 => "1 minute 40 seconds",
- 121 => "2 minutes 1 second",
- 3721 => "62 minutes 1 second",
+ 60 => "1 minute",
+ 100 => "1 minute and 40 seconds",
+ 100.32 => "1 minute and 40 seconds",
+ 120 => "2 minutes",
+ 121 => "2 minutes and 1 second",
+ 3721 => "62 minutes and 1 second",
0 => "0 seconds"
}
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index b18c045848f..b079802cb81 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -25,8 +25,20 @@ describe UsersHelper do
allow(helper).to receive(:can?).and_return(true)
end
- it 'includes all the expected tabs' do
- expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
+ context 'with public profile' do
+ it 'includes all the expected tabs' do
+ expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
+ end
+ end
+
+ context 'with private profile' do
+ before do
+ allow(helper).to receive(:can?).with(user, :read_user_profile, nil).and_return(false)
+ end
+
+ it 'is empty' do
+ expect(tabs).to be_empty
+ end
end
end
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index 5077c89d7b4..a3be222b7bd 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -6,6 +6,29 @@ describe VisibilityLevelHelper do
let(:personal_snippet) { build(:personal_snippet) }
let(:project_snippet) { build(:project_snippet) }
+ describe 'visibility_icon_description' do
+ context 'used with a Project' do
+ it 'delegates projects to #project_visibility_icon_description' do
+ expect(visibility_icon_description(project))
+ .to match /project/i
+ end
+
+ context 'used with a ProjectPresenter' do
+ it 'delegates projects to #project_visibility_icon_description' do
+ expect(visibility_icon_description(project.present))
+ .to match /project/i
+ end
+ end
+
+ context 'used with a Group' do
+ it 'delegates groups to #group_visibility_icon_description' do
+ expect(visibility_icon_description(group))
+ .to match /group/i
+ end
+ end
+ end
+ end
+
describe 'visibility_level_description' do
context 'used with a Project' do
it 'delegates projects to #project_visibility_level_description' do
diff --git a/spec/javascripts/.eslintrc.yml b/spec/javascripts/.eslintrc.yml
index 78e2f3b521f..5525c9f5bd0 100644
--- a/spec/javascripts/.eslintrc.yml
+++ b/spec/javascripts/.eslintrc.yml
@@ -30,7 +30,6 @@ rules:
jasmine/no-spec-dupes:
- warn
- branch
- no-console: off
prefer-arrow-callback: off
import/no-unresolved:
- error
diff --git a/spec/javascripts/api_spec.js b/spec/javascripts/api_spec.js
index c53b6da4b48..54cb6d84109 100644
--- a/spec/javascripts/api_spec.js
+++ b/spec/javascripts/api_spec.js
@@ -242,7 +242,7 @@ describe('Api', () => {
},
]);
- Api.groupProjects(groupId, query, response => {
+ Api.groupProjects(groupId, query, {}, response => {
expect(response.length).toBe(1);
expect(response[0].name).toBe('test');
done();
diff --git a/spec/javascripts/autosave_spec.js b/spec/javascripts/autosave_spec.js
index 38ae5b7e00c..dcb1c781591 100644
--- a/spec/javascripts/autosave_spec.js
+++ b/spec/javascripts/autosave_spec.js
@@ -59,12 +59,10 @@ describe('Autosave', () => {
Autosave.prototype.restore.call(autosave);
- expect(
- field.trigger,
- ).toHaveBeenCalled();
+ expect(field.trigger).toHaveBeenCalled();
});
- it('triggers native event', (done) => {
+ it('triggers native event', done => {
autosave.field.get(0).addEventListener('change', () => {
done();
});
@@ -81,9 +79,7 @@ describe('Autosave', () => {
it('does not trigger event', () => {
spyOn(field, 'trigger').and.callThrough();
- expect(
- field.trigger,
- ).not.toHaveBeenCalled();
+ expect(field.trigger).not.toHaveBeenCalled();
});
});
});
diff --git a/spec/javascripts/avatar_helper_spec.js b/spec/javascripts/avatar_helper_spec.js
new file mode 100644
index 00000000000..b2f80678ae7
--- /dev/null
+++ b/spec/javascripts/avatar_helper_spec.js
@@ -0,0 +1,98 @@
+import { TEST_HOST } from 'spec/test_constants';
+import { getFirstCharacterCapitalized } from '~/lib/utils/text_utility';
+import {
+ DEFAULT_SIZE_CLASS,
+ IDENTICON_BG_COUNT,
+ renderAvatar,
+ renderIdenticon,
+ getIdenticonBackgroundClass,
+ getIdenticonTitle,
+} from '~/helpers/avatar_helper';
+
+function matchAll(str) {
+ return new RegExp(`^${str}$`);
+}
+
+describe('avatar_helper', () => {
+ describe('getIdenticonBackgroundClass', () => {
+ it('returns identicon bg class from id', () => {
+ expect(getIdenticonBackgroundClass(1)).toEqual('bg2');
+ });
+
+ it(`wraps around if id is bigger than ${IDENTICON_BG_COUNT}`, () => {
+ expect(getIdenticonBackgroundClass(IDENTICON_BG_COUNT + 4)).toEqual('bg5');
+ expect(getIdenticonBackgroundClass((IDENTICON_BG_COUNT * 5) + 6)).toEqual('bg7');
+ });
+ });
+
+ describe('getIdenticonTitle', () => {
+ it('returns identicon title from name', () => {
+ expect(getIdenticonTitle('Lorem')).toEqual('L');
+ expect(getIdenticonTitle('dolar-sit-amit')).toEqual('D');
+ expect(getIdenticonTitle('%-with-special-chars')).toEqual('%');
+ });
+
+ it('returns space if name is falsey', () => {
+ expect(getIdenticonTitle('')).toEqual(' ');
+ expect(getIdenticonTitle(null)).toEqual(' ');
+ });
+ });
+
+ describe('renderIdenticon', () => {
+ it('renders with the first letter as title and bg based on id', () => {
+ const entity = {
+ id: IDENTICON_BG_COUNT + 3,
+ name: 'Xavior',
+ };
+ const options = {
+ sizeClass: 's32',
+ };
+
+ const result = renderIdenticon(entity, options);
+
+ expect(result).toHaveClass(`identicon ${options.sizeClass} bg4`);
+ expect(result).toHaveText(matchAll(getFirstCharacterCapitalized(entity.name)));
+ });
+
+ it('renders with defaults, if no options are given', () => {
+ const entity = {
+ id: 1,
+ name: 'tanuki',
+ };
+
+ const result = renderIdenticon(entity);
+
+ expect(result).toHaveClass(`identicon ${DEFAULT_SIZE_CLASS} bg2`);
+ expect(result).toHaveText(matchAll(getFirstCharacterCapitalized(entity.name)));
+ });
+ });
+
+ describe('renderAvatar', () => {
+ it('renders an image with the avatarUrl', () => {
+ const avatarUrl = `${TEST_HOST}/not-real-assets/test.png`;
+
+ const result = renderAvatar({
+ avatar_url: avatarUrl,
+ });
+
+ expect(result).toBeMatchedBy('img');
+ expect(result).toHaveAttr('src', avatarUrl);
+ expect(result).toHaveClass(DEFAULT_SIZE_CLASS);
+ });
+
+ it('renders an identicon if no avatarUrl', () => {
+ const entity = {
+ id: 1,
+ name: 'walrus',
+ };
+ const options = {
+ sizeClass: 's16',
+ };
+
+ const result = renderAvatar(entity, options);
+
+ expect(result).toHaveClass(`identicon ${options.sizeClass} bg2`);
+ expect(result).toHaveText(matchAll(getFirstCharacterCapitalized(entity.name)));
+ });
+ });
+});
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index f7af099b3bf..1ee6f4cf680 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -161,6 +161,28 @@ describe('Store', () => {
}, 0);
});
+ it('moves an issue from backlog to a list', (done) => {
+ const backlog = gl.issueBoards.BoardsStore.addList({
+ ...listObj,
+ list_type: 'backlog',
+ });
+ const listTwo = gl.issueBoards.BoardsStore.addList(listObjDuplicate);
+
+ expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2);
+
+ setTimeout(() => {
+ expect(backlog.issues.length).toBe(1);
+ expect(listTwo.issues.length).toBe(1);
+
+ gl.issueBoards.BoardsStore.moveIssueToList(backlog, listTwo, backlog.findIssue(1));
+
+ expect(backlog.issues.length).toBe(0);
+ expect(listTwo.issues.length).toBe(1);
+
+ done();
+ }, 0);
+ });
+
it('moves issue to top of another list', (done) => {
const listOne = gl.issueBoards.BoardsStore.addList(listObj);
const listTwo = gl.issueBoards.BoardsStore.addList(listObjDuplicate);
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index 7a32e84bced..b6c61e7bad7 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -69,109 +69,100 @@ describe('Issue card component', () => {
});
it('renders issue title', () => {
- expect(
- component.$el.querySelector('.board-card-title').textContent,
- ).toContain(issue.title);
+ expect(component.$el.querySelector('.board-card-title').textContent).toContain(issue.title);
});
it('includes issue base in link', () => {
- expect(
- component.$el.querySelector('.board-card-title a').getAttribute('href'),
- ).toContain('/test');
+ expect(component.$el.querySelector('.board-card-title a').getAttribute('href')).toContain(
+ '/test',
+ );
});
it('includes issue title on link', () => {
- expect(
- component.$el.querySelector('.board-card-title a').getAttribute('title'),
- ).toBe(issue.title);
+ expect(component.$el.querySelector('.board-card-title a').getAttribute('title')).toBe(
+ issue.title,
+ );
});
it('does not render confidential icon', () => {
- expect(
- component.$el.querySelector('.fa-eye-flash'),
- ).toBeNull();
+ expect(component.$el.querySelector('.fa-eye-flash')).toBeNull();
});
- it('renders confidential icon', (done) => {
+ it('renders confidential icon', done => {
component.issue.confidential = true;
Vue.nextTick(() => {
- expect(
- component.$el.querySelector('.confidential-icon'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.confidential-icon')).not.toBeNull();
done();
});
});
it('renders issue ID with #', () => {
- expect(
- component.$el.querySelector('.board-card-number').textContent,
- ).toContain(`#${issue.id}`);
+ expect(component.$el.querySelector('.board-card-number').textContent).toContain(`#${issue.id}`);
});
describe('assignee', () => {
it('does not render assignee', () => {
- expect(
- component.$el.querySelector('.board-card-assignee .avatar'),
- ).toBeNull();
+ expect(component.$el.querySelector('.board-card-assignee .avatar')).toBeNull();
});
describe('exists', () => {
- beforeEach((done) => {
+ beforeEach(done => {
component.issue.assignees = [user];
Vue.nextTick(() => done());
});
it('renders assignee', () => {
- expect(
- component.$el.querySelector('.board-card-assignee .avatar'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-card-assignee .avatar')).not.toBeNull();
});
it('sets title', () => {
expect(
- component.$el.querySelector('.board-card-assignee img').getAttribute('data-original-title'),
+ component.$el
+ .querySelector('.board-card-assignee img')
+ .getAttribute('data-original-title'),
).toContain(`Assigned to ${user.name}`);
});
it('sets users path', () => {
- expect(
- component.$el.querySelector('.board-card-assignee a').getAttribute('href'),
- ).toBe('/test');
+ expect(component.$el.querySelector('.board-card-assignee a').getAttribute('href')).toBe(
+ '/test',
+ );
});
it('renders avatar', () => {
- expect(
- component.$el.querySelector('.board-card-assignee img'),
- ).not.toBeNull();
+ expect(component.$el.querySelector('.board-card-assignee img')).not.toBeNull();
});
});
describe('assignee default avatar', () => {
- beforeEach((done) => {
- component.issue.assignees = [new ListAssignee({
- id: 1,
- name: 'testing 123',
- username: 'test',
- }, 'default_avatar')];
+ beforeEach(done => {
+ component.issue.assignees = [
+ new ListAssignee(
+ {
+ id: 1,
+ name: 'testing 123',
+ username: 'test',
+ },
+ 'default_avatar',
+ ),
+ ];
Vue.nextTick(done);
});
it('displays defaults avatar if users avatar is null', () => {
- expect(
- component.$el.querySelector('.board-card-assignee img'),
- ).not.toBeNull();
- expect(
- component.$el.querySelector('.board-card-assignee img').getAttribute('src'),
- ).toBe('default_avatar');
+ expect(component.$el.querySelector('.board-card-assignee img')).not.toBeNull();
+ expect(component.$el.querySelector('.board-card-assignee img').getAttribute('src')).toBe(
+ 'default_avatar?width=20',
+ );
});
});
});
describe('multiple assignees', () => {
- beforeEach((done) => {
+ beforeEach(done => {
component.issue.assignees = [
user,
new ListAssignee({
@@ -191,7 +182,8 @@ describe('Issue card component', () => {
name: 'user4',
username: 'user4',
avatar: 'test_image',
- })];
+ }),
+ ];
Vue.nextTick(() => done());
});
@@ -201,26 +193,30 @@ describe('Issue card component', () => {
});
describe('more than four assignees', () => {
- beforeEach((done) => {
- component.issue.assignees.push(new ListAssignee({
- id: 5,
- name: 'user5',
- username: 'user5',
- avatar: 'test_image',
- }));
+ beforeEach(done => {
+ component.issue.assignees.push(
+ new ListAssignee({
+ id: 5,
+ name: 'user5',
+ username: 'user5',
+ avatar: 'test_image',
+ }),
+ );
Vue.nextTick(() => done());
});
it('renders more avatar counter', () => {
- expect(component.$el.querySelector('.board-card-assignee .avatar-counter').innerText).toEqual('+2');
+ expect(
+ component.$el.querySelector('.board-card-assignee .avatar-counter').innerText,
+ ).toEqual('+2');
});
it('renders three assignees', () => {
expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(3);
});
- it('renders 99+ avatar counter', (done) => {
+ it('renders 99+ avatar counter', done => {
for (let i = 5; i < 104; i += 1) {
const u = new ListAssignee({
id: i,
@@ -232,7 +228,9 @@ describe('Issue card component', () => {
}
Vue.nextTick(() => {
- expect(component.$el.querySelector('.board-card-assignee .avatar-counter').innerText).toEqual('99+');
+ expect(
+ component.$el.querySelector('.board-card-assignee .avatar-counter').innerText,
+ ).toEqual('99+');
done();
});
});
@@ -240,59 +238,51 @@ describe('Issue card component', () => {
});
describe('labels', () => {
- beforeEach((done) => {
+ beforeEach(done => {
component.issue.addLabel(label1);
Vue.nextTick(() => done());
});
it('renders list label', () => {
- expect(
- component.$el.querySelectorAll('.badge').length,
- ).toBe(2);
+ expect(component.$el.querySelectorAll('.badge').length).toBe(2);
});
it('renders label', () => {
const nodes = [];
- component.$el.querySelectorAll('.badge').forEach((label) => {
+ component.$el.querySelectorAll('.badge').forEach(label => {
nodes.push(label.getAttribute('data-original-title'));
});
- expect(
- nodes.includes(label1.description),
- ).toBe(true);
+ expect(nodes.includes(label1.description)).toBe(true);
});
it('sets label description as title', () => {
- expect(
- component.$el.querySelector('.badge').getAttribute('data-original-title'),
- ).toContain(label1.description);
+ expect(component.$el.querySelector('.badge').getAttribute('data-original-title')).toContain(
+ label1.description,
+ );
});
it('sets background color of button', () => {
const nodes = [];
- component.$el.querySelectorAll('.badge').forEach((label) => {
+ component.$el.querySelectorAll('.badge').forEach(label => {
nodes.push(label.style.backgroundColor);
});
- expect(
- nodes.includes(label1.color),
- ).toBe(true);
+ expect(nodes.includes(label1.color)).toBe(true);
});
- it('does not render label if label does not have an ID', (done) => {
- component.issue.addLabel(new ListLabel({
- title: 'closed',
- }));
+ it('does not render label if label does not have an ID', done => {
+ component.issue.addLabel(
+ new ListLabel({
+ title: 'closed',
+ }),
+ );
Vue.nextTick()
.then(() => {
- expect(
- component.$el.querySelectorAll('.badge').length,
- ).toBe(2);
- expect(
- component.$el.textContent,
- ).not.toContain('closed');
+ expect(component.$el.querySelectorAll('.badge').length).toBe(2);
+ expect(component.$el.textContent).not.toContain('closed');
done();
})
diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js
index abe2954d506..d0e0b214509 100644
--- a/spec/javascripts/clusters/clusters_bundle_spec.js
+++ b/spec/javascripts/clusters/clusters_bundle_spec.js
@@ -1,11 +1,9 @@
import Clusters from '~/clusters/clusters_bundle';
import {
- APPLICATION_INSTALLABLE,
- APPLICATION_INSTALLING,
- APPLICATION_INSTALLED,
REQUEST_LOADING,
REQUEST_SUCCESS,
REQUEST_FAILURE,
+ APPLICATION_STATUS,
} from '~/clusters/constants';
import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
@@ -45,17 +43,33 @@ describe('Clusters', () => {
});
describe('showToken', () => {
- it('should update tye field type', () => {
+ it('should update token field type', () => {
cluster.showTokenButton.click();
+
expect(
cluster.tokenField.getAttribute('type'),
).toEqual('text');
cluster.showTokenButton.click();
+
expect(
cluster.tokenField.getAttribute('type'),
).toEqual('password');
});
+
+ it('should update show token button text', () => {
+ cluster.showTokenButton.click();
+
+ expect(
+ cluster.showTokenButton.textContent,
+ ).toEqual('Hide');
+
+ cluster.showTokenButton.click();
+
+ expect(
+ cluster.showTokenButton.textContent,
+ ).toEqual('Show');
+ });
});
describe('checkForNewInstalls', () => {
@@ -68,7 +82,7 @@ describe('Clusters', () => {
it('does not show alert when things transition from initial null state to something', () => {
cluster.checkForNewInstalls(INITIAL_APP_MAP, {
...INITIAL_APP_MAP,
- helm: { status: APPLICATION_INSTALLABLE, title: 'Helm Tiller' },
+ helm: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Helm Tiller' },
});
const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
@@ -78,10 +92,10 @@ describe('Clusters', () => {
it('shows an alert when something gets newly installed', () => {
cluster.checkForNewInstalls({
...INITIAL_APP_MAP,
- helm: { status: APPLICATION_INSTALLING, title: 'Helm Tiller' },
+ helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' },
}, {
...INITIAL_APP_MAP,
- helm: { status: APPLICATION_INSTALLED, title: 'Helm Tiller' },
+ helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' },
});
const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
@@ -92,12 +106,12 @@ describe('Clusters', () => {
it('shows an alert when multiple things gets newly installed', () => {
cluster.checkForNewInstalls({
...INITIAL_APP_MAP,
- helm: { status: APPLICATION_INSTALLING, title: 'Helm Tiller' },
- ingress: { status: APPLICATION_INSTALLABLE, title: 'Ingress' },
+ helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' },
+ ingress: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Ingress' },
}, {
...INITIAL_APP_MAP,
- helm: { status: APPLICATION_INSTALLED, title: 'Helm Tiller' },
- ingress: { status: APPLICATION_INSTALLED, title: 'Ingress' },
+ helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' },
+ ingress: { status: APPLICATION_STATUS.INSTALLED, title: 'Ingress' },
});
const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js
index c83cbe90a57..9da5c248371 100644
--- a/spec/javascripts/clusters/components/application_row_spec.js
+++ b/spec/javascripts/clusters/components/application_row_spec.js
@@ -1,12 +1,7 @@
import Vue from 'vue';
import eventHub from '~/clusters/event_hub';
import {
- APPLICATION_NOT_INSTALLABLE,
- APPLICATION_SCHEDULED,
- APPLICATION_INSTALLABLE,
- APPLICATION_INSTALLING,
- APPLICATION_INSTALLED,
- APPLICATION_ERROR,
+ APPLICATION_STATUS,
REQUEST_LOADING,
REQUEST_SUCCESS,
REQUEST_FAILURE,
@@ -62,10 +57,10 @@ describe('Application Row', () => {
expect(vm.installButtonLabel).toBeUndefined();
});
- it('has disabled "Install" when APPLICATION_NOT_INSTALLABLE', () => {
+ it('has disabled "Install" when APPLICATION_STATUS.NOT_INSTALLABLE', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_NOT_INSTALLABLE,
+ status: APPLICATION_STATUS.NOT_INSTALLABLE,
});
expect(vm.installButtonLabel).toEqual('Install');
@@ -73,10 +68,10 @@ describe('Application Row', () => {
expect(vm.installButtonDisabled).toEqual(true);
});
- it('has enabled "Install" when APPLICATION_INSTALLABLE', () => {
+ it('has enabled "Install" when APPLICATION_STATUS.INSTALLABLE', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLABLE,
+ status: APPLICATION_STATUS.INSTALLABLE,
});
expect(vm.installButtonLabel).toEqual('Install');
@@ -84,10 +79,10 @@ describe('Application Row', () => {
expect(vm.installButtonDisabled).toEqual(false);
});
- it('has loading "Installing" when APPLICATION_SCHEDULED', () => {
+ it('has loading "Installing" when APPLICATION_STATUS.SCHEDULED', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_SCHEDULED,
+ status: APPLICATION_STATUS.SCHEDULED,
});
expect(vm.installButtonLabel).toEqual('Installing');
@@ -95,10 +90,10 @@ describe('Application Row', () => {
expect(vm.installButtonDisabled).toEqual(true);
});
- it('has loading "Installing" when APPLICATION_INSTALLING', () => {
+ it('has loading "Installing" when APPLICATION_STATUS.INSTALLING', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLING,
+ status: APPLICATION_STATUS.INSTALLING,
});
expect(vm.installButtonLabel).toEqual('Installing');
@@ -106,10 +101,10 @@ describe('Application Row', () => {
expect(vm.installButtonDisabled).toEqual(true);
});
- it('has disabled "Installed" when APPLICATION_INSTALLED', () => {
+ it('has disabled "Installed" when APPLICATION_STATUS.INSTALLED', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLED,
+ status: APPLICATION_STATUS.INSTALLED,
});
expect(vm.installButtonLabel).toEqual('Installed');
@@ -117,10 +112,10 @@ describe('Application Row', () => {
expect(vm.installButtonDisabled).toEqual(true);
});
- it('has enabled "Install" when APPLICATION_ERROR', () => {
+ it('has enabled "Install" when APPLICATION_STATUS.ERROR', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_ERROR,
+ status: APPLICATION_STATUS.ERROR,
});
expect(vm.installButtonLabel).toEqual('Install');
@@ -131,7 +126,7 @@ describe('Application Row', () => {
it('has loading "Install" when REQUEST_LOADING', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLABLE,
+ status: APPLICATION_STATUS.INSTALLABLE,
requestStatus: REQUEST_LOADING,
});
@@ -143,7 +138,7 @@ describe('Application Row', () => {
it('has disabled "Install" when REQUEST_SUCCESS', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLABLE,
+ status: APPLICATION_STATUS.INSTALLABLE,
requestStatus: REQUEST_SUCCESS,
});
@@ -155,7 +150,7 @@ describe('Application Row', () => {
it('has enabled "Install" when REQUEST_FAILURE (so you can try installing again)', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLABLE,
+ status: APPLICATION_STATUS.INSTALLABLE,
requestStatus: REQUEST_FAILURE,
});
@@ -168,7 +163,7 @@ describe('Application Row', () => {
spyOn(eventHub, '$emit');
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLABLE,
+ status: APPLICATION_STATUS.INSTALLABLE,
});
const installButton = vm.$el.querySelector('.js-cluster-application-install-button');
@@ -184,7 +179,7 @@ describe('Application Row', () => {
spyOn(eventHub, '$emit');
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLABLE,
+ status: APPLICATION_STATUS.INSTALLABLE,
installApplicationRequestParams: { hostname: 'jupyter' },
});
const installButton = vm.$el.querySelector('.js-cluster-application-install-button');
@@ -201,7 +196,7 @@ describe('Application Row', () => {
spyOn(eventHub, '$emit');
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLING,
+ status: APPLICATION_STATUS.INSTALLING,
});
const installButton = vm.$el.querySelector('.js-cluster-application-install-button');
@@ -225,11 +220,11 @@ describe('Application Row', () => {
expect(generalErrorMessage).toBeNull();
});
- it('shows status reason when APPLICATION_ERROR', () => {
+ it('shows status reason when APPLICATION_STATUS.ERROR', () => {
const statusReason = 'We broke it 0.0';
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_ERROR,
+ status: APPLICATION_STATUS.ERROR,
statusReason,
});
const generalErrorMessage = vm.$el.querySelector('.js-cluster-application-general-error-message');
@@ -243,7 +238,7 @@ describe('Application Row', () => {
const requestReason = 'We broke thre request 0.0';
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_INSTALLABLE,
+ status: APPLICATION_STATUS.INSTALLABLE,
requestStatus: REQUEST_FAILURE,
requestReason,
});
diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js
index b2b0ebf840b..c7c1412e1c6 100644
--- a/spec/javascripts/clusters/services/mock_data.js
+++ b/spec/javascripts/clusters/services/mock_data.js
@@ -1,9 +1,4 @@
-import {
- APPLICATION_INSTALLED,
- APPLICATION_INSTALLABLE,
- APPLICATION_INSTALLING,
- APPLICATION_ERROR,
-} from '~/clusters/constants';
+import { APPLICATION_STATUS } from '~/clusters/constants';
const CLUSTERS_MOCK_DATA = {
GET: {
@@ -13,25 +8,25 @@ const CLUSTERS_MOCK_DATA = {
status_reason: 'Failed to request to CloudPlatform.',
applications: [{
name: 'helm',
- status: APPLICATION_INSTALLABLE,
+ status: APPLICATION_STATUS.INSTALLABLE,
status_reason: null,
}, {
name: 'ingress',
- status: APPLICATION_ERROR,
+ status: APPLICATION_STATUS.ERROR,
status_reason: 'Cannot connect',
external_ip: null,
}, {
name: 'runner',
- status: APPLICATION_INSTALLING,
+ status: APPLICATION_STATUS.INSTALLING,
status_reason: null,
},
{
name: 'prometheus',
- status: APPLICATION_ERROR,
+ status: APPLICATION_STATUS.ERROR,
status_reason: 'Cannot connect',
}, {
name: 'jupyter',
- status: APPLICATION_INSTALLING,
+ status: APPLICATION_STATUS.INSTALLING,
status_reason: 'Cannot connect',
}],
},
@@ -42,25 +37,25 @@ const CLUSTERS_MOCK_DATA = {
status_reason: 'Failed to request to CloudPlatform.',
applications: [{
name: 'helm',
- status: APPLICATION_INSTALLED,
+ status: APPLICATION_STATUS.INSTALLED,
status_reason: null,
}, {
name: 'ingress',
- status: APPLICATION_INSTALLED,
+ status: APPLICATION_STATUS.INSTALLED,
status_reason: 'Cannot connect',
external_ip: '1.1.1.1',
}, {
name: 'runner',
- status: APPLICATION_INSTALLING,
+ status: APPLICATION_STATUS.INSTALLING,
status_reason: null,
},
{
name: 'prometheus',
- status: APPLICATION_ERROR,
+ status: APPLICATION_STATUS.ERROR,
status_reason: 'Cannot connect',
}, {
name: 'jupyter',
- status: APPLICATION_INSTALLABLE,
+ status: APPLICATION_STATUS.INSTALLABLE,
status_reason: 'Cannot connect',
}],
},
diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js
index 9e43552f740..104a064bdd3 100644
--- a/spec/javascripts/clusters/stores/clusters_store_spec.js
+++ b/spec/javascripts/clusters/stores/clusters_store_spec.js
@@ -1,5 +1,5 @@
import ClustersStore from '~/clusters/stores/clusters_store';
-import { APPLICATION_INSTALLING } from '~/clusters/constants';
+import { APPLICATION_STATUS } from '~/clusters/constants';
import { CLUSTERS_MOCK_DATA } from '../services/mock_data';
describe('Clusters Store', () => {
@@ -35,7 +35,7 @@ describe('Clusters Store', () => {
it('should store new request status', () => {
expect(store.state.applications.helm.requestStatus).toEqual(null);
- const newStatus = APPLICATION_INSTALLING;
+ const newStatus = APPLICATION_STATUS.INSTALLING;
store.updateAppProperty('helm', 'requestStatus', newStatus);
expect(store.state.applications.helm.requestStatus).toEqual(newStatus);
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
index e224ed46d18..492171684dc 100644
--- a/spec/javascripts/datetime_utility_spec.js
+++ b/spec/javascripts/datetime_utility_spec.js
@@ -162,7 +162,6 @@ describe('getTimeframeWindowFrom', () => {
const timeframe = datetimeUtility.getTimeframeWindowFrom(startDate, 5);
expect(timeframe.length).toBe(5);
timeframe.forEach((timeframeItem, index) => {
- console.log(timeframeItem);
expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBe(true);
expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBe(true);
expect(timeframeItem.getDate() === mockTimeframe[index].getDate()).toBeTruthy();
diff --git a/spec/javascripts/diffs/components/changed_files_spec.js b/spec/javascripts/diffs/components/changed_files_spec.js
index 2d57af6137c..f737e8fa38e 100644
--- a/spec/javascripts/diffs/components/changed_files_spec.js
+++ b/spec/javascripts/diffs/components/changed_files_spec.js
@@ -1,12 +1,17 @@
import Vue from 'vue';
-import $ from 'jquery';
+import Vuex from 'vuex';
import { mountComponentWithStore } from 'spec/helpers';
-import store from '~/diffs/store';
-import ChangedFiles from '~/diffs/components/changed_files.vue';
+import diffsModule from '~/diffs/store/modules';
+import changedFiles from '~/diffs/components/changed_files.vue';
describe('ChangedFiles', () => {
- const Component = Vue.extend(ChangedFiles);
- const createComponent = props => mountComponentWithStore(Component, { props, store });
+ const Component = Vue.extend(changedFiles);
+ const store = new Vuex.Store({
+ modules: {
+ diffs: diffsModule,
+ },
+ });
+
let vm;
beforeEach(() => {
@@ -14,6 +19,7 @@ describe('ChangedFiles', () => {
<div id="dummy-element"></div>
<div class="js-tabs-affix"></div>
`);
+
const props = {
diffFiles: [
{
@@ -26,7 +32,8 @@ describe('ChangedFiles', () => {
},
],
};
- vm = createComponent(props);
+
+ vm = mountComponentWithStore(Component, { props, store });
});
describe('with single file added', () => {
@@ -40,58 +47,56 @@ describe('ChangedFiles', () => {
});
});
- describe('template', () => {
- describe('diff view mode buttons', () => {
- let inlineButton;
- let parallelButton;
+ describe('diff view mode buttons', () => {
+ let inlineButton;
+ let parallelButton;
- beforeEach(() => {
- inlineButton = vm.$el.querySelector('.js-inline-diff-button');
- parallelButton = vm.$el.querySelector('.js-parallel-diff-button');
- });
+ beforeEach(() => {
+ inlineButton = vm.$el.querySelector('.js-inline-diff-button');
+ parallelButton = vm.$el.querySelector('.js-parallel-diff-button');
+ });
+
+ it('should have Inline and Side-by-side buttons', () => {
+ expect(inlineButton).toBeDefined();
+ expect(parallelButton).toBeDefined();
+ });
+
+ it('should add active class to Inline button', done => {
+ vm.$store.state.diffs.diffViewType = 'inline';
+
+ vm.$nextTick(() => {
+ expect(inlineButton.classList.contains('active')).toEqual(true);
+ expect(parallelButton.classList.contains('active')).toEqual(false);
- it('should have Inline and Side-by-side buttons', () => {
- expect(inlineButton).toBeDefined();
- expect(parallelButton).toBeDefined();
+ done();
});
+ });
- it('should add active class to Inline button', done => {
- vm.$store.state.diffs.diffViewType = 'inline';
+ it('should toggle active state of buttons when diff view type changed', done => {
+ vm.$store.state.diffs.diffViewType = 'parallel';
- vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(true);
- expect(parallelButton.classList.contains('active')).toEqual(false);
+ vm.$nextTick(() => {
+ expect(inlineButton.classList.contains('active')).toEqual(false);
+ expect(parallelButton.classList.contains('active')).toEqual(true);
- done();
- });
+ done();
});
+ });
- it('should toggle active state of buttons when diff view type changed', done => {
- vm.$store.state.diffs.diffViewType = 'parallel';
+ describe('clicking them', () => {
+ it('should toggle the diff view type', done => {
+ parallelButton.click();
vm.$nextTick(() => {
expect(inlineButton.classList.contains('active')).toEqual(false);
expect(parallelButton.classList.contains('active')).toEqual(true);
- done();
- });
- });
-
- describe('clicking them', () => {
- it('should toggle the diff view type', done => {
- $(parallelButton).click();
+ inlineButton.click();
vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(false);
- expect(parallelButton.classList.contains('active')).toEqual(true);
-
- $(inlineButton).click();
-
- vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(true);
- expect(parallelButton.classList.contains('active')).toEqual(false);
- done();
- });
+ expect(inlineButton.classList.contains('active')).toEqual(true);
+ expect(parallelButton.classList.contains('active')).toEqual(false);
+ done();
});
});
});
diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
index d0f1700bee6..92b2004c4d7 100644
--- a/spec/javascripts/diffs/components/diff_file_header_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -1,21 +1,31 @@
import Vue from 'vue';
+import Vuex from 'vuex';
+import diffsModule from '~/diffs/store/modules';
+import notesModule from '~/notes/stores/modules';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
const discussionFixture = 'merge_requests/diff_discussion.json';
describe('diff_file_header', () => {
let vm;
let props;
+ const diffDiscussionMock = getJSONFixture(discussionFixture)[0];
const Component = Vue.extend(DiffFileHeader);
+ const store = new Vuex.Store({
+ modules: {
+ diffs: diffsModule,
+ notes: notesModule,
+ },
+ });
+
beforeEach(() => {
- const diffDiscussionMock = getJSONFixture(discussionFixture)[0];
const diffFile = convertObjectPropsToCamelCase(diffDiscussionMock.diff_file, { deep: true });
props = {
diffFile,
- currentUser: {},
+ canCurrentUserFork: false,
};
});
@@ -26,13 +36,13 @@ describe('diff_file_header', () => {
describe('computed', () => {
describe('icon', () => {
beforeEach(() => {
- props.diffFile.blob.icon = 'dummy icon';
+ props.diffFile.blob.icon = 'file-text-o';
});
it('returns the blob icon for files', () => {
props.diffFile.submodule = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.icon).toBe(props.diffFile.blob.icon);
});
@@ -40,7 +50,7 @@ describe('diff_file_header', () => {
it('returns the archive icon for submodules', () => {
props.diffFile.submodule = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.icon).toBe('archive');
});
@@ -58,7 +68,7 @@ describe('diff_file_header', () => {
it('returns the fileHash for files', () => {
props.diffFile.submodule = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleLink).toBe(`#${props.diffFile.fileHash}`);
});
@@ -66,7 +76,7 @@ describe('diff_file_header', () => {
it('returns the submoduleTreeUrl for submodules', () => {
props.diffFile.submodule = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleLink).toBe(props.diffFile.submoduleTreeUrl);
});
@@ -77,7 +87,7 @@ describe('diff_file_header', () => {
submoduleTreeUrl: null,
});
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleLink).toBe(props.diffFile.submoduleLink);
});
@@ -94,7 +104,7 @@ describe('diff_file_header', () => {
it('returns the filePath for files', () => {
props.diffFile.submodule = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.filePath).toBe(props.diffFile.filePath);
});
@@ -102,7 +112,7 @@ describe('diff_file_header', () => {
it('appends the truncated blob id for submodules', () => {
props.diffFile.submodule = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.filePath).toBe(
`${props.diffFile.filePath} @ ${props.diffFile.blob.id.substr(0, 8)}`,
@@ -114,7 +124,7 @@ describe('diff_file_header', () => {
it('returns a link tag if fileHash is set', () => {
props.diffFile.fileHash = 'some hash';
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleTag).toBe('a');
});
@@ -122,7 +132,7 @@ describe('diff_file_header', () => {
it('returns a span tag if fileHash is not set', () => {
props.diffFile.fileHash = null;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleTag).toBe('span');
});
@@ -137,7 +147,7 @@ describe('diff_file_header', () => {
});
it('returns true if file is stored in LFS', () => {
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.isUsingLfs).toBe(true);
});
@@ -145,7 +155,7 @@ describe('diff_file_header', () => {
it('returns false if file is not stored externally', () => {
props.diffFile.storedExternally = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.isUsingLfs).toBe(false);
});
@@ -153,7 +163,7 @@ describe('diff_file_header', () => {
it('returns false if file is not stored in LFS', () => {
props.diffFile.externalStorage = 'not lfs';
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.isUsingLfs).toBe(false);
});
@@ -163,7 +173,7 @@ describe('diff_file_header', () => {
it('returns chevron-down if the diff is expanded', () => {
props.expanded = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.collapseIcon).toBe('chevron-down');
});
@@ -171,49 +181,18 @@ describe('diff_file_header', () => {
it('returns chevron-right if the diff is collapsed', () => {
props.expanded = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.collapseIcon).toBe('chevron-right');
});
});
- describe('isDiscussionsExpanded', () => {
- beforeEach(() => {
- Object.assign(props, {
- discussionsExpanded: true,
- expanded: true,
- });
- });
-
- it('returns true if diff and discussion are expanded', () => {
- vm = mountComponent(Component, props);
-
- expect(vm.isDiscussionsExpanded).toBe(true);
- });
-
- it('returns false if discussion is collapsed', () => {
- props.discussionsExpanded = false;
-
- vm = mountComponent(Component, props);
-
- expect(vm.isDiscussionsExpanded).toBe(false);
- });
-
- it('returns false if diff is collapsed', () => {
- props.expanded = false;
-
- vm = mountComponent(Component, props);
-
- expect(vm.isDiscussionsExpanded).toBe(false);
- });
- });
-
describe('viewFileButtonText', () => {
it('contains the truncated content SHA', () => {
const dummySha = 'deebd00f is no SHA';
props.diffFile.contentSha = dummySha;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.viewFileButtonText).not.toContain(dummySha);
expect(vm.viewFileButtonText).toContain(dummySha.substr(0, 8));
@@ -225,7 +204,7 @@ describe('diff_file_header', () => {
const dummySha = 'deadabba sings no more';
props.diffFile.diffRefs.baseSha = dummySha;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.viewReplacedFileButtonText).not.toContain(dummySha);
expect(vm.viewReplacedFileButtonText).toContain(dummySha.substr(0, 8));
@@ -234,25 +213,25 @@ describe('diff_file_header', () => {
});
describe('methods', () => {
- describe('handleToggle', () => {
+ describe('handleToggleFile', () => {
beforeEach(() => {
spyOn(vm, '$emit').and.stub();
});
it('emits toggleFile if checkTarget is false', () => {
- vm.handleToggle(null, false);
+ vm.handleToggleFile(null, false);
expect(vm.$emit).toHaveBeenCalledWith('toggleFile');
});
it('emits toggleFile if checkTarget is true and event target is header', () => {
- vm.handleToggle({ target: vm.$refs.header }, true);
+ vm.handleToggleFile({ target: vm.$refs.header }, true);
expect(vm.$emit).toHaveBeenCalledWith('toggleFile');
});
it('does not emit toggleFile if checkTarget is true and event target is not header', () => {
- vm.handleToggle({ target: 'not header' }, true);
+ vm.handleToggleFile({ target: 'not header' }, true);
expect(vm.$emit).not.toHaveBeenCalled();
});
@@ -266,7 +245,7 @@ describe('diff_file_header', () => {
it('is visible if collapsible is true', () => {
props.collapsible = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(collapseToggle()).not.toBe(null);
});
@@ -274,17 +253,17 @@ describe('diff_file_header', () => {
it('is hidden if collapsible is false', () => {
props.collapsible = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(collapseToggle()).toBe(null);
});
});
- it('displays an icon in the title', () => {
- vm = mountComponent(Component, props);
-
- const icon = vm.$el.querySelector(`i[class="fa fa-fw fa-${vm.icon}"]`);
- expect(icon).not.toBe(null);
+ it('displays an file icon in the title', () => {
+ vm = mountComponentWithStore(Component, { props, store });
+ expect(vm.$el.querySelector('svg.js-file-icon use').getAttribute('xlink:href')).toContain(
+ 'ruby',
+ );
});
describe('file paths', () => {
@@ -293,7 +272,7 @@ describe('diff_file_header', () => {
it('displays the path of a added file', () => {
props.diffFile.renamedFile = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(1);
expect(filePaths()[0]).toHaveText(props.diffFile.filePath);
@@ -303,7 +282,7 @@ describe('diff_file_header', () => {
props.diffFile.renamedFile = false;
props.diffFile.deletedFile = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(1);
expect(filePaths()[0]).toHaveText(`${props.diffFile.filePath} deleted`);
@@ -312,7 +291,7 @@ describe('diff_file_header', () => {
it('displays old and new path if the file was renamed', () => {
props.diffFile.renamedFile = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(2);
expect(filePaths()[0]).toHaveText(props.diffFile.oldPath);
@@ -321,18 +300,18 @@ describe('diff_file_header', () => {
});
it('displays a copy to clipboard button', () => {
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
const button = vm.$el.querySelector('.btn-clipboard');
expect(button).not.toBe(null);
- expect(button.dataset.clipboardText).toBe(props.diffFile.filePath);
+ expect(button.dataset.clipboardText).toBe('{"text":"files/ruby/popen.rb","gfm":"`files/ruby/popen.rb`"}');
});
describe('file mode', () => {
it('it displays old and new file mode if it changed', () => {
props.diffFile.modeChanged = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
const { fileMode } = vm.$refs;
expect(fileMode).not.toBe(undefined);
@@ -343,7 +322,7 @@ describe('diff_file_header', () => {
it('does not display the file mode if it has not changed', () => {
props.diffFile.modeChanged = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
const { fileMode } = vm.$refs;
expect(fileMode).toBe(undefined);
@@ -359,7 +338,7 @@ describe('diff_file_header', () => {
externalStorage: 'lfs',
});
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(lfsLabel()).not.toBe(null);
expect(lfsLabel()).toHaveText('LFS');
@@ -368,7 +347,7 @@ describe('diff_file_header', () => {
it('does not display the LFS label for files stored in repository', () => {
props.diffFile.storedExternally = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(lfsLabel()).toBe(null);
});
@@ -376,7 +355,7 @@ describe('diff_file_header', () => {
describe('edit button', () => {
it('should not render edit button if addMergeRequestButtons is not true', () => {
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null);
});
@@ -384,7 +363,7 @@ describe('diff_file_header', () => {
it('should show edit button when file is editable', () => {
props.addMergeRequestButtons = true;
props.diffFile.editPath = '/';
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector('.js-edit-blob')).toContainText('Edit');
});
@@ -393,7 +372,7 @@ describe('diff_file_header', () => {
props.addMergeRequestButtons = true;
props.diffFile.deletedFile = true;
props.diffFile.editPath = '/';
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null);
});
@@ -413,7 +392,7 @@ describe('diff_file_header', () => {
props.diffFile.externalUrl = url;
props.diffFile.formattedExternalUrl = title;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector(`a[href="${url}"]`)).not.toBe(null);
expect(vm.$el.querySelector(`a[data-original-title="View on ${title}"]`)).not.toBe(null);
@@ -423,11 +402,72 @@ describe('diff_file_header', () => {
props.diffFile.externalUrl = '';
props.diffFile.formattedExternalUrl = title;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector(`a[data-original-title="View on ${title}"]`)).toBe(null);
});
});
});
+
+ describe('handles toggle discussions', () => {
+ it('renders a disabled button when diff has no discussions', () => {
+ const propsCopy = Object.assign({}, props);
+ propsCopy.diffFile.submodule = false;
+ propsCopy.diffFile.blob = {
+ id: '848ed9407c6730ff16edb3dd24485a0eea24292a',
+ path: 'lib/base.js',
+ name: 'base.js',
+ mode: '100644',
+ readableText: true,
+ icon: 'file-text-o',
+ };
+ propsCopy.addMergeRequestButtons = true;
+ propsCopy.diffFile.deletedFile = true;
+
+ vm = mountComponentWithStore(Component, {
+ props: propsCopy,
+ store,
+ });
+
+ expect(
+ vm.$el.querySelector('.js-btn-vue-toggle-comments').getAttribute('disabled'),
+ ).toEqual('disabled');
+ });
+
+ describe('with discussions', () => {
+ it('dispatches toggleFileDiscussions when user clicks on toggle discussions button', () => {
+ const propsCopy = Object.assign({}, props);
+ propsCopy.diffFile.submodule = false;
+ propsCopy.diffFile.blob = {
+ id: '848ed9407c6730ff16edb3dd24485a0eea24292a',
+ path: 'lib/base.js',
+ name: 'base.js',
+ mode: '100644',
+ readableText: true,
+ icon: 'file-text-o',
+ };
+ propsCopy.addMergeRequestButtons = true;
+ propsCopy.diffFile.deletedFile = true;
+
+ const discussionGetter = () => [diffDiscussionMock];
+ notesModule.getters.discussions = discussionGetter;
+ vm = mountComponentWithStore(Component, {
+ props: propsCopy,
+ store: new Vuex.Store({
+ modules: {
+ diffs: diffsModule,
+ notes: notesModule,
+ },
+ }),
+ });
+
+ spyOn(vm, 'toggleFileDiscussions');
+
+ vm.$el.querySelector('.js-btn-vue-toggle-comments').click();
+
+ expect(vm.toggleFileDiscussions).toHaveBeenCalled();
+ });
+ });
+ });
});
});
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 1c1edfac68c..7a4616ec8eb 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -11,7 +11,7 @@ describe('DiffFile', () => {
beforeEach(() => {
vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, {
file: getDiffFileMock(),
- currentUser: {},
+ canCurrentUserFork: false,
}).$mount();
});
@@ -31,12 +31,12 @@ describe('DiffFile', () => {
describe('collapsed', () => {
it('should not have file content', done => {
- expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
+ expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(1);
expect(vm.file.collapsed).toEqual(false);
vm.file.collapsed = true;
vm.$nextTick(() => {
- expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(1);
+ expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(0);
done();
});
diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
index 2d136a63c52..a1a37b342b7 100644
--- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
@@ -48,7 +48,11 @@ describe('DiffLineGutterContent', () => {
it('should return discussions for the given lineCode', () => {
const { lineCode } = getDiffFileMock().highlightedDiffLines[1];
- const component = createComponent({ lineCode, showCommentButton: true });
+ const component = createComponent({
+ lineCode,
+ showCommentButton: true,
+ discussions: getDiscussionsMockData(),
+ });
setDiscussions(component);
diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
index 81cd4f9769a..6fe5fdaf7f9 100644
--- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
@@ -3,6 +3,7 @@ import DiffLineNoteForm from '~/diffs/components/diff_line_note_form.vue';
import store from '~/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
+import { noteableDataMock } from '../../notes/mock_data';
describe('DiffLineNoteForm', () => {
let component;
@@ -15,16 +16,15 @@ describe('DiffLineNoteForm', () => {
diffLines = diffFile.highlightedDiffLines;
component = createComponentWithStore(Vue.extend(DiffLineNoteForm), store, {
- diffFile,
+ diffFileHash: diffFile.fileHash,
diffLines,
line: diffLines[0],
noteTargetLine: diffLines[0],
});
- Object.defineProperty(component, 'isLoggedIn', {
- get() {
- return true;
- },
+ Object.defineProperties(component, {
+ noteableData: { value: noteableDataMock },
+ isLoggedIn: { value: true },
});
component.$mount();
@@ -32,12 +32,37 @@ describe('DiffLineNoteForm', () => {
describe('methods', () => {
describe('handleCancelCommentForm', () => {
- it('should call cancelCommentForm with lineCode', () => {
+ it('should ask for confirmation when shouldConfirm and isDirty passed as truthy', () => {
+ spyOn(window, 'confirm').and.returnValue(false);
+
+ component.handleCancelCommentForm(true, true);
+ expect(window.confirm).toHaveBeenCalled();
+ });
+
+ it('should ask for confirmation when one of the params false', () => {
+ spyOn(window, 'confirm').and.returnValue(false);
+
+ component.handleCancelCommentForm(true, false);
+ expect(window.confirm).not.toHaveBeenCalled();
+
+ component.handleCancelCommentForm(false, true);
+ expect(window.confirm).not.toHaveBeenCalled();
+ });
+
+ it('should call cancelCommentForm with lineCode', done => {
+ spyOn(window, 'confirm');
spyOn(component, 'cancelCommentForm');
+ spyOn(component, 'resetAutoSave');
component.handleCancelCommentForm();
- expect(component.cancelCommentForm).toHaveBeenCalledWith({
- lineCode: diffLines[0].lineCode,
+ expect(window.confirm).not.toHaveBeenCalled();
+ component.$nextTick(() => {
+ expect(component.cancelCommentForm).toHaveBeenCalledWith({
+ lineCode: diffLines[0].lineCode,
+ });
+ expect(component.resetAutoSave).toHaveBeenCalled();
+
+ done();
});
});
});
@@ -66,7 +91,7 @@ describe('DiffLineNoteForm', () => {
describe('mounted', () => {
it('should init autosave', () => {
- const key = 'autosave/Note/issue///DiffNote//1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1';
+ const key = 'autosave/Note/Issue/98//DiffNote//1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1';
expect(component.autosave).toBeDefined();
expect(component.autosave.key).toEqual(key);
diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js
index e1adf60962e..b02328dd359 100644
--- a/spec/javascripts/diffs/components/inline_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js
@@ -13,7 +13,7 @@ describe('InlineDiffView', () => {
beforeEach(() => {
const diffFile = getDiffFileMock();
- store.dispatch('setInlineDiffViewType');
+ store.dispatch('diffs/setInlineDiffViewType');
component = createComponentWithStore(Vue.extend(InlineDiffView), store, {
diffFile,
diffLines: diffFile.highlightedDiffLines,
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index 6829c1e956a..c1560dac1a0 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -191,4 +191,48 @@ describe('DiffsStoreActions', () => {
);
});
});
+
+ describe('toggleFileDiscussions', () => {
+ it('should dispatch collapseDiscussion when all discussions are expanded', () => {
+ const getters = {
+ getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ id: 1 }]),
+ diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(true),
+ diffHasAllCollpasedDiscussions: jasmine.createSpy().and.returnValue(false),
+ };
+
+ const dispatch = jasmine.createSpy('dispatch');
+
+ actions.toggleFileDiscussions({ getters, dispatch });
+
+ expect(dispatch).toHaveBeenCalledWith('collapseDiscussion', { discussionId: 1 }, { root: true });
+ });
+
+ it('should dispatch expandDiscussion when all discussions are collapsed', () => {
+ const getters = {
+ getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ id: 1 }]),
+ diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(false),
+ diffHasAllCollpasedDiscussions: jasmine.createSpy().and.returnValue(true),
+ };
+
+ const dispatch = jasmine.createSpy();
+
+ actions.toggleFileDiscussions({ getters, dispatch });
+
+ expect(dispatch).toHaveBeenCalledWith('expandDiscussion', { discussionId: 1 }, { root: true });
+ });
+
+ it('should dispatch expandDiscussion when some discussions are collapsed and others are expanded for the collapsed discussion', () => {
+ const getters = {
+ getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ expanded: false, id: 1 }]),
+ diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(false),
+ diffHasAllCollpasedDiscussions: jasmine.createSpy().and.returnValue(false),
+ };
+
+ const dispatch = jasmine.createSpy();
+
+ actions.toggleFileDiscussions({ getters, dispatch });
+
+ expect(dispatch).toHaveBeenCalledWith('expandDiscussion', { discussionId: 1 }, { root: true });
+ });
+ });
});
diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js
index 7945ddea911..a59b26b2634 100644
--- a/spec/javascripts/diffs/store/getters_spec.js
+++ b/spec/javascripts/diffs/store/getters_spec.js
@@ -1,24 +1,322 @@
-import getters from '~/diffs/store/getters';
+import * as getters from '~/diffs/store/getters';
+import state from '~/diffs/store/modules/diff_state';
import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
+import discussion from '../mock_data/diff_discussions';
+
+describe('Diffs Module Getters', () => {
+ let localState;
+ let discussionMock;
+ let discussionMock1;
+
+ const diffFileMock = {
+ fileHash: '9732849daca6ae818696d9575f5d1207d1a7f8bb',
+ };
+
+ beforeEach(() => {
+ localState = state();
+ discussionMock = Object.assign({}, discussion);
+ discussionMock.diff_file.file_hash = diffFileMock.fileHash;
+
+ discussionMock1 = Object.assign({}, discussion);
+ discussionMock1.diff_file.file_hash = diffFileMock.fileHash;
+ });
-describe('DiffsStoreGetters', () => {
describe('isParallelView', () => {
it('should return true if view set to parallel view', () => {
- expect(getters.isParallelView({ diffViewType: PARALLEL_DIFF_VIEW_TYPE })).toBeTruthy();
+ localState.diffViewType = PARALLEL_DIFF_VIEW_TYPE;
+
+ expect(getters.isParallelView(localState)).toEqual(true);
});
it('should return false if view not to parallel view', () => {
- expect(getters.isParallelView({ diffViewType: 'foo' })).toBeFalsy();
+ localState.diffViewType = INLINE_DIFF_VIEW_TYPE;
+
+ expect(getters.isParallelView(localState)).toEqual(false);
});
});
describe('isInlineView', () => {
it('should return true if view set to inline view', () => {
- expect(getters.isInlineView({ diffViewType: INLINE_DIFF_VIEW_TYPE })).toBeTruthy();
+ localState.diffViewType = INLINE_DIFF_VIEW_TYPE;
+
+ expect(getters.isInlineView(localState)).toEqual(true);
});
it('should return false if view not to inline view', () => {
- expect(getters.isInlineView({ diffViewType: PARALLEL_DIFF_VIEW_TYPE })).toBeFalsy();
+ localState.diffViewType = PARALLEL_DIFF_VIEW_TYPE;
+
+ expect(getters.isInlineView(localState)).toEqual(false);
+ });
+ });
+
+ describe('areAllFilesCollapsed', () => {
+ it('returns true when all files are collapsed', () => {
+ localState.diffFiles = [{ collapsed: true }, { collapsed: true }];
+ expect(getters.areAllFilesCollapsed(localState)).toEqual(true);
+ });
+
+ it('returns false when at least one file is not collapsed', () => {
+ localState.diffFiles = [{ collapsed: false }, { collapsed: true }];
+ expect(getters.areAllFilesCollapsed(localState)).toEqual(false);
+ });
+ });
+
+ describe('commitId', () => {
+ it('returns commit id when is set', () => {
+ const commitID = '800f7a91';
+ localState.commit = {
+ id: commitID,
+ };
+
+ expect(getters.commitId(localState)).toEqual(commitID);
+ });
+
+ it('returns null when no commit is set', () => {
+ expect(getters.commitId(localState)).toEqual(null);
+ });
+ });
+
+ describe('diffHasAllExpandedDiscussions', () => {
+ it('returns true when all discussions are expanded', () => {
+ expect(
+ getters.diffHasAllExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock],
+ })(diffFileMock),
+ ).toEqual(true);
+ });
+
+ it('returns false when there are no discussions', () => {
+ expect(
+ getters.diffHasAllExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+
+ it('returns false when one discussions is collapsed', () => {
+ discussionMock1.expanded = false;
+
+ expect(
+ getters.diffHasAllExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock1],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+ });
+
+ describe('diffHasAllCollpasedDiscussions', () => {
+ it('returns true when all discussions are collapsed', () => {
+ discussionMock.diff_file.file_hash = diffFileMock.fileHash;
+ discussionMock.expanded = false;
+
+ expect(
+ getters.diffHasAllCollpasedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock],
+ })(diffFileMock),
+ ).toEqual(true);
+ });
+
+ it('returns false when there are no discussions', () => {
+ expect(
+ getters.diffHasAllCollpasedDiscussions(localState, {
+ getDiffFileDiscussions: () => [],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+
+ it('returns false when one discussions is expanded', () => {
+ discussionMock1.expanded = false;
+
+ expect(
+ getters.diffHasAllCollpasedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock1],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+ });
+
+ describe('diffHasExpandedDiscussions', () => {
+ it('returns true when one of the discussions is expanded', () => {
+ discussionMock1.expanded = false;
+
+ expect(
+ getters.diffHasExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock],
+ })(diffFileMock),
+ ).toEqual(true);
+ });
+
+ it('returns false when there are no discussions', () => {
+ expect(
+ getters.diffHasExpandedDiscussions(localState, { getDiffFileDiscussions: () => [] })(
+ diffFileMock,
+ ),
+ ).toEqual(false);
+ });
+
+ it('returns false when no discussion is expanded', () => {
+ discussionMock.expanded = false;
+ discussionMock1.expanded = false;
+
+ expect(
+ getters.diffHasExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock1],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+ });
+
+ describe('diffHasDiscussions', () => {
+ it('returns true when getDiffFileDiscussions returns discussions', () => {
+ expect(
+ getters.diffHasDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock],
+ })(diffFileMock),
+ ).toEqual(true);
+ });
+
+ it('returns false when getDiffFileDiscussions returns no discussions', () => {
+ expect(
+ getters.diffHasDiscussions(localState, {
+ getDiffFileDiscussions: () => [],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+ });
+
+ describe('singleDiscussionByLineCode', () => {
+ it('returns found discussion per line Code', () => {
+ const discussionsMock = {};
+ discussionsMock.ABC = discussionMock;
+
+ expect(
+ getters.singleDiscussionByLineCode(localState, {}, null, {
+ discussionsByLineCode: () => discussionsMock,
+ })('DEF'),
+ ).toEqual([]);
+ });
+
+ it('returns empty array when no discussions match', () => {
+ expect(
+ getters.singleDiscussionByLineCode(localState, {}, null, {
+ discussionsByLineCode: () => {},
+ })('DEF'),
+ ).toEqual([]);
+ });
+ });
+
+ describe('shouldRenderParallelCommentRow', () => {
+ let line;
+
+ beforeEach(() => {
+ line = {};
+
+ line.left = {
+ lineCode: 'ABC',
+ };
+
+ line.right = {
+ lineCode: 'DEF',
+ };
+ });
+
+ it('returns true when discussion is expanded', () => {
+ discussionMock.expanded = true;
+
+ expect(
+ getters.shouldRenderParallelCommentRow(localState, {
+ singleDiscussionByLineCode: () => [discussionMock],
+ })(line),
+ ).toEqual(true);
+ });
+
+ it('returns false when no discussion was found', () => {
+ localState.diffLineCommentForms.ABC = false;
+ localState.diffLineCommentForms.DEF = false;
+
+ expect(
+ getters.shouldRenderParallelCommentRow(localState, {
+ singleDiscussionByLineCode: () => [],
+ })(line),
+ ).toEqual(false);
+ });
+
+ it('returns true when discussionForm was found', () => {
+ localState.diffLineCommentForms.ABC = {};
+
+ expect(
+ getters.shouldRenderParallelCommentRow(localState, {
+ singleDiscussionByLineCode: () => [discussionMock],
+ })(line),
+ ).toEqual(true);
+ });
+ });
+
+ describe('shouldRenderInlineCommentRow', () => {
+ it('returns true when diffLineCommentForms has form', () => {
+ localState.diffLineCommentForms.ABC = {};
+
+ expect(
+ getters.shouldRenderInlineCommentRow(localState)({
+ lineCode: 'ABC',
+ }),
+ ).toEqual(true);
+ });
+
+ it('returns false when no line discussions were found', () => {
+ expect(
+ getters.shouldRenderInlineCommentRow(localState, {
+ singleDiscussionByLineCode: () => [],
+ })('DEF'),
+ ).toEqual(false);
+ });
+
+ it('returns true if all found discussions are expanded', () => {
+ discussionMock.expanded = true;
+
+ expect(
+ getters.shouldRenderInlineCommentRow(localState, {
+ singleDiscussionByLineCode: () => [discussionMock],
+ })('ABC'),
+ ).toEqual(true);
+ });
+ });
+
+ describe('getDiffFileDiscussions', () => {
+ it('returns an array with discussions when fileHash matches and the discussion belongs to a diff', () => {
+ discussionMock.diff_file.file_hash = diffFileMock.fileHash;
+
+ expect(
+ getters.getDiffFileDiscussions(localState, {}, {}, { discussions: [discussionMock] })(
+ diffFileMock,
+ ).length,
+ ).toEqual(1);
+ });
+
+ it('returns an empty array when no discussions are found in the given diff', () => {
+ expect(
+ getters.getDiffFileDiscussions(localState, {}, {}, { discussions: [] })(diffFileMock)
+ .length,
+ ).toEqual(0);
+ });
+ });
+
+ describe('getDiffFileByHash', () => {
+ it('returns file by hash', () => {
+ const fileA = {
+ fileHash: '123',
+ };
+ const fileB = {
+ fileHash: '456',
+ };
+ localState.diffFiles = [fileA, fileB];
+
+ expect(getters.getDiffFileByHash(localState)('456')).toEqual(fileB);
+ });
+
+ it('returns null if no matching file is found', () => {
+ localState.diffFiles = [];
+ expect(getters.getDiffFileByHash(localState)('123')).toBeUndefined();
});
});
});
diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js
index 02836fcaeea..1af49f4985c 100644
--- a/spec/javascripts/diffs/store/mutations_spec.js
+++ b/spec/javascripts/diffs/store/mutations_spec.js
@@ -24,21 +24,6 @@ describe('DiffsStoreMutations', () => {
});
});
- describe('SET_DIFF_FILES', () => {
- it('should set diff files to state', () => {
- const filePath = '/first-diff-file-path';
- const state = {};
- const diffFiles = {
- a_mode: 1,
- highlighted_diff_lines: [{ file_path: filePath }],
- };
-
- mutations[types.SET_DIFF_FILES](state, diffFiles);
- expect(state.diffFiles.aMode).toEqual(1);
- expect(state.diffFiles.highlightedDiffLines[0].filePath).toEqual(filePath);
- });
- });
-
describe('SET_DIFF_VIEW_TYPE', () => {
it('should set diff view type properly', () => {
const state = {};
diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js
index 5a024a0f2ad..32136d9ebff 100644
--- a/spec/javascripts/diffs/store/utils_spec.js
+++ b/spec/javascripts/diffs/store/utils_spec.js
@@ -176,4 +176,35 @@ describe('DiffsStoreUtils', () => {
expect(linesWithReferences[1].metaData.newPos).toEqual(3);
});
});
+
+ describe('trimFirstCharOfLineContent', () => {
+ it('trims the line when it starts with a space', () => {
+ expect(utils.trimFirstCharOfLineContent({ richText: ' diff' })).toEqual({ richText: 'diff' });
+ });
+
+ it('trims the line when it starts with a +', () => {
+ expect(utils.trimFirstCharOfLineContent({ richText: '+diff' })).toEqual({ richText: 'diff' });
+ });
+
+ it('trims the line when it starts with a -', () => {
+ expect(utils.trimFirstCharOfLineContent({ richText: '-diff' })).toEqual({ richText: 'diff' });
+ });
+
+ it('does not trims the line when it starts with a letter', () => {
+ expect(utils.trimFirstCharOfLineContent({ richText: 'diff' })).toEqual({ richText: 'diff' });
+ });
+
+ it('does not modify the provided object', () => {
+ const lineObj = {
+ richText: ' diff',
+ };
+
+ utils.trimFirstCharOfLineContent(lineObj);
+ expect(lineObj).toEqual({ richText: ' diff' });
+ });
+
+ it('handles a undefined or null parameter', () => {
+ expect(utils.trimFirstCharOfLineContent()).toEqual({});
+ });
+ });
});
diff --git a/spec/javascripts/environments/environment_item_spec.js b/spec/javascripts/environments/environment_item_spec.js
index 7a34126eef7..0b933dda431 100644
--- a/spec/javascripts/environments/environment_item_spec.js
+++ b/spec/javascripts/environments/environment_item_spec.js
@@ -104,7 +104,7 @@ describe('Environment item', () => {
},
],
},
- 'stop_action?': true,
+ has_stop_action: true,
environment_path: 'root/ci-folders/environments/31',
created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-10T15:55:58.778Z',
diff --git a/spec/javascripts/environments/environment_rollback_spec.js b/spec/javascripts/environments/environment_rollback_spec.js
index eb8e49d81fe..79f33c5bc8a 100644
--- a/spec/javascripts/environments/environment_rollback_spec.js
+++ b/spec/javascripts/environments/environment_rollback_spec.js
@@ -18,7 +18,7 @@ describe('Rollback Component', () => {
},
}).$mount();
- expect(component.$el.querySelector('span').textContent).toContain('Re-deploy');
+ expect(component.$el).toHaveSpriteIcon('repeat');
});
it('Should render Rollback label when isLastDeployment is false', () => {
@@ -30,6 +30,6 @@ describe('Rollback Component', () => {
},
}).$mount();
- expect(component.$el.querySelector('span').textContent).toContain('Rollback');
+ expect(component.$el).toHaveSpriteIcon('redo');
});
});
diff --git a/spec/javascripts/environments/environment_stop_spec.js b/spec/javascripts/environments/environment_stop_spec.js
index 3f95faf466a..4d9caa57566 100644
--- a/spec/javascripts/environments/environment_stop_spec.js
+++ b/spec/javascripts/environments/environment_stop_spec.js
@@ -4,7 +4,6 @@ import stopComp from '~/environments/components/environment_stop.vue';
describe('Stop Component', () => {
let StopComponent;
let component;
- const stopURL = '/stop';
beforeEach(() => {
StopComponent = Vue.extend(stopComp);
@@ -12,20 +11,13 @@ describe('Stop Component', () => {
component = new StopComponent({
propsData: {
- stopUrl: stopURL,
+ environment: {},
},
}).$mount();
});
- describe('computed', () => {
- it('title', () => {
- expect(component.title).toEqual('Stop');
- });
- });
-
it('should render a button to stop the environment', () => {
expect(component.$el.tagName).toEqual('BUTTON');
- expect(component.$el.getAttribute('data-original-title')).toEqual('Stop');
- expect(component.$el.getAttribute('aria-label')).toEqual('Stop');
+ expect(component.$el.getAttribute('data-original-title')).toEqual('Stop environment');
});
});
diff --git a/spec/javascripts/environments/mock_data.js b/spec/javascripts/environments/mock_data.js
index 8a1e26935d9..7bb57f938b8 100644
--- a/spec/javascripts/environments/mock_data.js
+++ b/spec/javascripts/environments/mock_data.js
@@ -7,7 +7,7 @@ export const environmentsList = [
external_url: null,
environment_type: null,
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/7',
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
@@ -22,7 +22,7 @@ export const environmentsList = [
external_url: null,
environment_type: 'build',
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/12',
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
@@ -41,7 +41,7 @@ export const serverData = [
external_url: null,
environment_type: null,
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/7',
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
@@ -58,7 +58,7 @@ export const serverData = [
external_url: null,
environment_type: 'build',
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/12',
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
@@ -77,7 +77,7 @@ export const environment = {
external_url: null,
environment_type: null,
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/7',
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
@@ -95,7 +95,7 @@ export const folder = {
external_url: null,
environment_type: 'build',
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/12',
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
diff --git a/spec/javascripts/fixtures/commit.rb b/spec/javascripts/fixtures/commit.rb
index 351db6ba184..24ab8159a18 100644
--- a/spec/javascripts/fixtures/commit.rb
+++ b/spec/javascripts/fixtures/commit.rb
@@ -14,7 +14,7 @@ describe Projects::CommitController, '(JavaScript fixtures)', type: :controller
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/javascripts/fixtures/graph.html.haml b/spec/javascripts/fixtures/graph.html.haml
deleted file mode 100644
index 4fedb0f1ded..00000000000
--- a/spec/javascripts/fixtures/graph.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-#js-pipeline-graph-vue{ data: { endpoint: "foo" } }
diff --git a/spec/javascripts/fixtures/groups.rb b/spec/javascripts/fixtures/groups.rb
index 35be52fbf97..a2035ceae15 100644
--- a/spec/javascripts/fixtures/groups.rb
+++ b/spec/javascripts/fixtures/groups.rb
@@ -13,7 +13,7 @@ describe 'Groups (JavaScript fixtures)', type: :controller do
end
before do
- group.add_master(admin)
+ group.add_maintainer(admin)
sign_in(admin)
end
diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb
index ee60489eb7c..7257d0c8556 100644
--- a/spec/javascripts/fixtures/merge_requests.rb
+++ b/spec/javascripts/fixtures/merge_requests.rb
@@ -80,6 +80,13 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
render_discussions_json(merge_request, example.description)
end
+ it 'merge_requests/resolved_diff_discussion.json' do |example|
+ 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)
+
+ render_discussions_json(merge_request, example.description)
+ end
+
context 'with image diff' do
let(:merge_request2) { create(:merge_request_with_diffs, :with_image_diffs, source_project: project, title: "Added images") }
let(:image_path) { "files/images/ee_repo_logo.png" }
diff --git a/spec/javascripts/fixtures/projects.rb b/spec/javascripts/fixtures/projects.rb
index e8865b04874..57c78182abc 100644
--- a/spec/javascripts/fixtures/projects.rb
+++ b/spec/javascripts/fixtures/projects.rb
@@ -17,7 +17,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
end
before do
- project.add_master(admin)
+ project.add_maintainer(admin)
sign_in(admin)
end
diff --git a/spec/javascripts/fixtures/search_autocomplete.html.haml b/spec/javascripts/fixtures/search_autocomplete.html.haml
index 0421ed2182f..4aa54da9411 100644
--- a/spec/javascripts/fixtures/search_autocomplete.html.haml
+++ b/spec/javascripts/fixtures/search_autocomplete.html.haml
@@ -1,8 +1,6 @@
-.search.search-form.has-location-badge
- %form.navbar-form
+.search.search-form
+ %form.form-inline
.search-input-container
- %div.location-badge
- This project
.search-input-wrap
.dropdown
%input#search.search-input.dropdown-menu-toggle
diff --git a/spec/javascripts/frequent_items/components/app_spec.js b/spec/javascripts/frequent_items/components/app_spec.js
new file mode 100644
index 00000000000..834f919524d
--- /dev/null
+++ b/spec/javascripts/frequent_items/components/app_spec.js
@@ -0,0 +1,251 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import Vue from 'vue';
+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 { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { currentSession, mockFrequentProjects, mockSearchedProjects } from '../mock_data';
+
+let session;
+const createComponentWithStore = (namespace = 'projects') => {
+ session = currentSession[namespace];
+ gon.api_version = session.apiVersion;
+ const Component = Vue.extend(appComponent);
+
+ return mountComponentWithStore(Component, {
+ store,
+ props: {
+ namespace,
+ currentUserName: session.username,
+ currentItem: session.project || session.group,
+ },
+ });
+};
+
+describe('Frequent Items App Component', () => {
+ let vm;
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ vm = createComponentWithStore();
+ });
+
+ afterEach(() => {
+ mock.restore();
+ vm.$destroy();
+ });
+
+ describe('methods', () => {
+ describe('dropdownOpenHandler', () => {
+ it('should fetch frequent items when no search has been previously made on desktop', () => {
+ spyOn(vm, 'fetchFrequentItems');
+
+ vm.dropdownOpenHandler();
+
+ expect(vm.fetchFrequentItems).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('logItemAccess', () => {
+ let storage;
+
+ beforeEach(() => {
+ storage = {};
+
+ spyOn(window.localStorage, 'setItem').and.callFake((storageKey, value) => {
+ storage[storageKey] = value;
+ });
+
+ spyOn(window.localStorage, 'getItem').and.callFake(storageKey => {
+ if (storage[storageKey]) {
+ return storage[storageKey];
+ }
+
+ return null;
+ });
+ });
+
+ it('should create a project store if it does not exist and adds a project', () => {
+ vm.logItemAccess(session.storageKey, session.project);
+
+ const projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects.length).toBe(1);
+ expect(projects[0].frequency).toBe(1);
+ expect(projects[0].lastAccessedOn).toBeDefined();
+ });
+
+ it('should prevent inserting same report multiple times into store', () => {
+ vm.logItemAccess(session.storageKey, session.project);
+ vm.logItemAccess(session.storageKey, session.project);
+
+ const projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects.length).toBe(1);
+ });
+
+ it('should increase frequency of report if it was logged multiple times over the course of an hour', () => {
+ let projects;
+ const newTimestamp = Date.now() + HOUR_IN_MS + 1;
+
+ vm.logItemAccess(session.storageKey, session.project);
+ projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects[0].frequency).toBe(1);
+
+ vm.logItemAccess(session.storageKey, {
+ ...session.project,
+ lastAccessedOn: newTimestamp,
+ });
+ projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects[0].frequency).toBe(2);
+ expect(projects[0].lastAccessedOn).not.toBe(session.project.lastAccessedOn);
+ });
+
+ it('should always update project metadata', () => {
+ let projects;
+ const oldProject = {
+ ...session.project,
+ };
+
+ const newProject = {
+ ...session.project,
+ name: 'New Name',
+ avatarUrl: 'new/avatar.png',
+ namespace: 'New / Namespace',
+ webUrl: 'http://localhost/new/web/url',
+ };
+
+ vm.logItemAccess(session.storageKey, oldProject);
+ projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects[0].name).toBe(oldProject.name);
+ expect(projects[0].avatarUrl).toBe(oldProject.avatarUrl);
+ expect(projects[0].namespace).toBe(oldProject.namespace);
+ expect(projects[0].webUrl).toBe(oldProject.webUrl);
+
+ vm.logItemAccess(session.storageKey, newProject);
+ projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects[0].name).toBe(newProject.name);
+ expect(projects[0].avatarUrl).toBe(newProject.avatarUrl);
+ expect(projects[0].namespace).toBe(newProject.namespace);
+ expect(projects[0].webUrl).toBe(newProject.webUrl);
+ });
+
+ it('should not add more than 20 projects in store', () => {
+ for (let id = 0; id < FREQUENT_ITEMS.MAX_COUNT; id += 1) {
+ const project = {
+ ...session.project,
+ id,
+ };
+ vm.logItemAccess(session.storageKey, project);
+ }
+
+ const projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects.length).toBe(FREQUENT_ITEMS.MAX_COUNT);
+ });
+ });
+ });
+
+ describe('created', () => {
+ it('should bind event listeners on eventHub', done => {
+ spyOn(eventHub, '$on');
+
+ createComponentWithStore().$mount();
+
+ Vue.nextTick(() => {
+ expect(eventHub.$on).toHaveBeenCalledWith('projects-dropdownOpen', jasmine.any(Function));
+ done();
+ });
+ });
+ });
+
+ describe('beforeDestroy', () => {
+ it('should unbind event listeners on eventHub', done => {
+ spyOn(eventHub, '$off');
+
+ vm.$mount();
+ vm.$destroy();
+
+ Vue.nextTick(() => {
+ expect(eventHub.$off).toHaveBeenCalledWith('projects-dropdownOpen', jasmine.any(Function));
+ done();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render search input', () => {
+ expect(vm.$el.querySelector('.search-input-container')).toBeDefined();
+ });
+
+ it('should render loading animation', done => {
+ vm.$store.dispatch('fetchSearchedItems');
+
+ Vue.nextTick(() => {
+ const loadingEl = vm.$el.querySelector('.loading-animation');
+
+ expect(loadingEl).toBeDefined();
+ expect(loadingEl.classList.contains('prepend-top-20')).toBe(true);
+ expect(loadingEl.querySelector('i').getAttribute('aria-label')).toBe('Loading projects');
+ done();
+ });
+ });
+
+ it('should render frequent projects list header', done => {
+ Vue.nextTick(() => {
+ const sectionHeaderEl = vm.$el.querySelector('.section-header');
+
+ expect(sectionHeaderEl).toBeDefined();
+ expect(sectionHeaderEl.innerText.trim()).toBe('Frequently visited');
+ done();
+ });
+ });
+
+ it('should render frequent projects list', done => {
+ const expectedResult = getTopFrequentItems(mockFrequentProjects);
+ spyOn(window.localStorage, 'getItem').and.callFake(() =>
+ JSON.stringify(mockFrequentProjects),
+ );
+
+ expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1);
+
+ vm.fetchFrequentItems();
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
+ expectedResult.length,
+ );
+ done();
+ });
+ });
+
+ it('should render searched projects list', done => {
+ mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects);
+
+ expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1);
+
+ vm.$store.dispatch('setSearchQuery', 'gitlab');
+ vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
+ })
+ .then(vm.$nextTick)
+ .then(vm.$nextTick)
+ .then(() => {
+ expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
+ mockSearchedProjects.length,
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js
new file mode 100644
index 00000000000..201aca77b10
--- /dev/null
+++ b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js
@@ -0,0 +1,75 @@
+import Vue from 'vue';
+import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { mockProject } from '../mock_data'; // can also use 'mockGroup', but not useful to test here
+
+const createComponent = () => {
+ const Component = Vue.extend(frequentItemsListItemComponent);
+
+ return mountComponent(Component, {
+ itemId: mockProject.id,
+ itemName: mockProject.name,
+ namespace: mockProject.namespace,
+ webUrl: mockProject.webUrl,
+ avatarUrl: mockProject.avatarUrl,
+ });
+};
+
+describe('FrequentItemsListItemComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('hasAvatar', () => {
+ it('should return `true` or `false` if whether avatar is present or not', () => {
+ vm.avatarUrl = 'path/to/avatar.png';
+ expect(vm.hasAvatar).toBe(true);
+
+ vm.avatarUrl = null;
+ expect(vm.hasAvatar).toBe(false);
+ });
+ });
+
+ describe('highlightedItemName', () => {
+ it('should enclose part of project name in <b> & </b> which matches with `matcher` prop', () => {
+ vm.matcher = 'lab';
+ expect(vm.highlightedItemName).toContain('<b>Lab</b>');
+ });
+
+ it('should return project name as it is if `matcher` is not available', () => {
+ vm.matcher = null;
+ expect(vm.highlightedItemName).toBe(mockProject.name);
+ });
+ });
+
+ describe('truncatedNamespace', () => {
+ it('should truncate project name from namespace string', () => {
+ vm.namespace = 'platform / nokia-3310';
+ expect(vm.truncatedNamespace).toBe('platform');
+ });
+
+ it('should truncate namespace string from the middle if it includes more than two groups in path', () => {
+ vm.namespace = 'platform / hardware / broadcom / Wifi Group / Mobile Chipset / nokia-3310';
+ expect(vm.truncatedNamespace).toBe('platform / ... / Mobile Chipset');
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render component element', () => {
+ expect(vm.$el.classList.contains('frequent-items-list-item-container')).toBeTruthy();
+ expect(vm.$el.querySelectorAll('a').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.frequent-items-item-avatar-container').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.frequent-items-item-metadata-container').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.frequent-items-item-title').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.frequent-items-item-namespace').length).toBe(1);
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/components/frequent_items_list_spec.js b/spec/javascripts/frequent_items/components/frequent_items_list_spec.js
new file mode 100644
index 00000000000..3003b7ee000
--- /dev/null
+++ b/spec/javascripts/frequent_items/components/frequent_items_list_spec.js
@@ -0,0 +1,84 @@
+import Vue from 'vue';
+import frequentItemsListComponent from '~/frequent_items/components/frequent_items_list.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { mockFrequentProjects } from '../mock_data';
+
+const createComponent = (namespace = 'projects') => {
+ const Component = Vue.extend(frequentItemsListComponent);
+
+ return mountComponent(Component, {
+ namespace,
+ items: mockFrequentProjects,
+ isFetchFailed: false,
+ hasSearchQuery: false,
+ matcher: 'lab',
+ });
+};
+
+describe('FrequentItemsListComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('isListEmpty', () => {
+ it('should return `true` or `false` representing whether if `items` is empty or not with projects', () => {
+ vm.items = [];
+ expect(vm.isListEmpty).toBe(true);
+
+ vm.items = mockFrequentProjects;
+ expect(vm.isListEmpty).toBe(false);
+ });
+ });
+
+ describe('fetched item messages', () => {
+ it('should return appropriate empty list message based on value of `localStorageFailed` prop with projects', () => {
+ vm.isFetchFailed = true;
+ expect(vm.listEmptyMessage).toBe('This feature requires browser localStorage support');
+
+ vm.isFetchFailed = false;
+ expect(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', () => {
+ vm.hasSearchQuery = true;
+ vm.isFetchFailed = true;
+ expect(vm.listEmptyMessage).toBe('Something went wrong on our end.');
+
+ vm.isFetchFailed = false;
+ expect(vm.listEmptyMessage).toBe('Sorry, no projects matched your search');
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render component element with list of projects', done => {
+ vm.items = mockFrequentProjects;
+
+ Vue.nextTick(() => {
+ expect(vm.$el.classList.contains('frequent-items-list-container')).toBe(true);
+ expect(vm.$el.querySelectorAll('ul.list-unstyled').length).toBe(1);
+ expect(vm.$el.querySelectorAll('li.frequent-items-list-item-container').length).toBe(5);
+ done();
+ });
+ });
+
+ it('should render component element with empty message', done => {
+ vm.items = [];
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelectorAll('li.section-empty').length).toBe(1);
+ expect(vm.$el.querySelectorAll('li.frequent-items-list-item-container').length).toBe(0);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js
new file mode 100644
index 00000000000..6a11038e70a
--- /dev/null
+++ b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js
@@ -0,0 +1,77 @@
+import Vue from 'vue';
+import searchComponent from '~/frequent_items/components/frequent_items_search_input.vue';
+import eventHub from '~/frequent_items/event_hub';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const createComponent = (namespace = 'projects') => {
+ const Component = Vue.extend(searchComponent);
+
+ return mountComponent(Component, { namespace });
+};
+
+describe('FrequentItemsSearchInputComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('methods', () => {
+ describe('setFocus', () => {
+ it('should set focus to search input', () => {
+ spyOn(vm.$refs.search, 'focus');
+
+ vm.setFocus();
+ expect(vm.$refs.search.focus).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('mounted', () => {
+ it('should listen `dropdownOpen` event', done => {
+ spyOn(eventHub, '$on');
+ const vmX = createComponent();
+
+ Vue.nextTick(() => {
+ expect(eventHub.$on).toHaveBeenCalledWith(
+ `${vmX.namespace}-dropdownOpen`,
+ jasmine.any(Function),
+ );
+ done();
+ });
+ });
+ });
+
+ describe('beforeDestroy', () => {
+ it('should unbind event listeners on eventHub', done => {
+ const vmX = createComponent();
+ spyOn(eventHub, '$off');
+
+ vmX.$mount();
+ vmX.$destroy();
+
+ Vue.nextTick(() => {
+ expect(eventHub.$off).toHaveBeenCalledWith(
+ `${vmX.namespace}-dropdownOpen`,
+ jasmine.any(Function),
+ );
+ done();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render component element', () => {
+ const inputEl = vm.$el.querySelector('input.form-control');
+
+ expect(vm.$el.classList.contains('search-input-container')).toBeTruthy();
+ expect(inputEl).not.toBe(null);
+ expect(inputEl.getAttribute('placeholder')).toBe('Search your projects');
+ expect(vm.$el.querySelector('.search-icon')).toBeDefined();
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/mock_data.js b/spec/javascripts/frequent_items/mock_data.js
new file mode 100644
index 00000000000..cf3602f42d6
--- /dev/null
+++ b/spec/javascripts/frequent_items/mock_data.js
@@ -0,0 +1,168 @@
+export const currentSession = {
+ groups: {
+ username: 'root',
+ storageKey: 'root/frequent-groups',
+ apiVersion: 'v4',
+ group: {
+ id: 1,
+ name: 'dummy-group',
+ full_name: 'dummy-parent-group',
+ webUrl: `${gl.TEST_HOST}/dummy-group`,
+ avatarUrl: null,
+ lastAccessedOn: Date.now(),
+ },
+ },
+ projects: {
+ username: 'root',
+ storageKey: 'root/frequent-projects',
+ apiVersion: 'v4',
+ project: {
+ id: 1,
+ name: 'dummy-project',
+ namespace: 'SampleGroup / Dummy-Project',
+ webUrl: `${gl.TEST_HOST}/samplegroup/dummy-project`,
+ avatarUrl: null,
+ lastAccessedOn: Date.now(),
+ },
+ },
+};
+
+export const mockNamespace = 'projects';
+
+export const mockStorageKey = 'test-user/frequent-projects';
+
+export const mockGroup = {
+ id: 1,
+ name: 'Sub451',
+ namespace: 'Commit451 / Sub451',
+ webUrl: `${gl.TEST_HOST}/Commit451/Sub451`,
+ avatarUrl: null,
+};
+
+export const mockRawGroup = {
+ id: 1,
+ name: 'Sub451',
+ full_name: 'Commit451 / Sub451',
+ web_url: `${gl.TEST_HOST}/Commit451/Sub451`,
+ avatar_url: null,
+};
+
+export const mockFrequentGroups = [
+ {
+ id: 3,
+ name: 'Subgroup451',
+ full_name: 'Commit451 / Subgroup451',
+ webUrl: '/Commit451/Subgroup451',
+ avatarUrl: null,
+ frequency: 7,
+ lastAccessedOn: 1497979281815,
+ },
+ {
+ id: 1,
+ name: 'Commit451',
+ full_name: 'Commit451',
+ webUrl: '/Commit451',
+ avatarUrl: null,
+ frequency: 3,
+ lastAccessedOn: 1497979281815,
+ },
+];
+
+export const mockSearchedGroups = [mockRawGroup];
+export const mockProcessedSearchedGroups = [mockGroup];
+
+export const mockProject = {
+ id: 1,
+ name: 'GitLab Community Edition',
+ namespace: 'gitlab-org / gitlab-ce',
+ webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-ce`,
+ avatarUrl: null,
+};
+
+export const mockRawProject = {
+ id: 1,
+ name: 'GitLab Community Edition',
+ name_with_namespace: 'gitlab-org / gitlab-ce',
+ web_url: `${gl.TEST_HOST}/gitlab-org/gitlab-ce`,
+ avatar_url: null,
+};
+
+export const mockFrequentProjects = [
+ {
+ id: 1,
+ name: 'GitLab Community Edition',
+ namespace: 'gitlab-org / gitlab-ce',
+ webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-ce`,
+ avatarUrl: null,
+ frequency: 1,
+ lastAccessedOn: Date.now(),
+ },
+ {
+ id: 2,
+ name: 'GitLab CI',
+ namespace: 'gitlab-org / gitlab-ci',
+ webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-ci`,
+ avatarUrl: null,
+ frequency: 9,
+ lastAccessedOn: Date.now(),
+ },
+ {
+ id: 3,
+ name: 'Typeahead.Js',
+ namespace: 'twitter / typeahead-js',
+ webUrl: `${gl.TEST_HOST}/twitter/typeahead-js`,
+ avatarUrl: '/uploads/-/system/project/avatar/7/TWBS.png',
+ frequency: 2,
+ lastAccessedOn: Date.now(),
+ },
+ {
+ id: 4,
+ name: 'Intel',
+ namespace: 'platform / hardware / bsp / intel',
+ webUrl: `${gl.TEST_HOST}/platform/hardware/bsp/intel`,
+ avatarUrl: null,
+ frequency: 3,
+ lastAccessedOn: Date.now(),
+ },
+ {
+ id: 5,
+ name: 'v4.4',
+ namespace: 'platform / hardware / bsp / kernel / common / v4.4',
+ webUrl: `${gl.TEST_HOST}/platform/hardware/bsp/kernel/common/v4.4`,
+ avatarUrl: null,
+ frequency: 8,
+ lastAccessedOn: Date.now(),
+ },
+];
+
+export const mockSearchedProjects = [mockRawProject];
+export const mockProcessedSearchedProjects = [mockProject];
+
+export const unsortedFrequentItems = [
+ { id: 1, frequency: 12, lastAccessedOn: 1491400843391 },
+ { id: 2, frequency: 14, lastAccessedOn: 1488240890738 },
+ { id: 3, frequency: 44, lastAccessedOn: 1497675908472 },
+ { id: 4, frequency: 8, lastAccessedOn: 1497979281815 },
+ { id: 5, frequency: 34, lastAccessedOn: 1488089211943 },
+ { id: 6, frequency: 14, lastAccessedOn: 1493517292488 },
+ { id: 7, frequency: 42, lastAccessedOn: 1486815299875 },
+ { id: 8, frequency: 33, lastAccessedOn: 1500762279114 },
+ { id: 10, frequency: 46, lastAccessedOn: 1483251641543 },
+];
+
+/**
+ * This const has a specific order which tests authenticity
+ * of `getTopFrequentItems` method so
+ * DO NOT change order of items in this const.
+ */
+export const sortedFrequentItems = [
+ { id: 10, frequency: 46, lastAccessedOn: 1483251641543 },
+ { id: 3, frequency: 44, lastAccessedOn: 1497675908472 },
+ { id: 7, frequency: 42, lastAccessedOn: 1486815299875 },
+ { id: 5, frequency: 34, lastAccessedOn: 1488089211943 },
+ { id: 8, frequency: 33, lastAccessedOn: 1500762279114 },
+ { id: 6, frequency: 14, lastAccessedOn: 1493517292488 },
+ { id: 2, frequency: 14, lastAccessedOn: 1488240890738 },
+ { id: 1, frequency: 12, lastAccessedOn: 1491400843391 },
+ { id: 4, frequency: 8, lastAccessedOn: 1497979281815 },
+];
diff --git a/spec/javascripts/frequent_items/store/actions_spec.js b/spec/javascripts/frequent_items/store/actions_spec.js
new file mode 100644
index 00000000000..0a8525e77d6
--- /dev/null
+++ b/spec/javascripts/frequent_items/store/actions_spec.js
@@ -0,0 +1,225 @@
+import testAction from 'spec/helpers/vuex_action_helper';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import AccessorUtilities from '~/lib/utils/accessor';
+import * as actions from '~/frequent_items/store/actions';
+import * as types from '~/frequent_items/store/mutation_types';
+import state from '~/frequent_items/store/state';
+import {
+ mockNamespace,
+ mockStorageKey,
+ mockFrequentProjects,
+ mockSearchedProjects,
+} from '../mock_data';
+
+describe('Frequent Items Dropdown Store Actions', () => {
+ let mockedState;
+ let mock;
+
+ beforeEach(() => {
+ mockedState = state();
+ mock = new MockAdapter(axios);
+
+ mockedState.namespace = mockNamespace;
+ mockedState.storageKey = mockStorageKey;
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('setNamespace', () => {
+ it('should set namespace', done => {
+ testAction(
+ actions.setNamespace,
+ mockNamespace,
+ mockedState,
+ [{ type: types.SET_NAMESPACE, payload: mockNamespace }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setStorageKey', () => {
+ it('should set storage key', done => {
+ testAction(
+ actions.setStorageKey,
+ mockStorageKey,
+ mockedState,
+ [{ type: types.SET_STORAGE_KEY, payload: mockStorageKey }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestFrequentItems', () => {
+ it('should request frequent items', done => {
+ testAction(
+ actions.requestFrequentItems,
+ null,
+ mockedState,
+ [{ type: types.REQUEST_FREQUENT_ITEMS }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveFrequentItemsSuccess', () => {
+ it('should set frequent items', done => {
+ testAction(
+ actions.receiveFrequentItemsSuccess,
+ mockFrequentProjects,
+ mockedState,
+ [{ type: types.RECEIVE_FREQUENT_ITEMS_SUCCESS, payload: mockFrequentProjects }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveFrequentItemsError', () => {
+ it('should set frequent items error state', done => {
+ testAction(
+ actions.receiveFrequentItemsError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_FREQUENT_ITEMS_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchFrequentItems', () => {
+ it('should dispatch `receiveFrequentItemsSuccess`', done => {
+ mockedState.namespace = mockNamespace;
+ mockedState.storageKey = mockStorageKey;
+
+ testAction(
+ actions.fetchFrequentItems,
+ null,
+ mockedState,
+ [],
+ [{ type: 'requestFrequentItems' }, { type: 'receiveFrequentItemsSuccess', payload: [] }],
+ done,
+ );
+ });
+
+ it('should dispatch `receiveFrequentItemsError`', done => {
+ spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(false);
+ mockedState.namespace = mockNamespace;
+ mockedState.storageKey = mockStorageKey;
+
+ testAction(
+ actions.fetchFrequentItems,
+ null,
+ mockedState,
+ [],
+ [{ type: 'requestFrequentItems' }, { type: 'receiveFrequentItemsError' }],
+ done,
+ );
+ });
+ });
+
+ describe('requestSearchedItems', () => {
+ it('should request searched items', done => {
+ testAction(
+ actions.requestSearchedItems,
+ null,
+ mockedState,
+ [{ type: types.REQUEST_SEARCHED_ITEMS }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveSearchedItemsSuccess', () => {
+ it('should set searched items', done => {
+ testAction(
+ actions.receiveSearchedItemsSuccess,
+ mockSearchedProjects,
+ mockedState,
+ [{ type: types.RECEIVE_SEARCHED_ITEMS_SUCCESS, payload: mockSearchedProjects }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveSearchedItemsError', () => {
+ it('should set searched items error state', done => {
+ testAction(
+ actions.receiveSearchedItemsError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_SEARCHED_ITEMS_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchSearchedItems', () => {
+ beforeEach(() => {
+ gon.api_version = 'v4';
+ });
+
+ it('should dispatch `receiveSearchedItemsSuccess`', done => {
+ mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects);
+
+ testAction(
+ actions.fetchSearchedItems,
+ null,
+ mockedState,
+ [],
+ [
+ { type: 'requestSearchedItems' },
+ { type: 'receiveSearchedItemsSuccess', payload: mockSearchedProjects },
+ ],
+ done,
+ );
+ });
+
+ it('should dispatch `receiveSearchedItemsError`', done => {
+ gon.api_version = 'v4';
+ mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(500);
+
+ testAction(
+ actions.fetchSearchedItems,
+ null,
+ mockedState,
+ [],
+ [{ type: 'requestSearchedItems' }, { type: 'receiveSearchedItemsError' }],
+ done,
+ );
+ });
+ });
+
+ describe('setSearchQuery', () => {
+ it('should commit query and dispatch `fetchSearchedItems` when query is present', done => {
+ testAction(
+ actions.setSearchQuery,
+ { query: 'test' },
+ mockedState,
+ [{ type: types.SET_SEARCH_QUERY, payload: { query: 'test' } }],
+ [{ type: 'fetchSearchedItems', payload: { query: 'test' } }],
+ done,
+ );
+ });
+
+ it('should commit query and dispatch `fetchFrequentItems` when query is empty', done => {
+ testAction(
+ actions.setSearchQuery,
+ null,
+ mockedState,
+ [{ type: types.SET_SEARCH_QUERY, payload: null }],
+ [{ type: 'fetchFrequentItems' }],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/store/getters_spec.js b/spec/javascripts/frequent_items/store/getters_spec.js
new file mode 100644
index 00000000000..1cd12eb6832
--- /dev/null
+++ b/spec/javascripts/frequent_items/store/getters_spec.js
@@ -0,0 +1,24 @@
+import state from '~/frequent_items/store/state';
+import * as getters from '~/frequent_items/store/getters';
+
+describe('Frequent Items Dropdown Store Getters', () => {
+ let mockedState;
+
+ beforeEach(() => {
+ mockedState = state();
+ });
+
+ describe('hasSearchQuery', () => {
+ it('should return `true` when search query is present', () => {
+ mockedState.searchQuery = 'test';
+
+ expect(getters.hasSearchQuery(mockedState)).toBe(true);
+ });
+
+ it('should return `false` when search query is empty', () => {
+ mockedState.searchQuery = '';
+
+ expect(getters.hasSearchQuery(mockedState)).toBe(false);
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/store/mutations_spec.js b/spec/javascripts/frequent_items/store/mutations_spec.js
new file mode 100644
index 00000000000..d36964b2600
--- /dev/null
+++ b/spec/javascripts/frequent_items/store/mutations_spec.js
@@ -0,0 +1,117 @@
+import state from '~/frequent_items/store/state';
+import mutations from '~/frequent_items/store/mutations';
+import * as types from '~/frequent_items/store/mutation_types';
+import {
+ mockNamespace,
+ mockStorageKey,
+ mockFrequentProjects,
+ mockSearchedProjects,
+ mockProcessedSearchedProjects,
+ mockSearchedGroups,
+ mockProcessedSearchedGroups,
+} from '../mock_data';
+
+describe('Frequent Items dropdown mutations', () => {
+ let stateCopy;
+
+ beforeEach(() => {
+ stateCopy = state();
+ });
+
+ describe('SET_NAMESPACE', () => {
+ it('should set namespace', () => {
+ mutations[types.SET_NAMESPACE](stateCopy, mockNamespace);
+
+ expect(stateCopy.namespace).toEqual(mockNamespace);
+ });
+ });
+
+ describe('SET_STORAGE_KEY', () => {
+ it('should set storage key', () => {
+ mutations[types.SET_STORAGE_KEY](stateCopy, mockStorageKey);
+
+ expect(stateCopy.storageKey).toEqual(mockStorageKey);
+ });
+ });
+
+ describe('SET_SEARCH_QUERY', () => {
+ it('should set search query', () => {
+ const searchQuery = 'gitlab-ce';
+
+ mutations[types.SET_SEARCH_QUERY](stateCopy, searchQuery);
+
+ expect(stateCopy.searchQuery).toEqual(searchQuery);
+ });
+ });
+
+ describe('REQUEST_FREQUENT_ITEMS', () => {
+ it('should set view states when requesting frequent items', () => {
+ mutations[types.REQUEST_FREQUENT_ITEMS](stateCopy);
+
+ expect(stateCopy.isLoadingItems).toEqual(true);
+ expect(stateCopy.hasSearchQuery).toEqual(false);
+ });
+ });
+
+ describe('RECEIVE_FREQUENT_ITEMS_SUCCESS', () => {
+ it('should set view states when receiving frequent items', () => {
+ mutations[types.RECEIVE_FREQUENT_ITEMS_SUCCESS](stateCopy, mockFrequentProjects);
+
+ expect(stateCopy.items).toEqual(mockFrequentProjects);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(false);
+ expect(stateCopy.isFetchFailed).toEqual(false);
+ });
+ });
+
+ describe('RECEIVE_FREQUENT_ITEMS_ERROR', () => {
+ it('should set items and view states when error occurs retrieving frequent items', () => {
+ mutations[types.RECEIVE_FREQUENT_ITEMS_ERROR](stateCopy);
+
+ expect(stateCopy.items).toEqual([]);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(false);
+ expect(stateCopy.isFetchFailed).toEqual(true);
+ });
+ });
+
+ describe('REQUEST_SEARCHED_ITEMS', () => {
+ it('should set view states when requesting searched items', () => {
+ mutations[types.REQUEST_SEARCHED_ITEMS](stateCopy);
+
+ expect(stateCopy.isLoadingItems).toEqual(true);
+ expect(stateCopy.hasSearchQuery).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_SEARCHED_ITEMS_SUCCESS', () => {
+ it('should set items and view states when receiving searched items', () => {
+ mutations[types.RECEIVE_SEARCHED_ITEMS_SUCCESS](stateCopy, mockSearchedProjects);
+
+ expect(stateCopy.items).toEqual(mockProcessedSearchedProjects);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(true);
+ expect(stateCopy.isFetchFailed).toEqual(false);
+ });
+
+ it('should also handle the different `full_name` key for namespace in groups payload', () => {
+ mutations[types.RECEIVE_SEARCHED_ITEMS_SUCCESS](stateCopy, mockSearchedGroups);
+
+ expect(stateCopy.items).toEqual(mockProcessedSearchedGroups);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(true);
+ expect(stateCopy.isFetchFailed).toEqual(false);
+ });
+ });
+
+ describe('RECEIVE_SEARCHED_ITEMS_ERROR', () => {
+ it('should set view states when error occurs retrieving searched items', () => {
+ mutations[types.RECEIVE_SEARCHED_ITEMS_ERROR](stateCopy);
+
+ expect(stateCopy.items).toEqual([]);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(true);
+ expect(stateCopy.isFetchFailed).toEqual(true);
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/utils_spec.js b/spec/javascripts/frequent_items/utils_spec.js
new file mode 100644
index 00000000000..cd27d79b29a
--- /dev/null
+++ b/spec/javascripts/frequent_items/utils_spec.js
@@ -0,0 +1,89 @@
+import bp from '~/breakpoints';
+import { isMobile, getTopFrequentItems, updateExistingFrequentItem } from '~/frequent_items/utils';
+import { HOUR_IN_MS, FREQUENT_ITEMS } from '~/frequent_items/constants';
+import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_data';
+
+describe('Frequent Items utils spec', () => {
+ describe('isMobile', () => {
+ it('returns true when the screen is small ', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('sm');
+
+ expect(isMobile()).toBe(true);
+ });
+
+ it('returns true when the screen is extra-small ', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('xs');
+
+ expect(isMobile()).toBe(true);
+ });
+
+ it('returns false when the screen is larger than small ', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('md');
+
+ expect(isMobile()).toBe(false);
+ });
+ });
+
+ describe('getTopFrequentItems', () => {
+ it('returns empty array if no items provided', () => {
+ const result = getTopFrequentItems();
+
+ expect(result.length).toBe(0);
+ });
+
+ it('returns correct amount of items for mobile', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('sm');
+ const result = getTopFrequentItems(unsortedFrequentItems);
+
+ expect(result.length).toBe(FREQUENT_ITEMS.LIST_COUNT_MOBILE);
+ });
+
+ it('returns correct amount of items for desktop', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('lg');
+ const result = getTopFrequentItems(unsortedFrequentItems);
+
+ expect(result.length).toBe(FREQUENT_ITEMS.LIST_COUNT_DESKTOP);
+ });
+
+ it('sorts frequent items in order of frequency and lastAccessedOn', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('lg');
+ const result = getTopFrequentItems(unsortedFrequentItems);
+ const expectedResult = sortedFrequentItems.slice(0, FREQUENT_ITEMS.LIST_COUNT_DESKTOP);
+
+ expect(result).toEqual(expectedResult);
+ });
+ });
+
+ describe('updateExistingFrequentItem', () => {
+ let mockedProject;
+
+ beforeEach(() => {
+ mockedProject = {
+ ...mockProject,
+ frequency: 1,
+ lastAccessedOn: 1497979281815,
+ };
+ });
+
+ it('updates item if accessed over an hour ago', () => {
+ const newTimestamp = Date.now() + HOUR_IN_MS + 1;
+ const newItem = {
+ ...mockedProject,
+ lastAccessedOn: newTimestamp,
+ };
+ const result = updateExistingFrequentItem(mockedProject, newItem);
+
+ expect(result.frequency).toBe(mockedProject.frequency + 1);
+ });
+
+ it('does not update item if accessed within the hour', () => {
+ const newItem = {
+ ...mockedProject,
+ lastAccessedOn: mockedProject.lastAccessedOn + HOUR_IN_MS,
+ };
+ const result = updateExistingFrequentItem(mockedProject, newItem);
+
+ expect(result.frequency).toBe(mockedProject.frequency);
+ });
+ });
+});
diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js
index 97c771dcfd3..78330dd9633 100644
--- a/spec/javascripts/gpg_badges_spec.js
+++ b/spec/javascripts/gpg_badges_spec.js
@@ -1,23 +1,27 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import GpgBadges from '~/gpg_badges';
+import { TEST_HOST } from 'spec/test_constants';
describe('GpgBadges', () => {
let mock;
const dummyCommitSha = 'n0m0rec0ffee';
const dummyBadgeHtml = 'dummy html';
const dummyResponse = {
- signatures: [{
- commit_sha: dummyCommitSha,
- html: dummyBadgeHtml,
- }],
+ signatures: [
+ {
+ commit_sha: dummyCommitSha,
+ html: dummyBadgeHtml,
+ },
+ ],
};
+ const dummyUrl = `${TEST_HOST}/dummy/signatures`;
beforeEach(() => {
mock = new MockAdapter(axios);
setFixtures(`
<form
- class="commits-search-form js-signature-container" data-signatures-path="/hello" action="/hello"
+ class="commits-search-form js-signature-container" data-signatures-path="${dummyUrl}" action="${dummyUrl}"
method="get">
<input name="utf8" type="hidden" value="✓">
<input type="search" name="search" id="commits-search"class="form-control search-text-input input-short">
@@ -32,25 +36,55 @@ describe('GpgBadges', () => {
mock.restore();
});
- it('displays a loading spinner', (done) => {
- mock.onGet('/hello').reply(200);
+ it('does not make a request if there is no container element', done => {
+ setFixtures('');
+ spyOn(axios, 'get');
- GpgBadges.fetch().then(() => {
- expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
- const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
- expect(spinners.length).toBe(1);
- done();
- }).catch(done.fail);
+ GpgBadges.fetch()
+ .then(() => {
+ expect(axios.get).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('replaces the loading spinner', (done) => {
- mock.onGet('/hello').reply(200, dummyResponse);
+ it('throws an error if the endpoint is missing', done => {
+ setFixtures('<div class="js-signature-container"></div>');
+ spyOn(axios, 'get');
- GpgBadges.fetch().then(() => {
- expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
- const parentContainer = document.querySelector('.parent-container');
- expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
- done();
- }).catch(done.fail);
+ GpgBadges.fetch()
+ .then(() => done.fail('Expected error to be thrown'))
+ .catch(error => {
+ expect(error.message).toBe('Missing commit signatures endpoint!');
+ expect(axios.get).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('displays a loading spinner', done => {
+ mock.onGet(dummyUrl).replyOnce(200);
+
+ GpgBadges.fetch()
+ .then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
+ const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
+ expect(spinners.length).toBe(1);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('replaces the loading spinner', done => {
+ mock.onGet(dummyUrl).replyOnce(200, dummyResponse);
+
+ GpgBadges.fetch()
+ .then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
+ const parentContainer = document.querySelector('.parent-container');
+ expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
+ done();
+ })
+ .catch(done.fail);
});
});
diff --git a/spec/javascripts/helpers/init_vue_mr_page_helper.js b/spec/javascripts/helpers/init_vue_mr_page_helper.js
index 05c6d587e9c..fc4288eb15b 100644
--- a/spec/javascripts/helpers/init_vue_mr_page_helper.js
+++ b/spec/javascripts/helpers/init_vue_mr_page_helper.js
@@ -5,12 +5,16 @@ import { userDataMock, notesDataMock, noteableDataMock } from '../notes/mock_dat
import diffFileMockData from '../diffs/mock_data/diff_file';
export default function initVueMRPage() {
+ const mrTestEl = document.createElement('div');
+ mrTestEl.className = 'js-merge-request-test';
+ document.body.appendChild(mrTestEl);
+
const diffsAppEndpoint = '/diffs/app/endpoint';
const diffsAppProjectPath = 'testproject';
const mrEl = document.createElement('div');
mrEl.className = 'merge-request fixture-mr';
mrEl.setAttribute('data-mr-action', 'diffs');
- document.body.appendChild(mrEl);
+ mrTestEl.appendChild(mrEl);
const mrDiscussionsEl = document.createElement('div');
mrDiscussionsEl.id = 'js-vue-mr-discussions';
@@ -18,18 +22,18 @@ export default function initVueMRPage() {
mrDiscussionsEl.setAttribute('data-noteable-data', JSON.stringify(noteableDataMock));
mrDiscussionsEl.setAttribute('data-notes-data', JSON.stringify(notesDataMock));
mrDiscussionsEl.setAttribute('data-noteable-type', 'merge-request');
- document.body.appendChild(mrDiscussionsEl);
+ mrTestEl.appendChild(mrDiscussionsEl);
const discussionCounterEl = document.createElement('div');
discussionCounterEl.id = 'js-vue-discussion-counter';
- document.body.appendChild(discussionCounterEl);
+ mrTestEl.appendChild(discussionCounterEl);
const diffsAppEl = document.createElement('div');
diffsAppEl.id = 'js-diffs-app';
diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint);
diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath);
diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock));
- document.body.appendChild(diffsAppEl);
+ mrTestEl.appendChild(diffsAppEl);
const mock = new MockAdapter(axios);
mock.onGet(diffsAppEndpoint).reply(200, {
diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js
index 5ba17ecf5b5..1057f0aca3e 100644
--- a/spec/javascripts/helpers/vue_mount_component_helper.js
+++ b/spec/javascripts/helpers/vue_mount_component_helper.js
@@ -15,4 +15,14 @@ export const mountComponentWithStore = (Component, { el, props, store }) =>
propsData: props || {},
}).$mount(el);
+export const mountComponentWithSlots = (Component, { props, slots }) => {
+ const component = new Component({
+ propsData: props || {},
+ });
+
+ component.$slots = slots;
+
+ return component.$mount();
+};
+
export default mountComponent;
diff --git a/spec/javascripts/helpers/vuex_action_helper.js b/spec/javascripts/helpers/vuex_action_helper.js
index d6ab0aeeed7..1972408356e 100644
--- a/spec/javascripts/helpers/vuex_action_helper.js
+++ b/spec/javascripts/helpers/vuex_action_helper.js
@@ -1,71 +1,101 @@
+const noop = () => {};
+
/**
- * helper for testing action with expected mutations inspired in
+ * Helper for testing action with expected mutations inspired in
* https://vuex.vuejs.org/en/testing.html
*
+ * @param {Function} action to be tested
+ * @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
+ * @param {Array} [expectedActions=[]] actions expected to be dispatched
+ * @param {Function} [done=noop] to be executed after the tests
+ * @return {Promise}
+ *
* @example
* testAction(
* actions.actionName, // action
- * { }, // mocked response
- * state, // state
+ * { }, // mocked payload
+ * state, //state
+ * // expected mutations
* [
* { type: types.MUTATION}
- * { type: types.MUTATION_1, payload: {}}
- * ], // mutations
+ * { type: types.MUTATION_1, payload: jasmine.any(Number)}
+ * ],
+ * // expected actions
* [
- * { type: 'actionName', payload: {}},
- * { type: 'actionName1', payload: {}}
- * ] //actions
+ * { type: 'actionName', payload: {param: 'foobar'}},
+ * { type: 'actionName1'}
+ * ]
* done,
* );
+ *
+ * @example
+ * testAction(
+ * actions.actionName, // action
+ * { }, // mocked payload
+ * state, //state
+ * [ { type: types.MUTATION} ], // expected mutations
+ * [], // expected actions
+ * ).then(done)
+ * .catch(done.fail);
*/
-export default (action, payload, state, expectedMutations, expectedActions, done) => {
- let mutationsCount = 0;
- let actionsCount = 0;
+export default (
+ action,
+ payload,
+ state,
+ expectedMutations = [],
+ expectedActions = [],
+ done = noop,
+) => {
+ const mutations = [];
+ const actions = [];
// mock commit
const commit = (type, mutationPayload) => {
- const mutation = expectedMutations[mutationsCount];
-
- expect(mutation.type).toEqual(type);
+ const mutation = { type };
- if (mutation.payload) {
- expect(mutation.payload).toEqual(mutationPayload);
+ if (typeof mutationPayload !== 'undefined') {
+ mutation.payload = mutationPayload;
}
- mutationsCount += 1;
- if (mutationsCount >= expectedMutations.length) {
- done();
- }
+ mutations.push(mutation);
};
// mock dispatch
const dispatch = (type, actionPayload) => {
- const actionExpected = expectedActions[actionsCount];
-
- expect(actionExpected.type).toEqual(type);
+ const dispatchedAction = { type };
- if (actionExpected.payload) {
- expect(actionExpected.payload).toEqual(actionPayload);
+ if (typeof actionPayload !== 'undefined') {
+ dispatchedAction.payload = actionPayload;
}
- actionsCount += 1;
- if (actionsCount >= expectedActions.length) {
- done();
- }
+ actions.push(dispatchedAction);
};
- // call the action with mocked store and arguments
- action({ commit, state, dispatch, rootState: state }, payload);
-
- // check if no mutations should have been dispatched
- if (expectedMutations.length === 0) {
- expect(mutationsCount).toEqual(0);
+ const validateResults = () => {
+ expect({
+ mutations,
+ actions,
+ }).toEqual({
+ mutations: expectedMutations,
+ actions: expectedActions,
+ });
done();
- }
+ };
- // check if no mutations should have been dispatched
- if (expectedActions.length === 0) {
- expect(actionsCount).toEqual(0);
- done();
- }
+ const result = action({ commit, state, dispatch, rootState: state, rootGetters: state }, payload);
+
+ return new Promise(resolve => {
+ setImmediate(resolve);
+ })
+ .then(() => result)
+ .catch(error => {
+ validateResults();
+ throw error;
+ })
+ .then(data => {
+ validateResults();
+ return data;
+ });
};
diff --git a/spec/javascripts/helpers/vuex_action_helper_spec.js b/spec/javascripts/helpers/vuex_action_helper_spec.js
new file mode 100644
index 00000000000..09f0bd395c3
--- /dev/null
+++ b/spec/javascripts/helpers/vuex_action_helper_spec.js
@@ -0,0 +1,166 @@
+import MockAdapter from 'axios-mock-adapter';
+import { TEST_HOST } from 'spec/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 = () => {};
+
+ beforeAll(() => {
+ mock = new MockAdapter(axios);
+ /*
+ In order to test the helper properly, we need to overwrite the jasmine `expect` helper.
+ We test that the testAction helper properly passes the dispatched actions/committed mutations
+ to the jasmine helper.
+ */
+ originalExpect = expect;
+ assertion = null;
+ global.expect = actual => ({
+ toEqual: () => {
+ originalExpect(actual).toEqual(assertion);
+ },
+ });
+ });
+
+ afterAll(() => {
+ mock.restore();
+ global.expect = originalExpect;
+ });
+
+ it('should properly pass on state and payload', () => {
+ const exampleState = { FOO: 12, BAR: 3 };
+ const examplePayload = { BAZ: 73, BIZ: 55 };
+
+ const action = ({ state }, payload) => {
+ originalExpect(state).toEqual(exampleState);
+ originalExpect(payload).toEqual(examplePayload);
+ };
+
+ assertion = { mutations: [], actions: [] };
+
+ testAction(action, examplePayload, exampleState);
+ });
+
+ describe('should work with synchronous actions', () => {
+ it('committing mutation', () => {
+ const action = ({ commit }) => {
+ commit('MUTATION');
+ };
+
+ assertion = { mutations: [{ type: 'MUTATION' }], actions: [] };
+
+ testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
+ });
+
+ it('dispatching action', () => {
+ const action = ({ dispatch }) => {
+ dispatch('ACTION');
+ };
+
+ assertion = { actions: [{ type: 'ACTION' }], mutations: [] };
+
+ testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
+ });
+
+ it('work with jasmine done once finished', done => {
+ assertion = { mutations: [], actions: [] };
+
+ testAction(noop, null, {}, assertion.mutations, assertion.actions, done);
+ });
+
+ it('provide promise interface', done => {
+ assertion = { mutations: [], actions: [] };
+
+ testAction(noop, null, {}, assertion.mutations, assertion.actions)
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('should work with promise based actions (fetch action)', () => {
+ let lastError;
+ const data = { FOO: 'BAR' };
+
+ const promiseAction = ({ commit, dispatch }) => {
+ dispatch('ACTION');
+
+ return axios
+ .get(TEST_HOST)
+ .catch(error => {
+ commit('ERROR');
+ lastError = error;
+ throw error;
+ })
+ .then(() => {
+ commit('SUCCESS');
+ return data;
+ });
+ };
+
+ beforeEach(() => {
+ lastError = null;
+ });
+
+ it('work with jasmine done once finished', done => {
+ mock.onGet(TEST_HOST).replyOnce(200, 42);
+
+ assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
+
+ testAction(promiseAction, null, {}, assertion.mutations, assertion.actions, done);
+ });
+
+ it('return original data of successful promise while checking actions/mutations', done => {
+ mock.onGet(TEST_HOST).replyOnce(200, 42);
+
+ assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
+
+ testAction(promiseAction, null, {}, assertion.mutations, assertion.actions)
+ .then(res => {
+ originalExpect(res).toEqual(data);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('return original error of rejected promise while checking actions/mutations', done => {
+ mock.onGet(TEST_HOST).replyOnce(500, '');
+
+ assertion = { mutations: [{ type: 'ERROR' }], actions: [{ type: 'ACTION' }] };
+
+ testAction(promiseAction, null, {}, assertion.mutations, assertion.actions)
+ .then(done.fail)
+ .catch(error => {
+ originalExpect(error).toBe(lastError);
+ done();
+ });
+ });
+ });
+
+ it('should work with async actions not returning promises', done => {
+ const data = { FOO: 'BAR' };
+
+ const promiseAction = ({ commit, dispatch }) => {
+ dispatch('ACTION');
+
+ axios
+ .get(TEST_HOST)
+ .then(() => {
+ commit('SUCCESS');
+ return data;
+ })
+ .catch(error => {
+ commit('ERROR');
+ throw error;
+ });
+ };
+
+ mock.onGet(TEST_HOST).replyOnce(200, 42);
+
+ assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
+
+ testAction(promiseAction, null, {}, assertion.mutations, assertion.actions, done);
+ });
+});
diff --git a/spec/javascripts/helpers/wait_for_promises.js b/spec/javascripts/helpers/wait_for_promises.js
new file mode 100644
index 00000000000..1d2b53fc770
--- /dev/null
+++ b/spec/javascripts/helpers/wait_for_promises.js
@@ -0,0 +1 @@
+export default () => new Promise(resolve => requestAnimationFrame(resolve));
diff --git a/spec/javascripts/ide/components/activity_bar_spec.js b/spec/javascripts/ide/components/activity_bar_spec.js
index 946c7e8e9c8..4d878e633fe 100644
--- a/spec/javascripts/ide/components/activity_bar_spec.js
+++ b/spec/javascripts/ide/components/activity_bar_spec.js
@@ -24,26 +24,6 @@ describe('IDE activity bar', () => {
resetStore(vm.$store);
});
- describe('goBackUrl', () => {
- it('renders the Go Back link with the referrer when present', () => {
- const fakeReferrer = '/example/README.md';
- spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
-
- vm.$mount();
-
- expect(vm.goBackUrl).toEqual(fakeReferrer);
- });
-
- it('renders the Go Back link with the project url when referrer is not present', () => {
- const fakeReferrer = '';
- spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
-
- vm.$mount();
-
- expect(vm.goBackUrl).toEqual('testing');
- });
- });
-
describe('updateActivityBarView', () => {
beforeEach(() => {
spyOn(vm, 'updateActivityBarView');
diff --git a/spec/javascripts/ide/components/branches/item_spec.js b/spec/javascripts/ide/components/branches/item_spec.js
new file mode 100644
index 00000000000..8b756c8f168
--- /dev/null
+++ b/spec/javascripts/ide/components/branches/item_spec.js
@@ -0,0 +1,53 @@
+import Vue from 'vue';
+import mountCompontent from 'spec/helpers/vue_mount_component_helper';
+import router from '~/ide/ide_router';
+import Item from '~/ide/components/branches/item.vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+import { projectData } from '../../mock_data';
+
+const TEST_BRANCH = {
+ name: 'master',
+ committedDate: '2018-01-05T05:50Z',
+};
+const TEST_PROJECT_ID = projectData.name_with_namespace;
+
+describe('IDE branch item', () => {
+ const Component = Vue.extend(Item);
+ let vm;
+
+ beforeEach(() => {
+ vm = mountCompontent(Component, {
+ item: { ...TEST_BRANCH },
+ projectId: TEST_PROJECT_ID,
+ isActive: false,
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders branch name and timeago', () => {
+ const timeText = getTimeago().format(TEST_BRANCH.committedDate);
+ expect(vm.$el).toContainText(TEST_BRANCH.name);
+ expect(vm.$el.querySelector('time')).toHaveText(timeText);
+ expect(vm.$el.querySelector('.ic-mobile-issue-close')).toBe(null);
+ });
+
+ it('renders link to branch', () => {
+ const expectedHref = router.resolve(`/project/${TEST_PROJECT_ID}/edit/${TEST_BRANCH.name}`).href;
+ expect(vm.$el).toMatch('a');
+ expect(vm.$el).toHaveAttr('href', expectedHref);
+ });
+
+ it('renders icon if isActive', done => {
+ vm.isActive = true;
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.ic-mobile-issue-close')).not.toBe(null);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/ide/components/branches/search_list_spec.js b/spec/javascripts/ide/components/branches/search_list_spec.js
new file mode 100644
index 00000000000..c3f84ba1c24
--- /dev/null
+++ b/spec/javascripts/ide/components/branches/search_list_spec.js
@@ -0,0 +1,79 @@
+import Vue from 'vue';
+import store from '~/ide/stores';
+import * as types from '~/ide/stores/modules/branches/mutation_types';
+import List from '~/ide/components/branches/search_list.vue';
+import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { branches as testBranches } from '../../mock_data';
+import { resetStore } from '../../helpers';
+
+describe('IDE branches search list', () => {
+ const Component = Vue.extend(List);
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponentWithStore(Component, store, {});
+
+ spyOn(vm, 'fetchBranches');
+
+ vm.$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(store);
+ });
+
+ it('calls fetch on mounted', () => {
+ expect(vm.fetchBranches).toHaveBeenCalledWith({
+ search: '',
+ });
+ });
+
+ it('renders loading icon', done => {
+ vm.$store.state.branches.isLoading = true;
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toContainElement('.loading-container');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders branches not found when search is not empty', done => {
+ vm.search = 'testing';
+
+ vm.$nextTick(() => {
+ expect(vm.$el).toContainText('No branches found');
+
+ done();
+ });
+ });
+
+ describe('with branches', () => {
+ const currentBranch = testBranches[1];
+
+ beforeEach(done => {
+ vm.$store.state.currentBranchId = currentBranch.name;
+ vm.$store.commit(`branches/${types.RECEIVE_BRANCHES_SUCCESS}`, testBranches);
+
+ vm.$nextTick(done);
+ });
+
+ it('renders list', () => {
+ const elementText = Array.from(vm.$el.querySelectorAll('li strong'))
+ .map(x => x.textContent.trim());
+
+ expect(elementText).toEqual(testBranches.map(x => x.name));
+ });
+
+ it('renders check next to active branch', () => {
+ const checkedText = Array.from(vm.$el.querySelectorAll('li'))
+ .filter(x => x.querySelector('.ide-search-list-current-icon svg'))
+ .map(x => x.querySelector('strong').textContent.trim());
+
+ expect(checkedText).toEqual([currentBranch.name]);
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/changed_file_icon_spec.js b/spec/javascripts/ide/components/changed_file_icon_spec.js
index 541864e912e..7308219f705 100644
--- a/spec/javascripts/ide/components/changed_file_icon_spec.js
+++ b/spec/javascripts/ide/components/changed_file_icon_spec.js
@@ -33,14 +33,14 @@ describe('IDE changed file icon', () => {
});
describe('changedIconClass', () => {
- it('includes multi-file-modified when not a temp file', () => {
- expect(vm.changedIconClass).toContain('multi-file-modified');
+ it('includes ide-file-modified when not a temp file', () => {
+ expect(vm.changedIconClass).toContain('ide-file-modified');
});
- it('includes multi-file-addition when a temp file', () => {
+ it('includes ide-file-addition when a temp file', () => {
vm.file.tempFile = true;
- expect(vm.changedIconClass).toContain('multi-file-addition');
+ expect(vm.changedIconClass).toContain('ide-file-addition');
});
});
});
diff --git a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js b/spec/javascripts/ide/components/commit_sidebar/actions_spec.js
index 27f10caccb1..3a5d6c8a90b 100644
--- a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/actions_spec.js
@@ -46,4 +46,12 @@ describe('IDE commit sidebar actions', () => {
done();
});
});
+
+ describe('commitToCurrentBranchText', () => {
+ it('escapes current branch', () => {
+ vm.$store.state.currentBranchId = '<img src="x" />';
+
+ expect(vm.commitToCurrentBranchText).not.toContain('<img src="x" />');
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
index bf96170f703..41d8bfff7e7 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
@@ -76,17 +76,29 @@ describe('Multi-file editor commit sidebar list item', () => {
expect(vm.iconName).toBe('file-addition');
});
+
+ it('returns deletion', () => {
+ f.deleted = true;
+
+ expect(vm.iconName).toBe('file-deletion');
+ });
});
describe('iconClass', () => {
it('returns modified when not a tempFile', () => {
- expect(vm.iconClass).toContain('multi-file-modified');
+ expect(vm.iconClass).toContain('ide-file-modified');
});
it('returns addition when not a tempFile', () => {
f.tempFile = true;
- expect(vm.iconClass).toContain('multi-file-addition');
+ expect(vm.iconClass).toContain('ide-file-addition');
+ });
+
+ it('returns deletion', () => {
+ f.deleted = true;
+
+ expect(vm.iconClass).toContain('ide-file-deletion');
});
});
});
diff --git a/spec/javascripts/ide/components/ide_spec.js b/spec/javascripts/ide/components/ide_spec.js
index 708c9fe69af..49b8e934cdd 100644
--- a/spec/javascripts/ide/components/ide_spec.js
+++ b/spec/javascripts/ide/components/ide_spec.js
@@ -45,6 +45,33 @@ describe('ide component', () => {
});
});
+ describe('onBeforeUnload', () => {
+ it('returns undefined when no staged files or changed files', () => {
+ expect(vm.onBeforeUnload()).toBe(undefined);
+ });
+
+ it('returns warning text when their are changed files', () => {
+ vm.$store.state.changedFiles.push(file());
+
+ expect(vm.onBeforeUnload()).toBe('Are you sure you want to lose unsaved changes?');
+ });
+
+ it('returns warning text when their are staged files', () => {
+ vm.$store.state.stagedFiles.push(file());
+
+ expect(vm.onBeforeUnload()).toBe('Are you sure you want to lose unsaved changes?');
+ });
+
+ it('updates event object', () => {
+ const event = {};
+ vm.$store.state.stagedFiles.push(file());
+
+ vm.onBeforeUnload(event);
+
+ expect(event.returnValue).toBe('Are you sure you want to lose unsaved changes?');
+ });
+ });
+
describe('file finder', () => {
beforeEach(done => {
spyOn(vm, 'toggleFileFinder');
diff --git a/spec/javascripts/ide/components/ide_status_bar_spec.js b/spec/javascripts/ide/components/ide_status_bar_spec.js
index 770dca9cb0f..0e93c5193a1 100644
--- a/spec/javascripts/ide/components/ide_status_bar_spec.js
+++ b/spec/javascripts/ide/components/ide_status_bar_spec.js
@@ -13,6 +13,7 @@ describe('ideStatusBar', () => {
store.state.currentProjectId = 'abcproject';
store.state.projects.abcproject = projectData;
+ store.state.currentBranchId = 'master';
vm = createComponentWithStore(Component, store).$mount();
});
@@ -60,4 +61,29 @@ describe('ideStatusBar', () => {
expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de');
});
});
+
+ describe('pipeline status', () => {
+ it('opens right sidebar on clicking icon', done => {
+ spyOn(vm, 'setRightPane');
+ Vue.set(vm.$store.state.pipelines, 'latestPipeline', {
+ details: {
+ status: {
+ text: 'success',
+ details_path: 'test',
+ icon: 'status_success',
+ },
+ },
+ });
+
+ vm
+ .$nextTick()
+ .then(() => {
+ vm.$el.querySelector('.ide-status-pipeline button').click();
+
+ expect(vm.setRightPane).toHaveBeenCalledWith('pipelines-list');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/jobs/detail/description_spec.js b/spec/javascripts/ide/components/jobs/detail/description_spec.js
index 9b715a41499..babae00d2f7 100644
--- a/spec/javascripts/ide/components/jobs/detail/description_spec.js
+++ b/spec/javascripts/ide/components/jobs/detail/description_spec.js
@@ -23,6 +23,6 @@ describe('IDE job description', () => {
});
it('renders CI icon', () => {
- expect(vm.$el.querySelector('.ci-status-icon .ic-status_passed_borderless')).not.toBe(null);
+ expect(vm.$el.querySelector('.ci-status-icon .ic-status_success_borderless')).not.toBe(null);
});
});
diff --git a/spec/javascripts/ide/components/jobs/item_spec.js b/spec/javascripts/ide/components/jobs/item_spec.js
index 79e07f00e7b..2f97d39e98e 100644
--- a/spec/javascripts/ide/components/jobs/item_spec.js
+++ b/spec/javascripts/ide/components/jobs/item_spec.js
@@ -24,7 +24,7 @@ describe('IDE jobs item', () => {
});
it('renders CI icon', () => {
- expect(vm.$el.querySelector('.ic-status_passed_borderless')).not.toBe(null);
+ expect(vm.$el.querySelector('.ic-status_success_borderless')).not.toBe(null);
});
it('does not render view logs button if not started', done => {
diff --git a/spec/javascripts/ide/components/merge_requests/dropdown_spec.js b/spec/javascripts/ide/components/merge_requests/dropdown_spec.js
deleted file mode 100644
index 74884c9a362..00000000000
--- a/spec/javascripts/ide/components/merge_requests/dropdown_spec.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import Vue from 'vue';
-import { createStore } from '~/ide/stores';
-import Dropdown from '~/ide/components/merge_requests/dropdown.vue';
-import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
-import { mergeRequests } from '../../mock_data';
-
-describe('IDE merge requests dropdown', () => {
- const Component = Vue.extend(Dropdown);
- let vm;
-
- beforeEach(() => {
- const store = createStore();
-
- vm = createComponentWithStore(Component, store, { show: false }).$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('does not render tabs when show is false', () => {
- expect(vm.$el.querySelector('.nav-links')).toBe(null);
- });
-
- describe('when show is true', () => {
- beforeEach(done => {
- vm.show = true;
- vm.$store.state.mergeRequests.assigned.mergeRequests.push(mergeRequests[0]);
-
- vm.$nextTick(done);
- });
-
- it('renders tabs', () => {
- expect(vm.$el.querySelector('.nav-links')).not.toBe(null);
- });
-
- it('renders count for assigned & created data', () => {
- expect(vm.$el.querySelector('.nav-links a').textContent).toContain('Created by me');
- expect(vm.$el.querySelector('.nav-links a .badge').textContent).toContain('0');
-
- expect(vm.$el.querySelectorAll('.nav-links a')[1].textContent).toContain('Assigned to me');
- expect(
- vm.$el.querySelectorAll('.nav-links a')[1].querySelector('.badge').textContent,
- ).toContain('1');
- });
- });
-});
diff --git a/spec/javascripts/ide/components/merge_requests/info_spec.js b/spec/javascripts/ide/components/merge_requests/info_spec.js
new file mode 100644
index 00000000000..98a29e5128b
--- /dev/null
+++ b/spec/javascripts/ide/components/merge_requests/info_spec.js
@@ -0,0 +1,51 @@
+import Vue from 'vue';
+import '~/behaviors/markdown/render_gfm';
+import { createStore } from '~/ide/stores';
+import Info from '~/ide/components/merge_requests/info.vue';
+import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+
+describe('IDE merge request details', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(Info);
+ });
+
+ beforeEach(() => {
+ const store = createStore();
+ store.state.currentProjectId = 'gitlab-ce';
+ store.state.currentMergeRequestId = 1;
+ store.state.projects['gitlab-ce'] = {
+ mergeRequests: {
+ 1: {
+ iid: 1,
+ title: 'Testing',
+ title_html: '<span class="title-html">Testing</span>',
+ description: 'Description',
+ description_html: '<p class="description-html">Description HTML</p>',
+ },
+ },
+ };
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders merge request IID', () => {
+ expect(vm.$el.querySelector('.detail-page-header').textContent).toContain('!1');
+ });
+
+ it('renders title as HTML', () => {
+ expect(vm.$el.querySelector('.title-html')).not.toBe(null);
+ expect(vm.$el.querySelector('.title').textContent).toContain('Testing');
+ });
+
+ it('renders description as HTML', () => {
+ expect(vm.$el.querySelector('.description-html')).not.toBe(null);
+ expect(vm.$el.querySelector('.description').textContent).toContain('Description HTML');
+ });
+});
diff --git a/spec/javascripts/ide/components/merge_requests/item_spec.js b/spec/javascripts/ide/components/merge_requests/item_spec.js
index 51c4cddef2f..750948cae3c 100644
--- a/spec/javascripts/ide/components/merge_requests/item_spec.js
+++ b/spec/javascripts/ide/components/merge_requests/item_spec.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import router from '~/ide/ide_router';
import Item from '~/ide/components/merge_requests/item.vue';
import mountCompontent from '../../../helpers/vue_mount_component_helper';
@@ -27,6 +28,12 @@ describe('IDE merge request item', () => {
expect(vm.$el.textContent).toContain('gitlab-org/gitlab-ce!1');
});
+ it('renders link with href', () => {
+ const expectedHref = router.resolve(`/project/${vm.item.projectPathWithNamespace}/merge_requests/${vm.item.iid}`).href;
+ expect(vm.$el).toMatch('a');
+ expect(vm.$el).toHaveAttr('href', expectedHref);
+ });
+
it('renders icon if ID matches currentId', () => {
expect(vm.$el.querySelector('.ic-mobile-issue-close')).not.toBe(null);
});
@@ -50,12 +57,4 @@ describe('IDE merge request item', () => {
done();
});
});
-
- it('emits click event on click', () => {
- spyOn(vm, '$emit');
-
- vm.$el.click();
-
- expect(vm.$emit).toHaveBeenCalledWith('click', vm.item);
- });
});
diff --git a/spec/javascripts/ide/components/merge_requests/list_spec.js b/spec/javascripts/ide/components/merge_requests/list_spec.js
index f4b393778dc..c761315444c 100644
--- a/spec/javascripts/ide/components/merge_requests/list_spec.js
+++ b/spec/javascripts/ide/components/merge_requests/list_spec.js
@@ -10,10 +10,7 @@ describe('IDE merge requests list', () => {
let vm;
beforeEach(() => {
- vm = createComponentWithStore(Component, store, {
- type: 'created',
- emptyText: 'empty text',
- });
+ vm = createComponentWithStore(Component, store, {});
spyOn(vm, 'fetchMergeRequests');
@@ -28,13 +25,13 @@ describe('IDE merge requests list', () => {
it('calls fetch on mounted', () => {
expect(vm.fetchMergeRequests).toHaveBeenCalledWith({
- type: 'created',
search: '',
+ type: '',
});
});
it('renders loading icon', done => {
- vm.$store.state.mergeRequests.created.isLoading = true;
+ vm.$store.state.mergeRequests.isLoading = true;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.loading-container')).not.toBe(null);
@@ -43,10 +40,6 @@ describe('IDE merge requests list', () => {
});
});
- it('renders empty text when no merge requests exist', () => {
- expect(vm.$el.textContent).toContain('empty text');
- });
-
it('renders no search results text when search is not empty', done => {
vm.search = 'testing';
@@ -57,9 +50,29 @@ describe('IDE merge requests list', () => {
});
});
+ it('clicking on search type, sets currentSearchType and loads merge requests', done => {
+ vm.onSearchFocus();
+
+ vm.$nextTick()
+ .then(() => {
+ vm.$el.querySelector('li button').click();
+
+ return vm.$nextTick();
+ })
+ .then(() => {
+ expect(vm.currentSearchType).toEqual(vm.$options.searchTypes[0]);
+ expect(vm.fetchMergeRequests).toHaveBeenCalledWith({
+ type: vm.currentSearchType.type,
+ search: '',
+ });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
describe('with merge requests', () => {
beforeEach(done => {
- vm.$store.state.mergeRequests.created.mergeRequests.push({
+ vm.$store.state.mergeRequests.mergeRequests.push({
...mergeRequests[0],
projectPathWithNamespace: 'gitlab-org/gitlab-ce',
});
@@ -71,35 +84,6 @@ describe('IDE merge requests list', () => {
expect(vm.$el.querySelectorAll('li').length).toBe(1);
expect(vm.$el.querySelector('li').textContent).toContain(mergeRequests[0].title);
});
-
- it('calls openMergeRequest when clicking merge request', done => {
- spyOn(vm, 'openMergeRequest');
- vm.$el.querySelector('li button').click();
-
- vm.$nextTick(() => {
- expect(vm.openMergeRequest).toHaveBeenCalledWith({
- projectPath: 'gitlab-org/gitlab-ce',
- id: 1,
- });
-
- done();
- });
- });
- });
-
- describe('focusSearch', () => {
- it('focuses search input when loading is false', done => {
- spyOn(vm.$refs.searchInput, 'focus');
-
- vm.$store.state.mergeRequests.created.isLoading = false;
- vm.focusSearch();
-
- vm.$nextTick(() => {
- expect(vm.$refs.searchInput.focus).toHaveBeenCalled();
-
- done();
- });
- });
});
describe('searchMergeRequests', () => {
@@ -123,4 +107,52 @@ describe('IDE merge requests list', () => {
expect(vm.loadMergeRequests).toHaveBeenCalled();
});
});
+
+ describe('onSearchFocus', () => {
+ it('shows search types', done => {
+ vm.$el.querySelector('input').dispatchEvent(new Event('focus'));
+
+ expect(vm.hasSearchFocus).toBe(true);
+ expect(vm.showSearchTypes).toBe(true);
+
+ vm.$nextTick()
+ .then(() => {
+ const expectedSearchTypes = vm.$options.searchTypes.map(x => x.label);
+ const renderedSearchTypes = Array.from(vm.$el.querySelectorAll('li'))
+ .map(x => x.textContent.trim());
+
+ expect(renderedSearchTypes).toEqual(expectedSearchTypes);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not show search types, if already has search value', () => {
+ vm.search = 'lorem ipsum';
+ vm.$el.querySelector('input').dispatchEvent(new Event('focus'));
+
+ expect(vm.hasSearchFocus).toBe(true);
+ expect(vm.showSearchTypes).toBe(false);
+ });
+
+ it('does not show search types, if already has a search type', () => {
+ vm.currentSearchType = {};
+ vm.$el.querySelector('input').dispatchEvent(new Event('focus'));
+
+ expect(vm.hasSearchFocus).toBe(true);
+ expect(vm.showSearchTypes).toBe(false);
+ });
+
+ it('resets hasSearchFocus when search changes', done => {
+ vm.hasSearchFocus = true;
+ vm.search = 'something else';
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.hasSearchFocus).toBe(false);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/nav_dropdown_button_spec.js b/spec/javascripts/ide/components/nav_dropdown_button_spec.js
new file mode 100644
index 00000000000..0a58e260280
--- /dev/null
+++ b/spec/javascripts/ide/components/nav_dropdown_button_spec.js
@@ -0,0 +1,63 @@
+import Vue from 'vue';
+import NavDropdownButton from '~/ide/components/nav_dropdown_button.vue';
+import store from '~/ide/stores';
+import { trimText } from 'spec/helpers/vue_component_helper';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { resetStore } from '../helpers';
+
+describe('NavDropdown', () => {
+ const TEST_BRANCH_ID = 'lorem-ipsum-dolar';
+ const TEST_MR_ID = '12345';
+ const Component = Vue.extend(NavDropdownButton);
+ let vm;
+
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, { store });
+
+ vm.$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(store);
+ });
+
+ it('renders empty placeholders, if state is falsey', () => {
+ expect(trimText(vm.$el.textContent)).toEqual('- -');
+ });
+
+ it('renders branch name, if state has currentBranchId', done => {
+ vm.$store.state.currentBranchId = TEST_BRANCH_ID;
+
+ vm.$nextTick()
+ .then(() => {
+ expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} -`);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders mr id, if state has currentMergeRequestId', done => {
+ vm.$store.state.currentMergeRequestId = TEST_MR_ID;
+
+ vm.$nextTick()
+ .then(() => {
+ expect(trimText(vm.$el.textContent)).toEqual(`- !${TEST_MR_ID}`);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders branch and mr, if state has both', done => {
+ vm.$store.state.currentBranchId = TEST_BRANCH_ID;
+ vm.$store.state.currentMergeRequestId = TEST_MR_ID;
+
+ vm.$nextTick()
+ .then(() => {
+ expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} !${TEST_MR_ID}`);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/ide/components/nav_dropdown_spec.js b/spec/javascripts/ide/components/nav_dropdown_spec.js
new file mode 100644
index 00000000000..af6665bcd62
--- /dev/null
+++ b/spec/javascripts/ide/components/nav_dropdown_spec.js
@@ -0,0 +1,50 @@
+import $ from 'jquery';
+import Vue from 'vue';
+import store from '~/ide/stores';
+import NavDropdown from '~/ide/components/nav_dropdown.vue';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+
+describe('IDE NavDropdown', () => {
+ const Component = Vue.extend(NavDropdown);
+ let vm;
+ let $dropdown;
+
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, { store });
+ $dropdown = $(vm.$el);
+
+ // block dispatch from doing anything
+ spyOn(vm.$store, 'dispatch');
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders nothing initially', () => {
+ expect(vm.$el).not.toContainElement('.ide-nav-form');
+ });
+
+ it('renders nav form when show.bs.dropdown', done => {
+ $dropdown.trigger('show.bs.dropdown');
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toContainElement('.ide-nav-form');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('destroys nav form when closed', done => {
+ $dropdown.trigger('show.bs.dropdown');
+ $dropdown.trigger('hide.bs.dropdown');
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).not.toContainElement('.ide-nav-form');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/ide/components/new_dropdown/button_spec.js b/spec/javascripts/ide/components/new_dropdown/button_spec.js
new file mode 100644
index 00000000000..ef083d06ba7
--- /dev/null
+++ b/spec/javascripts/ide/components/new_dropdown/button_spec.js
@@ -0,0 +1,49 @@
+import Vue from 'vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import Button from '~/ide/components/new_dropdown/button.vue';
+
+describe('IDE new entry dropdown button component', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(Button);
+ });
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ label: 'Testing',
+ icon: 'doc-new',
+ });
+
+ spyOn(vm, '$emit');
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders button with label', () => {
+ expect(vm.$el.textContent).toContain('Testing');
+ });
+
+ it('renders icon', () => {
+ expect(vm.$el.querySelector('.ic-doc-new')).not.toBe(null);
+ });
+
+ it('emits click event', () => {
+ vm.$el.click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('click');
+ });
+
+ it('hides label if showLabel is false', done => {
+ vm.showLabel = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.textContent).not.toContain('Testing');
+
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/new_dropdown/index_spec.js b/spec/javascripts/ide/components/new_dropdown/index_spec.js
index 7b637f37eba..8a8cbd2cee4 100644
--- a/spec/javascripts/ide/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/index_spec.js
@@ -13,6 +13,8 @@ describe('new dropdown component', () => {
vm = createComponentWithStore(component, store, {
branch: 'master',
path: '',
+ mouseOver: false,
+ type: 'tree',
});
vm.$store.state.currentProjectId = 'abcproject';
@@ -21,6 +23,8 @@ describe('new dropdown component', () => {
tree: [],
};
+ spyOn(vm, 'openNewEntryModal');
+
vm.$mount();
});
@@ -31,50 +35,23 @@ describe('new dropdown component', () => {
});
it('renders new file, upload and new directory links', () => {
- expect(vm.$el.querySelectorAll('a')[0].textContent.trim()).toBe('New file');
- expect(vm.$el.querySelectorAll('a')[1].textContent.trim()).toBe('Upload file');
- expect(vm.$el.querySelectorAll('a')[2].textContent.trim()).toBe('New directory');
+ const buttons = vm.$el.querySelectorAll('.dropdown-menu button');
+ expect(buttons[0].textContent.trim()).toBe('New file');
+ expect(buttons[1].textContent.trim()).toBe('Upload file');
+ expect(buttons[2].textContent.trim()).toBe('New directory');
});
describe('createNewItem', () => {
it('sets modalType to blob when new file is clicked', () => {
- vm.$el.querySelectorAll('a')[0].click();
+ vm.$el.querySelectorAll('.dropdown-menu button')[0].click();
- expect(vm.modalType).toBe('blob');
+ expect(vm.openNewEntryModal).toHaveBeenCalledWith({ type: 'blob', path: '' });
});
it('sets modalType to tree when new directory is clicked', () => {
- vm.$el.querySelectorAll('a')[2].click();
-
- expect(vm.modalType).toBe('tree');
- });
-
- it('opens modal when link is clicked', done => {
- vm.$el.querySelectorAll('a')[0].click();
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.modal')).not.toBeNull();
-
- done();
- });
- });
- });
+ vm.$el.querySelectorAll('.dropdown-menu button')[2].click();
- describe('hideModal', () => {
- beforeAll(done => {
- vm.openModal = true;
- Vue.nextTick(done);
- });
-
- it('closes modal after toggling', done => {
- vm.hideModal();
-
- Vue.nextTick()
- .then(() => {
- expect(vm.$el.querySelector('.modal')).toBeNull();
- })
- .then(done)
- .catch(done.fail);
+ expect(vm.openNewEntryModal).toHaveBeenCalledWith({ type: 'tree', path: '' });
});
});
@@ -85,10 +62,22 @@ describe('new dropdown component', () => {
vm.dropdownOpen = true;
setTimeout(() => {
- expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalled();
+ expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({
+ block: 'nearest',
+ });
done();
});
});
});
+
+ describe('delete entry', () => {
+ it('calls delete action', () => {
+ spyOn(vm, 'deleteEntry');
+
+ vm.$el.querySelectorAll('.dropdown-menu button')[4].click();
+
+ expect(vm.deleteEntry).toHaveBeenCalledWith('');
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/new_dropdown/modal_spec.js b/spec/javascripts/ide/components/new_dropdown/modal_spec.js
index f362ed4db65..595a2f927e9 100644
--- a/spec/javascripts/ide/components/new_dropdown/modal_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/modal_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
+import { createStore } from '~/ide/stores';
import modal from '~/ide/components/new_dropdown/modal.vue';
-import createComponent from 'spec/helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
describe('new file modal component', () => {
const Component = Vue.extend(modal);
@@ -13,13 +14,15 @@ describe('new file modal component', () => {
['tree', 'blob'].forEach(type => {
describe(type, () => {
beforeEach(() => {
- vm = createComponent(Component, {
+ const store = createStore();
+ store.state.entryModal = {
type,
- branchId: 'master',
path: '',
- });
+ };
+
+ vm = createComponentWithStore(Component, store).$mount();
- vm.entryName = 'testing';
+ vm.name = 'testing';
});
it(`sets modal title as ${type}`, () => {
@@ -35,17 +38,16 @@ describe('new file modal component', () => {
});
it(`sets form label as ${type}`, () => {
- expect(vm.$el.querySelector('.label-light').textContent.trim()).toBe('Name');
+ expect(vm.$el.querySelector('.label-bold').textContent.trim()).toBe('Name');
});
describe('createEntryInStore', () => {
it('$emits create', () => {
- spyOn(vm, '$emit');
+ spyOn(vm, 'createTempEntry');
- vm.createEntryInStore();
+ vm.submitForm();
- expect(vm.$emit).toHaveBeenCalledWith('create', {
- branchId: 'master',
+ expect(vm.createTempEntry).toHaveBeenCalledWith({
name: 'testing',
type,
});
@@ -54,21 +56,46 @@ describe('new file modal component', () => {
});
});
- it('focuses field on mount', () => {
- document.body.innerHTML += '<div class="js-test"></div>';
-
- vm = createComponent(
- Component,
- {
- type: 'tree',
- branchId: 'master',
+ describe('rename entry', () => {
+ beforeEach(() => {
+ const store = createStore();
+ store.state.entryModal = {
+ type: 'rename',
path: '',
- },
- '.js-test',
- );
+ entry: {
+ name: 'test',
+ type: 'blob',
+ },
+ };
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ ['tree', 'blob'].forEach(type => {
+ it(`renders title and button for renaming ${type}`, done => {
+ const text = type === 'tree' ? 'folder' : 'file';
+
+ vm.$store.state.entryModal.entry.type = type;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.modal-title').textContent.trim()).toBe(`Rename ${text}`);
+ expect(vm.$el.querySelector('.btn-success').textContent.trim()).toBe(`Rename ${text}`);
- expect(document.activeElement).toBe(vm.$refs.fieldName);
+ done();
+ });
+ });
+ });
+
+ describe('entryName', () => {
+ it('returns entries name', () => {
+ expect(vm.entryName).toBe('test');
+ });
+
+ it('updated name', () => {
+ vm.name = 'index.js';
- vm.$el.remove();
+ expect(vm.entryName).toBe('index.js');
+ });
+ });
});
});
diff --git a/spec/javascripts/ide/components/new_dropdown/upload_spec.js b/spec/javascripts/ide/components/new_dropdown/upload_spec.js
index 2bc5d701601..9c76500cfe5 100644
--- a/spec/javascripts/ide/components/new_dropdown/upload_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/upload_spec.js
@@ -9,7 +9,6 @@ describe('new dropdown upload', () => {
const Component = Vue.extend(upload);
vm = createComponent(Component, {
- branchId: 'master',
path: '',
});
@@ -65,7 +64,6 @@ describe('new dropdown upload', () => {
expect(vm.$emit).toHaveBeenCalledWith('create', {
name: file.name,
- branchId: 'master',
type: 'blob',
content: target.result,
base64: false,
@@ -77,7 +75,6 @@ describe('new dropdown upload', () => {
expect(vm.$emit).toHaveBeenCalledWith('create', {
name: file.name,
- branchId: 'master',
type: 'blob',
content: binaryTarget.result.split('base64,')[1],
base64: true,
diff --git a/spec/javascripts/ide/components/panes/right_spec.js b/spec/javascripts/ide/components/panes/right_spec.js
new file mode 100644
index 00000000000..c75975d2af6
--- /dev/null
+++ b/spec/javascripts/ide/components/panes/right_spec.js
@@ -0,0 +1,85 @@
+import Vue from 'vue';
+import '~/behaviors/markdown/render_gfm';
+import { createStore } from '~/ide/stores';
+import RightPane from '~/ide/components/panes/right.vue';
+import { rightSidebarViews } from '~/ide/constants';
+import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+
+describe('IDE right pane', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(RightPane);
+ });
+
+ beforeEach(() => {
+ const store = createStore();
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('active', () => {
+ it('renders merge request button as active', done => {
+ vm.$store.state.rightPane = rightSidebarViews.mergeRequestInfo;
+ vm.$store.state.currentMergeRequestId = '123';
+ vm.$store.state.currentProjectId = 'gitlab-ce';
+ vm.$store.state.currentMergeRequestId = 1;
+ vm.$store.state.projects['gitlab-ce'] = {
+ mergeRequests: {
+ 1: {
+ iid: 1,
+ title: 'Testing',
+ title_html: '<span class="title-html">Testing</span>',
+ description: 'Description',
+ description_html: '<p class="description-html">Description HTML</p>',
+ },
+ },
+ };
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.ide-sidebar-link.active')).not.toBe(null);
+ expect(
+ vm.$el.querySelector('.ide-sidebar-link.active').getAttribute('data-original-title'),
+ ).toBe('Merge Request');
+
+ done();
+ });
+ });
+ });
+
+ describe('click', () => {
+ beforeEach(() => {
+ spyOn(vm, 'setRightPane');
+ });
+
+ it('sets view to merge request', done => {
+ vm.$store.state.currentMergeRequestId = '123';
+
+ vm.$nextTick(() => {
+ vm.$el.querySelector('.ide-sidebar-link').click();
+
+ expect(vm.setRightPane).toHaveBeenCalledWith(rightSidebarViews.mergeRequestInfo);
+
+ done();
+ });
+ });
+ });
+
+ describe('live preview', () => {
+ it('renders live preview button', done => {
+ Vue.set(vm.$store.state.entries, 'package.json', { name: 'package.json' });
+ vm.$store.state.clientsidePreviewEnabled = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('button[aria-label="Live preview"]')).not.toBeNull();
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/preview/clientside_spec.js b/spec/javascripts/ide/components/preview/clientside_spec.js
new file mode 100644
index 00000000000..3ec65882418
--- /dev/null
+++ b/spec/javascripts/ide/components/preview/clientside_spec.js
@@ -0,0 +1,362 @@
+import Vue from 'vue';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { createStore } from '~/ide/stores';
+import Clientside from '~/ide/components/preview/clientside.vue';
+import timeoutPromise from 'spec/helpers/set_timeout_promise_helper';
+import { resetStore, file } from '../../helpers';
+
+describe('IDE clientside preview', () => {
+ let vm;
+ let Component;
+
+ beforeAll(() => {
+ Component = Vue.extend(Clientside);
+ });
+
+ beforeEach(done => {
+ const store = createStore();
+
+ Vue.set(store.state.entries, 'package.json', {
+ ...file('package.json'),
+ });
+ Vue.set(store.state, 'currentProjectId', 'gitlab-ce');
+ Vue.set(store.state.projects, 'gitlab-ce', {
+ visibility: 'public',
+ });
+
+ vm = createComponentWithStore(Component, store);
+
+ spyOn(vm, 'getFileData').and.returnValue(Promise.resolve());
+ spyOn(vm, 'getRawFileData').and.returnValue(Promise.resolve(''));
+ spyOn(vm, 'initManager');
+
+ vm.$mount();
+
+ timeoutPromise()
+ .then(() => vm.$nextTick())
+ .then(done)
+ .catch(done.fail);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ resetStore(vm.$store);
+ });
+
+ describe('without main entry', () => {
+ it('creates sandpack manager', () => {
+ expect(vm.initManager).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('with main entry', () => {
+ beforeEach(done => {
+ Vue.set(
+ vm.$store.state.entries['package.json'],
+ 'raw',
+ JSON.stringify({
+ main: 'index.js',
+ }),
+ );
+
+ vm
+ .$nextTick()
+ .then(() => vm.initPreview())
+ .then(vm.$nextTick)
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('creates sandpack manager', () => {
+ expect(vm.initManager).toHaveBeenCalledWith(
+ '#ide-preview',
+ {
+ files: jasmine.any(Object),
+ entry: '/index.js',
+ showOpenInCodeSandbox: true,
+ },
+ {
+ fileResolver: {
+ isFile: jasmine.any(Function),
+ readFile: jasmine.any(Function),
+ },
+ },
+ );
+ });
+ });
+
+ describe('computed', () => {
+ describe('normalizedEntries', () => {
+ beforeEach(done => {
+ vm.$store.state.entries['index.js'] = {
+ ...file('index.js'),
+ type: 'blob',
+ raw: 'test',
+ };
+ vm.$store.state.entries['index2.js'] = {
+ ...file('index2.js'),
+ type: 'blob',
+ content: 'content',
+ };
+ vm.$store.state.entries.tree = {
+ ...file('tree'),
+ type: 'tree',
+ };
+ vm.$store.state.entries.empty = {
+ ...file('empty'),
+ type: 'blob',
+ };
+
+ vm.$nextTick(done);
+ });
+
+ it('returns flattened list of blobs with content', () => {
+ expect(vm.normalizedEntries).toEqual({
+ '/index.js': {
+ code: 'test',
+ },
+ '/index2.js': {
+ code: 'content',
+ },
+ });
+ });
+ });
+
+ describe('mainEntry', () => {
+ it('returns false when package.json is empty', () => {
+ expect(vm.mainEntry).toBe(false);
+ });
+
+ it('returns main key from package.json', done => {
+ Vue.set(
+ vm.$store.state.entries['package.json'],
+ 'raw',
+ JSON.stringify({
+ main: 'index.js',
+ }),
+ );
+
+ vm.$nextTick(() => {
+ expect(vm.mainEntry).toBe('index.js');
+
+ done();
+ });
+ });
+ });
+
+ describe('showPreview', () => {
+ it('returns false if no mainEntry', () => {
+ expect(vm.showPreview).toBe(false);
+ });
+
+ it('returns false if loading', done => {
+ Vue.set(
+ vm.$store.state.entries['package.json'],
+ 'raw',
+ JSON.stringify({
+ main: 'index.js',
+ }),
+ );
+ vm.loading = true;
+
+ vm.$nextTick(() => {
+ expect(vm.showPreview).toBe(false);
+
+ done();
+ });
+ });
+
+ it('returns true if not loading and mainEntry exists', done => {
+ Vue.set(
+ vm.$store.state.entries['package.json'],
+ 'raw',
+ JSON.stringify({
+ main: 'index.js',
+ }),
+ );
+ vm.loading = false;
+
+ vm.$nextTick(() => {
+ expect(vm.showPreview).toBe(true);
+
+ done();
+ });
+ });
+ });
+
+ describe('showEmptyState', () => {
+ it('returns true if no mainEnry exists', () => {
+ expect(vm.showEmptyState).toBe(true);
+ });
+
+ it('returns false if loading', done => {
+ Vue.set(
+ vm.$store.state.entries['package.json'],
+ 'raw',
+ JSON.stringify({
+ main: 'index.js',
+ }),
+ );
+ vm.loading = true;
+
+ vm.$nextTick(() => {
+ expect(vm.showEmptyState).toBe(false);
+
+ done();
+ });
+ });
+
+ it('returns false if not loading and mainEntry exists', done => {
+ Vue.set(
+ vm.$store.state.entries['package.json'],
+ 'raw',
+ JSON.stringify({
+ main: 'index.js',
+ }),
+ );
+ vm.loading = false;
+
+ vm.$nextTick(() => {
+ expect(vm.showEmptyState).toBe(false);
+
+ done();
+ });
+ });
+ });
+
+ describe('showOpenInCodeSandbox', () => {
+ it('returns true when visiblity is public', () => {
+ expect(vm.showOpenInCodeSandbox).toBe(true);
+ });
+
+ it('returns false when visiblity is private', done => {
+ vm.$store.state.projects['gitlab-ce'].visibility = 'private';
+
+ vm.$nextTick(() => {
+ expect(vm.showOpenInCodeSandbox).toBe(false);
+
+ done();
+ });
+ });
+ });
+
+ describe('sandboxOpts', () => {
+ beforeEach(done => {
+ vm.$store.state.entries['index.js'] = {
+ ...file('index.js'),
+ type: 'blob',
+ raw: 'test',
+ };
+ Vue.set(
+ vm.$store.state.entries['package.json'],
+ 'raw',
+ JSON.stringify({
+ main: 'index.js',
+ }),
+ );
+
+ vm.$nextTick(done);
+ });
+
+ it('returns sandbox options', () => {
+ expect(vm.sandboxOpts).toEqual({
+ files: {
+ '/index.js': {
+ code: 'test',
+ },
+ '/package.json': {
+ code: '{"main":"index.js"}',
+ },
+ },
+ entry: '/index.js',
+ showOpenInCodeSandbox: true,
+ });
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('loadFileContent', () => {
+ it('calls getFileData', () => {
+ expect(vm.getFileData).toHaveBeenCalledWith({
+ path: 'package.json',
+ makeFileActive: false,
+ });
+ });
+
+ it('calls getRawFileData', () => {
+ expect(vm.getRawFileData).toHaveBeenCalledWith({ path: 'package.json' });
+ });
+ });
+
+ describe('update', () => {
+ beforeEach(() => {
+ jasmine.clock().install();
+ vm.manager.updatePreview = jasmine.createSpy('updatePreview');
+ vm.manager.listener = jasmine.createSpy('updatePreview');
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
+ it('calls initPreview if manager is empty', () => {
+ spyOn(vm, 'initPreview');
+ vm.manager = {};
+
+ vm.update();
+
+ jasmine.clock().tick(500);
+
+ expect(vm.initPreview).toHaveBeenCalled();
+ });
+
+ it('calls updatePreview', () => {
+ vm.update();
+
+ jasmine.clock().tick(500);
+
+ expect(vm.manager.updatePreview).toHaveBeenCalledWith(vm.sandboxOpts);
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders ide-preview element when showPreview is true', done => {
+ Vue.set(
+ vm.$store.state.entries['package.json'],
+ 'raw',
+ JSON.stringify({
+ main: 'index.js',
+ }),
+ );
+ vm.loading = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('#ide-preview')).not.toBe(null);
+ done();
+ });
+ });
+
+ it('renders empty state', done => {
+ vm.loading = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.textContent).toContain(
+ 'Preview your web application using Web IDE client-side evaluation.',
+ );
+
+ done();
+ });
+ });
+
+ it('renders loading icon', done => {
+ vm.loading = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.loading-container')).not.toBe(null);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/preview/navigator_spec.js b/spec/javascripts/ide/components/preview/navigator_spec.js
new file mode 100644
index 00000000000..576d2fae003
--- /dev/null
+++ b/spec/javascripts/ide/components/preview/navigator_spec.js
@@ -0,0 +1,185 @@
+import Vue from 'vue';
+import ClientsideNavigator from '~/ide/components/preview/navigator.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('IDE clientside preview navigator', () => {
+ let vm;
+ let Component;
+ let manager;
+
+ beforeAll(() => {
+ Component = Vue.extend(ClientsideNavigator);
+ });
+
+ beforeEach(() => {
+ manager = {
+ bundlerURL: gl.TEST_HOST,
+ iframe: { src: '' },
+ };
+
+ vm = mountComponent(Component, {
+ manager,
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders readonly URL bar', () => {
+ expect(vm.$el.querySelector('input[readonly]').value).toBe('/');
+ });
+
+ it('disables back button when navigationStack is empty', () => {
+ expect(vm.$el.querySelector('.ide-navigator-btn')).toHaveAttr('disabled');
+ expect(vm.$el.querySelector('.ide-navigator-btn').classList).toContain('disabled-content');
+ });
+
+ it('disables forward button when forwardNavigationStack is empty', () => {
+ vm.forwardNavigationStack = [];
+
+ expect(vm.$el.querySelectorAll('.ide-navigator-btn')[1]).toHaveAttr('disabled');
+ expect(vm.$el.querySelectorAll('.ide-navigator-btn')[1].classList).toContain(
+ 'disabled-content',
+ );
+ });
+
+ it('calls back method when clicking back button', done => {
+ vm.navigationStack.push('/test');
+ vm.navigationStack.push('/test2');
+ spyOn(vm, 'back');
+
+ vm.$nextTick(() => {
+ vm.$el.querySelector('.ide-navigator-btn').click();
+
+ expect(vm.back).toHaveBeenCalled();
+
+ done();
+ });
+ });
+
+ it('calls forward method when clicking forward button', done => {
+ vm.forwardNavigationStack.push('/test');
+ spyOn(vm, 'forward');
+
+ vm.$nextTick(() => {
+ vm.$el.querySelectorAll('.ide-navigator-btn')[1].click();
+
+ expect(vm.forward).toHaveBeenCalled();
+
+ done();
+ });
+ });
+
+ describe('onUrlChange', () => {
+ it('updates the path', () => {
+ vm.onUrlChange({
+ url: `${gl.TEST_HOST}/url`,
+ });
+
+ expect(vm.path).toBe('/url');
+ });
+
+ it('sets currentBrowsingIndex 0 if not already set', () => {
+ vm.onUrlChange({
+ url: `${gl.TEST_HOST}/url`,
+ });
+
+ expect(vm.currentBrowsingIndex).toBe(0);
+ });
+
+ it('increases currentBrowsingIndex if path doesnt match', () => {
+ vm.onUrlChange({
+ url: `${gl.TEST_HOST}/url`,
+ });
+
+ vm.onUrlChange({
+ url: `${gl.TEST_HOST}/url2`,
+ });
+
+ expect(vm.currentBrowsingIndex).toBe(1);
+ });
+
+ it('does not increase currentBrowsingIndex if path matches', () => {
+ vm.onUrlChange({
+ url: `${gl.TEST_HOST}/url`,
+ });
+
+ vm.onUrlChange({
+ url: `${gl.TEST_HOST}/url`,
+ });
+
+ expect(vm.currentBrowsingIndex).toBe(0);
+ });
+
+ it('pushes path into navigation stack', () => {
+ vm.onUrlChange({
+ url: `${gl.TEST_HOST}/url`,
+ });
+
+ expect(vm.navigationStack).toEqual(['/url']);
+ });
+ });
+
+ describe('back', () => {
+ beforeEach(() => {
+ vm.path = '/test2';
+ vm.currentBrowsingIndex = 1;
+ vm.navigationStack.push('/test');
+ vm.navigationStack.push('/test2');
+
+ spyOn(vm, 'visitPath');
+
+ vm.back();
+ });
+
+ it('visits the last entry in navigationStack', () => {
+ expect(vm.visitPath).toHaveBeenCalledWith('/test');
+ });
+
+ it('adds last entry to forwardNavigationStack', () => {
+ expect(vm.forwardNavigationStack).toEqual(['/test2']);
+ });
+
+ it('clears navigation stack if currentBrowsingIndex is 1', () => {
+ expect(vm.navigationStack).toEqual([]);
+ });
+
+ it('sets currentBrowsingIndex to null is currentBrowsingIndex is 1', () => {
+ expect(vm.currentBrowsingIndex).toBe(null);
+ });
+ });
+
+ describe('forward', () => {
+ it('calls visitPath with first entry in forwardNavigationStack', () => {
+ spyOn(vm, 'visitPath');
+
+ vm.forwardNavigationStack.push('/test');
+ vm.forwardNavigationStack.push('/test2');
+
+ vm.forward();
+
+ expect(vm.visitPath).toHaveBeenCalledWith('/test');
+ });
+ });
+
+ describe('refresh', () => {
+ it('calls refresh with current path', () => {
+ spyOn(vm, 'visitPath');
+
+ vm.path = '/test';
+
+ vm.refresh();
+
+ expect(vm.visitPath).toHaveBeenCalledWith('/test');
+ });
+ });
+
+ describe('visitPath', () => {
+ it('updates iframe src with passed in path', () => {
+ vm.visitPath('/testpath');
+
+ expect(manager.iframe.src).toBe(`${gl.TEST_HOST}/testpath`);
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index 2256deb7dac..0e2e246defd 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
+import '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
import repoEditor from '~/ide/components/repo_editor.vue';
@@ -25,6 +26,8 @@ describe('RepoEditor', () => {
vm.$store.state.openFiles.push(f);
Vue.set(vm.$store.state.entries, f.path, f);
+ spyOn(vm, 'getFileData').and.returnValue(Promise.resolve());
+
vm.$mount();
Vue.nextTick(() => setTimeout(done));
diff --git a/spec/javascripts/ide/components/repo_file_spec.js b/spec/javascripts/ide/components/repo_file_spec.js
index 156233653ab..f99d1f9890a 100644
--- a/spec/javascripts/ide/components/repo_file_spec.js
+++ b/spec/javascripts/ide/components/repo_file_spec.js
@@ -91,25 +91,6 @@ describe('RepoFile', () => {
done();
});
});
-
- it('disables action dropdown', done => {
- createComponent({
- file: {
- ...file('t4'),
- type: 'tree',
- branchId: 'master',
- projectId: 'project',
- },
- level: 0,
- disableActionDropdown: true,
- });
-
- setTimeout(() => {
- expect(vm.$el.querySelector('.ide-new-btn')).toBeNull();
-
- done();
- });
- });
});
describe('locked file', () => {
diff --git a/spec/javascripts/ide/components/repo_tab_spec.js b/spec/javascripts/ide/components/repo_tab_spec.js
index fc0695a4263..278a0753322 100644
--- a/spec/javascripts/ide/components/repo_tab_spec.js
+++ b/spec/javascripts/ide/components/repo_tab_spec.js
@@ -93,13 +93,13 @@ describe('RepoTab', () => {
Vue.nextTick()
.then(() => {
- expect(vm.$el.querySelector('.multi-file-modified')).toBeNull();
+ expect(vm.$el.querySelector('.ide-file-modified')).toBeNull();
vm.$el.dispatchEvent(new Event('mouseout'));
})
.then(Vue.nextTick)
.then(() => {
- expect(vm.$el.querySelector('.multi-file-modified')).not.toBeNull();
+ expect(vm.$el.querySelector('.ide-file-modified')).not.toBeNull();
done();
})
diff --git a/spec/javascripts/ide/components/shared/tokened_input_spec.js b/spec/javascripts/ide/components/shared/tokened_input_spec.js
new file mode 100644
index 00000000000..09940fe8c6a
--- /dev/null
+++ b/spec/javascripts/ide/components/shared/tokened_input_spec.js
@@ -0,0 +1,132 @@
+import Vue from 'vue';
+import TokenedInput from '~/ide/components/shared/tokened_input.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const TEST_PLACEHOLDER = 'Searching in test';
+const TEST_TOKENS = [
+ { label: 'lorem', id: 1 },
+ { label: 'ipsum', id: 2 },
+ { label: 'dolar', id: 3 },
+];
+const TEST_VALUE = 'lorem';
+
+function getTokenElements(vm) {
+ return Array.from(vm.$el.querySelectorAll('.filtered-search-token button'));
+}
+
+function createBackspaceEvent() {
+ const e = new Event('keyup');
+ e.keyCode = 8;
+ e.which = e.keyCode;
+ e.altKey = false;
+ e.ctrlKey = true;
+ e.shiftKey = false;
+ e.metaKey = false;
+ return e;
+}
+
+describe('IDE shared/TokenedInput', () => {
+ const Component = Vue.extend(TokenedInput);
+ let vm;
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ tokens: TEST_TOKENS,
+ placeholder: TEST_PLACEHOLDER,
+ value: TEST_VALUE,
+ });
+
+ spyOn(vm, '$emit');
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders tokens', () => {
+ const renderedTokens = getTokenElements(vm)
+ .map(x => x.textContent.trim());
+
+ expect(renderedTokens).toEqual(TEST_TOKENS.map(x => x.label));
+ });
+
+ it('renders input', () => {
+ expect(vm.$refs.input).toBeTruthy();
+ expect(vm.$refs.input).toHaveValue(TEST_VALUE);
+ });
+
+ it('renders placeholder, when tokens are empty', done => {
+ vm.tokens = [];
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$refs.input).toHaveAttr('placeholder', TEST_PLACEHOLDER);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('triggers "removeToken" on token click', () => {
+ getTokenElements(vm)[0].click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('removeToken', TEST_TOKENS[0]);
+ });
+
+ it('when input triggers backspace event, it calls "onBackspace"', () => {
+ spyOn(vm, 'onBackspace');
+
+ vm.$refs.input.dispatchEvent(createBackspaceEvent());
+ vm.$refs.input.dispatchEvent(createBackspaceEvent());
+
+ expect(vm.onBackspace).toHaveBeenCalledTimes(2);
+ });
+
+ it('triggers "removeToken" on backspaces when value is empty', () => {
+ vm.value = '';
+
+ vm.onBackspace();
+ expect(vm.$emit).not.toHaveBeenCalled();
+ expect(vm.backspaceCount).toEqual(1);
+
+ vm.onBackspace();
+ expect(vm.$emit).toHaveBeenCalledWith('removeToken', TEST_TOKENS[TEST_TOKENS.length - 1]);
+ expect(vm.backspaceCount).toEqual(0);
+ });
+
+ it('does not trigger "removeToken" on backspaces when value is not empty', () => {
+ vm.onBackspace();
+ vm.onBackspace();
+
+ expect(vm.backspaceCount).toEqual(0);
+ expect(vm.$emit).not.toHaveBeenCalled();
+ });
+
+ it('does not trigger "removeToken" on backspaces when tokens are empty', () => {
+ vm.tokens = [];
+
+ vm.onBackspace();
+ vm.onBackspace();
+
+ expect(vm.backspaceCount).toEqual(0);
+ expect(vm.$emit).not.toHaveBeenCalled();
+ });
+
+ it('triggers "focus" on input focus', () => {
+ vm.$refs.input.dispatchEvent(new Event('focus'));
+
+ expect(vm.$emit).toHaveBeenCalledWith('focus');
+ });
+
+ it('triggers "blur" on input blur', () => {
+ vm.$refs.input.dispatchEvent(new Event('blur'));
+
+ expect(vm.$emit).toHaveBeenCalledWith('blur');
+ });
+
+ it('triggers "input" with value on input change', () => {
+ vm.$refs.input.value = 'something-else';
+ vm.$refs.input.dispatchEvent(new Event('input'));
+
+ expect(vm.$emit).toHaveBeenCalledWith('input', 'something-else');
+ });
+});
diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js
index 569fa5c7aae..c11c482fef8 100644
--- a/spec/javascripts/ide/helpers.js
+++ b/spec/javascripts/ide/helpers.js
@@ -4,6 +4,7 @@ import state from '~/ide/stores/state';
import commitState from '~/ide/stores/modules/commit/state';
import mergeRequestsState from '~/ide/stores/modules/merge_requests/state';
import pipelinesState from '~/ide/stores/modules/pipelines/state';
+import branchesState from '~/ide/stores/modules/branches/state';
export const resetStore = store => {
const newState = {
@@ -11,6 +12,7 @@ export const resetStore = store => {
commit: commitState(),
mergeRequests: mergeRequestsState(),
pipelines: pipelinesState(),
+ branches: branchesState(),
};
store.replaceState(newState);
};
diff --git a/spec/javascripts/ide/ide_router_spec.js b/spec/javascripts/ide/ide_router_spec.js
new file mode 100644
index 00000000000..52ea0882bf4
--- /dev/null
+++ b/spec/javascripts/ide/ide_router_spec.js
@@ -0,0 +1,44 @@
+import router from '~/ide/ide_router';
+import store from '~/ide/stores';
+
+describe('IDE router', () => {
+ const PROJECT_NAMESPACE = 'my-group/sub-group';
+ const PROJECT_NAME = 'my-project';
+
+ afterEach(() => {
+ router.push('/');
+ });
+
+ afterAll(() => {
+ // VueRouter leaves this window.history at the "base" url. We need to clean this up.
+ window.history.replaceState({}, '', '/');
+ });
+
+ [
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/master/-/src/blob/`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/master/-/src/blob`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob/-/src/blob`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/master/-/src/tree/`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/weird:branch/name-123/-/src/tree/`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/master/-/src/blob`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/master/-/src/edit`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/master/-/src/merge_requests/2`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/blob/-/src/blob`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit/blob/-/src/blob`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/merge_requests/2`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit`,
+ `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}`,
+ ].forEach(route => {
+ it(`finds project path when route is "${route}"`, () => {
+ spyOn(store, 'dispatch').and.returnValue(new Promise(() => {}));
+
+ router.push(route);
+
+ expect(store.dispatch).toHaveBeenCalledWith('getProjectData', {
+ namespace: PROJECT_NAMESPACE,
+ projectId: PROJECT_NAME,
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js
index 80bf664d491..4fe826943b2 100644
--- a/spec/javascripts/ide/mock_data.js
+++ b/spec/javascripts/ide/mock_data.js
@@ -9,6 +9,9 @@ export const projectData = {
master: {
treeId: 'abcproject/master',
can_push: true,
+ commit: {
+ id: '123',
+ },
},
},
mergeRequests: {},
@@ -71,7 +74,7 @@ export const jobs = [
name: 'test',
path: 'testing',
status: {
- icon: 'status_passed',
+ icon: 'status_success',
text: 'passed',
},
stage: 'test',
@@ -83,7 +86,7 @@ export const jobs = [
name: 'test 2',
path: 'testing2',
status: {
- icon: 'status_passed',
+ icon: 'status_success',
text: 'passed',
},
stage: 'test',
@@ -95,7 +98,7 @@ export const jobs = [
name: 'test 3',
path: 'testing3',
status: {
- icon: 'status_passed',
+ icon: 'status_success',
text: 'passed',
},
stage: 'test',
@@ -143,7 +146,7 @@ export const fullPipelinesResponse = {
},
details: {
status: {
- icon: 'status_passed',
+ icon: 'status_success',
text: 'passed',
},
stages: [...stages],
@@ -162,3 +165,33 @@ export const mergeRequests = [
web_url: `${gl.TEST_HOST}/namespace/project-path/merge_requests/1`,
},
];
+
+export const branches = [
+ {
+ id: 1,
+ name: 'master',
+ commit: {
+ message: 'Update master branch',
+ committed_date: '2018-08-01T00:20:05Z',
+ },
+ can_push: true,
+ },
+ {
+ id: 2,
+ name: 'feature/lorem-ipsum',
+ commit: {
+ message: 'Update some stuff',
+ committed_date: '2018-08-02T00:00:05Z',
+ },
+ can_push: true,
+ },
+ {
+ id: 3,
+ name: 'feature/dolar-amit',
+ commit: {
+ message: 'Update some more stuff',
+ committed_date: '2018-06-30T00:20:05Z',
+ },
+ can_push: true,
+ },
+];
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index 58d3ffc6d94..72eb20bdc87 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -366,6 +366,23 @@ describe('IDE store file actions', () => {
});
});
+ describe('return JSON', () => {
+ beforeEach(() => {
+ mock.onGet(/(.*)/).replyOnce(200, JSON.stringify({ test: '123' }));
+ });
+
+ it('does not parse returned JSON', done => {
+ store
+ .dispatch('getRawFileData', { path: tmpFile.path })
+ .then(() => {
+ expect(tmpFile.raw).toEqual('{"test":"123"}');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
describe('error', () => {
beforeEach(() => {
mock.onGet(/(.*)/).networkError();
@@ -601,10 +618,7 @@ describe('IDE store file actions', () => {
actions.unstageChange,
'path',
store.state,
- [
- { type: types.UNSTAGE_CHANGE, payload: 'path' },
- { type: types.SET_LAST_COMMIT_MSG, payload: '' },
- ],
+ [{ type: types.UNSTAGE_CHANGE, payload: 'path' }],
[],
done,
);
diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js
index c99ccc70c6a..90c28c769f7 100644
--- a/spec/javascripts/ide/stores/actions/merge_request_spec.js
+++ b/spec/javascripts/ide/stores/actions/merge_request_spec.js
@@ -39,7 +39,9 @@ describe('IDE store merge request actions', () => {
store
.dispatch('getMergeRequestData', { projectId: 'abcproject', mergeRequestId: 1 })
.then(() => {
- expect(service.getProjectMergeRequestData).toHaveBeenCalledWith('abcproject', 1);
+ expect(service.getProjectMergeRequestData).toHaveBeenCalledWith('abcproject', 1, {
+ render_html: true,
+ });
done();
})
diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js
index ca79edafb7e..6a85968e199 100644
--- a/spec/javascripts/ide/stores/actions/project_spec.js
+++ b/spec/javascripts/ide/stores/actions/project_spec.js
@@ -73,6 +73,7 @@ describe('IDE store project actions', () => {
branchId: store.state.currentBranchId,
},
store.state,
+ // mutations
[
{
type: 'SET_BRANCH_COMMIT',
@@ -82,17 +83,9 @@ describe('IDE store project actions', () => {
commit: { id: '123' },
},
},
- ], // mutations
- [
- {
- type: 'getLastCommitPipeline',
- payload: {
- projectId: 'abc/def',
- projectIdNumber: store.state.projects['abc/def'].id,
- branchId: 'master',
- },
- },
- ], // action
+ ],
+ // action
+ [],
done,
);
});
diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js
index 6860e6cdb91..9f098eded08 100644
--- a/spec/javascripts/ide/stores/actions/tree_spec.js
+++ b/spec/javascripts/ide/stores/actions/tree_spec.js
@@ -192,11 +192,8 @@ describe('Multi-file store tree actions', () => {
showTreeEntry,
'grandparent/parent/child.txt',
store.state,
- [
- { type: types.SET_TREE_OPEN, payload: 'grandparent/parent' },
- { type: types.SET_TREE_OPEN, payload: 'grandparent' },
- ],
- [{ type: 'showTreeEntry' }],
+ [{ type: types.SET_TREE_OPEN, payload: 'grandparent/parent' }],
+ [{ type: 'showTreeEntry', payload: 'grandparent/parent' }],
done,
);
});
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index 8b665a6d79e..d84f1717a61 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -7,6 +7,8 @@ import actions, {
updateActivityBarView,
updateTempFlagForEntry,
setErrorMessage,
+ deleteEntry,
+ renameEntry,
} from '~/ide/stores/actions';
import store from '~/ide/stores';
import * as types from '~/ide/stores/mutation_types';
@@ -457,4 +459,73 @@ describe('Multi-file store actions', () => {
);
});
});
+
+ describe('deleteEntry', () => {
+ it('commits entry deletion', done => {
+ store.state.entries.path = 'testing';
+
+ testAction(
+ deleteEntry,
+ 'path',
+ store.state,
+ [{ type: types.DELETE_ENTRY, payload: 'path' }],
+ [{ type: 'burstUnusedSeal' }],
+ done,
+ );
+ });
+ });
+
+ describe('renameEntry', () => {
+ it('renames entry', done => {
+ store.state.entries.test = {
+ tree: [],
+ };
+
+ testAction(
+ renameEntry,
+ { path: 'test', name: 'new-name' },
+ store.state,
+ [
+ {
+ type: types.RENAME_ENTRY,
+ payload: { path: 'test', name: 'new-name', entryPath: null },
+ },
+ ],
+ [{ type: 'deleteEntry', payload: 'test' }],
+ done,
+ );
+ });
+
+ it('renames all entries in tree', done => {
+ store.state.entries.test = {
+ type: 'tree',
+ tree: [
+ {
+ path: 'tree-1',
+ },
+ {
+ path: 'tree-2',
+ },
+ ],
+ };
+
+ testAction(
+ renameEntry,
+ { path: 'test', name: 'new-name' },
+ store.state,
+ [
+ {
+ type: types.RENAME_ENTRY,
+ payload: { path: 'test', name: 'new-name', entryPath: null },
+ },
+ ],
+ [
+ { type: 'renameEntry', payload: { path: 'test', name: 'new-name', entryPath: 'tree-1' } },
+ { type: 'renameEntry', payload: { path: 'test', name: 'new-name', entryPath: 'tree-2' } },
+ { type: 'deleteEntry', payload: 'test' },
+ ],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js
index 70883e16b0d..9c135661997 100644
--- a/spec/javascripts/ide/stores/getters_spec.js
+++ b/spec/javascripts/ide/stores/getters_spec.js
@@ -179,4 +179,14 @@ describe('IDE store getters', () => {
});
});
});
+
+ describe('packageJson', () => {
+ it('returns package.json entry', () => {
+ localState.entries['package.json'] = { name: 'package.json' };
+
+ expect(getters.packageJson(localState)).toEqual({
+ name: 'package.json',
+ });
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/modules/branches/actions_spec.js b/spec/javascripts/ide/stores/modules/branches/actions_spec.js
new file mode 100644
index 00000000000..a0fce578958
--- /dev/null
+++ b/spec/javascripts/ide/stores/modules/branches/actions_spec.js
@@ -0,0 +1,193 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import state from '~/ide/stores/modules/branches/state';
+import * as types from '~/ide/stores/modules/branches/mutation_types';
+import testAction from 'spec/helpers/vuex_action_helper';
+import {
+ requestBranches,
+ receiveBranchesError,
+ receiveBranchesSuccess,
+ fetchBranches,
+ resetBranches,
+ openBranch,
+} from '~/ide/stores/modules/branches/actions';
+import { branches, projectData } from '../../../mock_data';
+
+describe('IDE branches actions', () => {
+ const TEST_SEARCH = 'foosearch';
+ let mockedContext;
+ let mockedState;
+ let mock;
+
+ beforeEach(() => {
+ mockedContext = {
+ dispatch() {},
+ rootState: {
+ currentProjectId: projectData.name_with_namespace,
+ },
+ rootGetters: {
+ currentProject: projectData,
+ },
+ state: state(),
+ };
+
+ // testAction looks for rootGetters in state,
+ // so they need to be concatenated here.
+ mockedState = {
+ ...mockedContext.state,
+ ...mockedContext.rootGetters,
+ ...mockedContext.rootState,
+ };
+
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('requestBranches', () => {
+ it('should commit request', done => {
+ testAction(
+ requestBranches,
+ null,
+ mockedContext.state,
+ [{ type: types.REQUEST_BRANCHES }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveBranchesError', () => {
+ it('should should commit error', done => {
+
+ testAction(
+ receiveBranchesError,
+ { search: TEST_SEARCH },
+ mockedContext.state,
+ [{ type: types.RECEIVE_BRANCHES_ERROR }],
+ [
+ {
+ type: 'setErrorMessage',
+ payload: {
+ text: 'Error loading branches.',
+ action: jasmine.any(Function),
+ actionText: 'Please try again',
+ actionPayload: { search: TEST_SEARCH },
+ },
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('receiveBranchesSuccess', () => {
+ it('should commit received data', done => {
+ testAction(
+ receiveBranchesSuccess,
+ branches,
+ mockedContext.state,
+ [{ type: types.RECEIVE_BRANCHES_SUCCESS, payload: branches }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchBranches', () => {
+ beforeEach(() => {
+ gon.api_version = 'v4';
+ });
+
+ describe('success', () => {
+ beforeEach(() => {
+ mock.onGet(/\/api\/v4\/projects\/\d+\/repository\/branches(.*)$/).replyOnce(200, branches);
+ });
+
+ it('calls API with params', () => {
+ const apiSpy = spyOn(axios, 'get').and.callThrough();
+
+ fetchBranches(mockedContext, { search: TEST_SEARCH });
+
+ expect(apiSpy).toHaveBeenCalledWith(jasmine.anything(), {
+ params: jasmine.objectContaining({
+ search: TEST_SEARCH,
+ sort: 'updated_desc',
+ }),
+ });
+ });
+
+ it('dispatches success with received data', done => {
+ testAction(
+ fetchBranches,
+ { search: TEST_SEARCH },
+ mockedState,
+ [],
+ [
+ { type: 'requestBranches' },
+ { type: 'resetBranches' },
+ {
+ type: 'receiveBranchesSuccess',
+ payload: branches,
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(/\/api\/v4\/projects\/\d+\/repository\/branches(.*)$/).replyOnce(500);
+ });
+
+ it('dispatches error', done => {
+ testAction(
+ fetchBranches,
+ { search: TEST_SEARCH },
+ mockedState,
+ [],
+ [
+ { type: 'requestBranches' },
+ { type: 'resetBranches' },
+ {
+ type: 'receiveBranchesError',
+ payload: { search: TEST_SEARCH },
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('resetBranches', () => {
+ it('commits reset', done => {
+ testAction(
+ resetBranches,
+ null,
+ mockedContext.state,
+ [{ type: types.RESET_BRANCHES }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('openBranch', () => {
+ it('dispatches goToRoute action with path', done => {
+ const branchId = branches[0].name;
+ const expectedPath = `/project/${projectData.name_with_namespace}/edit/${branchId}`;
+ testAction(
+ openBranch,
+ branchId,
+ mockedState,
+ [],
+ [{ type: 'goToRoute', payload: expectedPath }],
+ done,
+ );
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/modules/branches/mutations_spec.js b/spec/javascripts/ide/stores/modules/branches/mutations_spec.js
new file mode 100644
index 00000000000..be91440f119
--- /dev/null
+++ b/spec/javascripts/ide/stores/modules/branches/mutations_spec.js
@@ -0,0 +1,51 @@
+import state from '~/ide/stores/modules/branches/state';
+import mutations from '~/ide/stores/modules/branches/mutations';
+import * as types from '~/ide/stores/modules/branches/mutation_types';
+import { branches } from '../../../mock_data';
+
+describe('IDE branches mutations', () => {
+ let mockedState;
+
+ beforeEach(() => {
+ mockedState = state();
+ });
+
+ describe(types.REQUEST_BRANCHES, () => {
+ it('sets loading to true', () => {
+ mutations[types.REQUEST_BRANCHES](mockedState);
+
+ expect(mockedState.isLoading).toBe(true);
+ });
+ });
+
+ describe(types.RECEIVE_BRANCHES_ERROR, () => {
+ it('sets loading to false', () => {
+ mutations[types.RECEIVE_BRANCHES_ERROR](mockedState);
+
+ expect(mockedState.isLoading).toBe(false);
+ });
+ });
+
+ describe(types.RECEIVE_BRANCHES_SUCCESS, () => {
+ it('sets branches', () => {
+ const expectedBranches = branches.map(branch => ({
+ name: branch.name,
+ committedDate: branch.commit.committed_date,
+ }));
+
+ mutations[types.RECEIVE_BRANCHES_SUCCESS](mockedState, branches);
+
+ expect(mockedState.branches).toEqual(expectedBranches);
+ });
+ });
+
+ describe(types.RESET_BRANCHES, () => {
+ it('clears branches array', () => {
+ mockedState.branches = ['test'];
+
+ mutations[types.RESET_BRANCHES](mockedState);
+
+ expect(mockedState.branches).toEqual([]);
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index 133ad627f34..24a7d76f30b 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -294,9 +294,10 @@ describe('IDE commit module actions', () => {
{
action: 'update',
file_path: jasmine.anything(),
- content: jasmine.anything(),
+ content: undefined,
encoding: jasmine.anything(),
last_commit_id: undefined,
+ previous_path: undefined,
},
],
start_branch: 'master',
@@ -320,9 +321,10 @@ describe('IDE commit module actions', () => {
{
action: 'update',
file_path: jasmine.anything(),
- content: jasmine.anything(),
+ content: undefined,
encoding: jasmine.anything(),
last_commit_id: '123456789',
+ previous_path: undefined,
},
],
start_branch: undefined,
diff --git a/spec/javascripts/ide/stores/modules/commit/getters_spec.js b/spec/javascripts/ide/stores/modules/commit/getters_spec.js
index 44c941d6dbb..3f4bf407a1f 100644
--- a/spec/javascripts/ide/stores/modules/commit/getters_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/getters_spec.js
@@ -123,6 +123,22 @@ describe('IDE commit module getters', () => {
'Update test-file, index.js files',
);
});
+
+ it('returns commitMessage with deleted files', () => {
+ rootState[key].push(
+ {
+ path: 'test-file',
+ deleted: true,
+ },
+ {
+ path: 'index.js',
+ },
+ );
+
+ expect(getters.preBuiltCommitMessage(state, null, rootState)).toBe(
+ 'Update index.js\nDeleted test-file',
+ );
+ });
});
});
});
diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
index fa4c18931e5..62699143a91 100644
--- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
@@ -2,15 +2,13 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import state from '~/ide/stores/modules/merge_requests/state';
import * as types from '~/ide/stores/modules/merge_requests/mutation_types';
-import actions, {
+import {
requestMergeRequests,
receiveMergeRequestsError,
receiveMergeRequestsSuccess,
fetchMergeRequests,
resetMergeRequests,
- openMergeRequest,
} from '~/ide/stores/modules/merge_requests/actions';
-import router from '~/ide/ide_router';
import { mergeRequests } from '../../../mock_data';
import testAction from '../../../../helpers/vuex_action_helper';
@@ -28,12 +26,12 @@ describe('IDE merge requests actions', () => {
});
describe('requestMergeRequests', () => {
- it('should should commit request', done => {
+ it('should commit request', done => {
testAction(
requestMergeRequests,
- 'created',
+ null,
mockedState,
- [{ type: types.REQUEST_MERGE_REQUESTS, payload: 'created' }],
+ [{ type: types.REQUEST_MERGE_REQUESTS }],
[],
done,
);
@@ -41,40 +39,38 @@ describe('IDE merge requests actions', () => {
});
describe('receiveMergeRequestsError', () => {
- let flashSpy;
-
- beforeEach(() => {
- flashSpy = spyOnDependency(actions, 'flash');
- });
-
it('should should commit error', done => {
testAction(
receiveMergeRequestsError,
- 'created',
+ { type: 'created', search: '' },
mockedState,
- [{ type: types.RECEIVE_MERGE_REQUESTS_ERROR, payload: 'created' }],
- [],
+ [{ type: types.RECEIVE_MERGE_REQUESTS_ERROR }],
+ [
+ {
+ type: 'setErrorMessage',
+ payload: {
+ text: 'Error loading merge requests.',
+ action: jasmine.any(Function),
+ actionText: 'Please try again',
+ actionPayload: { type: 'created', search: '' },
+ },
+ },
+ ],
done,
);
});
-
- it('creates flash message', () => {
- receiveMergeRequestsError({ commit() {} }, 'created');
-
- expect(flashSpy).toHaveBeenCalled();
- });
});
describe('receiveMergeRequestsSuccess', () => {
it('should commit received data', done => {
testAction(
receiveMergeRequestsSuccess,
- { type: 'created', data: 'data' },
+ mergeRequests,
mockedState,
[
{
type: types.RECEIVE_MERGE_REQUESTS_SUCCESS,
- payload: { type: 'created', data: 'data' },
+ payload: mergeRequests,
},
],
[],
@@ -124,21 +120,6 @@ describe('IDE merge requests actions', () => {
});
});
- it('dispatches request', done => {
- testAction(
- fetchMergeRequests,
- { type: 'created' },
- mockedState,
- [],
- [
- { type: 'requestMergeRequests' },
- { type: 'resetMergeRequests' },
- { type: 'receiveMergeRequestsSuccess' },
- ],
- done,
- );
- });
-
it('dispatches success with received data', done => {
testAction(
fetchMergeRequests,
@@ -150,7 +131,7 @@ describe('IDE merge requests actions', () => {
{ type: 'resetMergeRequests' },
{
type: 'receiveMergeRequestsSuccess',
- payload: { type: 'created', data: mergeRequests },
+ payload: mergeRequests,
},
],
done,
@@ -166,13 +147,13 @@ describe('IDE merge requests actions', () => {
it('dispatches error', done => {
testAction(
fetchMergeRequests,
- { type: 'created' },
+ { type: 'created', search: '' },
mockedState,
[],
[
{ type: 'requestMergeRequests' },
{ type: 'resetMergeRequests' },
- { type: 'receiveMergeRequestsError' },
+ { type: 'receiveMergeRequestsError', payload: { type: 'created', search: '' } },
],
done,
);
@@ -184,59 +165,12 @@ describe('IDE merge requests actions', () => {
it('commits reset', done => {
testAction(
resetMergeRequests,
- 'created',
+ null,
mockedState,
- [{ type: types.RESET_MERGE_REQUESTS, payload: 'created' }],
+ [{ type: types.RESET_MERGE_REQUESTS }],
[],
done,
);
});
});
-
- describe('openMergeRequest', () => {
- beforeEach(() => {
- spyOn(router, 'push');
- });
-
- it('commits reset mutations and actions', done => {
- const commit = jasmine.createSpy();
- const dispatch = jasmine.createSpy().and.returnValue(Promise.resolve());
- openMergeRequest({ commit, dispatch }, { projectPath: 'gitlab-org/gitlab-ce', id: '1' });
-
- setTimeout(() => {
- expect(commit.calls.argsFor(0)).toEqual(['CLEAR_PROJECTS', null, { root: true }]);
- expect(commit.calls.argsFor(1)).toEqual(['SET_CURRENT_MERGE_REQUEST', '1', { root: true }]);
- expect(commit.calls.argsFor(2)).toEqual(['RESET_OPEN_FILES', null, { root: true }]);
-
- expect(dispatch.calls.argsFor(0)).toEqual(['setCurrentBranchId', '', { root: true }]);
- expect(dispatch.calls.argsFor(1)).toEqual([
- 'pipelines/stopPipelinePolling',
- null,
- { root: true },
- ]);
- expect(dispatch.calls.argsFor(2)).toEqual(['setRightPane', null, { root: true }]);
- expect(dispatch.calls.argsFor(3)).toEqual([
- 'pipelines/resetLatestPipeline',
- null,
- { root: true },
- ]);
- expect(dispatch.calls.argsFor(4)).toEqual([
- 'pipelines/clearEtagPoll',
- null,
- { root: true },
- ]);
-
- done();
- });
- });
-
- it('pushes new route', () => {
- openMergeRequest(
- { commit() {}, dispatch: () => Promise.resolve() },
- { projectPath: 'gitlab-org/gitlab-ce', id: '1' },
- );
-
- expect(router.push).toHaveBeenCalledWith('/project/gitlab-org/gitlab-ce/merge_requests/1');
- });
- });
});
diff --git a/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js
index ea03131d90d..664d3914564 100644
--- a/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js
+++ b/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js
@@ -12,29 +12,26 @@ describe('IDE merge requests mutations', () => {
describe(types.REQUEST_MERGE_REQUESTS, () => {
it('sets loading to true', () => {
- mutations[types.REQUEST_MERGE_REQUESTS](mockedState, 'created');
+ mutations[types.REQUEST_MERGE_REQUESTS](mockedState);
- expect(mockedState.created.isLoading).toBe(true);
+ expect(mockedState.isLoading).toBe(true);
});
});
describe(types.RECEIVE_MERGE_REQUESTS_ERROR, () => {
it('sets loading to false', () => {
- mutations[types.RECEIVE_MERGE_REQUESTS_ERROR](mockedState, 'created');
+ mutations[types.RECEIVE_MERGE_REQUESTS_ERROR](mockedState);
- expect(mockedState.created.isLoading).toBe(false);
+ expect(mockedState.isLoading).toBe(false);
});
});
describe(types.RECEIVE_MERGE_REQUESTS_SUCCESS, () => {
it('sets merge requests', () => {
gon.gitlab_url = gl.TEST_HOST;
- mutations[types.RECEIVE_MERGE_REQUESTS_SUCCESS](mockedState, {
- type: 'created',
- data: mergeRequests,
- });
+ mutations[types.RECEIVE_MERGE_REQUESTS_SUCCESS](mockedState, mergeRequests);
- expect(mockedState.created.mergeRequests).toEqual([
+ expect(mockedState.mergeRequests).toEqual([
{
id: 1,
iid: 1,
@@ -50,9 +47,9 @@ describe('IDE merge requests mutations', () => {
it('clears merge request array', () => {
mockedState.mergeRequests = ['test'];
- mutations[types.RESET_MERGE_REQUESTS](mockedState, 'created');
+ mutations[types.RESET_MERGE_REQUESTS](mockedState);
- expect(mockedState.created.mergeRequests).toEqual([]);
+ expect(mockedState.mergeRequests).toEqual([]);
});
});
});
diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
index f47e69d6e5b..91edb388791 100644
--- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
@@ -1,7 +1,7 @@
import Visibility from 'visibilityjs';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import actions, {
+import {
requestLatestPipeline,
receiveLatestPipelineError,
receiveLatestPipelineSuccess,
@@ -59,7 +59,7 @@ describe('IDE pipelines actions', () => {
it('commits error', done => {
testAction(
receiveLatestPipelineError,
- null,
+ { status: 404 },
mockedState,
[{ type: types.RECEIVE_LASTEST_PIPELINE_ERROR }],
[{ type: 'stopPipelinePolling' }],
@@ -67,12 +67,26 @@ describe('IDE pipelines actions', () => {
);
});
- it('creates flash message', () => {
- const flashSpy = spyOnDependency(actions, 'flash');
-
- receiveLatestPipelineError({ commit() {}, dispatch() {} });
-
- expect(flashSpy).toHaveBeenCalled();
+ it('dispatches setErrorMessage is not 404', done => {
+ testAction(
+ receiveLatestPipelineError,
+ { status: 500 },
+ mockedState,
+ [{ type: types.RECEIVE_LASTEST_PIPELINE_ERROR }],
+ [
+ {
+ type: 'setErrorMessage',
+ payload: {
+ text: 'An error occured whilst fetching the latest pipline.',
+ action: jasmine.any(Function),
+ actionText: 'Please try again',
+ actionPayload: null,
+ },
+ },
+ { type: 'stopPipelinePolling' },
+ ],
+ done,
+ );
});
});
@@ -181,7 +195,10 @@ describe('IDE pipelines actions', () => {
new Promise(resolve => requestAnimationFrame(resolve))
.then(() => {
- expect(dispatch.calls.argsFor(1)).toEqual(['receiveLatestPipelineError']);
+ expect(dispatch.calls.argsFor(1)).toEqual([
+ 'receiveLatestPipelineError',
+ jasmine.anything(),
+ ]);
})
.then(done)
.catch(done.fail);
@@ -199,21 +216,23 @@ describe('IDE pipelines actions', () => {
it('commits error', done => {
testAction(
receiveJobsError,
- 1,
+ { id: 1 },
mockedState,
[{ type: types.RECEIVE_JOBS_ERROR, payload: 1 }],
- [],
+ [
+ {
+ type: 'setErrorMessage',
+ payload: {
+ text: 'An error occured whilst loading the pipelines jobs.',
+ action: jasmine.anything(),
+ actionText: 'Please try again',
+ actionPayload: { id: 1 },
+ },
+ },
+ ],
done,
);
});
-
- it('creates flash message', () => {
- const flashSpy = spyOnDependency(actions, 'flash');
-
- receiveJobsError({ commit() {} }, 1);
-
- expect(flashSpy).toHaveBeenCalled();
- });
});
describe('receiveJobsSuccess', () => {
@@ -268,7 +287,7 @@ describe('IDE pipelines actions', () => {
[],
[
{ type: 'requestJobs', payload: stage.id },
- { type: 'receiveJobsError', payload: stage.id },
+ { type: 'receiveJobsError', payload: stage },
],
done,
);
@@ -296,7 +315,7 @@ describe('IDE pipelines actions', () => {
'job',
mockedState,
[{ type: types.SET_DETAIL_JOB, payload: 'job' }],
- [{ type: 'setRightPane' }],
+ [{ type: 'setRightPane', payload: 'jobs-detail' }],
done,
);
});
@@ -306,7 +325,7 @@ describe('IDE pipelines actions', () => {
setDetailJob,
null,
mockedState,
- [{ type: types.SET_DETAIL_JOB }],
+ [{ type: types.SET_DETAIL_JOB, payload: null }],
[{ type: 'setRightPane', payload: rightSidebarViews.pipelines }],
done,
);
@@ -317,7 +336,7 @@ describe('IDE pipelines actions', () => {
setDetailJob,
'job',
mockedState,
- [{ type: types.SET_DETAIL_JOB }],
+ [{ type: types.SET_DETAIL_JOB, payload: 'job' }],
[{ type: 'setRightPane', payload: rightSidebarViews.jobsDetail }],
done,
);
@@ -337,18 +356,20 @@ describe('IDE pipelines actions', () => {
null,
mockedState,
[{ type: types.RECEIVE_JOB_TRACE_ERROR }],
- [],
+ [
+ {
+ type: 'setErrorMessage',
+ payload: {
+ text: 'An error occured whilst fetching the job trace.',
+ action: jasmine.any(Function),
+ actionText: 'Please try again',
+ actionPayload: null,
+ },
+ },
+ ],
done,
);
});
-
- it('creates flash message', () => {
- const flashSpy = spyOnDependency(actions, 'flash');
-
- receiveJobTraceError({ commit() {} });
-
- expect(flashSpy).toHaveBeenCalled();
- });
});
describe('receiveJobTraceSuccess', () => {
diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js
index 52f83be8e8c..efd0d86552b 100644
--- a/spec/javascripts/ide/stores/mutations/file_spec.js
+++ b/spec/javascripts/ide/stores/mutations/file_spec.js
@@ -94,6 +94,35 @@ describe('IDE store file mutations', () => {
expect(localFile.raw).toBe('testing');
});
+
+ it('adds raw data to open pending file', () => {
+ localState.openFiles.push({
+ ...localFile,
+ pending: true,
+ });
+
+ mutations.SET_FILE_RAW_DATA(localState, {
+ file: localFile,
+ raw: 'testing',
+ });
+
+ expect(localState.openFiles[0].raw).toBe('testing');
+ });
+
+ it('does not add raw data to open pending tempFile file', () => {
+ localState.openFiles.push({
+ ...localFile,
+ pending: true,
+ tempFile: true,
+ });
+
+ mutations.SET_FILE_RAW_DATA(localState, {
+ file: localFile,
+ raw: 'testing',
+ });
+
+ expect(localState.openFiles[0].raw).not.toBe('testing');
+ });
});
describe('SET_FILE_BASE_RAW_DATA', () => {
@@ -205,6 +234,11 @@ describe('IDE store file mutations', () => {
beforeEach(() => {
localFile.content = 'test';
localFile.changed = true;
+ localState.currentProjectId = 'gitlab-ce';
+ localState.currentBranchId = 'master';
+ localState.trees['gitlab-ce/master'] = {
+ tree: [],
+ };
});
it('resets content and changed', () => {
@@ -213,6 +247,36 @@ describe('IDE store file mutations', () => {
expect(localFile.content).toBe('');
expect(localFile.changed).toBeFalsy();
});
+
+ it('adds to root tree if deleted', () => {
+ localFile.deleted = true;
+
+ mutations.DISCARD_FILE_CHANGES(localState, localFile.path);
+
+ expect(localState.trees['gitlab-ce/master'].tree).toEqual([
+ {
+ ...localFile,
+ deleted: false,
+ },
+ ]);
+ });
+
+ it('adds to parent tree if deleted', () => {
+ localFile.deleted = true;
+ localFile.parentPath = 'parentPath';
+ localState.entries.parentPath = {
+ tree: [],
+ };
+
+ mutations.DISCARD_FILE_CHANGES(localState, localFile.path);
+
+ expect(localState.entries.parentPath.tree).toEqual([
+ {
+ ...localFile,
+ deleted: false,
+ },
+ ]);
+ });
});
describe('ADD_FILE_TO_CHANGED', () => {
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index 98016f593aa..1e836dbc3f9 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -156,4 +156,161 @@ describe('Multi-file store mutations', () => {
expect(localState.errorMessage).toBe('error');
});
});
+
+ describe('DELETE_ENTRY', () => {
+ beforeEach(() => {
+ localState.currentProjectId = 'gitlab-ce';
+ localState.currentBranchId = 'master';
+ localState.trees['gitlab-ce/master'] = {
+ tree: [],
+ };
+ });
+
+ it('sets deleted flag', () => {
+ localState.entries.filePath = {
+ deleted: false,
+ };
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.entries.filePath.deleted).toBe(true);
+ });
+
+ it('removes from root tree', () => {
+ localState.entries.filePath = {
+ path: 'filePath',
+ deleted: false,
+ };
+ localState.trees['gitlab-ce/master'].tree.push(localState.entries.filePath);
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.trees['gitlab-ce/master'].tree).toEqual([]);
+ });
+
+ it('removes from parent tree', () => {
+ localState.entries.filePath = {
+ path: 'filePath',
+ deleted: false,
+ parentPath: 'parentPath',
+ };
+ localState.entries.parentPath = {
+ tree: [localState.entries.filePath],
+ };
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.entries.parentPath.tree).toEqual([]);
+ });
+
+ it('adds to changedFiles', () => {
+ localState.entries.filePath = {
+ deleted: false,
+ type: 'blob',
+ };
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.changedFiles).toEqual([localState.entries.filePath]);
+ });
+ });
+
+ describe('UPDATE_FILE_AFTER_COMMIT', () => {
+ it('updates URLs if prevPath is set', () => {
+ const f = {
+ ...file(),
+ path: 'test',
+ prevPath: 'testing-123',
+ rawPath: `${gl.TEST_HOST}/testing-123`,
+ permalink: `${gl.TEST_HOST}/testing-123`,
+ commitsPath: `${gl.TEST_HOST}/testing-123`,
+ blamePath: `${gl.TEST_HOST}/testing-123`,
+ };
+ localState.entries.test = f;
+ localState.changedFiles.push(f);
+
+ mutations.UPDATE_FILE_AFTER_COMMIT(localState, { file: f, lastCommit: { commit: {} } });
+
+ expect(f.rawPath).toBe(`${gl.TEST_HOST}/test`);
+ expect(f.permalink).toBe(`${gl.TEST_HOST}/test`);
+ expect(f.commitsPath).toBe(`${gl.TEST_HOST}/test`);
+ expect(f.blamePath).toBe(`${gl.TEST_HOST}/test`);
+ });
+ });
+
+ describe('OPEN_NEW_ENTRY_MODAL', () => {
+ it('sets entryModal', () => {
+ localState.entries.testPath = {
+ ...file(),
+ };
+
+ mutations.OPEN_NEW_ENTRY_MODAL(localState, { type: 'test', path: 'testPath' });
+
+ expect(localState.entryModal).toEqual({
+ type: 'test',
+ path: 'testPath',
+ entry: localState.entries.testPath,
+ });
+ });
+ });
+
+ describe('RENAME_ENTRY', () => {
+ beforeEach(() => {
+ localState.trees = {
+ 'gitlab-ce/master': { tree: [] },
+ };
+ localState.currentProjectId = 'gitlab-ce';
+ localState.currentBranchId = 'master';
+ localState.entries.oldPath = {
+ ...file(),
+ type: 'blob',
+ name: 'oldPath',
+ path: 'oldPath',
+ url: `${gl.TEST_HOST}/oldPath`,
+ };
+ });
+
+ it('creates new renamed entry', () => {
+ mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
+
+ expect(localState.entries.newPath).toEqual({
+ ...localState.entries.oldPath,
+ id: 'newPath',
+ name: 'newPath',
+ key: 'newPath-blob-name',
+ path: 'newPath',
+ tempFile: true,
+ prevPath: 'oldPath',
+ tree: [],
+ parentPath: '',
+ url: `${gl.TEST_HOST}/newPath`,
+ moved: jasmine.anything(),
+ movedPath: jasmine.anything(),
+ });
+ });
+
+ it('adds new entry to changedFiles', () => {
+ mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
+
+ expect(localState.changedFiles.length).toBe(1);
+ expect(localState.changedFiles[0].path).toBe('newPath');
+ });
+
+ it('sets oldEntry as moved', () => {
+ mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
+
+ expect(localState.entries.oldPath.moved).toBe(true);
+ });
+
+ it('adds to parents tree', () => {
+ localState.entries.oldPath.parentPath = 'parentPath';
+ localState.entries.parentPath = {
+ ...file(),
+ };
+
+ mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
+
+ expect(localState.entries.parentPath.tree.length).toBe(1);
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index 6c5980cfae4..9f18034f8a3 100644
--- a/spec/javascripts/ide/stores/utils_spec.js
+++ b/spec/javascripts/ide/stores/utils_spec.js
@@ -86,6 +86,11 @@ describe('Multi-file store utils', () => {
base64: true,
lastCommitSha: '123456789',
},
+ {
+ ...file('deletedFile'),
+ path: 'deletedFile',
+ deleted: true,
+ },
],
currentBranchId: 'master',
};
@@ -107,6 +112,7 @@ describe('Multi-file store utils', () => {
content: 'updated file content',
encoding: 'text',
last_commit_id: '123456789',
+ previous_path: undefined,
},
{
action: 'create',
@@ -114,6 +120,15 @@ describe('Multi-file store utils', () => {
content: 'new file content',
encoding: 'base64',
last_commit_id: '123456789',
+ previous_path: undefined,
+ },
+ {
+ action: 'delete',
+ file_path: 'deletedFile',
+ content: undefined,
+ encoding: 'text',
+ last_commit_id: undefined,
+ previous_path: undefined,
},
],
start_branch: undefined,
@@ -160,6 +175,7 @@ describe('Multi-file store utils', () => {
content: 'updated file content',
encoding: 'text',
last_commit_id: '123456789',
+ previous_path: undefined,
},
{
action: 'create',
@@ -167,10 +183,56 @@ describe('Multi-file store utils', () => {
content: 'new file content',
encoding: 'base64',
last_commit_id: '123456789',
+ previous_path: undefined,
},
],
start_branch: undefined,
});
});
});
+
+ describe('commitActionForFile', () => {
+ it('returns deleted for deleted file', () => {
+ expect(utils.commitActionForFile({ deleted: true })).toBe('delete');
+ });
+
+ it('returns create for tempFile', () => {
+ expect(utils.commitActionForFile({ tempFile: true })).toBe('create');
+ });
+
+ it('returns move for moved file', () => {
+ expect(utils.commitActionForFile({ prevPath: 'test' })).toBe('move');
+ });
+
+ it('returns update by default', () => {
+ expect(utils.commitActionForFile({})).toBe('update');
+ });
+ });
+
+ describe('getCommitFiles', () => {
+ it('returns list of files excluding moved files', () => {
+ const files = [
+ {
+ path: 'a',
+ type: 'blob',
+ deleted: true,
+ },
+ {
+ path: 'c',
+ type: 'blob',
+ moved: true,
+ },
+ ];
+
+ const flattendFiles = utils.getCommitFiles(files);
+
+ expect(flattendFiles).toEqual([
+ {
+ path: 'a',
+ type: 'blob',
+ deleted: true,
+ },
+ ]);
+ });
+ });
});
diff --git a/spec/javascripts/issuable_time_tracker_spec.js b/spec/javascripts/issuable_time_tracker_spec.js
deleted file mode 100644
index 5add150f874..00000000000
--- a/spec/javascripts/issuable_time_tracker_spec.js
+++ /dev/null
@@ -1,201 +0,0 @@
-/* eslint-disable no-unused-vars, func-call-spacing, no-spaced-func, semi, quotes, space-infix-ops, max-len */
-
-import $ from 'jquery';
-import Vue from 'vue';
-
-import timeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
-
-function initTimeTrackingComponent(opts) {
- setFixtures(`
- <div>
- <div id="mock-container"></div>
- </div>
- `);
-
- this.initialData = {
- time_estimate: opts.timeEstimate,
- time_spent: opts.timeSpent,
- human_time_estimate: opts.timeEstimateHumanReadable,
- human_time_spent: opts.timeSpentHumanReadable,
- rootPath: '/',
- };
-
- const TimeTrackingComponent = Vue.extend(timeTracker);
- this.timeTracker = new TimeTrackingComponent({
- el: '#mock-container',
- propsData: this.initialData,
- });
-}
-
-describe('Issuable Time Tracker', function() {
- describe('Initialization', function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 100000, timeSpent: 5000, timeEstimateHumanReadable: '2h 46m', timeSpentHumanReadable: '1h 23m' });
- });
-
- it('should return something defined', function() {
- expect(this.timeTracker).toBeDefined();
- });
-
- it ('should correctly set timeEstimate', function(done) {
- Vue.nextTick(() => {
- expect(this.timeTracker.timeEstimate).toBe(this.initialData.time_estimate);
- done();
- });
- });
- it ('should correctly set time_spent', function(done) {
- Vue.nextTick(() => {
- expect(this.timeTracker.timeSpent).toBe(this.initialData.time_spent);
- done();
- });
- });
- });
-
- describe('Content Display', function() {
- describe('Panes', function() {
- describe('Comparison pane', function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 100000, timeSpent: 5000, timeEstimateHumanReadable: '', timeSpentHumanReadable: '' });
- });
-
- it('should show the "Comparison" pane when timeEstimate and time_spent are truthy', function(done) {
- Vue.nextTick(() => {
- const $comparisonPane = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane');
- expect(this.timeTracker.showComparisonState).toBe(true);
- done();
- });
- });
-
- describe('Remaining meter', function() {
- it('should display the remaining meter with the correct width', function(done) {
- Vue.nextTick(() => {
- const meterWidth = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane .meter-fill').style.width;
- const correctWidth = '5%';
-
- expect(meterWidth).toBe(correctWidth);
- done();
- })
- });
-
- it('should display the remaining meter with the correct background color when within estimate', function(done) {
- Vue.nextTick(() => {
- const styledMeter = $(this.timeTracker.$el).find('.time-tracking-comparison-pane .within_estimate .meter-fill');
- expect(styledMeter.length).toBe(1);
- done()
- });
- });
-
- it('should display the remaining meter with the correct background color when over estimate', function(done) {
- this.timeTracker.time_estimate = 100000;
- this.timeTracker.time_spent = 20000000;
- Vue.nextTick(() => {
- const styledMeter = $(this.timeTracker.$el).find('.time-tracking-comparison-pane .over_estimate .meter-fill');
- expect(styledMeter.length).toBe(1);
- done();
- });
- });
- });
- });
-
- describe("Estimate only pane", function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 100000, timeSpent: 0, timeEstimateHumanReadable: '2h 46m', timeSpentHumanReadable: '' });
- });
-
- it('should display the human readable version of time estimated', function(done) {
- Vue.nextTick(() => {
- const estimateText = this.timeTracker.$el.querySelector('.time-tracking-estimate-only-pane').innerText;
- const correctText = 'Estimated: 2h 46m';
-
- expect(estimateText).toBe(correctText);
- done();
- });
- });
- });
-
- describe('Spent only pane', function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 0, timeSpent: 5000, timeEstimateHumanReadable: '2h 46m', timeSpentHumanReadable: '1h 23m' });
- });
-
- it('should display the human readable version of time spent', function(done) {
- Vue.nextTick(() => {
- const spentText = this.timeTracker.$el.querySelector('.time-tracking-spend-only-pane').innerText;
- const correctText = 'Spent: 1h 23m';
-
- expect(spentText).toBe(correctText);
- done();
- });
- });
- });
-
- describe('No time tracking pane', function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 0, timeSpent: 0, timeEstimateHumanReadable: '', timeSpentHumanReadable: '' });
- });
-
- it('should only show the "No time tracking" pane when both timeEstimate and time_spent are falsey', function(done) {
- Vue.nextTick(() => {
- const $noTrackingPane = this.timeTracker.$el.querySelector('.time-tracking-no-tracking-pane');
- const noTrackingText =$noTrackingPane.innerText;
- const correctText = 'No estimate or time spent';
-
- expect(this.timeTracker.showNoTimeTrackingState).toBe(true);
- expect($noTrackingPane).toBeVisible();
- expect(noTrackingText).toBe(correctText);
- done();
- });
- });
- });
-
- describe("Help pane", function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 0, timeSpent: 0 });
- });
-
- it('should not show the "Help" pane by default', function(done) {
- Vue.nextTick(() => {
- const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
-
- expect(this.timeTracker.showHelpState).toBe(false);
- expect($helpPane).toBeNull();
- done();
- });
- });
-
- it('should show the "Help" pane when help button is clicked', function(done) {
- Vue.nextTick(() => {
- $(this.timeTracker.$el).find('.help-button').click();
-
- setTimeout(() => {
- const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
- expect(this.timeTracker.showHelpState).toBe(true);
- expect($helpPane).toBeVisible();
- done();
- }, 10);
- });
- });
-
- it('should not show the "Help" pane when help button is clicked and then closed', function(done) {
- Vue.nextTick(() => {
- $(this.timeTracker.$el).find('.help-button').click();
-
- setTimeout(() => {
-
- $(this.timeTracker.$el).find('.close-help-button').click();
-
- setTimeout(() => {
- const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
-
- expect(this.timeTracker.showHelpState).toBe(false);
- expect($helpPane).toBeNull();
-
- done();
- }, 1000);
- }, 1000);
- });
- });
- });
- });
- });
-});
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index eb5e0bddb74..36328382448 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -76,7 +76,7 @@ describe('Issuable output', () => {
expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>');
expect(vm.$el.querySelector('.js-task-list-field').value).toContain('this is a description');
expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/);
- expect(editedText.querySelector('.author_link').href).toMatch(/\/some_user$/);
+ expect(editedText.querySelector('.author-link').href).toMatch(/\/some_user$/);
expect(editedText.querySelector('time')).toBeTruthy();
})
.then(() => {
@@ -90,7 +90,7 @@ describe('Issuable output', () => {
expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42');
expect(vm.$el.querySelector('.edited-text')).toBeTruthy();
expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/);
- expect(editedText.querySelector('.author_link').href).toMatch(/\/other_user$/);
+ expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/);
expect(editedText.querySelector('time')).toBeTruthy();
})
.then(done)
diff --git a/spec/javascripts/issue_show/components/edited_spec.js b/spec/javascripts/issue_show/components/edited_spec.js
index 2061def699b..7f09db837bb 100644
--- a/spec/javascripts/issue_show/components/edited_spec.js
+++ b/spec/javascripts/issue_show/components/edited_spec.js
@@ -18,7 +18,7 @@ describe('edited', () => {
}).$mount();
expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited[\s\S]+?by Some User/);
- expect(editedComponent.$el.querySelector('.author_link').href).toMatch(/\/some_user$/);
+ expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/);
expect(editedComponent.$el.querySelector('time')).toBeTruthy();
});
@@ -31,7 +31,7 @@ describe('edited', () => {
}).$mount();
expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited by Some User/);
- expect(editedComponent.$el.querySelector('.author_link').href).toMatch(/\/some_user$/);
+ expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/);
expect(editedComponent.$el.querySelector('time')).toBeFalsy();
});
@@ -43,7 +43,7 @@ describe('edited', () => {
}).$mount();
expect(formatText(editedComponent.$el.innerText)).not.toMatch(/by Some User/);
- expect(editedComponent.$el.querySelector('.author_link')).toBeFalsy();
+ expect(editedComponent.$el.querySelector('.author-link')).toBeFalsy();
expect(editedComponent.$el.querySelector('time')).toBeTruthy();
});
diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js
index 79e375aa02e..2fcb5566ebc 100644
--- a/spec/javascripts/job_spec.js
+++ b/spec/javascripts/job_spec.js
@@ -5,6 +5,7 @@ import { numberToHumanSize } from '~/lib/utils/number_utils';
import '~/lib/utils/datetime_utility';
import Job from '~/job';
import '~/breakpoints';
+import waitForPromises from 'spec/helpers/wait_for_promises';
describe('Job', () => {
const JOB_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1`;
@@ -12,10 +13,6 @@ describe('Job', () => {
let response;
let job;
- function waitForPromise() {
- return new Promise(resolve => requestAnimationFrame(resolve));
- }
-
preloadFixtures('builds/build-with-artifacts.html.raw');
beforeEach(() => {
@@ -49,7 +46,7 @@ describe('Job', () => {
beforeEach(function (done) {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(done)
.catch(done.fail);
});
@@ -93,7 +90,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
expect(job.state).toBe('newstate');
@@ -107,7 +104,7 @@ describe('Job', () => {
};
})
.then(() => jasmine.clock().tick(4001))
- .then(waitForPromise)
+ .then(waitForPromises)
.then(() => {
expect($('#build-trace .js-build-output').text()).toMatch(/UpdateMore/);
expect(job.state).toBe('finalstate');
@@ -126,7 +123,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
@@ -137,7 +134,7 @@ describe('Job', () => {
};
})
.then(() => jasmine.clock().tick(4001))
- .then(waitForPromise)
+ .then(waitForPromises)
.then(() => {
expect($('#build-trace .js-build-output').text()).not.toMatch(/Update/);
expect($('#build-trace .js-build-output').text()).toMatch(/Different/);
@@ -160,7 +157,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect(document.querySelector('.js-truncated-info').classList).not.toContain('hidden');
})
@@ -181,7 +178,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
@@ -203,7 +200,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
@@ -219,7 +216,7 @@ describe('Job', () => {
};
})
.then(() => jasmine.clock().tick(4001))
- .then(waitForPromise)
+ .then(waitForPromises)
.then(() => {
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
@@ -258,7 +255,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect(document.querySelector('.js-truncated-info').classList).toContain('hidden');
})
@@ -280,7 +277,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(done)
.catch(done.fail);
});
diff --git a/spec/javascripts/jobs/header_spec.js b/spec/javascripts/jobs/header_spec.js
index cef30a007db..e21e2c6d6e3 100644
--- a/spec/javascripts/jobs/header_spec.js
+++ b/spec/javascripts/jobs/header_spec.js
@@ -20,7 +20,7 @@ describe('Job details header', () => {
job: {
status: {
group: 'failed',
- icon: 'ci-status-failed',
+ icon: 'status_failed',
label: 'failed',
text: 'failed',
details_path: 'path',
diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js
index dd025255bd1..8fdd9b309b7 100644
--- a/spec/javascripts/jobs/mock_data.js
+++ b/spec/javascripts/jobs/mock_data.js
@@ -14,7 +14,7 @@ export default {
finished_at: threeWeeksAgo.toISOString(),
queued: 9.54,
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -72,7 +72,7 @@ export default {
},
details: {
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 41ff59949e5..71b26a315af 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -627,4 +627,23 @@ describe('common_utils', () => {
});
});
});
+
+ describe('roundOffFloat', () => {
+ it('Rounds off decimal places of a float number with provided precision', () => {
+ expect(commonUtils.roundOffFloat(3.141592, 3)).toBe(3.142);
+ });
+
+ it('Rounds off a float number to a whole number when provided precision is zero', () => {
+ expect(commonUtils.roundOffFloat(3.141592, 0)).toBe(3);
+ expect(commonUtils.roundOffFloat(3.5, 0)).toBe(4);
+ });
+
+ it('Rounds off float number to nearest 0, 10, 100, 1000 and so on when provided precision is below 0', () => {
+ expect(commonUtils.roundOffFloat(34567.14159, -1)).toBe(34570);
+ expect(commonUtils.roundOffFloat(34567.14159, -2)).toBe(34600);
+ expect(commonUtils.roundOffFloat(34567.14159, -3)).toBe(35000);
+ expect(commonUtils.roundOffFloat(34567.14159, -4)).toBe(30000);
+ expect(commonUtils.roundOffFloat(34567.14159, -5)).toBe(0);
+ });
+ });
});
diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js
index 9b8f68f1676..523f4997bc0 100644
--- a/spec/javascripts/lib/utils/poll_spec.js
+++ b/spec/javascripts/lib/utils/poll_spec.js
@@ -1,4 +1,5 @@
import Poll from '~/lib/utils/poll';
+import { successCodes } from '~/lib/utils/http_status';
const waitForAllCallsToFinish = (service, waitForCount, successCallback) => {
const timer = () => {
@@ -91,28 +92,32 @@ describe('Poll', () => {
}).catch(done.fail);
});
- it('starts polling when http status is 200 and interval header is provided', (done) => {
- mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } });
+ describe('for 2xx status code', () => {
+ successCodes.forEach(httpCode => {
+ it(`starts polling when http status is ${httpCode} and interval header is provided`, (done) => {
+ mockServiceCall(service, { status: httpCode, headers: { 'poll-interval': 1 } });
- const Polling = new Poll({
- resource: service,
- method: 'fetch',
- data: { page: 1 },
- successCallback: callbacks.success,
- errorCallback: callbacks.error,
- });
+ const Polling = new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ });
- Polling.makeRequest();
+ Polling.makeRequest();
- waitForAllCallsToFinish(service, 2, () => {
- Polling.stop();
+ waitForAllCallsToFinish(service, 2, () => {
+ Polling.stop();
- expect(service.fetch.calls.count()).toEqual(2);
- expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
- expect(callbacks.success).toHaveBeenCalled();
- expect(callbacks.error).not.toHaveBeenCalled();
+ expect(service.fetch.calls.count()).toEqual(2);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
- done();
+ done();
+ });
+ });
});
});
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index 33987574f00..d60485b1308 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -112,4 +112,21 @@ describe('text_utility', () => {
expect(textUtils.splitCamelCase('HelloWorld')).toBe('Hello World');
});
});
+
+ describe('getFirstCharacterCapitalized', () => {
+ it('returns the first character captialized, if first character is alphabetic', () => {
+ expect(textUtils.getFirstCharacterCapitalized('loremIpsumDolar')).toEqual('L');
+ expect(textUtils.getFirstCharacterCapitalized('Sit amit !')).toEqual('S');
+ });
+
+ it('returns the first character, if first character is non-alphabetic', () => {
+ expect(textUtils.getFirstCharacterCapitalized(' lorem')).toEqual(' ');
+ expect(textUtils.getFirstCharacterCapitalized('%#!')).toEqual('%');
+ });
+
+ it('returns an empty string, if string is falsey', () => {
+ expect(textUtils.getFirstCharacterCapitalized('')).toEqual('');
+ expect(textUtils.getFirstCharacterCapitalized(null)).toEqual('');
+ });
+ });
});
diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js
index 22eb0ad7143..7502f1fa2e1 100644
--- a/spec/javascripts/merge_request_spec.js
+++ b/spec/javascripts/merge_request_spec.js
@@ -19,9 +19,11 @@ import IssuablesHelper from '~/helpers/issuables_helper';
spyOn(axios, 'patch').and.callThrough();
mock = new MockAdapter(axios);
- mock.onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`).reply(200, {});
+ mock
+ .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`)
+ .reply(200, {});
- return this.merge = new MergeRequest();
+ return (this.merge = new MergeRequest());
});
afterEach(() => {
@@ -32,17 +34,22 @@ import IssuablesHelper from '~/helpers/issuables_helper';
spyOn($, 'ajax').and.stub();
const changeEvent = document.createEvent('HTMLEvents');
changeEvent.initEvent('change', true, true);
- $('input[type=checkbox]').attr('checked', true)[0].dispatchEvent(changeEvent);
+ $('input[type=checkbox]')
+ .attr('checked', true)[0]
+ .dispatchEvent(changeEvent);
return expect($('.js-task-list-field').val()).toBe('- [x] Task List Item');
});
- it('submits an ajax request on tasklist:changed', (done) => {
+ it('submits an ajax request on tasklist:changed', done => {
$('.js-task-list-field').trigger('tasklist:changed');
setTimeout(() => {
- expect(axios.patch).toHaveBeenCalledWith(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`, {
- merge_request: { description: '- [ ] Task List Item' },
- });
+ expect(axios.patch).toHaveBeenCalledWith(
+ `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`,
+ {
+ merge_request: { description: '- [ ] Task List Item' },
+ },
+ );
done();
});
});
@@ -119,4 +126,4 @@ import IssuablesHelper from '~/helpers/issuables_helper';
});
});
});
-}).call(window);
+}.call(window));
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 08928e13985..7251ce19a90 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -40,6 +40,7 @@ describe('MergeRequestTabs', function() {
this.class.unbindEvents();
this.class.destroyPipelinesView();
mrPageMock.restore();
+ $('.js-merge-request-test').remove();
});
describe('opensInNewTab', function() {
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index eba6dcf47c5..997163c7602 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue';
import axios from '~/lib/utils/axios_utils';
-import { metricsGroupsAPIResponse, mockApiEndpoint } from './mock_data';
+import { metricsGroupsAPIResponse, mockApiEndpoint, environmentData } from './mock_data';
describe('Dashboard', () => {
let DashboardComponent;
@@ -20,6 +20,8 @@ describe('Dashboard', () => {
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyNoDataSvgPath: '/path/to/no-data.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
+ environmentsEndpoint: '/root/hello-prometheus/environments/35',
+ currentEnvironmentName: 'production',
};
beforeEach(() => {
@@ -50,7 +52,7 @@ describe('Dashboard', () => {
mock.restore();
});
- it('shows up a loading state', (done) => {
+ it('shows up a loading state', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true },
@@ -62,7 +64,7 @@ describe('Dashboard', () => {
});
});
- it('hides the legend when showLegend is false', (done) => {
+ it('hides the legend when showLegend is false', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showLegend: false },
@@ -76,7 +78,7 @@ describe('Dashboard', () => {
});
});
- it('hides the group panels when showPanels is false', (done) => {
+ it('hides the group panels when showPanels is false', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showPanels: false },
@@ -89,5 +91,40 @@ describe('Dashboard', () => {
done();
});
});
+
+ it('renders the dropdown with a number of environments', done => {
+ const component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: { ...propsData, hasMetrics: true, showPanels: false },
+ });
+
+ component.store.storeEnvironmentsData(environmentData);
+
+ setTimeout(() => {
+ const dropdownMenuEnvironments = component.$el.querySelectorAll('.dropdown-menu ul li a');
+ expect(dropdownMenuEnvironments.length).toEqual(component.store.environmentsData.length);
+ done();
+ });
+ });
+
+ it('renders the dropdown with a single is-active element', done => {
+ const component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: { ...propsData, hasMetrics: true, showPanels: false },
+ });
+
+ component.store.storeEnvironmentsData(environmentData);
+
+ setTimeout(() => {
+ const dropdownIsActiveElement = component.$el.querySelectorAll(
+ '.dropdown-menu ul li a.is-active',
+ );
+ expect(dropdownIsActiveElement.length).toEqual(1);
+ expect(dropdownIsActiveElement[0].textContent.trim()).toEqual(
+ component.currentEnvironmentName,
+ );
+ done();
+ });
+ });
});
});
diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index 799d03f6b57..e4c98a3bcb5 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -6542,3 +6542,61 @@ export function convertDatesMultipleSeries(multipleSeries) {
});
return convertedMultiple;
}
+
+export const environmentData = [
+ {
+ name: 'production',
+ size: 1,
+ latest: {
+ id: 34,
+ name: 'production',
+ state: 'available',
+ external_url: 'http://root-autodevops-deploy.my-fake-domain.com',
+ environment_type: null,
+ stop_action: false,
+ metrics_path: '/root/hello-prometheus/environments/34/metrics',
+ environment_path: '/root/hello-prometheus/environments/34',
+ stop_path: '/root/hello-prometheus/environments/34/stop',
+ terminal_path: '/root/hello-prometheus/environments/34/terminal',
+ folder_path: '/root/hello-prometheus/environments/folders/production',
+ created_at: '2018-06-29T16:53:38.301Z',
+ updated_at: '2018-06-29T16:57:09.825Z',
+ last_deployment: {
+ id: 127,
+ },
+ },
+ },
+ {
+ name: 'review',
+ size: 1,
+ latest: {
+ id: 35,
+ name: 'review/noop-branch',
+ state: 'available',
+ external_url: 'http://root-autodevops-deploy-review-noop-branc-die93w.my-fake-domain.com',
+ environment_type: 'review',
+ stop_action: true,
+ metrics_path: '/root/hello-prometheus/environments/35/metrics',
+ environment_path: '/root/hello-prometheus/environments/35',
+ stop_path: '/root/hello-prometheus/environments/35/stop',
+ terminal_path: '/root/hello-prometheus/environments/35/terminal',
+ folder_path: '/root/hello-prometheus/environments/folders/review',
+ created_at: '2018-07-03T18:39:41.702Z',
+ updated_at: '2018-07-03T18:44:54.010Z',
+ last_deployment: {
+ id: 128,
+ },
+ },
+ },
+ {
+ name: 'no-deployment',
+ size: 1,
+ latest: {
+ id: 36,
+ name: 'no-deployment/noop-branch',
+ state: 'available',
+ created_at: '2018-07-04T18:39:41.702Z',
+ updated_at: '2018-07-04T18:44:54.010Z',
+ },
+ },
+];
diff --git a/spec/javascripts/monitoring/monitoring_store_spec.js b/spec/javascripts/monitoring/monitoring_store_spec.js
index 08d54946787..ccdf4eda563 100644
--- a/spec/javascripts/monitoring/monitoring_store_spec.js
+++ b/spec/javascripts/monitoring/monitoring_store_spec.js
@@ -1,5 +1,5 @@
import MonitoringStore from '~/monitoring/stores/monitoring_store';
-import MonitoringMock, { deploymentData } from './mock_data';
+import MonitoringMock, { deploymentData, environmentData } from './mock_data';
describe('MonitoringStore', function () {
this.store = new MonitoringStore();
@@ -21,4 +21,9 @@ describe('MonitoringStore', function () {
expect(this.store.deploymentData.length).toEqual(3);
expect(typeof this.store.deploymentData[0]).toEqual('object');
});
+
+ it('only stores environment data that contains deployments', () => {
+ this.store.storeEnvironmentsData(environmentData);
+ expect(this.store.environmentsData.length).toEqual(2);
+ });
});
diff --git a/spec/javascripts/notes/components/discussion_counter_spec.js b/spec/javascripts/notes/components/discussion_counter_spec.js
index 7b2302e6f47..d09bc5037ef 100644
--- a/spec/javascripts/notes/components/discussion_counter_spec.js
+++ b/spec/javascripts/notes/components/discussion_counter_spec.js
@@ -32,12 +32,12 @@ describe('DiscussionCounter component', () => {
{
...discussionMock,
id: discussionMock.id,
- notes: [{ ...discussionMock.notes[0], resolved: true }],
+ notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: true }],
},
{
...discussionMock,
id: discussionMock.id + 1,
- notes: [{ ...discussionMock.notes[0], resolved: false }],
+ notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: false }],
},
];
const firstDiscussionId = discussionMock.id + 1;
@@ -46,7 +46,7 @@ describe('DiscussionCounter component', () => {
discussions,
});
setFixtures(`
- <div data-discussion-id="${firstDiscussionId}"></div>
+ <div class="discussion" data-discussion-id="${firstDiscussionId}"></div>
`);
vm.jumpToFirstUnresolvedDiscussion();
diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js
index 058ddb6202f..2a01bd85520 100644
--- a/spec/javascripts/notes/components/noteable_discussion_spec.js
+++ b/spec/javascripts/notes/components/noteable_discussion_spec.js
@@ -4,22 +4,24 @@ import noteableDiscussion from '~/notes/components/noteable_discussion.vue';
import '~/behaviors/markdown/render_gfm';
import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
+const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json';
+
describe('noteable_discussion component', () => {
+ const Component = Vue.extend(noteableDiscussion);
let store;
let vm;
- beforeEach(() => {
- const Component = Vue.extend(noteableDiscussion);
+ preloadFixtures(discussionWithTwoUnresolvedNotes);
+ beforeEach(() => {
+ window.mrTabs = {};
store = createStore();
store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
vm = new Component({
store,
- propsData: {
- discussion: discussionMock,
- },
+ propsData: { discussion: discussionMock },
}).$mount();
});
@@ -45,10 +47,15 @@ describe('noteable_discussion component', () => {
it('should toggle reply form', done => {
vm.$el.querySelector('.js-vue-discussion-reply').click();
+
Vue.nextTick(() => {
- expect(vm.$refs.noteForm).not.toBeNull();
expect(vm.isReplying).toEqual(true);
- done();
+
+ // There is a watcher for `isReplying` which will init autosave in the next tick
+ Vue.nextTick(() => {
+ expect(vm.$refs.noteForm).not.toBeNull();
+ done();
+ });
});
});
@@ -84,7 +91,9 @@ describe('noteable_discussion component', () => {
});
it('is true if there are two unresolved discussions', done => {
- spyOnProperty(vm, 'unresolvedDiscussions').and.returnValue([{}, {}]);
+ const discussion = getJSONFixture(discussionWithTwoUnresolvedNotes)[0];
+ discussion.notes[0].resolved = false;
+ vm.$store.dispatch('setInitialNotes', [discussion, discussion]);
Vue.nextTick()
.then(() => {
@@ -98,33 +107,29 @@ describe('noteable_discussion component', () => {
describe('methods', () => {
describe('jumpToNextDiscussion', () => {
- it('expands next unresolved discussion', () => {
- spyOn(vm, 'expandDiscussion').and.stub();
- const discussions = [
- discussionMock,
- {
- ...discussionMock,
- id: discussionMock.id + 1,
- notes: [{ ...discussionMock.notes[0], resolved: true }],
- },
- {
- ...discussionMock,
- id: discussionMock.id + 2,
- notes: [{ ...discussionMock.notes[0], resolved: false }],
- },
- ];
- const nextDiscussionId = discussionMock.id + 2;
- store.replaceState({
- ...store.state,
- discussions,
- });
- setFixtures(`
- <div data-discussion-id="${nextDiscussionId}"></div>
- `);
+ it('expands next unresolved discussion', done => {
+ const discussion2 = getJSONFixture(discussionWithTwoUnresolvedNotes)[0];
+ discussion2.resolved = false;
+ discussion2.id = 'next'; // prepare this for being identified as next one (to be jumped to)
+ vm.$store.dispatch('setInitialNotes', [discussionMock, discussion2]);
+ window.mrTabs.currentAction = 'show';
- vm.jumpToNextDiscussion();
+ Vue.nextTick()
+ .then(() => {
+ spyOn(vm, 'expandDiscussion').and.stub();
+
+ const nextDiscussionId = discussion2.id;
+
+ setFixtures(`
+ <div class="discussion" data-discussion-id="${nextDiscussionId}"></div>
+ `);
+
+ vm.jumpToNextDiscussion();
- expect(vm.expandDiscussion).toHaveBeenCalledWith({ discussionId: nextDiscussionId });
+ expect(vm.expandDiscussion).toHaveBeenCalledWith({ discussionId: nextDiscussionId });
+ })
+ .then(done)
+ .catch(done.fail);
});
});
});
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index 547efa32694..67f6a9629d9 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -165,6 +165,7 @@ export const note = {
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1',
path: '/gitlab-org/gitlab-ce/notes/546',
+ cached_markdown_version: 11,
};
export const discussionMock = {
@@ -303,6 +304,7 @@ export const discussionMock = {
},
],
individual_note: false,
+ resolvable: true,
};
export const loggedOutnoteableData = {
@@ -1166,3 +1168,87 @@ export const collapsedSystemNotes = [
diff_discussion: false,
},
];
+
+export const discussion1 = {
+ id: 'abc1',
+ resolvable: true,
+ resolved: false,
+ diff_file: {
+ file_path: 'about.md',
+ },
+ position: {
+ formatter: {
+ new_line: 50,
+ old_line: null,
+ },
+ },
+ notes: [
+ {
+ created_at: '2018-07-04T16:25:41.749Z',
+ },
+ ],
+};
+
+export const resolvedDiscussion1 = {
+ id: 'abc1',
+ resolvable: true,
+ resolved: true,
+ diff_file: {
+ file_path: 'about.md',
+ },
+ position: {
+ formatter: {
+ new_line: 50,
+ old_line: null,
+ },
+ },
+ notes: [
+ {
+ created_at: '2018-07-04T16:25:41.749Z',
+ },
+ ],
+};
+
+export const discussion2 = {
+ id: 'abc2',
+ resolvable: true,
+ resolved: false,
+ diff_file: {
+ file_path: 'README.md',
+ },
+ position: {
+ formatter: {
+ new_line: null,
+ old_line: 20,
+ },
+ },
+ notes: [
+ {
+ created_at: '2018-07-04T12:05:41.749Z',
+ },
+ ],
+};
+
+export const discussion3 = {
+ id: 'abc3',
+ resolvable: true,
+ resolved: false,
+ diff_file: {
+ file_path: 'README.md',
+ },
+ position: {
+ formatter: {
+ new_line: 21,
+ old_line: null,
+ },
+ },
+ notes: [
+ {
+ created_at: '2018-07-05T17:25:41.749Z',
+ },
+ ],
+};
+
+export const unresolvableDiscussion = {
+ resolvable: false,
+};
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index 71ef3aa9b03..b66e8e1ceb3 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -128,6 +128,19 @@ describe('Actions Notes Store', () => {
});
});
+ describe('collapseDiscussion', () => {
+ it('should commit collapse discussion', done => {
+ testAction(
+ actions.collapseDiscussion,
+ { discussionId: discussionMock.id },
+ { notes: [discussionMock] },
+ [{ type: 'COLLAPSE_DISCUSSION', payload: { discussionId: discussionMock.id } }],
+ [],
+ done,
+ );
+ });
+ });
+
describe('async methods', () => {
const interceptor = (request, next) => {
next(
diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js
index 815cc09621f..7f8ede51508 100644
--- a/spec/javascripts/notes/stores/getters_spec.js
+++ b/spec/javascripts/notes/stores/getters_spec.js
@@ -5,11 +5,20 @@ import {
noteableDataMock,
individualNote,
collapseNotesMock,
+ discussion1,
+ discussion2,
+ discussion3,
+ resolvedDiscussion1,
+ unresolvableDiscussion,
} from '../mock_data';
+const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json';
+
describe('Getters Notes Store', () => {
let state;
+ preloadFixtures(discussionWithTwoUnresolvedNotes);
+
beforeEach(() => {
state = {
discussions: [individualNote],
@@ -22,12 +31,26 @@ describe('Getters Notes Store', () => {
noteableData: noteableDataMock,
};
});
+
describe('discussions', () => {
it('should return all discussions in the store', () => {
expect(getters.discussions(state)).toEqual([individualNote]);
});
});
+ describe('resolvedDiscussionsById', () => {
+ it('ignores unresolved system notes', () => {
+ const [discussion] = getJSONFixture(discussionWithTwoUnresolvedNotes);
+ discussion.notes[0].resolved = true;
+ discussion.notes[1].resolved = false;
+ state.discussions.push(discussion);
+
+ expect(getters.resolvedDiscussionsById(state)).toEqual({
+ [discussion.id]: discussion,
+ });
+ });
+ });
+
describe('Collapsed notes', () => {
const stateCollapsedNotes = {
discussions: collapseNotesMock,
@@ -91,4 +114,154 @@ describe('Getters Notes Store', () => {
expect(getters.isNotesFetched(state)).toBeFalsy();
});
});
+
+ describe('allResolvableDiscussions', () => {
+ it('should return only resolvable discussions in same order', () => {
+ const localGetters = {
+ allDiscussions: [
+ discussion3,
+ unresolvableDiscussion,
+ discussion1,
+ unresolvableDiscussion,
+ discussion2,
+ ],
+ };
+
+ expect(getters.allResolvableDiscussions(state, localGetters)).toEqual([
+ discussion3,
+ discussion1,
+ discussion2,
+ ]);
+ });
+
+ it('should return empty array if there are no resolvable discussions', () => {
+ const localGetters = {
+ allDiscussions: [unresolvableDiscussion, unresolvableDiscussion],
+ };
+
+ expect(getters.allResolvableDiscussions(state, localGetters)).toEqual([]);
+ });
+ });
+
+ describe('unresolvedDiscussionsIdsByDiff', () => {
+ it('should return all discussions IDs in diff order', () => {
+ const localGetters = {
+ allResolvableDiscussions: [discussion3, discussion1, discussion2],
+ };
+
+ expect(getters.unresolvedDiscussionsIdsByDiff(state, localGetters)).toEqual([
+ 'abc1',
+ 'abc2',
+ 'abc3',
+ ]);
+ });
+
+ it('should return empty array if all discussions have been resolved', () => {
+ const localGetters = {
+ allResolvableDiscussions: [resolvedDiscussion1],
+ };
+
+ expect(getters.unresolvedDiscussionsIdsByDiff(state, localGetters)).toEqual([]);
+ });
+ });
+
+ describe('unresolvedDiscussionsIdsByDate', () => {
+ it('should return all discussions in date ascending order', () => {
+ const localGetters = {
+ allResolvableDiscussions: [discussion3, discussion1, discussion2],
+ };
+
+ expect(getters.unresolvedDiscussionsIdsByDate(state, localGetters)).toEqual([
+ 'abc2',
+ 'abc1',
+ 'abc3',
+ ]);
+ });
+
+ it('should return empty array if all discussions have been resolved', () => {
+ const localGetters = {
+ allResolvableDiscussions: [resolvedDiscussion1],
+ };
+
+ expect(getters.unresolvedDiscussionsIdsByDate(state, localGetters)).toEqual([]);
+ });
+ });
+
+ describe('unresolvedDiscussionsIdsOrdered', () => {
+ const localGetters = {
+ unresolvedDiscussionsIdsByDate: ['123', '456'],
+ unresolvedDiscussionsIdsByDiff: ['abc', 'def'],
+ };
+
+ it('should return IDs ordered by diff when diffOrder param is true', () => {
+ expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(true)).toEqual([
+ 'abc',
+ 'def',
+ ]);
+ });
+
+ it('should return IDs ordered by date when diffOrder param is not true', () => {
+ expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(false)).toEqual([
+ '123',
+ '456',
+ ]);
+ expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(undefined)).toEqual([
+ '123',
+ '456',
+ ]);
+ });
+ });
+
+ describe('isLastUnresolvedDiscussion', () => {
+ const localGetters = {
+ unresolvedDiscussionsIdsOrdered: () => ['123', '456', '789'],
+ };
+
+ it('should return true if the discussion id provided is the last', () => {
+ expect(getters.isLastUnresolvedDiscussion(state, localGetters)('789')).toBe(true);
+ });
+
+ it('should return false if the discussion id provided is not the last', () => {
+ expect(getters.isLastUnresolvedDiscussion(state, localGetters)('123')).toBe(false);
+ expect(getters.isLastUnresolvedDiscussion(state, localGetters)('456')).toBe(false);
+ });
+ });
+
+ describe('nextUnresolvedDiscussionId', () => {
+ const localGetters = {
+ unresolvedDiscussionsIdsOrdered: () => ['123', '456', '789'],
+ };
+
+ it('should return the ID of the discussion after the ID provided', () => {
+ expect(getters.nextUnresolvedDiscussionId(state, localGetters)('123')).toBe('456');
+ expect(getters.nextUnresolvedDiscussionId(state, localGetters)('456')).toBe('789');
+ expect(getters.nextUnresolvedDiscussionId(state, localGetters)('789')).toBe(undefined);
+ });
+ });
+
+ describe('firstUnresolvedDiscussionId', () => {
+ const localGetters = {
+ unresolvedDiscussionsIdsByDate: ['123', '456'],
+ unresolvedDiscussionsIdsByDiff: ['abc', 'def'],
+ };
+
+ it('should return the first discussion id by diff when diffOrder param is true', () => {
+ expect(getters.firstUnresolvedDiscussionId(state, localGetters)(true)).toBe('abc');
+ });
+
+ it('should return the first discussion id by date when diffOrder param is not true', () => {
+ expect(getters.firstUnresolvedDiscussionId(state, localGetters)(false)).toBe('123');
+ expect(getters.firstUnresolvedDiscussionId(state, localGetters)(undefined)).toBe('123');
+ });
+
+ it('should be falsy if all discussions are resolved', () => {
+ const localGettersFalsy = {
+ unresolvedDiscussionsIdsByDiff: [],
+ unresolvedDiscussionsIdsByDate: [],
+ };
+
+ expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(true)).toBeFalsy();
+ expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(false)).toBeFalsy();
+ });
+ });
});
diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js
index ccc7328447b..a15ff1a5888 100644
--- a/spec/javascripts/notes/stores/mutation_spec.js
+++ b/spec/javascripts/notes/stores/mutation_spec.js
@@ -74,6 +74,20 @@ describe('Notes Store mutations', () => {
});
});
+ describe('COLLAPSE_DISCUSSION', () => {
+ it('should collpase an expanded discussion', () => {
+ const discussion = Object.assign({}, discussionMock, { expanded: true });
+
+ const state = {
+ discussions: [discussion],
+ };
+
+ mutations.COLLAPSE_DISCUSSION(state, { discussionId: discussion.id });
+
+ expect(state.discussions[0].expanded).toEqual(false);
+ });
+ });
+
describe('REMOVE_PLACEHOLDER_NOTES', () => {
it('should remove all placeholder notes in indivudal notes and discussion', () => {
const placeholderNote = Object.assign({}, individualNote, { isPlaceholderNote: true });
diff --git a/spec/javascripts/pages/profiles/show/emoji_menu_spec.js b/spec/javascripts/pages/profiles/show/emoji_menu_spec.js
new file mode 100644
index 00000000000..b70368fc92f
--- /dev/null
+++ b/spec/javascripts/pages/profiles/show/emoji_menu_spec.js
@@ -0,0 +1,117 @@
+import $ from 'jquery';
+import axios from '~/lib/utils/axios_utils';
+import EmojiMenu from '~/pages/profiles/show/emoji_menu';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('EmojiMenu', () => {
+ const dummyEmojiTag = '<dummy></tag>';
+ const dummyToggleButtonSelector = '.toggle-button-selector';
+ const dummyMenuClass = 'dummy-menu-class';
+
+ let emojiMenu;
+ let dummySelectEmojiCallback;
+ let dummyEmojiList;
+
+ beforeEach(() => {
+ dummySelectEmojiCallback = jasmine.createSpy('dummySelectEmojiCallback');
+ dummyEmojiList = {
+ glEmojiTag() {
+ return dummyEmojiTag;
+ },
+ normalizeEmojiName(emoji) {
+ return emoji;
+ },
+ isEmojiNameValid() {
+ return true;
+ },
+ getEmojiCategoryMap() {
+ return { dummyCategory: [] };
+ },
+ };
+
+ emojiMenu = new EmojiMenu(
+ dummyEmojiList,
+ dummyToggleButtonSelector,
+ dummyMenuClass,
+ dummySelectEmojiCallback,
+ );
+ });
+
+ afterEach(() => {
+ emojiMenu.destroy();
+ });
+
+ describe('addAward', () => {
+ const dummyAwardUrl = `${TEST_HOST}/award/url`;
+ const dummyEmoji = 'tropical_fish';
+ const dummyVotesBlock = () => $('<div />');
+
+ it('calls selectEmojiCallback', done => {
+ expect(dummySelectEmojiCallback).not.toHaveBeenCalled();
+
+ emojiMenu.addAward(dummyVotesBlock(), dummyAwardUrl, dummyEmoji, false, () => {
+ expect(dummySelectEmojiCallback).toHaveBeenCalledWith(dummyEmoji, dummyEmojiTag);
+ done();
+ });
+ });
+
+ it('does not make an axios requst', done => {
+ spyOn(axios, 'request').and.stub();
+
+ emojiMenu.addAward(dummyVotesBlock(), dummyAwardUrl, dummyEmoji, false, () => {
+ expect(axios.request).not.toHaveBeenCalled();
+ done();
+ });
+ });
+ });
+
+ describe('bindEvents', () => {
+ beforeEach(() => {
+ spyOn(emojiMenu, 'registerEventListener').and.stub();
+ });
+
+ it('binds event listeners to custom toggle button', () => {
+ emojiMenu.bindEvents();
+
+ expect(emojiMenu.registerEventListener).toHaveBeenCalledWith(
+ 'one',
+ jasmine.anything(),
+ 'mouseenter focus',
+ dummyToggleButtonSelector,
+ 'mouseenter focus',
+ jasmine.anything(),
+ );
+ expect(emojiMenu.registerEventListener).toHaveBeenCalledWith(
+ 'on',
+ jasmine.anything(),
+ 'click',
+ dummyToggleButtonSelector,
+ jasmine.anything(),
+ );
+ });
+
+ it('binds event listeners to custom menu class', () => {
+ emojiMenu.bindEvents();
+
+ expect(emojiMenu.registerEventListener).toHaveBeenCalledWith(
+ 'on',
+ jasmine.anything(),
+ 'click',
+ `.js-awards-block .js-emoji-btn, .${dummyMenuClass} .js-emoji-btn`,
+ jasmine.anything(),
+ );
+ });
+ });
+
+ describe('createEmojiMenu', () => {
+ it('renders the menu with custom menu class', () => {
+ const menuElement = () =>
+ document.body.querySelector(`.emoji-menu.${dummyMenuClass} .emoji-menu-content`);
+ expect(menuElement()).toBe(null);
+
+ emojiMenu.createEmojiMenu();
+
+ expect(menuElement()).not.toBe(null);
+ });
+ });
+});
diff --git a/spec/javascripts/pdf/page_spec.js b/spec/javascripts/pdf/page_spec.js
index 9c686748c10..ff1bfd7f650 100644
--- a/spec/javascripts/pdf/page_spec.js
+++ b/spec/javascripts/pdf/page_spec.js
@@ -30,7 +30,7 @@ describe('Page component', () => {
done();
})
.catch((error) => {
- console.error(error);
+ done.fail(error);
});
});
diff --git a/spec/javascripts/performance_bar/components/performance_bar_app_spec.js b/spec/javascripts/performance_bar/components/performance_bar_app_spec.js
index 9ab9ab1c9f4..7926db44429 100644
--- a/spec/javascripts/performance_bar/components/performance_bar_app_spec.js
+++ b/spec/javascripts/performance_bar/components/performance_bar_app_spec.js
@@ -1,39 +1,15 @@
import Vue from 'vue';
-import axios from '~/lib/utils/axios_utils';
import performanceBarApp from '~/performance_bar/components/performance_bar_app.vue';
-import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
import PerformanceBarStore from '~/performance_bar/stores/performance_bar_store';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import MockAdapter from 'axios-mock-adapter';
-describe('performance bar', () => {
- let mock;
+describe('performance bar app', () => {
let vm;
beforeEach(() => {
const store = new PerformanceBarStore();
- mock = new MockAdapter(axios);
-
- mock.onGet('/-/peek/results').reply(
- 200,
- {
- data: {
- gc: {
- invokes: 0,
- invoke_time: '0.00',
- use_size: 0,
- total_size: 0,
- total_object: 0,
- gc_time: '0.00',
- },
- host: { hostname: 'web-01' },
- },
- },
- {},
- );
-
vm = mountComponent(Vue.extend(performanceBarApp), {
store,
env: 'development',
@@ -45,44 +21,9 @@ describe('performance bar', () => {
afterEach(() => {
vm.$destroy();
- mock.restore();
});
it('sets the class to match the environment', () => {
expect(vm.$el.getAttribute('class')).toContain('development');
});
-
- describe('loadRequestDetails', () => {
- beforeEach(() => {
- spyOn(vm.store, 'addRequest').and.callThrough();
- });
-
- it('does nothing if the request cannot be tracked', () => {
- spyOn(vm.store, 'canTrackRequest').and.callFake(() => false);
-
- vm.loadRequestDetails('123', 'https://gitlab.com/');
-
- expect(vm.store.addRequest).not.toHaveBeenCalled();
- });
-
- it('adds the request immediately', () => {
- vm.loadRequestDetails('123', 'https://gitlab.com/');
-
- expect(vm.store.addRequest).toHaveBeenCalledWith(
- '123',
- 'https://gitlab.com/',
- );
- });
-
- it('makes an HTTP request for the request details', () => {
- spyOn(PerformanceBarService, 'fetchRequestDetails').and.callThrough();
-
- vm.loadRequestDetails('456', 'https://gitlab.com/');
-
- expect(PerformanceBarService.fetchRequestDetails).toHaveBeenCalledWith(
- '/-/peek/results',
- '456',
- );
- });
- });
});
diff --git a/spec/javascripts/performance_bar/index_spec.js b/spec/javascripts/performance_bar/index_spec.js
new file mode 100644
index 00000000000..1784bd64adb
--- /dev/null
+++ b/spec/javascripts/performance_bar/index_spec.js
@@ -0,0 +1,83 @@
+import axios from '~/lib/utils/axios_utils';
+import performanceBar from '~/performance_bar';
+import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
+
+import MockAdapter from 'axios-mock-adapter';
+
+describe('performance bar wrapper', () => {
+ let mock;
+ let vm;
+
+ beforeEach(() => {
+ const peekWrapper = document.createElement('div');
+
+ peekWrapper.setAttribute('id', 'js-peek');
+ peekWrapper.setAttribute('data-env', 'development');
+ peekWrapper.setAttribute('data-request-id', '123');
+ peekWrapper.setAttribute('data-peek-url', '/-/peek/results');
+ peekWrapper.setAttribute('data-profile-url', '?lineprofiler=true');
+
+ document.body.appendChild(peekWrapper);
+
+ mock = new MockAdapter(axios);
+
+ mock.onGet('/-/peek/results').reply(
+ 200,
+ {
+ data: {
+ gc: {
+ invokes: 0,
+ invoke_time: '0.00',
+ use_size: 0,
+ total_size: 0,
+ total_object: 0,
+ gc_time: '0.00',
+ },
+ host: { hostname: 'web-01' },
+ },
+ },
+ {},
+ );
+
+ vm = performanceBar({ container: '#js-peek' });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ mock.restore();
+ });
+
+ describe('loadRequestDetails', () => {
+ beforeEach(() => {
+ spyOn(vm.store, 'addRequest').and.callThrough();
+ });
+
+ it('does nothing if the request cannot be tracked', () => {
+ spyOn(vm.store, 'canTrackRequest').and.callFake(() => false);
+
+ vm.loadRequestDetails('123', 'https://gitlab.com/');
+
+ expect(vm.store.addRequest).not.toHaveBeenCalled();
+ });
+
+ it('adds the request immediately', () => {
+ vm.loadRequestDetails('123', 'https://gitlab.com/');
+
+ expect(vm.store.addRequest).toHaveBeenCalledWith(
+ '123',
+ 'https://gitlab.com/',
+ );
+ });
+
+ it('makes an HTTP request for the request details', () => {
+ spyOn(PerformanceBarService, 'fetchRequestDetails').and.callThrough();
+
+ vm.loadRequestDetails('456', 'https://gitlab.com/');
+
+ expect(PerformanceBarService.fetchRequestDetails).toHaveBeenCalledWith(
+ '/-/peek/results',
+ '456',
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js
new file mode 100644
index 00000000000..ff584396d61
--- /dev/null
+++ b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js
@@ -0,0 +1,93 @@
+import Vue from 'vue';
+import component from '~/pipelines/components/graph/dropdown_job_component.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('dropdown job component', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const mock = {
+ jobs: [
+ {
+ id: 4256,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4256',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4256/retry',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 4299,
+ name: 'test',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4299',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4299/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ name: 'rspec:linux',
+ size: 2,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4256',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4256/retry',
+ method: 'post',
+ },
+ },
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ beforeEach(() => {
+ vm = mountComponent(Component, { job: mock });
+ });
+
+ it('renders button with job name and size', () => {
+ expect(vm.$el.querySelector('button').textContent).toContain(mock.name);
+ expect(vm.$el.querySelector('button').textContent).toContain(mock.size);
+ });
+
+ it('renders dropdown with jobs', () => {
+ expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(mock.jobs.length);
+ });
+
+ it('escapes tooltip title', () => {
+ expect(
+ vm.$el.querySelector('.js-pipeline-graph-job-link').getAttribute('data-original-title'),
+ ).toEqual(
+ '&lt;img src=x onerror=alert(document.domain)&gt; - passed',
+ );
+ });
+});
diff --git a/spec/javascripts/pipelines/graph/graph_component_spec.js b/spec/javascripts/pipelines/graph/graph_component_spec.js
index 713baa65a17..b6fa4272c8b 100644
--- a/spec/javascripts/pipelines/graph/graph_component_spec.js
+++ b/spec/javascripts/pipelines/graph/graph_component_spec.js
@@ -1,37 +1,33 @@
import Vue from 'vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import graphComponent from '~/pipelines/components/graph/graph_component.vue';
import graphJSON from './mock_data';
describe('graph component', () => {
- preloadFixtures('static/graph.html.raw');
+ const GraphComponent = Vue.extend(graphComponent);
+ let component;
- let GraphComponent;
-
- beforeEach(() => {
- loadFixtures('static/graph.html.raw');
- GraphComponent = Vue.extend(graphComponent);
+ afterEach(() => {
+ component.$destroy();
});
describe('while is loading', () => {
it('should render a loading icon', () => {
- const component = new GraphComponent({
- propsData: {
- isLoading: true,
- pipeline: {},
- },
- }).$mount('#js-pipeline-graph-vue');
+ component = mountComponent(GraphComponent, {
+ isLoading: true,
+ pipeline: {},
+ });
+
expect(component.$el.querySelector('.loading-icon')).toBeDefined();
});
});
describe('with data', () => {
it('should render the graph', () => {
- const component = new GraphComponent({
- propsData: {
- isLoading: false,
- pipeline: graphJSON,
- },
- }).$mount('#js-pipeline-graph-vue');
+ component = mountComponent(GraphComponent, {
+ isLoading: false,
+ pipeline: graphJSON,
+ });
expect(component.$el.classList.contains('js-pipeline-graph')).toEqual(true);
@@ -52,4 +48,15 @@ describe('graph component', () => {
expect(component.$el.querySelector('.stage-column-list')).toBeDefined();
});
});
+
+ describe('capitalizeStageName', () => {
+ it('capitalizes and escapes stage name', () => {
+ component = mountComponent(GraphComponent, {
+ isLoading: false,
+ pipeline: graphJSON,
+ });
+
+ expect(component.$el.querySelector('.stage-column:nth-child(2) .stage-name').textContent.trim()).toEqual('Deploy &lt;img src=x onerror=alert(document.domain)&gt;');
+ });
+ });
});
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
index 9c55a19ebc7..215ce1e81b5 100644
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_component_spec.js
@@ -3,14 +3,14 @@ import jobComponent from '~/pipelines/components/graph/job_component.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('pipeline graph job component', () => {
- let JobComponent;
+ const JobComponent = Vue.extend(jobComponent);
let component;
const mockJob = {
id: 4256,
name: 'test',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
tooltip: 'passed',
@@ -26,10 +26,6 @@ describe('pipeline graph job component', () => {
},
};
- beforeEach(() => {
- JobComponent = Vue.extend(jobComponent);
- });
-
afterEach(() => {
component.$destroy();
});
@@ -65,7 +61,7 @@ describe('pipeline graph job component', () => {
id: 4257,
name: 'test',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -111,7 +107,7 @@ describe('pipeline graph job component', () => {
id: 4258,
name: 'test',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
},
},
});
@@ -125,7 +121,7 @@ describe('pipeline graph job component', () => {
id: 4259,
name: 'test',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
label: 'success',
tooltip: 'success',
},
@@ -165,4 +161,24 @@ describe('pipeline graph job component', () => {
expect(component.$el.querySelector(tooltipBoundary)).toBeNull();
});
});
+
+ describe('tooltipText', () => {
+ it('escapes job name', () => {
+ component = mountComponent(JobComponent, {
+ job: {
+ id: 4259,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'status_success',
+ label: 'success',
+ tooltip: 'failed',
+ },
+ },
+ });
+
+ expect(
+ component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title'),
+ ).toEqual('&lt;img src=x onerror=alert(document.domain)&gt; - failed');
+ });
+ });
});
diff --git a/spec/javascripts/pipelines/graph/job_name_component_spec.js b/spec/javascripts/pipelines/graph/job_name_component_spec.js
index 8e2071ba0b3..c861d452dd0 100644
--- a/spec/javascripts/pipelines/graph/job_name_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_name_component_spec.js
@@ -10,7 +10,7 @@ describe('job name component', () => {
propsData: {
name: 'foo',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
},
},
}).$mount();
diff --git a/spec/javascripts/pipelines/graph/mock_data.js b/spec/javascripts/pipelines/graph/mock_data.js
index 9e25a4b3fed..a4a5d78f906 100644
--- a/spec/javascripts/pipelines/graph/mock_data.js
+++ b/spec/javascripts/pipelines/graph/mock_data.js
@@ -13,7 +13,7 @@ export default {
path: '/root/ci-mock/pipelines/123',
details: {
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -33,7 +33,7 @@ export default {
name: 'test',
size: 1,
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -58,7 +58,7 @@ export default {
created_at: '2017-04-13T09:25:18.959Z',
updated_at: '2017-04-13T09:25:23.118Z',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -78,7 +78,7 @@ export default {
},
],
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -91,14 +91,14 @@ export default {
dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=test',
},
{
- name: 'deploy',
+ name: 'deploy <img src=x onerror=alert(document.domain)>',
title: 'deploy: passed',
groups: [
{
name: 'deploy to production',
size: 1,
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -123,7 +123,7 @@ export default {
created_at: '2017-04-19T14:29:46.463Z',
updated_at: '2017-04-19T14:30:27.498Z',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -145,7 +145,7 @@ export default {
name: 'deploy to staging',
size: 1,
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -170,7 +170,7 @@ export default {
created_at: '2017-04-18T16:32:08.420Z',
updated_at: '2017-04-18T16:32:12.631Z',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -190,7 +190,7 @@ export default {
},
],
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
diff --git a/spec/javascripts/pipelines/graph/stage_column_component_spec.js b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
index f744f1af5e6..f6e6bd3132e 100644
--- a/spec/javascripts/pipelines/graph/stage_column_component_spec.js
+++ b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
@@ -1,13 +1,16 @@
import Vue from 'vue';
import stageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('stage column component', () => {
let component;
+ const StageColumnComponent = Vue.extend(stageColumnComponent);
+
const mockJob = {
id: 4250,
name: 'test',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
@@ -22,7 +25,6 @@ describe('stage column component', () => {
};
beforeEach(() => {
- const StageColumnComponent = Vue.extend(stageColumnComponent);
const mockJobs = [];
for (let i = 0; i < 3; i += 1) {
@@ -31,12 +33,10 @@ describe('stage column component', () => {
mockJobs.push(mockedJob);
}
- component = new StageColumnComponent({
- propsData: {
- title: 'foo',
- jobs: mockJobs,
- },
- }).$mount();
+ component = mountComponent(StageColumnComponent, {
+ title: 'foo',
+ jobs: mockJobs,
+ });
});
it('should render provided title', () => {
@@ -46,4 +46,27 @@ describe('stage column component', () => {
it('should render the provided jobs', () => {
expect(component.$el.querySelectorAll('.builds-container > ul > li').length).toEqual(3);
});
+
+ describe('jobId', () => {
+ it('escapes job name', () => {
+ component = mountComponent(StageColumnComponent, {
+ jobs: [
+ {
+ id: 4259,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'icon_status_success',
+ label: 'success',
+ tooltip: '<img src=x onerror=alert(document.domain)>',
+ },
+ },
+ ],
+ title: 'test',
+ });
+
+ expect(
+ component.$el.querySelector('.builds-container li').getAttribute('id'),
+ ).toEqual('ci-badge-&lt;img src=x onerror=alert(document.domain)&gt;');
+ });
+ });
});
diff --git a/spec/javascripts/pipelines/header_component_spec.js b/spec/javascripts/pipelines/header_component_spec.js
index cecc7ceb53d..034d3b4957d 100644
--- a/spec/javascripts/pipelines/header_component_spec.js
+++ b/spec/javascripts/pipelines/header_component_spec.js
@@ -18,7 +18,7 @@ describe('Pipeline details header', () => {
details: {
status: {
group: 'failed',
- icon: 'ci-status-failed',
+ icon: 'status_failed',
label: 'failed',
text: 'failed',
details_path: 'path',
diff --git a/spec/javascripts/pipelines/pipeline_url_spec.js b/spec/javascripts/pipelines/pipeline_url_spec.js
index 4a4f2259d23..ddd580ae8b7 100644
--- a/spec/javascripts/pipelines/pipeline_url_spec.js
+++ b/spec/javascripts/pipelines/pipeline_url_spec.js
@@ -35,7 +35,9 @@ describe('Pipeline Url Component', () => {
},
}).$mount();
- expect(component.$el.querySelector('.js-pipeline-url-link').getAttribute('href')).toEqual('foo');
+ expect(component.$el.querySelector('.js-pipeline-url-link').getAttribute('href')).toEqual(
+ 'foo',
+ );
expect(component.$el.querySelector('.js-pipeline-url-link span').textContent).toEqual('#1');
});
@@ -61,11 +63,11 @@ describe('Pipeline Url Component', () => {
const image = component.$el.querySelector('.js-pipeline-url-user img');
- expect(
- component.$el.querySelector('.js-pipeline-url-user').getAttribute('href'),
- ).toEqual(mockData.pipeline.user.web_url);
+ expect(component.$el.querySelector('.js-pipeline-url-user').getAttribute('href')).toEqual(
+ mockData.pipeline.user.web_url,
+ );
expect(image.getAttribute('data-original-title')).toEqual(mockData.pipeline.user.name);
- expect(image.getAttribute('src')).toEqual(mockData.pipeline.user.avatar_url);
+ expect(image.getAttribute('src')).toEqual(`${mockData.pipeline.user.avatar_url}?width=20`);
});
it('should render "API" when no user is provided', () => {
@@ -100,7 +102,9 @@ describe('Pipeline Url Component', () => {
}).$mount();
expect(component.$el.querySelector('.js-pipeline-url-latest').textContent).toContain('latest');
- expect(component.$el.querySelector('.js-pipeline-url-yaml').textContent).toContain('yaml invalid');
+ expect(component.$el.querySelector('.js-pipeline-url-yaml').textContent).toContain(
+ 'yaml invalid',
+ );
expect(component.$el.querySelector('.js-pipeline-url-stuck').textContent).toContain('stuck');
});
@@ -121,9 +125,9 @@ describe('Pipeline Url Component', () => {
},
}).$mount();
- expect(
- component.$el.querySelector('.js-pipeline-url-autodevops').textContent.trim(),
- ).toEqual('Auto DevOps');
+ expect(component.$el.querySelector('.js-pipeline-url-autodevops').textContent.trim()).toEqual(
+ 'Auto DevOps',
+ );
});
it('should render error badge when pipeline has a failure reason set', () => {
@@ -142,6 +146,8 @@ describe('Pipeline Url Component', () => {
}).$mount();
expect(component.$el.querySelector('.js-pipeline-url-failure').textContent).toContain('error');
- expect(component.$el.querySelector('.js-pipeline-url-failure').getAttribute('data-original-title')).toContain('some reason');
+ expect(
+ component.$el.querySelector('.js-pipeline-url-failure').getAttribute('data-original-title'),
+ ).toContain('some reason');
});
});
diff --git a/spec/javascripts/pipelines/stage_spec.js b/spec/javascripts/pipelines/stage_spec.js
index 16f6db39d6a..3f6789759ae 100644
--- a/spec/javascripts/pipelines/stage_spec.js
+++ b/spec/javascripts/pipelines/stage_spec.js
@@ -20,7 +20,7 @@ describe('Pipelines stage component', () => {
stage: {
status: {
group: 'success',
- icon: 'icon_status_success',
+ icon: 'status_success',
title: 'success',
},
dropdown_path: 'path.json',
@@ -84,7 +84,7 @@ describe('Pipelines stage component', () => {
component.stage = {
status: {
group: 'running',
- icon: 'running',
+ icon: 'status_running',
title: 'running',
},
dropdown_path: 'bar.json',
diff --git a/spec/javascripts/profile/add_ssh_key_validation_spec.js b/spec/javascripts/profile/add_ssh_key_validation_spec.js
new file mode 100644
index 00000000000..c71a2885acc
--- /dev/null
+++ b/spec/javascripts/profile/add_ssh_key_validation_spec.js
@@ -0,0 +1,69 @@
+import AddSshKeyValidation from '../../../app/assets/javascripts/profile/add_ssh_key_validation';
+
+describe('AddSshKeyValidation', () => {
+ describe('submit', () => {
+ it('returns true if isValid is true', () => {
+ const addSshKeyValidation = new AddSshKeyValidation({});
+ spyOn(AddSshKeyValidation, 'isPublicKey').and.returnValue(true);
+
+ expect(addSshKeyValidation.submit()).toBeTruthy();
+ });
+
+ it('calls preventDefault and toggleWarning if isValid is false', () => {
+ const addSshKeyValidation = new AddSshKeyValidation({});
+ const event = jasmine.createSpyObj('event', ['preventDefault']);
+ spyOn(AddSshKeyValidation, 'isPublicKey').and.returnValue(false);
+ spyOn(addSshKeyValidation, 'toggleWarning');
+
+ addSshKeyValidation.submit(event);
+
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(addSshKeyValidation.toggleWarning).toHaveBeenCalledWith(true);
+ });
+ });
+
+ describe('toggleWarning', () => {
+ it('shows warningElement and hides originalSubmitElement if isVisible is true', () => {
+ const warningElement = document.createElement('div');
+ const originalSubmitElement = document.createElement('div');
+ warningElement.classList.add('hide');
+
+ const addSshKeyValidation = new AddSshKeyValidation(
+ {},
+ warningElement,
+ originalSubmitElement,
+ );
+ addSshKeyValidation.toggleWarning(true);
+
+ expect(warningElement.classList.contains('hide')).toBeFalsy();
+ expect(originalSubmitElement.classList.contains('hide')).toBeTruthy();
+ });
+
+ it('hides warningElement and shows originalSubmitElement if isVisible is false', () => {
+ const warningElement = document.createElement('div');
+ const originalSubmitElement = document.createElement('div');
+ originalSubmitElement.classList.add('hide');
+
+ const addSshKeyValidation = new AddSshKeyValidation(
+ {},
+ warningElement,
+ originalSubmitElement,
+ );
+ addSshKeyValidation.toggleWarning(false);
+
+ expect(warningElement.classList.contains('hide')).toBeTruthy();
+ expect(originalSubmitElement.classList.contains('hide')).toBeFalsy();
+ });
+ });
+
+ describe('isPublicKey', () => {
+ it('returns false if probably invalid public ssh key', () => {
+ expect(AddSshKeyValidation.isPublicKey('nope')).toBeFalsy();
+ });
+
+ it('returns true if probably valid public ssh key', () => {
+ expect(AddSshKeyValidation.isPublicKey('ssh-')).toBeTruthy();
+ expect(AddSshKeyValidation.isPublicKey('ecdsa-sha2-')).toBeTruthy();
+ });
+ });
+});
diff --git a/spec/javascripts/projects_dropdown/components/app_spec.js b/spec/javascripts/projects_dropdown/components/app_spec.js
deleted file mode 100644
index 38b31c3d727..00000000000
--- a/spec/javascripts/projects_dropdown/components/app_spec.js
+++ /dev/null
@@ -1,349 +0,0 @@
-import Vue from 'vue';
-
-import bp from '~/breakpoints';
-import appComponent from '~/projects_dropdown/components/app.vue';
-import eventHub from '~/projects_dropdown/event_hub';
-import ProjectsStore from '~/projects_dropdown/store/projects_store';
-import ProjectsService from '~/projects_dropdown/service/projects_service';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { currentSession, mockProject, mockRawProject } from '../mock_data';
-
-const createComponent = () => {
- gon.api_version = currentSession.apiVersion;
- const Component = Vue.extend(appComponent);
- const store = new ProjectsStore();
- const service = new ProjectsService(currentSession.username);
-
- return mountComponent(Component, {
- store,
- service,
- currentUserName: currentSession.username,
- currentProject: currentSession.project,
- });
-};
-
-const returnServicePromise = (data, failed) =>
- new Promise((resolve, reject) => {
- if (failed) {
- reject(data);
- } else {
- resolve({
- json() {
- return data;
- },
- });
- }
- });
-
-describe('AppComponent', () => {
- describe('computed', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('frequentProjects', () => {
- it('should return list of frequently accessed projects from store', () => {
- expect(vm.frequentProjects).toBeDefined();
- expect(vm.frequentProjects.length).toBe(0);
-
- vm.store.setFrequentProjects([mockProject]);
- expect(vm.frequentProjects).toBeDefined();
- expect(vm.frequentProjects.length).toBe(1);
- });
- });
-
- describe('searchProjects', () => {
- it('should return list of frequently accessed projects from store', () => {
- expect(vm.searchProjects).toBeDefined();
- expect(vm.searchProjects.length).toBe(0);
-
- vm.store.setSearchedProjects([mockRawProject]);
- expect(vm.searchProjects).toBeDefined();
- expect(vm.searchProjects.length).toBe(1);
- });
- });
- });
-
- describe('methods', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('toggleFrequentProjectsList', () => {
- it('should toggle props which control visibility of Frequent Projects list from state passed', () => {
- vm.toggleFrequentProjectsList(true);
- expect(vm.isLoadingProjects).toBeFalsy();
- expect(vm.isSearchListVisible).toBeFalsy();
- expect(vm.isFrequentsListVisible).toBeTruthy();
-
- vm.toggleFrequentProjectsList(false);
- expect(vm.isLoadingProjects).toBeTruthy();
- expect(vm.isSearchListVisible).toBeTruthy();
- expect(vm.isFrequentsListVisible).toBeFalsy();
- });
- });
-
- describe('toggleSearchProjectsList', () => {
- it('should toggle props which control visibility of Searched Projects list from state passed', () => {
- vm.toggleSearchProjectsList(true);
- expect(vm.isLoadingProjects).toBeFalsy();
- expect(vm.isFrequentsListVisible).toBeFalsy();
- expect(vm.isSearchListVisible).toBeTruthy();
-
- vm.toggleSearchProjectsList(false);
- expect(vm.isLoadingProjects).toBeTruthy();
- expect(vm.isFrequentsListVisible).toBeTruthy();
- expect(vm.isSearchListVisible).toBeFalsy();
- });
- });
-
- describe('toggleLoader', () => {
- it('should toggle props which control visibility of list loading animation from state passed', () => {
- vm.toggleLoader(true);
- expect(vm.isFrequentsListVisible).toBeFalsy();
- expect(vm.isSearchListVisible).toBeFalsy();
- expect(vm.isLoadingProjects).toBeTruthy();
-
- vm.toggleLoader(false);
- expect(vm.isFrequentsListVisible).toBeTruthy();
- expect(vm.isSearchListVisible).toBeTruthy();
- expect(vm.isLoadingProjects).toBeFalsy();
- });
- });
-
- describe('fetchFrequentProjects', () => {
- it('should set props for loading animation to `true` while frequent projects list is being loaded', () => {
- spyOn(vm, 'toggleLoader');
-
- vm.fetchFrequentProjects();
- expect(vm.isLocalStorageFailed).toBeFalsy();
- expect(vm.toggleLoader).toHaveBeenCalledWith(true);
- });
-
- it('should set props for loading animation to `false` and props for frequent projects list to `true` once data is loaded', () => {
- const mockData = [mockProject];
-
- spyOn(vm.service, 'getFrequentProjects').and.returnValue(mockData);
- spyOn(vm.store, 'setFrequentProjects');
- spyOn(vm, 'toggleFrequentProjectsList');
-
- vm.fetchFrequentProjects();
- expect(vm.service.getFrequentProjects).toHaveBeenCalled();
- expect(vm.store.setFrequentProjects).toHaveBeenCalledWith(mockData);
- expect(vm.toggleFrequentProjectsList).toHaveBeenCalledWith(true);
- });
-
- it('should set props for failure message to `true` when method fails to fetch frequent projects list', () => {
- spyOn(vm.service, 'getFrequentProjects').and.returnValue(null);
- spyOn(vm.store, 'setFrequentProjects');
- spyOn(vm, 'toggleFrequentProjectsList');
-
- expect(vm.isLocalStorageFailed).toBeFalsy();
-
- vm.fetchFrequentProjects();
- expect(vm.service.getFrequentProjects).toHaveBeenCalled();
- expect(vm.store.setFrequentProjects).toHaveBeenCalledWith([]);
- expect(vm.toggleFrequentProjectsList).toHaveBeenCalledWith(true);
- expect(vm.isLocalStorageFailed).toBeTruthy();
- });
-
- it('should set props for search results list to `true` if search query was already made previously', () => {
- spyOn(bp, 'getBreakpointSize').and.returnValue('md');
- spyOn(vm.service, 'getFrequentProjects');
- spyOn(vm, 'toggleSearchProjectsList');
-
- vm.searchQuery = 'test';
- vm.fetchFrequentProjects();
- expect(vm.service.getFrequentProjects).not.toHaveBeenCalled();
- expect(vm.toggleSearchProjectsList).toHaveBeenCalledWith(true);
- });
-
- it('should set props for frequent projects list to `true` if search query was already made but screen size is less than 768px', () => {
- spyOn(bp, 'getBreakpointSize').and.returnValue('sm');
- spyOn(vm, 'toggleSearchProjectsList');
- spyOn(vm.service, 'getFrequentProjects');
-
- vm.searchQuery = 'test';
- vm.fetchFrequentProjects();
- expect(vm.service.getFrequentProjects).toHaveBeenCalled();
- expect(vm.toggleSearchProjectsList).not.toHaveBeenCalled();
- });
- });
-
- describe('fetchSearchedProjects', () => {
- const searchQuery = 'test';
-
- it('should perform search with provided search query', done => {
- const mockData = [mockRawProject];
- spyOn(vm, 'toggleLoader');
- spyOn(vm, 'toggleSearchProjectsList');
- spyOn(vm.service, 'getSearchedProjects').and.returnValue(returnServicePromise(mockData));
- spyOn(vm.store, 'setSearchedProjects');
-
- vm.fetchSearchedProjects(searchQuery);
- setTimeout(() => {
- expect(vm.searchQuery).toBe(searchQuery);
- expect(vm.toggleLoader).toHaveBeenCalledWith(true);
- expect(vm.service.getSearchedProjects).toHaveBeenCalledWith(searchQuery);
- expect(vm.toggleSearchProjectsList).toHaveBeenCalledWith(true);
- expect(vm.store.setSearchedProjects).toHaveBeenCalledWith(mockData);
- done();
- }, 0);
- });
-
- it('should update props for showing search failure', done => {
- spyOn(vm, 'toggleSearchProjectsList');
- spyOn(vm.service, 'getSearchedProjects').and.returnValue(returnServicePromise({}, true));
-
- vm.fetchSearchedProjects(searchQuery);
- setTimeout(() => {
- expect(vm.searchQuery).toBe(searchQuery);
- expect(vm.service.getSearchedProjects).toHaveBeenCalledWith(searchQuery);
- expect(vm.isSearchFailed).toBeTruthy();
- expect(vm.toggleSearchProjectsList).toHaveBeenCalledWith(true);
- done();
- }, 0);
- });
- });
-
- describe('logCurrentProjectAccess', () => {
- it('should log current project access via service', done => {
- spyOn(vm.service, 'logProjectAccess');
-
- vm.currentProject = mockProject;
- vm.logCurrentProjectAccess();
-
- setTimeout(() => {
- expect(vm.service.logProjectAccess).toHaveBeenCalledWith(mockProject);
- done();
- }, 1);
- });
- });
-
- describe('handleSearchClear', () => {
- it('should show frequent projects list when search input is cleared', () => {
- spyOn(vm.store, 'clearSearchedProjects');
- spyOn(vm, 'toggleFrequentProjectsList');
-
- vm.handleSearchClear();
-
- expect(vm.toggleFrequentProjectsList).toHaveBeenCalledWith(true);
- expect(vm.store.clearSearchedProjects).toHaveBeenCalled();
- expect(vm.searchQuery).toBe('');
- });
- });
-
- describe('handleSearchFailure', () => {
- it('should show failure message within dropdown', () => {
- spyOn(vm, 'toggleSearchProjectsList');
-
- vm.handleSearchFailure();
- expect(vm.toggleSearchProjectsList).toHaveBeenCalledWith(true);
- expect(vm.isSearchFailed).toBeTruthy();
- });
- });
- });
-
- describe('created', () => {
- it('should bind event listeners on eventHub', done => {
- spyOn(eventHub, '$on');
-
- createComponent().$mount();
-
- Vue.nextTick(() => {
- expect(eventHub.$on).toHaveBeenCalledWith('dropdownOpen', jasmine.any(Function));
- expect(eventHub.$on).toHaveBeenCalledWith('searchProjects', jasmine.any(Function));
- expect(eventHub.$on).toHaveBeenCalledWith('searchCleared', jasmine.any(Function));
- expect(eventHub.$on).toHaveBeenCalledWith('searchFailed', jasmine.any(Function));
- done();
- });
- });
- });
-
- describe('beforeDestroy', () => {
- it('should unbind event listeners on eventHub', done => {
- const vm = createComponent();
- spyOn(eventHub, '$off');
-
- vm.$mount();
- vm.$destroy();
-
- Vue.nextTick(() => {
- expect(eventHub.$off).toHaveBeenCalledWith('dropdownOpen', jasmine.any(Function));
- expect(eventHub.$off).toHaveBeenCalledWith('searchProjects', jasmine.any(Function));
- expect(eventHub.$off).toHaveBeenCalledWith('searchCleared', jasmine.any(Function));
- expect(eventHub.$off).toHaveBeenCalledWith('searchFailed', jasmine.any(Function));
- done();
- });
- });
- });
-
- describe('template', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('should render search input', () => {
- expect(vm.$el.querySelector('.search-input-container')).toBeDefined();
- });
-
- it('should render loading animation', done => {
- vm.toggleLoader(true);
- Vue.nextTick(() => {
- const loadingEl = vm.$el.querySelector('.loading-animation');
-
- expect(loadingEl).toBeDefined();
- expect(loadingEl.classList.contains('prepend-top-20')).toBeTruthy();
- expect(loadingEl.querySelector('i').getAttribute('aria-label')).toBe('Loading projects');
- done();
- });
- });
-
- it('should render frequent projects list header', done => {
- vm.toggleFrequentProjectsList(true);
- Vue.nextTick(() => {
- const sectionHeaderEl = vm.$el.querySelector('.section-header');
-
- expect(sectionHeaderEl).toBeDefined();
- expect(sectionHeaderEl.innerText.trim()).toBe('Frequently visited');
- done();
- });
- });
-
- it('should render frequent projects list', done => {
- vm.toggleFrequentProjectsList(true);
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.projects-list-frequent-container')).toBeDefined();
- done();
- });
- });
-
- it('should render searched projects list', done => {
- vm.toggleSearchProjectsList(true);
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.section-header')).toBe(null);
- expect(vm.$el.querySelector('.projects-list-search-container')).toBeDefined();
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js b/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js
deleted file mode 100644
index 2bafb4e81ca..00000000000
--- a/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import Vue from 'vue';
-
-import projectsListFrequentComponent from '~/projects_dropdown/components/projects_list_frequent.vue';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { mockFrequents } from '../mock_data';
-
-const createComponent = () => {
- const Component = Vue.extend(projectsListFrequentComponent);
-
- return mountComponent(Component, {
- projects: mockFrequents,
- localStorageFailed: false,
- });
-};
-
-describe('ProjectsListFrequentComponent', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('computed', () => {
- describe('isListEmpty', () => {
- it('should return `true` or `false` representing whether if `projects` is empty of not', () => {
- vm.projects = [];
- expect(vm.isListEmpty).toBeTruthy();
-
- vm.projects = mockFrequents;
- expect(vm.isListEmpty).toBeFalsy();
- });
- });
-
- describe('listEmptyMessage', () => {
- it('should return appropriate empty list message based on value of `localStorageFailed` prop', () => {
- vm.localStorageFailed = true;
- expect(vm.listEmptyMessage).toBe('This feature requires browser localStorage support');
-
- vm.localStorageFailed = false;
- expect(vm.listEmptyMessage).toBe('Projects you visit often will appear here');
- });
- });
- });
-
- describe('template', () => {
- it('should render component element with list of projects', (done) => {
- vm.projects = mockFrequents;
-
- Vue.nextTick(() => {
- expect(vm.$el.classList.contains('projects-list-frequent-container')).toBeTruthy();
- expect(vm.$el.querySelectorAll('ul.list-unstyled').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(5);
- done();
- });
- });
-
- it('should render component element with empty message', (done) => {
- vm.projects = [];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('li.section-empty').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(0);
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js b/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js
deleted file mode 100644
index c193258474e..00000000000
--- a/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import Vue from 'vue';
-
-import projectsListItemComponent from '~/projects_dropdown/components/projects_list_item.vue';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { mockProject } from '../mock_data';
-
-const createComponent = () => {
- const Component = Vue.extend(projectsListItemComponent);
-
- return mountComponent(Component, {
- projectId: mockProject.id,
- projectName: mockProject.name,
- namespace: mockProject.namespace,
- webUrl: mockProject.webUrl,
- avatarUrl: mockProject.avatarUrl,
- });
-};
-
-describe('ProjectsListItemComponent', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('computed', () => {
- describe('hasAvatar', () => {
- it('should return `true` or `false` if whether avatar is present or not', () => {
- vm.avatarUrl = 'path/to/avatar.png';
- expect(vm.hasAvatar).toBeTruthy();
-
- vm.avatarUrl = null;
- expect(vm.hasAvatar).toBeFalsy();
- });
- });
-
- describe('highlightedProjectName', () => {
- it('should enclose part of project name in <b> & </b> which matches with `matcher` prop', () => {
- vm.matcher = 'lab';
- expect(vm.highlightedProjectName).toContain('<b>Lab</b>');
- });
-
- it('should return project name as it is if `matcher` is not available', () => {
- vm.matcher = null;
- expect(vm.highlightedProjectName).toBe(mockProject.name);
- });
- });
-
- describe('truncatedNamespace', () => {
- it('should truncate project name from namespace string', () => {
- vm.namespace = 'platform / nokia-3310';
- expect(vm.truncatedNamespace).toBe('platform');
- });
-
- it('should truncate namespace string from the middle if it includes more than two groups in path', () => {
- vm.namespace = 'platform / hardware / broadcom / Wifi Group / Mobile Chipset / nokia-3310';
- expect(vm.truncatedNamespace).toBe('platform / ... / Mobile Chipset');
- });
- });
- });
-
- describe('template', () => {
- it('should render component element', () => {
- expect(vm.$el.classList.contains('projects-list-item-container')).toBeTruthy();
- expect(vm.$el.querySelectorAll('a').length).toBe(1);
- expect(vm.$el.querySelectorAll('.project-item-avatar-container').length).toBe(1);
- expect(vm.$el.querySelectorAll('.project-item-metadata-container').length).toBe(1);
- expect(vm.$el.querySelectorAll('.project-title').length).toBe(1);
- expect(vm.$el.querySelectorAll('.project-namespace').length).toBe(1);
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js b/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js
deleted file mode 100644
index c4b86d77034..00000000000
--- a/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import Vue from 'vue';
-
-import projectsListSearchComponent from '~/projects_dropdown/components/projects_list_search.vue';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { mockProject } from '../mock_data';
-
-const createComponent = () => {
- const Component = Vue.extend(projectsListSearchComponent);
-
- return mountComponent(Component, {
- projects: [mockProject],
- matcher: 'lab',
- searchFailed: false,
- });
-};
-
-describe('ProjectsListSearchComponent', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('computed', () => {
- describe('isListEmpty', () => {
- it('should return `true` or `false` representing whether if `projects` is empty of not', () => {
- vm.projects = [];
- expect(vm.isListEmpty).toBeTruthy();
-
- vm.projects = [mockProject];
- expect(vm.isListEmpty).toBeFalsy();
- });
- });
-
- describe('listEmptyMessage', () => {
- it('should return appropriate empty list message based on value of `searchFailed` prop', () => {
- vm.searchFailed = true;
- expect(vm.listEmptyMessage).toBe('Something went wrong on our end.');
-
- vm.searchFailed = false;
- expect(vm.listEmptyMessage).toBe('Sorry, no projects matched your search');
- });
- });
- });
-
- describe('template', () => {
- it('should render component element with list of projects', (done) => {
- vm.projects = [mockProject];
-
- Vue.nextTick(() => {
- expect(vm.$el.classList.contains('projects-list-search-container')).toBeTruthy();
- expect(vm.$el.querySelectorAll('ul.list-unstyled').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(1);
- done();
- });
- });
-
- it('should render component element with empty message', (done) => {
- vm.projects = [];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('li.section-empty').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(0);
- done();
- });
- });
-
- it('should render component element with failure message', (done) => {
- vm.searchFailed = true;
- vm.projects = [];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('li.section-empty.section-failure').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(0);
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/components/search_spec.js b/spec/javascripts/projects_dropdown/components/search_spec.js
deleted file mode 100644
index 427f5024e3a..00000000000
--- a/spec/javascripts/projects_dropdown/components/search_spec.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import Vue from 'vue';
-
-import searchComponent from '~/projects_dropdown/components/search.vue';
-import eventHub from '~/projects_dropdown/event_hub';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-const createComponent = () => {
- const Component = Vue.extend(searchComponent);
-
- return mountComponent(Component);
-};
-
-describe('SearchComponent', () => {
- describe('methods', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('setFocus', () => {
- it('should set focus to search input', () => {
- spyOn(vm.$refs.search, 'focus');
-
- vm.setFocus();
- expect(vm.$refs.search.focus).toHaveBeenCalled();
- });
- });
-
- describe('emitSearchEvents', () => {
- it('should emit `searchProjects` event via eventHub when `searchQuery` present', () => {
- const searchQuery = 'test';
- spyOn(eventHub, '$emit');
- vm.searchQuery = searchQuery;
- vm.emitSearchEvents();
- expect(eventHub.$emit).toHaveBeenCalledWith('searchProjects', searchQuery);
- });
-
- it('should emit `searchCleared` event via eventHub when `searchQuery` is cleared', () => {
- spyOn(eventHub, '$emit');
- vm.searchQuery = '';
- vm.emitSearchEvents();
- expect(eventHub.$emit).toHaveBeenCalledWith('searchCleared');
- });
- });
- });
-
- describe('mounted', () => {
- it('should listen `dropdownOpen` event', (done) => {
- spyOn(eventHub, '$on');
- createComponent();
-
- Vue.nextTick(() => {
- expect(eventHub.$on).toHaveBeenCalledWith('dropdownOpen', jasmine.any(Function));
- done();
- });
- });
- });
-
- describe('beforeDestroy', () => {
- it('should unbind event listeners on eventHub', (done) => {
- const vm = createComponent();
- spyOn(eventHub, '$off');
-
- vm.$mount();
- vm.$destroy();
-
- Vue.nextTick(() => {
- expect(eventHub.$off).toHaveBeenCalledWith('dropdownOpen', jasmine.any(Function));
- done();
- });
- });
- });
-
- describe('template', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('should render component element', () => {
- const inputEl = vm.$el.querySelector('input.form-control');
-
- expect(vm.$el.classList.contains('search-input-container')).toBeTruthy();
- expect(inputEl).not.toBe(null);
- expect(inputEl.getAttribute('placeholder')).toBe('Search your projects');
- expect(vm.$el.querySelector('.search-icon')).toBeDefined();
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/mock_data.js b/spec/javascripts/projects_dropdown/mock_data.js
deleted file mode 100644
index d6a79fb8ac1..00000000000
--- a/spec/javascripts/projects_dropdown/mock_data.js
+++ /dev/null
@@ -1,96 +0,0 @@
-export const currentSession = {
- username: 'root',
- storageKey: 'root/frequent-projects',
- apiVersion: 'v4',
- project: {
- id: 1,
- name: 'dummy-project',
- namespace: 'SamepleGroup / Dummy-Project',
- webUrl: 'http://127.0.0.1/samplegroup/dummy-project',
- avatarUrl: null,
- lastAccessedOn: Date.now(),
- },
-};
-
-export const mockProject = {
- id: 1,
- name: 'GitLab Community Edition',
- namespace: 'gitlab-org / gitlab-ce',
- webUrl: 'http://127.0.0.1:3000/gitlab-org/gitlab-ce',
- avatarUrl: null,
-};
-
-export const mockRawProject = {
- id: 1,
- name: 'GitLab Community Edition',
- name_with_namespace: 'gitlab-org / gitlab-ce',
- web_url: 'http://127.0.0.1:3000/gitlab-org/gitlab-ce',
- avatar_url: null,
-};
-
-export const mockFrequents = [
- {
- id: 1,
- name: 'GitLab Community Edition',
- namespace: 'gitlab-org / gitlab-ce',
- webUrl: 'http://127.0.0.1:3000/gitlab-org/gitlab-ce',
- avatarUrl: null,
- },
- {
- id: 2,
- name: 'GitLab CI',
- namespace: 'gitlab-org / gitlab-ci',
- webUrl: 'http://127.0.0.1:3000/gitlab-org/gitlab-ci',
- avatarUrl: null,
- },
- {
- id: 3,
- name: 'Typeahead.Js',
- namespace: 'twitter / typeahead-js',
- webUrl: 'http://127.0.0.1:3000/twitter/typeahead-js',
- avatarUrl: '/uploads/-/system/project/avatar/7/TWBS.png',
- },
- {
- id: 4,
- name: 'Intel',
- namespace: 'platform / hardware / bsp / intel',
- webUrl: 'http://127.0.0.1:3000/platform/hardware/bsp/intel',
- avatarUrl: null,
- },
- {
- id: 5,
- name: 'v4.4',
- namespace: 'platform / hardware / bsp / kernel / common / v4.4',
- webUrl: 'http://localhost:3000/platform/hardware/bsp/kernel/common/v4.4',
- avatarUrl: null,
- },
-];
-
-export const unsortedFrequents = [
- { id: 1, frequency: 12, lastAccessedOn: 1491400843391 },
- { id: 2, frequency: 14, lastAccessedOn: 1488240890738 },
- { id: 3, frequency: 44, lastAccessedOn: 1497675908472 },
- { id: 4, frequency: 8, lastAccessedOn: 1497979281815 },
- { id: 5, frequency: 34, lastAccessedOn: 1488089211943 },
- { id: 6, frequency: 14, lastAccessedOn: 1493517292488 },
- { id: 7, frequency: 42, lastAccessedOn: 1486815299875 },
- { id: 8, frequency: 33, lastAccessedOn: 1500762279114 },
- { id: 10, frequency: 46, lastAccessedOn: 1483251641543 },
-];
-
-/**
- * This const has a specific order which tests authenticity
- * of `ProjectsService.getTopFrequentProjects` method so
- * DO NOT change order of items in this const.
- */
-export const sortedFrequents = [
- { id: 10, frequency: 46, lastAccessedOn: 1483251641543 },
- { id: 3, frequency: 44, lastAccessedOn: 1497675908472 },
- { id: 7, frequency: 42, lastAccessedOn: 1486815299875 },
- { id: 5, frequency: 34, lastAccessedOn: 1488089211943 },
- { id: 8, frequency: 33, lastAccessedOn: 1500762279114 },
- { id: 6, frequency: 14, lastAccessedOn: 1493517292488 },
- { id: 2, frequency: 14, lastAccessedOn: 1488240890738 },
- { id: 1, frequency: 12, lastAccessedOn: 1491400843391 },
- { id: 4, frequency: 8, lastAccessedOn: 1497979281815 },
-];
diff --git a/spec/javascripts/projects_dropdown/service/projects_service_spec.js b/spec/javascripts/projects_dropdown/service/projects_service_spec.js
deleted file mode 100644
index cfd1bb7d24f..00000000000
--- a/spec/javascripts/projects_dropdown/service/projects_service_spec.js
+++ /dev/null
@@ -1,179 +0,0 @@
-import Vue from 'vue';
-import VueResource from 'vue-resource';
-
-import bp from '~/breakpoints';
-import ProjectsService from '~/projects_dropdown/service/projects_service';
-import { FREQUENT_PROJECTS } from '~/projects_dropdown/constants';
-import { currentSession, unsortedFrequents, sortedFrequents } from '../mock_data';
-
-Vue.use(VueResource);
-
-FREQUENT_PROJECTS.MAX_COUNT = 3;
-
-describe('ProjectsService', () => {
- let service;
-
- beforeEach(() => {
- gon.api_version = currentSession.apiVersion;
- gon.current_user_id = 1;
- service = new ProjectsService(currentSession.username);
- });
-
- describe('contructor', () => {
- it('should initialize default properties of class', () => {
- expect(service.isLocalStorageAvailable).toBeTruthy();
- expect(service.currentUserName).toBe(currentSession.username);
- expect(service.storageKey).toBe(currentSession.storageKey);
- expect(service.projectsPath).toBeDefined();
- });
- });
-
- describe('getSearchedProjects', () => {
- it('should return promise from VueResource HTTP GET', () => {
- spyOn(service.projectsPath, 'get').and.stub();
-
- const searchQuery = 'lab';
- const queryParams = {
- simple: true,
- per_page: 20,
- membership: true,
- order_by: 'last_activity_at',
- search: searchQuery,
- };
-
- service.getSearchedProjects(searchQuery);
- expect(service.projectsPath.get).toHaveBeenCalledWith(queryParams);
- });
- });
-
- describe('logProjectAccess', () => {
- let storage;
-
- beforeEach(() => {
- storage = {};
-
- spyOn(window.localStorage, 'setItem').and.callFake((storageKey, value) => {
- storage[storageKey] = value;
- });
-
- spyOn(window.localStorage, 'getItem').and.callFake((storageKey) => {
- if (storage[storageKey]) {
- return storage[storageKey];
- }
-
- return null;
- });
- });
-
- it('should create a project store if it does not exist and adds a project', () => {
- service.logProjectAccess(currentSession.project);
-
- const projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects.length).toBe(1);
- expect(projects[0].frequency).toBe(1);
- expect(projects[0].lastAccessedOn).toBeDefined();
- });
-
- it('should prevent inserting same report multiple times into store', () => {
- service.logProjectAccess(currentSession.project);
- service.logProjectAccess(currentSession.project);
-
- const projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects.length).toBe(1);
- });
-
- it('should increase frequency of report if it was logged multiple times over the course of an hour', () => {
- let projects;
- spyOn(Math, 'abs').and.returnValue(3600001); // this will lead to `diff` > 1;
- service.logProjectAccess(currentSession.project);
-
- projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects[0].frequency).toBe(1);
-
- service.logProjectAccess(currentSession.project);
- projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects[0].frequency).toBe(2);
- expect(projects[0].lastAccessedOn).not.toBe(currentSession.project.lastAccessedOn);
- });
-
- it('should always update project metadata', () => {
- let projects;
- const oldProject = {
- ...currentSession.project,
- };
-
- const newProject = {
- ...currentSession.project,
- name: 'New Name',
- avatarUrl: 'new/avatar.png',
- namespace: 'New / Namespace',
- webUrl: 'http://localhost/new/web/url',
- };
-
- service.logProjectAccess(oldProject);
- projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects[0].name).toBe(oldProject.name);
- expect(projects[0].avatarUrl).toBe(oldProject.avatarUrl);
- expect(projects[0].namespace).toBe(oldProject.namespace);
- expect(projects[0].webUrl).toBe(oldProject.webUrl);
-
- service.logProjectAccess(newProject);
- projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects[0].name).toBe(newProject.name);
- expect(projects[0].avatarUrl).toBe(newProject.avatarUrl);
- expect(projects[0].namespace).toBe(newProject.namespace);
- expect(projects[0].webUrl).toBe(newProject.webUrl);
- });
-
- it('should not add more than 20 projects in store', () => {
- for (let i = 1; i <= 5; i += 1) {
- const project = Object.assign(currentSession.project, { id: i });
- service.logProjectAccess(project);
- }
-
- const projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects.length).toBe(3);
- });
- });
-
- describe('getTopFrequentProjects', () => {
- let storage = {};
-
- beforeEach(() => {
- storage[currentSession.storageKey] = JSON.stringify(unsortedFrequents);
-
- spyOn(window.localStorage, 'getItem').and.callFake((storageKey) => {
- if (storage[storageKey]) {
- return storage[storageKey];
- }
-
- return null;
- });
- });
-
- it('should return top 5 frequently accessed projects for desktop screens', () => {
- spyOn(bp, 'getBreakpointSize').and.returnValue('md');
- const frequentProjects = service.getTopFrequentProjects();
-
- expect(frequentProjects.length).toBe(5);
- frequentProjects.forEach((project, index) => {
- expect(project.id).toBe(sortedFrequents[index].id);
- });
- });
-
- it('should return top 3 frequently accessed projects for mobile screens', () => {
- spyOn(bp, 'getBreakpointSize').and.returnValue('sm');
- const frequentProjects = service.getTopFrequentProjects();
-
- expect(frequentProjects.length).toBe(3);
- frequentProjects.forEach((project, index) => {
- expect(project.id).toBe(sortedFrequents[index].id);
- });
- });
-
- it('should return empty array if there are no projects available in store', () => {
- storage = {};
- expect(service.getTopFrequentProjects().length).toBe(0);
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/store/projects_store_spec.js b/spec/javascripts/projects_dropdown/store/projects_store_spec.js
deleted file mode 100644
index e57399d37cd..00000000000
--- a/spec/javascripts/projects_dropdown/store/projects_store_spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import ProjectsStore from '~/projects_dropdown/store/projects_store';
-import { mockProject, mockRawProject } from '../mock_data';
-
-describe('ProjectsStore', () => {
- let store;
-
- beforeEach(() => {
- store = new ProjectsStore();
- });
-
- describe('setFrequentProjects', () => {
- it('should set frequent projects list to state', () => {
- store.setFrequentProjects([mockProject]);
-
- expect(store.getFrequentProjects().length).toBe(1);
- expect(store.getFrequentProjects()[0].id).toBe(mockProject.id);
- });
- });
-
- describe('setSearchedProjects', () => {
- it('should set searched projects list to state', () => {
- store.setSearchedProjects([mockRawProject]);
-
- const processedProjects = store.getSearchedProjects();
- expect(processedProjects.length).toBe(1);
- expect(processedProjects[0].id).toBe(mockRawProject.id);
- expect(processedProjects[0].namespace).toBe(mockRawProject.name_with_namespace);
- expect(processedProjects[0].webUrl).toBe(mockRawProject.web_url);
- expect(processedProjects[0].avatarUrl).toBe(mockRawProject.avatar_url);
- });
- });
-
- describe('clearSearchedProjects', () => {
- it('should clear searched projects list from state', () => {
- store.setSearchedProjects([mockRawProject]);
- expect(store.getSearchedProjects().length).toBe(1);
- store.clearSearchedProjects();
- expect(store.getSearchedProjects().length).toBe(0);
- });
- });
-});
diff --git a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
new file mode 100644
index 00000000000..333cefe5f8a
--- /dev/null
+++ b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
@@ -0,0 +1,199 @@
+import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import state from '~/reports/store/state';
+import component from '~/reports/components/grouped_test_reports_app.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+import newFailedTestReports from '../mock_data/new_failures_report.json';
+import successTestReports from '../mock_data/no_failures_report.json';
+import mixedResultsTestReports from '../mock_data/new_and_fixed_failures_report.json';
+import resolvedFailures from '../mock_data/resolved_failures.json';
+
+describe('Grouped Test Reports App', () => {
+ let vm;
+ let mock;
+ const Component = Vue.extend(component);
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ vm.$store.replaceState(state());
+ vm.$destroy();
+ mock.restore();
+ });
+
+ describe('with success result', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, successTestReports, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders success summary text', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.fa-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary contained no changed test results out of 11 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain(
+ 'rspec:pg found no changed test results out of 8 total tests',
+ );
+ expect(vm.$el.textContent).toContain(
+ 'java ant found no changed test results out of 3 total tests',
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ describe('with 204 result', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(204, {}, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders success summary text', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.fa-spinner')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary results are being parsed',
+ );
+
+ done();
+ }, 0);
+ });
+ });
+
+ describe('with new failed result', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, newFailedTestReports, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders failed summary text + new badge', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.fa-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary contained 2 failed test results out of 11 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain(
+ 'rspec:pg found 2 failed test results out of 8 total tests',
+ );
+ expect(vm.$el.textContent).toContain('New');
+ expect(vm.$el.textContent).toContain(
+ 'java ant found no changed test results out of 3 total tests',
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ describe('with mixed results', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, mixedResultsTestReports, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders summary text', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.fa-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary contained 2 failed test results and 2 fixed test results out of 11 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain(
+ 'rspec:pg found 1 failed test result and 2 fixed test results out of 8 total tests',
+ );
+ expect(vm.$el.textContent).toContain('New');
+ expect(vm.$el.textContent).toContain(
+ ' java ant found 1 failed test result out of 3 total tests',
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ describe('with resolved failures', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, resolvedFailures, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders summary text', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.fa-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary contained 2 fixed test results out of 11 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain(
+ 'rspec:pg found 2 fixed test results out of 8 total tests',
+ );
+ done();
+ }, 0);
+ });
+
+ it('renders resolved failures', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-mr-code-resolved-issues').textContent).toContain(
+ resolvedFailures.suites[0].resolved_failures[0].name,
+ );
+ expect(vm.$el.querySelector('.js-mr-code-resolved-issues').textContent).toContain(
+ resolvedFailures.suites[0].resolved_failures[1].name,
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ describe('with error', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(500, {}, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders loading summary text with loading icon', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary failed loading results',
+ );
+ done();
+ }, 0);
+ });
+ });
+
+ describe('while loading', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, {}, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders loading summary text with loading icon', done => {
+ expect(vm.$el.querySelector('.fa-spinner')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary results are being parsed',
+ );
+
+ setTimeout(() => {
+ done();
+ }, 0);
+ });
+ });
+});
diff --git a/spec/javascripts/reports/components/modal_open_name_spec.js b/spec/javascripts/reports/components/modal_open_name_spec.js
new file mode 100644
index 00000000000..b18b3ef03d1
--- /dev/null
+++ b/spec/javascripts/reports/components/modal_open_name_spec.js
@@ -0,0 +1,45 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import component from '~/reports/components/modal_open_name.vue';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+
+describe('Modal open name', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const store = new Vuex.Store({
+ actions: {
+ openModal: () => {},
+ },
+ state: {},
+ mutations: {},
+ });
+
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: {
+ issue: {
+ title: 'Issue',
+ },
+ status: 'failed',
+ },
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders the issue name', () => {
+ expect(vm.$el.textContent.trim()).toEqual('Issue');
+ });
+
+ it('calls openModal actions when button is clicked', () => {
+ spyOn(vm, 'openModal');
+
+ vm.$el.click();
+
+ expect(vm.openModal).toHaveBeenCalled();
+ });
+});
diff --git a/spec/javascripts/reports/components/modal_spec.js b/spec/javascripts/reports/components/modal_spec.js
new file mode 100644
index 00000000000..3a567c40eca
--- /dev/null
+++ b/spec/javascripts/reports/components/modal_spec.js
@@ -0,0 +1,45 @@
+import Vue from 'vue';
+import component from '~/reports/components/modal.vue';
+import state from '~/reports/store/state';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+import { trimText } from '../../helpers/vue_component_helper';
+
+describe('Grouped Test Reports Modal', () => {
+ const Component = Vue.extend(component);
+ const modalDataStructure = state().modal.data;
+
+ // populate data
+ modalDataStructure.execution_time.value = 0.009411;
+ modalDataStructure.system_output.value = 'Failure/Error: is_expected.to eq(3)\n\n';
+ modalDataStructure.class.value = 'link';
+
+ let vm;
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ title: 'Test#sum when a is 1 and b is 2 returns summary',
+ modalData: modalDataStructure,
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders code block', () => {
+ expect(vm.$el.querySelector('code').textContent).toEqual(modalDataStructure.system_output.value);
+ });
+
+ it('renders link', () => {
+ expect(vm.$el.querySelector('.js-modal-link').getAttribute('href')).toEqual(modalDataStructure.class.value);
+ expect(trimText(vm.$el.querySelector('.js-modal-link').textContent)).toEqual(modalDataStructure.class.value);
+ });
+
+ it('renders miliseconds', () => {
+ expect(vm.$el.textContent).toContain(`${modalDataStructure.execution_time.value} ms`);
+ });
+
+ it('render title', () => {
+ expect(trimText(vm.$el.querySelector('.modal-title').textContent)).toEqual('Test#sum when a is 1 and b is 2 returns summary');
+ });
+});
diff --git a/spec/javascripts/reports/components/report_link_spec.js b/spec/javascripts/reports/components/report_link_spec.js
new file mode 100644
index 00000000000..cd6911e2f59
--- /dev/null
+++ b/spec/javascripts/reports/components/report_link_spec.js
@@ -0,0 +1,71 @@
+import Vue from 'vue';
+import component from '~/reports/components/report_link.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('report link', () => {
+ let vm;
+
+ const Component = Vue.extend(component);
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('With url', () => {
+ it('renders link', () => {
+ vm = mountComponent(Component, {
+ issue: {
+ path: 'Gemfile.lock',
+ urlPath: '/Gemfile.lock',
+ },
+ });
+
+ expect(vm.$el.textContent.trim()).toContain('in');
+ expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('/Gemfile.lock');
+ expect(vm.$el.querySelector('a').textContent.trim()).toEqual('Gemfile.lock');
+ });
+ });
+
+ describe('Without url', () => {
+ it('does not render link', () => {
+ vm = mountComponent(Component, {
+ issue: {
+ path: 'Gemfile.lock',
+ },
+ });
+
+ expect(vm.$el.querySelector('a')).toBeNull();
+ expect(vm.$el.textContent.trim()).toContain('in');
+ expect(vm.$el.textContent.trim()).toContain('Gemfile.lock');
+ });
+ });
+
+ describe('with line', () => {
+ it('renders line number', () => {
+ vm = mountComponent(Component, {
+ issue: {
+ path: 'Gemfile.lock',
+ urlPath:
+ 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
+ line: 22,
+ },
+ });
+
+ expect(vm.$el.querySelector('a').textContent.trim()).toContain('Gemfile.lock:22');
+ });
+ });
+
+ describe('without line', () => {
+ it('does not render line number', () => {
+ vm = mountComponent(Component, {
+ issue: {
+ path: 'Gemfile.lock',
+ urlPath:
+ 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
+ },
+ });
+
+ expect(vm.$el.querySelector('a').textContent.trim()).not.toContain(':22');
+ });
+ });
+});
diff --git a/spec/javascripts/reports/components/report_section_spec.js b/spec/javascripts/reports/components/report_section_spec.js
new file mode 100644
index 00000000000..6f6eb161d14
--- /dev/null
+++ b/spec/javascripts/reports/components/report_section_spec.js
@@ -0,0 +1,197 @@
+import Vue from 'vue';
+import reportSection from '~/reports/components/report_section.vue';
+import mountComponent, { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper';
+
+describe('Report section', () => {
+ let vm;
+ const ReportSection = Vue.extend(reportSection);
+
+ const resolvedIssues = [
+ {
+ name: 'Insecure Dependency',
+ fingerprint: 'ca2e59451e98ae60ba2f54e3857c50e5',
+ path: 'Gemfile.lock',
+ line: 12,
+ urlPath: 'foo/Gemfile.lock',
+ },
+ ];
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ beforeEach(() => {
+ vm = mountComponent(ReportSection, {
+ component: '',
+ status: 'SUCCESS',
+ loadingText: 'Loading codeclimate report',
+ errorText: 'foo',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ resolvedIssues,
+ hasIssues: false,
+ alwaysOpen: false,
+ });
+ });
+
+ describe('isCollapsible', () => {
+ const testMatrix = [
+ { hasIssues: false, alwaysOpen: false, isCollapsible: false },
+ { hasIssues: false, alwaysOpen: true, isCollapsible: false },
+ { hasIssues: true, alwaysOpen: false, isCollapsible: true },
+ { hasIssues: true, alwaysOpen: true, isCollapsible: false },
+ ];
+
+ testMatrix.forEach(({ hasIssues, alwaysOpen, isCollapsible }) => {
+ const issues = hasIssues ? 'has issues' : 'has no issues';
+ const open = alwaysOpen ? 'is always open' : 'is not always open';
+
+ it(`is ${isCollapsible}, if the report ${issues} and ${open}`, done => {
+ vm.hasIssues = hasIssues;
+ vm.alwaysOpen = alwaysOpen;
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.isCollapsible).toBe(isCollapsible);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('isExpanded', () => {
+ const testMatrix = [
+ { isCollapsed: false, alwaysOpen: false, isExpanded: true },
+ { isCollapsed: false, alwaysOpen: true, isExpanded: true },
+ { isCollapsed: true, alwaysOpen: false, isExpanded: false },
+ { isCollapsed: true, alwaysOpen: true, isExpanded: true },
+ ];
+
+ testMatrix.forEach(({ isCollapsed, alwaysOpen, isExpanded }) => {
+ const issues = isCollapsed ? 'is collapsed' : 'is not collapsed';
+ const open = alwaysOpen ? 'is always open' : 'is not always open';
+
+ it(`is ${isExpanded}, if the report ${issues} and ${open}`, done => {
+ vm.isCollapsed = isCollapsed;
+ vm.alwaysOpen = alwaysOpen;
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.isExpanded).toBe(isExpanded);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+ });
+ describe('when it is loading', () => {
+ it('should render loading indicator', () => {
+ vm = mountComponent(ReportSection, {
+ component: '',
+ status: 'LOADING',
+ loadingText: 'Loading codeclimate report',
+ errorText: 'foo',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ hasIssues: false,
+ });
+ expect(vm.$el.textContent.trim()).toEqual('Loading codeclimate report');
+ });
+ });
+
+ describe('with success status', () => {
+ beforeEach(() => {
+ vm = mountComponent(ReportSection, {
+ component: '',
+ status: 'SUCCESS',
+ loadingText: 'Loading codeclimate report',
+ errorText: 'foo',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ resolvedIssues,
+ hasIssues: true,
+ });
+ });
+
+ it('should render provided data', () => {
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Code quality improved on 1 point and degraded on 1 point',
+ );
+
+ expect(vm.$el.querySelectorAll('.js-mr-code-resolved-issues li').length).toEqual(
+ resolvedIssues.length,
+ );
+ });
+
+ describe('toggleCollapsed', () => {
+ const hiddenCss = { display: 'none' };
+
+ it('toggles issues', done => {
+ vm.$el.querySelector('button').click();
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-report-section-container')).not.toHaveCss(hiddenCss);
+ expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Collapse');
+
+ vm.$el.querySelector('button').click();
+ })
+ .then(Vue.nextTick)
+ .then(() => {
+ expect(vm.$el.querySelector('.js-report-section-container')).toHaveCss(hiddenCss);
+ expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Expand');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('is always expanded, if always-open is set to true', done => {
+ vm.alwaysOpen = true;
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-report-section-container')).not.toHaveCss(hiddenCss);
+ expect(vm.$el.querySelector('button')).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('with failed request', () => {
+ it('should render error indicator', () => {
+ vm = mountComponent(ReportSection, {
+ component: '',
+ status: 'ERROR',
+ loadingText: 'Loading codeclimate report',
+ errorText: 'Failed to load codeclimate report',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ hasIssues: false,
+ });
+ expect(vm.$el.textContent.trim()).toEqual('Failed to load codeclimate report');
+ });
+ });
+
+ describe('with action buttons passed to the slot', () => {
+ beforeEach(() => {
+ vm = mountComponentWithSlots(ReportSection, {
+ props: {
+ status: 'SUCCESS',
+ successText: 'success',
+ hasIssues: true,
+ },
+ slots: {
+ actionButtons: ['Action!'],
+ },
+ });
+ });
+
+ it('should render the passed button', () => {
+ expect(vm.$el.textContent.trim()).toContain('Action!');
+ });
+
+ it('should still render the expand/collapse button', () => {
+ expect(vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand');
+ });
+ });
+});
diff --git a/spec/javascripts/reports/components/summary_row_spec.js b/spec/javascripts/reports/components/summary_row_spec.js
new file mode 100644
index 00000000000..fab7693581c
--- /dev/null
+++ b/spec/javascripts/reports/components/summary_row_spec.js
@@ -0,0 +1,37 @@
+import Vue from 'vue';
+import component from '~/reports/components/summary_row.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('Summary row', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const props = {
+ summary: 'SAST detected 1 new vulnerability and 1 fixed vulnerability',
+ popoverOptions: {
+ title: 'Static Application Security Testing (SAST)',
+ content: '<a>Learn more about SAST</a>',
+ },
+ statusIcon: 'warning',
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders provided summary', () => {
+ expect(
+ vm.$el.querySelector('.report-block-list-issue-description-text').textContent.trim(),
+ ).toEqual(props.summary);
+ });
+
+ it('renders provided icon', () => {
+ expect(vm.$el.querySelector('.report-block-list-icon span').classList).toContain(
+ 'js-ci-status-icon-warning',
+ );
+ });
+});
diff --git a/spec/javascripts/reports/components/test_issue_body_spec.js b/spec/javascripts/reports/components/test_issue_body_spec.js
new file mode 100644
index 00000000000..0ea81f714e7
--- /dev/null
+++ b/spec/javascripts/reports/components/test_issue_body_spec.js
@@ -0,0 +1,71 @@
+import Vue from 'vue';
+import component from '~/reports/components/test_issue_body.vue';
+import createStore from '~/reports/store';
+import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { trimText } from '../../helpers/vue_component_helper';
+import { issue } from '../mock_data/mock_data';
+
+describe('Test Issue body', () => {
+ let vm;
+ const Component = Vue.extend(component);
+ const store = createStore();
+
+ const commonProps = {
+ issue,
+ status: 'failed',
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('on click', () => {
+ it('calls openModal action', () => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: commonProps,
+ });
+
+ spyOn(vm, 'openModal');
+
+ vm.$el.querySelector('button').click();
+ expect(vm.openModal).toHaveBeenCalledWith({
+ issue: commonProps.issue,
+ });
+ });
+ });
+
+ describe('is new', () => {
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: Object.assign({}, commonProps, { isNew: true }),
+ });
+ });
+
+ it('renders issue name', () => {
+ expect(vm.$el.textContent).toContain(commonProps.issue.name);
+ });
+
+ it('renders new badge', () => {
+ expect(trimText(vm.$el.querySelector('.badge').textContent)).toEqual('New');
+ });
+ });
+
+ describe('not new', () => {
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: commonProps,
+ });
+ });
+
+ it('renders issue name', () => {
+ expect(vm.$el.textContent).toContain(commonProps.issue.name);
+ });
+
+ it('does not renders new badge', () => {
+ expect(vm.$el.querySelector('.badge')).toEqual(null);
+ });
+ });
+});
diff --git a/spec/javascripts/reports/mock_data/mock_data.js b/spec/javascripts/reports/mock_data/mock_data.js
new file mode 100644
index 00000000000..0d90253bad2
--- /dev/null
+++ b/spec/javascripts/reports/mock_data/mock_data.js
@@ -0,0 +1,8 @@
+// eslint-disable-next-line import/prefer-default-export
+export const issue = {
+ result: 'failure',
+ name: 'Test#sum when a is 1 and b is 2 returns summary',
+ execution_time: 0.009411,
+ system_output:
+ "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in \u003ctop (required)\u003e'",
+};
diff --git a/spec/javascripts/reports/mock_data/new_and_fixed_failures_report.json b/spec/javascripts/reports/mock_data/new_and_fixed_failures_report.json
new file mode 100644
index 00000000000..ceaf894375a
--- /dev/null
+++ b/spec/javascripts/reports/mock_data/new_and_fixed_failures_report.json
@@ -0,0 +1 @@
+{"status":"failed","summary":{"total":11,"resolved":2,"failed":2},"suites":[{"name":"rspec:pg","status":"failed","summary":{"total":8,"resolved":2,"failed":1},"new_failures":[{"status":"failed","name":"Test#subtract when a is 2 and b is 1 returns correct result","execution_time":0.00908,"system_output":"Failure/Error: is_expected.to eq(1)\n\n expected: 1\n got: 3\n\n (compared using ==)\n./spec/test_spec.rb:43:in `block (4 levels) in <top (required)>'"}],"resolved_failures":[{"status":"success","name":"Test#sum when a is 1 and b is 2 returns summary","execution_time":0.000318,"system_output":null},{"status":"success","name":"Test#sum when a is 100 and b is 200 returns summary","execution_time":0.000074,"system_output":null}],"existing_failures":[]},{"name":"java ant","status":"failed","summary":{"total":3,"resolved":0,"failed":1},"new_failures":[],"resolved_failures":[],"existing_failures":[{"status":"failed","name":"sumTest","execution_time":0.004,"system_output":"junit.framework.AssertionFailedError: expected:<3> but was:<-1>\n\tat CalculatorTest.sumTest(Unknown Source)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n"}]}]} \ No newline at end of file
diff --git a/spec/javascripts/reports/mock_data/new_failures_report.json b/spec/javascripts/reports/mock_data/new_failures_report.json
new file mode 100644
index 00000000000..930efe16f65
--- /dev/null
+++ b/spec/javascripts/reports/mock_data/new_failures_report.json
@@ -0,0 +1 @@
+{"summary":{"total":11,"resolved":0,"failed":2},"suites":[{"name":"rspec:pg","summary":{"total":8,"resolved":0,"failed":2},"new_failures":[{"result":"failure","name":"Test#sum when a is 1 and b is 2 returns summary","execution_time":0.009411,"system_output":"Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'"},{"result":"failure","name":"Test#sum when a is 100 and b is 200 returns summary","execution_time":0.000162,"system_output":"Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'"}],"resolved_failures":[],"existing_failures":[]},{"name":"java ant","summary":{"total":3,"resolved":0,"failed":0},"new_failures":[],"resolved_failures":[],"existing_failures":[]}]} \ No newline at end of file
diff --git a/spec/javascripts/reports/mock_data/no_failures_report.json b/spec/javascripts/reports/mock_data/no_failures_report.json
new file mode 100644
index 00000000000..6c0675ff7dc
--- /dev/null
+++ b/spec/javascripts/reports/mock_data/no_failures_report.json
@@ -0,0 +1 @@
+{"status":"success","summary":{"total":11,"resolved":0,"failed":0},"suites":[{"name":"rspec:pg","status":"success","summary":{"total":8,"resolved":0,"failed":0},"new_failures":[],"resolved_failures":[],"existing_failures":[]},{"name":"java ant","status":"success","summary":{"total":3,"resolved":0,"failed":0},"new_failures":[],"resolved_failures":[],"existing_failures":[]}]} \ No newline at end of file
diff --git a/spec/javascripts/reports/mock_data/resolved_failures.json b/spec/javascripts/reports/mock_data/resolved_failures.json
new file mode 100644
index 00000000000..d1f347ce5e6
--- /dev/null
+++ b/spec/javascripts/reports/mock_data/resolved_failures.json
@@ -0,0 +1,37 @@
+{
+ "status": "success",
+ "summary": { "total": 11, "resolved": 2, "failed": 0 },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "status": "success",
+ "summary": { "total": 8, "resolved": 2, "failed": 0 },
+ "new_failures": [],
+ "resolved_failures": [
+ {
+ "status": "success",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "execution_time": 0.000411,
+ "system_output": null,
+ "stack_trace": null
+ },
+ {
+ "status": "success",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 7.6e-5,
+ "system_output": null,
+ "stack_trace": null
+ }
+ ],
+ "existing_failures": []
+ },
+ {
+ "name": "java ant",
+ "status": "success",
+ "summary": { "total": 3, "resolved": 0, "failed": 0 },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": []
+ }
+ ]
+}
diff --git a/spec/javascripts/reports/store/actions_spec.js b/spec/javascripts/reports/store/actions_spec.js
new file mode 100644
index 00000000000..41137b50847
--- /dev/null
+++ b/spec/javascripts/reports/store/actions_spec.js
@@ -0,0 +1,171 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import {
+ setEndpoint,
+ requestReports,
+ fetchReports,
+ stopPolling,
+ clearEtagPoll,
+ receiveReportsSuccess,
+ receiveReportsError,
+ openModal,
+ setModalData,
+} from '~/reports/store/actions';
+import state from '~/reports/store/state';
+import * as types from '~/reports/store/mutation_types';
+import testAction from 'spec/helpers/vuex_action_helper';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('Reports Store Actions', () => {
+ let mockedState;
+
+ beforeEach(() => {
+ mockedState = state();
+ });
+
+ describe('setEndpoint', () => {
+ it('should commit SET_ENDPOINT mutation', done => {
+ testAction(
+ setEndpoint,
+ 'endpoint.json',
+ mockedState,
+ [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestReports', () => {
+ it('should commit REQUEST_REPORTS mutation', done => {
+ testAction(requestReports, null, mockedState, [{ type: types.REQUEST_REPORTS }], [], done);
+ });
+ });
+
+ describe('fetchReports', () => {
+ let mock;
+
+ beforeEach(() => {
+ mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ stopPolling();
+ clearEtagPoll();
+ });
+
+ describe('success', () => {
+ it('dispatches requestReports and receiveReportsSuccess ', done => {
+ mock
+ .onGet(`${TEST_HOST}/endpoint.json`)
+ .replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] });
+
+ testAction(
+ fetchReports,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestReports',
+ },
+ {
+ payload: { data: { summary: {}, suites: [{ name: 'rspec' }] }, status: 200 },
+ type: 'receiveReportsSuccess',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+ });
+
+ it('dispatches requestReports and receiveReportsError ', done => {
+ testAction(
+ fetchReports,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestReports',
+ },
+ {
+ type: 'receiveReportsError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('receiveReportsSuccess', () => {
+ it('should commit RECEIVE_REPORTS_SUCCESS mutation with 200', done => {
+ testAction(
+ receiveReportsSuccess,
+ { data: { summary: {} }, status: 200 },
+ mockedState,
+ [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: { summary: {} } }],
+ [],
+ done,
+ );
+ });
+
+ it('should not commit RECEIVE_REPORTS_SUCCESS mutation with 204', done => {
+ testAction(
+ receiveReportsSuccess,
+ { data: { summary: {} }, status: 204 },
+ mockedState,
+ [],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveReportsError', () => {
+ it('should commit RECEIVE_REPORTS_ERROR mutation', done => {
+ testAction(
+ receiveReportsError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_REPORTS_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('openModal', () => {
+ it('should dispatch setModalData', done => {
+ testAction(
+ openModal,
+ { name: 'foo' },
+ mockedState,
+ [],
+ [{ type: 'setModalData', payload: { name: 'foo' } }],
+ done,
+ );
+ });
+ });
+
+ describe('setModalData', () => {
+ it('should commit SET_ISSUE_MODAL_DATA', done => {
+ testAction(
+ setModalData,
+ { name: 'foo' },
+ mockedState,
+ [{ type: types.SET_ISSUE_MODAL_DATA, payload: { name: 'foo' } }],
+ [],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/reports/store/mutations_spec.js b/spec/javascripts/reports/store/mutations_spec.js
new file mode 100644
index 00000000000..7d19b16efb9
--- /dev/null
+++ b/spec/javascripts/reports/store/mutations_spec.js
@@ -0,0 +1,124 @@
+import state from '~/reports/store/state';
+import mutations from '~/reports/store/mutations';
+import * as types from '~/reports/store/mutation_types';
+import { issue } from '../mock_data/mock_data';
+
+describe('Reports Store Mutations', () => {
+ let stateCopy;
+
+ beforeEach(() => {
+ stateCopy = state();
+ });
+
+ describe('SET_ENDPOINT', () => {
+ it('should set endpoint', () => {
+ mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json');
+ expect(stateCopy.endpoint).toEqual('endpoint.json');
+ });
+ });
+
+ describe('REQUEST_REPORTS', () => {
+ it('should set isLoading to true', () => {
+ mutations[types.REQUEST_REPORTS](stateCopy);
+ expect(stateCopy.isLoading).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_SUCCESS', () => {
+ const mockedResponse = {
+ summary: {
+ total: 14,
+ resolved: 0,
+ failed: 7,
+ },
+ suites: [
+ {
+ name: 'build:linux',
+ summary: {
+ total: 2,
+ resolved: 0,
+ failed: 1,
+ },
+ new_failures: [
+ {
+ 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')",
+ },
+ ],
+ resolved_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 0.009235,
+ system_output: "Failure/Error: is_expected.to eq('gitlab')",
+ },
+ ],
+ existing_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 1232.08,
+ system_output: "Failure/Error: is_expected.to eq('gitlab')",
+ },
+ ],
+ },
+ ],
+ };
+
+ beforeEach(() => {
+ mutations[types.RECEIVE_REPORTS_SUCCESS](stateCopy, mockedResponse);
+ });
+
+ it('should reset isLoading', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should reset hasError', () => {
+ expect(stateCopy.hasError).toEqual(false);
+ });
+
+ it('should set summary counts', () => {
+ expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total);
+ expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved);
+ expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed);
+ });
+
+ it('should set reports', () => {
+ expect(stateCopy.reports).toEqual(mockedResponse.suites);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_ERROR', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_REPORTS_ERROR](stateCopy);
+ });
+
+ it('should reset isLoading', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should set hasError to true', () => {
+ expect(stateCopy.hasError).toEqual(true);
+ });
+
+ it('should reset reports', () => {
+ expect(stateCopy.reports).toEqual([]);
+ });
+ });
+
+ describe('SET_ISSUE_MODAL_DATA', () => {
+ beforeEach(() => {
+ mutations[types.SET_ISSUE_MODAL_DATA](stateCopy, {
+ issue,
+ });
+ });
+
+ it('should set modal title', () => {
+ expect(stateCopy.modal.title).toEqual(issue.name);
+ });
+
+ it('should set modal data', () => {
+ expect(stateCopy.modal.data.execution_time.value).toEqual(issue.execution_time);
+ expect(stateCopy.modal.data.system_output.value).toEqual(issue.system_output);
+ });
+ });
+});
diff --git a/spec/javascripts/reports/store/utils_spec.js b/spec/javascripts/reports/store/utils_spec.js
new file mode 100644
index 00000000000..1679d120db2
--- /dev/null
+++ b/spec/javascripts/reports/store/utils_spec.js
@@ -0,0 +1,138 @@
+import * as utils from '~/reports/store/utils';
+import {
+ STATUS_FAILED,
+ STATUS_SUCCESS,
+ ICON_WARNING,
+ ICON_SUCCESS,
+ ICON_NOTFOUND,
+} from '~/reports/constants';
+
+describe('Reports store utils', () => {
+ describe('summaryTextbuilder', () => {
+ it('should render text for no changed results in multiple tests', () => {
+ const name = 'Test summary';
+ const data = { total: 10 };
+ const result = utils.summaryTextBuilder(name, data);
+
+ expect(result).toBe('Test summary contained no changed test results out of 10 total tests');
+ });
+
+ it('should render text for no changed results in one test', () => {
+ const name = 'Test summary';
+ const data = { total: 1 };
+ const result = utils.summaryTextBuilder(name, data);
+
+ expect(result).toBe('Test summary contained no changed test results out of 1 total test');
+ });
+
+ it('should render text for multiple failed results', () => {
+ const name = 'Test summary';
+ const data = { failed: 3, total: 10 };
+ const result = utils.summaryTextBuilder(name, data);
+
+ expect(result).toBe('Test summary contained 3 failed test results out of 10 total tests');
+ });
+
+ it('should render text for multiple fixed results', () => {
+ const name = 'Test summary';
+ const data = { resolved: 4, total: 10 };
+ const result = utils.summaryTextBuilder(name, data);
+
+ expect(result).toBe('Test summary contained 4 fixed test results out of 10 total tests');
+ });
+
+ it('should render text for multiple fixed, and multiple failed results', () => {
+ const name = 'Test summary';
+ const data = { failed: 3, resolved: 4, total: 10 };
+ const result = utils.summaryTextBuilder(name, data);
+
+ expect(result).toBe(
+ 'Test summary contained 3 failed test results and 4 fixed test results out of 10 total tests',
+ );
+ });
+
+ it('should render text for a singular fixed, and a singular failed result', () => {
+ const name = 'Test summary';
+ const data = { failed: 1, resolved: 1, total: 10 };
+ const result = utils.summaryTextBuilder(name, data);
+
+ expect(result).toBe(
+ 'Test summary contained 1 failed test result and 1 fixed test result out of 10 total tests',
+ );
+ });
+ });
+
+ describe('reportTextBuilder', () => {
+ it('should render text for no changed results in multiple tests', () => {
+ const name = 'Rspec';
+ const data = { total: 10 };
+ const result = utils.reportTextBuilder(name, data);
+
+ expect(result).toBe('Rspec found no changed test results out of 10 total tests');
+ });
+
+ it('should render text for no changed results in one test', () => {
+ const name = 'Rspec';
+ const data = { total: 1 };
+ const result = utils.reportTextBuilder(name, data);
+
+ expect(result).toBe('Rspec found no changed test results out of 1 total test');
+ });
+
+ it('should render text for multiple failed results', () => {
+ const name = 'Rspec';
+ const data = { failed: 3, total: 10 };
+ const result = utils.reportTextBuilder(name, data);
+
+ expect(result).toBe('Rspec found 3 failed test results out of 10 total tests');
+ });
+
+ it('should render text for multiple fixed results', () => {
+ const name = 'Rspec';
+ const data = { resolved: 4, total: 10 };
+ const result = utils.reportTextBuilder(name, data);
+
+ expect(result).toBe('Rspec found 4 fixed test results out of 10 total tests');
+ });
+
+ it('should render text for multiple fixed, and multiple failed results', () => {
+ const name = 'Rspec';
+ const data = { failed: 3, resolved: 4, total: 10 };
+ const result = utils.reportTextBuilder(name, data);
+
+ expect(result).toBe(
+ 'Rspec found 3 failed test results and 4 fixed test results out of 10 total tests',
+ );
+ });
+
+ it('should render text for a singular fixed, and a singular failed result', () => {
+ const name = 'Rspec';
+ const data = { failed: 1, resolved: 1, total: 10 };
+ const result = utils.reportTextBuilder(name, data);
+
+ expect(result).toBe(
+ 'Rspec found 1 failed test result and 1 fixed test result out of 10 total tests',
+ );
+ });
+ });
+
+ describe('statusIcon', () => {
+ describe('with failed status', () => {
+ it('returns ICON_WARNING', () => {
+ expect(utils.statusIcon(STATUS_FAILED)).toEqual(ICON_WARNING);
+ });
+ });
+
+ describe('with success status', () => {
+ it('returns ICON_SUCCESS', () => {
+ expect(utils.statusIcon(STATUS_SUCCESS)).toEqual(ICON_SUCCESS);
+ });
+ });
+
+ describe('without a status', () => {
+ it('returns ICON_NOTFOUND', () => {
+ expect(utils.statusIcon()).toEqual(ICON_NOTFOUND);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/sidebar/assignees_spec.js b/spec/javascripts/sidebar/assignees_spec.js
index 4e4343812bd..843e7002180 100644
--- a/spec/javascripts/sidebar/assignees_spec.js
+++ b/spec/javascripts/sidebar/assignees_spec.js
@@ -102,13 +102,13 @@ describe('Assignee component', () => {
},
}).$mount();
- expect(component.$el.querySelector('.author_link')).not.toBeNull();
+ expect(component.$el.querySelector('.author-link')).not.toBeNull();
// The image
- expect(component.$el.querySelector('.author_link img').getAttribute('src')).toEqual(UsersMock.user.avatar);
+ expect(component.$el.querySelector('.author-link img').getAttribute('src')).toEqual(UsersMock.user.avatar);
// Author name
- expect(component.$el.querySelector('.author_link .author').innerText.trim()).toEqual(UsersMock.user.name);
+ expect(component.$el.querySelector('.author-link .author').innerText.trim()).toEqual(UsersMock.user.name);
// Username
- expect(component.$el.querySelector('.author_link .username').innerText.trim()).toEqual(`@${UsersMock.user.username}`);
+ expect(component.$el.querySelector('.author-link .username').innerText.trim()).toEqual(`@${UsersMock.user.username}`);
});
it('has the root url present in the assigneeUrl method', () => {
diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
new file mode 100644
index 00000000000..9dff52a9d49
--- /dev/null
+++ b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
@@ -0,0 +1,232 @@
+import Vue from 'vue';
+
+import TimeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
+
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('Issuable Time Tracker', () => {
+ let initialData;
+ let vm;
+
+ const initTimeTrackingComponent = opts => {
+ setFixtures(`
+ <div>
+ <div id="mock-container"></div>
+ </div>
+ `);
+
+ initialData = {
+ time_estimate: opts.timeEstimate,
+ time_spent: opts.timeSpent,
+ human_time_estimate: opts.timeEstimateHumanReadable,
+ human_time_spent: opts.timeSpentHumanReadable,
+ rootPath: '/',
+ };
+
+ const TimeTrackingComponent = Vue.extend({
+ ...TimeTracker,
+ components: {
+ ...TimeTracker.components,
+ transition: {
+ // disable animations
+ template: '<div><slot></slot></div>',
+ },
+ },
+ });
+ vm = mountComponent(TimeTrackingComponent, initialData, '#mock-container');
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('Initialization', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 100000,
+ timeSpent: 5000,
+ timeEstimateHumanReadable: '2h 46m',
+ timeSpentHumanReadable: '1h 23m',
+ });
+ });
+
+ it('should return something defined', () => {
+ expect(vm).toBeDefined();
+ });
+
+ it('should correctly set timeEstimate', done => {
+ Vue.nextTick(() => {
+ expect(vm.timeEstimate).toBe(initialData.time_estimate);
+ done();
+ });
+ });
+
+ it('should correctly set time_spent', done => {
+ Vue.nextTick(() => {
+ expect(vm.timeSpent).toBe(initialData.time_spent);
+ done();
+ });
+ });
+ });
+
+ describe('Content Display', () => {
+ describe('Panes', () => {
+ describe('Comparison pane', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 100000,
+ timeSpent: 5000,
+ timeEstimateHumanReadable: '',
+ timeSpentHumanReadable: '',
+ });
+ });
+
+ it('should show the "Comparison" pane when timeEstimate and time_spent are truthy', done => {
+ Vue.nextTick(() => {
+ expect(vm.showComparisonState).toBe(true);
+ const $comparisonPane = vm.$el.querySelector('.time-tracking-comparison-pane');
+ expect($comparisonPane).toBeVisible();
+ done();
+ });
+ });
+
+ describe('Remaining meter', () => {
+ it('should display the remaining meter with the correct width', done => {
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[value="5"]')).not.toBeNull();
+ done();
+ });
+ });
+
+ it('should display the remaining meter with the correct background color when within estimate', done => {
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="primary"]')).not.toBeNull();
+ done();
+ });
+ });
+
+ it('should display the remaining meter with the correct background color when over estimate', done => {
+ vm.time_estimate = 100000;
+ vm.time_spent = 20000000;
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="danger"]')).not.toBeNull();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('Estimate only pane', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 100000,
+ timeSpent: 0,
+ timeEstimateHumanReadable: '2h 46m',
+ timeSpentHumanReadable: '',
+ });
+ });
+
+ it('should display the human readable version of time estimated', done => {
+ Vue.nextTick(() => {
+ const estimateText = vm.$el.querySelector('.time-tracking-estimate-only-pane')
+ .innerText;
+ const correctText = 'Estimated: 2h 46m';
+
+ expect(estimateText).toBe(correctText);
+ done();
+ });
+ });
+ });
+
+ describe('Spent only pane', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 0,
+ timeSpent: 5000,
+ timeEstimateHumanReadable: '2h 46m',
+ timeSpentHumanReadable: '1h 23m',
+ });
+ });
+
+ it('should display the human readable version of time spent', done => {
+ Vue.nextTick(() => {
+ const spentText = vm.$el.querySelector('.time-tracking-spend-only-pane').innerText;
+ const correctText = 'Spent: 1h 23m';
+
+ expect(spentText).toBe(correctText);
+ done();
+ });
+ });
+ });
+
+ describe('No time tracking pane', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 0,
+ timeSpent: 0,
+ timeEstimateHumanReadable: '',
+ timeSpentHumanReadable: '',
+ });
+ });
+
+ it('should only show the "No time tracking" pane when both timeEstimate and time_spent are falsey', done => {
+ Vue.nextTick(() => {
+ const $noTrackingPane = vm.$el.querySelector('.time-tracking-no-tracking-pane');
+ const noTrackingText = $noTrackingPane.innerText;
+ const correctText = 'No estimate or time spent';
+
+ expect(vm.showNoTimeTrackingState).toBe(true);
+ expect($noTrackingPane).toBeVisible();
+ expect(noTrackingText).toBe(correctText);
+ done();
+ });
+ });
+ });
+
+ describe('Help pane', () => {
+ const helpButton = () => vm.$el.querySelector('.help-button');
+ const closeHelpButton = () => vm.$el.querySelector('.close-help-button');
+ const helpPane = () => vm.$el.querySelector('.time-tracking-help-state');
+
+ beforeEach(done => {
+ initTimeTrackingComponent({ timeEstimate: 0, timeSpent: 0 });
+
+ Vue.nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should not show the "Help" pane by default', () => {
+ expect(vm.showHelpState).toBe(false);
+ expect(helpPane()).toBeNull();
+ });
+
+ it('should show the "Help" pane when help button is clicked', done => {
+ helpButton().click();
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.showHelpState).toBe(true);
+ expect(helpPane()).toBeVisible();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should not show the "Help" pane when help button is clicked and then closed', done => {
+ helpButton().click();
+
+ Vue.nextTick()
+ .then(() => closeHelpButton().click())
+ .then(() => Vue.nextTick())
+ .then(() => {
+ expect(vm.showHelpState).toBe(false);
+ expect(helpPane()).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/sidebar/todo_spec.js b/spec/javascripts/sidebar/todo_spec.js
new file mode 100644
index 00000000000..a929b804a29
--- /dev/null
+++ b/spec/javascripts/sidebar/todo_spec.js
@@ -0,0 +1,158 @@
+import Vue from 'vue';
+
+import SidebarTodos from '~/sidebar/components/todo_toggle/todo.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const createComponent = ({
+ issuableId = 1,
+ issuableType = 'epic',
+ isTodo,
+ isActionActive,
+ collapsed,
+}) => {
+ const Component = Vue.extend(SidebarTodos);
+
+ return mountComponent(Component, {
+ issuableId,
+ issuableType,
+ isTodo,
+ isActionActive,
+ collapsed,
+ });
+};
+
+describe('SidebarTodo', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent({});
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('buttonClasses', () => {
+ it('returns todo button classes for when `collapsed` prop is `false`', () => {
+ expect(vm.buttonClasses).toBe('btn btn-default btn-todo issuable-header-btn float-right');
+ });
+
+ it('returns todo button classes for when `collapsed` prop is `true`', done => {
+ vm.collapsed = true;
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.buttonClasses).toBe('btn-blank btn-todo sidebar-collapsed-icon dont-change-state');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('buttonLabel', () => {
+ it('returns todo button text for marking todo as done when `isTodo` prop is `true`', () => {
+ expect(vm.buttonLabel).toBe('Mark todo as done');
+ });
+
+ it('returns todo button text for add todo when `isTodo` prop is `false`', done => {
+ vm.isTodo = false;
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.buttonLabel).toBe('Add todo');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('collapsedButtonIconClasses', () => {
+ it('returns collapsed button icon class when `isTodo` prop is `true`', () => {
+ expect(vm.collapsedButtonIconClasses).toBe('todo-undone');
+ });
+
+ it('returns empty string when `isTodo` prop is `false`', done => {
+ vm.isTodo = false;
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.collapsedButtonIconClasses).toBe('');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('collapsedButtonIcon', () => {
+ it('returns button icon name when `isTodo` prop is `true`', () => {
+ expect(vm.collapsedButtonIcon).toBe('todo-done');
+ });
+
+ it('returns button icon name when `isTodo` prop is `false`', done => {
+ vm.isTodo = false;
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.collapsedButtonIcon).toBe('todo-add');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('handleButtonClick', () => {
+ it('emits `toggleTodo` event on component', () => {
+ spyOn(vm, '$emit');
+ vm.handleButtonClick();
+ expect(vm.$emit).toHaveBeenCalledWith('toggleTodo');
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component container element', () => {
+ const dataAttributes = {
+ issuableId: '1',
+ issuableType: 'epic',
+ originalTitle: 'Mark todo as done',
+ placement: 'left',
+ container: 'body',
+ boundary: 'viewport',
+ };
+ expect(vm.$el.nodeName).toBe('BUTTON');
+
+ const elDataAttrs = vm.$el.dataset;
+ Object.keys(elDataAttrs).forEach((attr) => {
+ expect(elDataAttrs[attr]).toBe(dataAttributes[attr]);
+ });
+ });
+
+ it('renders button label element when `collapsed` prop is `false`', () => {
+ const buttonLabelEl = vm.$el.querySelector('span.issuable-todo-inner');
+ expect(buttonLabelEl).not.toBeNull();
+ expect(buttonLabelEl.innerText.trim()).toBe('Mark todo as done');
+ });
+
+ it('renders button icon when `collapsed` prop is `true`', done => {
+ vm.collapsed = true;
+ Vue.nextTick()
+ .then(() => {
+ const buttonIconEl = vm.$el.querySelector('svg');
+ expect(buttonIconEl).not.toBeNull();
+ expect(buttonIconEl.querySelector('use').getAttribute('xlink:href')).toContain('todo-done');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders loading icon when `isActionActive` prop is true', done => {
+ vm.isActionActive = true;
+ Vue.nextTick()
+ .then(() => {
+ const loadingEl = vm.$el.querySelector('span.loading-container');
+ expect(loadingEl).not.toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js
index 60153672214..d9b6dd1d487 100644
--- a/spec/javascripts/smart_interval_spec.js
+++ b/spec/javascripts/smart_interval_spec.js
@@ -1,12 +1,12 @@
import $ from 'jquery';
import _ from 'underscore';
import SmartInterval from '~/smart_interval';
+import waitForPromises from 'spec/helpers/wait_for_promises';
describe('SmartInterval', function () {
const DEFAULT_MAX_INTERVAL = 100;
const DEFAULT_STARTING_INTERVAL = 5;
const DEFAULT_SHORT_TIMEOUT = 75;
- const DEFAULT_LONG_TIMEOUT = 1000;
const DEFAULT_INCREMENT_FACTOR = 2;
function createDefaultSmartInterval(config) {
@@ -27,52 +27,65 @@ describe('SmartInterval', function () {
return new SmartInterval(defaultParams);
}
+ beforeEach(() => {
+ jasmine.clock().install();
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
describe('Increment Interval', function () {
- beforeEach(function () {
- this.smartInterval = createDefaultSmartInterval();
- });
+ it('should increment the interval delay', (done) => {
+ const smartInterval = createDefaultSmartInterval();
- it('should increment the interval delay', function (done) {
- const interval = this.smartInterval;
- setTimeout(() => {
- const intervalConfig = this.smartInterval.cfg;
- const iterationCount = 4;
- const maxIntervalAfterIterations = intervalConfig.startingInterval *
- (intervalConfig.incrementByFactorOf ** (iterationCount - 1)); // 40
- const currentInterval = interval.getCurrentInterval();
-
- // Provide some flexibility for performance of testing environment
- expect(currentInterval).toBeGreaterThan(intervalConfig.startingInterval);
- expect(currentInterval <= maxIntervalAfterIterations).toBeTruthy();
-
- done();
- }, DEFAULT_SHORT_TIMEOUT); // 4 iterations, increment by 2x = (5 + 10 + 20 + 40)
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
+
+ waitForPromises()
+ .then(() => {
+ const intervalConfig = smartInterval.cfg;
+ const iterationCount = 4;
+ const maxIntervalAfterIterations = intervalConfig.startingInterval *
+ (intervalConfig.incrementByFactorOf ** iterationCount);
+ const currentInterval = smartInterval.getCurrentInterval();
+
+ // Provide some flexibility for performance of testing environment
+ expect(currentInterval).toBeGreaterThan(intervalConfig.startingInterval);
+ expect(currentInterval).toBeLessThanOrEqual(maxIntervalAfterIterations);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('should not increment past maxInterval', function (done) {
- const interval = this.smartInterval;
+ it('should not increment past maxInterval', (done) => {
+ const smartInterval = createDefaultSmartInterval({ maxInterval: DEFAULT_STARTING_INTERVAL });
- setTimeout(() => {
- const currentInterval = interval.getCurrentInterval();
- expect(currentInterval).toBe(interval.cfg.maxInterval);
+ jasmine.clock().tick(DEFAULT_STARTING_INTERVAL);
+ jasmine.clock().tick(DEFAULT_STARTING_INTERVAL * DEFAULT_INCREMENT_FACTOR);
- done();
- }, DEFAULT_LONG_TIMEOUT);
+ waitForPromises()
+ .then(() => {
+ const currentInterval = smartInterval.getCurrentInterval();
+ expect(currentInterval).toBe(smartInterval.cfg.maxInterval);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('does not increment while waiting for callback', function () {
- jasmine.clock().install();
-
+ it('does not increment while waiting for callback', done => {
const smartInterval = createDefaultSmartInterval({
callback: () => new Promise($.noop),
});
jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
- const oneInterval = smartInterval.cfg.startingInterval * DEFAULT_INCREMENT_FACTOR;
- expect(smartInterval.getCurrentInterval()).toEqual(oneInterval);
-
- jasmine.clock().uninstall();
+ waitForPromises()
+ .then(() => {
+ const oneInterval = smartInterval.cfg.startingInterval * DEFAULT_INCREMENT_FACTOR;
+ expect(smartInterval.getCurrentInterval()).toEqual(oneInterval);
+ })
+ .then(done)
+ .catch(done.fail);
});
});
@@ -84,34 +97,39 @@ describe('SmartInterval', function () {
it('should cancel an interval', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- interval.cancel();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
- const { intervalId } = interval.state;
- const currentInterval = interval.getCurrentInterval();
- const intervalLowerLimit = interval.cfg.startingInterval;
+ interval.cancel();
- expect(intervalId).toBeUndefined();
- expect(currentInterval).toBe(intervalLowerLimit);
+ waitForPromises()
+ .then(() => {
+ const { intervalId } = interval.state;
+ const currentInterval = interval.getCurrentInterval();
+ const intervalLowerLimit = interval.cfg.startingInterval;
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ expect(intervalId).toBeUndefined();
+ expect(currentInterval).toBe(intervalLowerLimit);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should resume an interval', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- interval.cancel();
-
- interval.resume();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
- const { intervalId } = interval.state;
+ interval.cancel();
- expect(intervalId).toBeTruthy();
+ interval.resume();
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ waitForPromises()
+ .then(() => {
+ const { intervalId } = interval.state;
+ expect(intervalId).toBeTruthy();
+ })
+ .then(done)
+ .catch(done.fail);
});
});
@@ -126,64 +144,79 @@ describe('SmartInterval', function () {
it('should pause when page is not visible', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- expect(interval.state.intervalId).toBeTruthy();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
+
+ waitForPromises()
+ .then(() => {
+ expect(interval.state.intervalId).toBeTruthy();
- // simulates triggering of visibilitychange event
- interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
+ // simulates triggering of visibilitychange event
+ interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
- expect(interval.state.intervalId).toBeUndefined();
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ expect(interval.state.intervalId).toBeUndefined();
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('should change to the hidden interval when page is not visible', function (done) {
+ it('should change to the hidden interval when page is not visible', done => {
const HIDDEN_INTERVAL = 1500;
const interval = createDefaultSmartInterval({ hiddenInterval: HIDDEN_INTERVAL });
- setTimeout(() => {
- expect(interval.state.intervalId).toBeTruthy();
- expect(interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL &&
- interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL).toBeTruthy();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
+
+ waitForPromises()
+ .then(() => {
+ expect(interval.state.intervalId).toBeTruthy();
+ expect(interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL &&
+ interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL).toBeTruthy();
- // simulates triggering of visibilitychange event
- interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
+ // simulates triggering of visibilitychange event
+ interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
- expect(interval.state.intervalId).toBeTruthy();
- expect(interval.getCurrentInterval()).toBe(HIDDEN_INTERVAL);
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ expect(interval.state.intervalId).toBeTruthy();
+ expect(interval.getCurrentInterval()).toBe(HIDDEN_INTERVAL);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should resume when page is becomes visible at the previous interval', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- expect(interval.state.intervalId).toBeTruthy();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
- // simulates triggering of visibilitychange event
- interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
+ waitForPromises()
+ .then(() => {
+ expect(interval.state.intervalId).toBeTruthy();
- expect(interval.state.intervalId).toBeUndefined();
+ // simulates triggering of visibilitychange event
+ interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
- // simulates triggering of visibilitychange event
- interval.handleVisibilityChange({ target: { visibilityState: 'visible' } });
+ expect(interval.state.intervalId).toBeUndefined();
- expect(interval.state.intervalId).toBeTruthy();
+ // simulates triggering of visibilitychange event
+ interval.handleVisibilityChange({ target: { visibilityState: 'visible' } });
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ expect(interval.state.intervalId).toBeTruthy();
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should cancel on page unload', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- $(document).triggerHandler('beforeunload');
- expect(interval.state.intervalId).toBeUndefined();
- expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval);
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
+
+ waitForPromises()
+ .then(() => {
+ $(document).triggerHandler('beforeunload');
+ expect(interval.state.intervalId).toBeUndefined();
+ expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should execute callback before first interval', function () {
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 0eff98bcc9d..4452c470b82 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -1,4 +1,6 @@
-/* eslint-disable jasmine/no-global-setup, jasmine/no-unsafe-spy, no-underscore-dangle */
+/* eslint-disable
+ jasmine/no-global-setup, jasmine/no-unsafe-spy, no-underscore-dangle, no-console
+*/
import $ from 'jquery';
import 'vendor/jasmine-jquery';
@@ -6,6 +8,7 @@ import '~/commons';
import Vue from 'vue';
import VueResource from 'vue-resource';
import Translate from '~/vue_shared/translate';
+import jasmineDiff from 'jasmine-diff';
import { getDefaultAdapter } from '~/lib/utils/axios_utils';
import { FIXTURES_PATH, TEST_HOST } from './test_constants';
@@ -35,7 +38,15 @@ Vue.use(Translate);
jasmine.getFixtures().fixturesPath = FIXTURES_PATH;
jasmine.getJSONFixtures().fixturesPath = FIXTURES_PATH;
-beforeAll(() => jasmine.addMatchers(customMatchers));
+beforeAll(() => {
+ jasmine.addMatchers(
+ jasmineDiff(jasmine, {
+ colors: true,
+ inline: true,
+ }),
+ );
+ jasmine.addMatchers(customMatchers);
+});
// globalize common libraries
window.$ = $;
@@ -82,6 +93,19 @@ beforeEach(() => {
Vue.http.interceptors = builtinVueHttpInterceptors.slice();
});
+let longRunningTestTimeoutHandle;
+
+beforeEach(done => {
+ longRunningTestTimeoutHandle = setTimeout(() => {
+ done.fail('Test is running too long!');
+ }, 2000);
+ done();
+});
+
+afterEach(() => {
+ clearTimeout(longRunningTestTimeoutHandle);
+});
+
const axiosDefaultAdapter = getDefaultAdapter();
// render all of our tests
@@ -166,13 +190,13 @@ if (process.env.BABEL_ENV === 'coverage') {
];
describe('Uncovered files', function() {
- const sourceFiles = require.context('~', true, /\.js$/);
+ const sourceFiles = require.context('~', true, /\.(js|vue)$/);
$.holdReady(true);
sourceFiles.keys().forEach(function(path) {
// ignore if there is a matching spec file
- if (testsContext.keys().indexOf(`${path.replace(/\.js$/, '')}_spec`) > -1) {
+ if (testsContext.keys().indexOf(`${path.replace(/\.(js|vue)$/, '')}_spec`) > -1) {
return;
}
diff --git a/spec/javascripts/vue_mr_widget/components/deployment_spec.js b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
index c82ba61a5b1..50c2b0e2bd0 100644
--- a/spec/javascripts/vue_mr_widget/components/deployment_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
@@ -153,7 +153,7 @@ describe('Deployment component', () => {
it('renders external URL', () => {
expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deploymentMockData.external_url);
- expect(el.querySelector('.js-deploy-url').innerText).toContain(deploymentMockData.external_url_formatted);
+ expect(el.querySelector('.js-deploy-url').innerText).toContain('View app');
});
it('renders stop button', () => {
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
index 3d36e46d863..8ac2f26979b 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
@@ -119,6 +119,7 @@ describe('MRWidgetHeader', () => {
beforeEach(() => {
vm = mountComponent(Component, {
mr: {
+ iid: 1,
divergedCommitsCount: 12,
sourceBranch: 'mr-widget-refactor',
sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">mr-widget-refactor</a>',
@@ -130,6 +131,8 @@ describe('MRWidgetHeader', () => {
emailPatchesPath: '/mr/email-patches',
plainDiffPath: '/mr/plainDiffPath',
statusPath: 'abc',
+ sourceProjectFullPath: 'root/gitlab-ce',
+ targetProjectFullPath: 'gitlab-org/gitlab-ce',
},
});
});
@@ -145,17 +148,41 @@ describe('MRWidgetHeader', () => {
it('renders web ide button', () => {
const button = vm.$el.querySelector('.js-web-ide');
- expect(button.textContent.trim()).toEqual('Web IDE');
- expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
+ expect(button.textContent.trim()).toEqual('Open in Web IDE');
+ expect(button.getAttribute('href')).toEqual(
+ '/-/ide/project/root/gitlab-ce/merge_requests/1?target_project=gitlab-org%2Fgitlab-ce',
+ );
+ });
+
+ it('renders web ide button with blank query string if target & source project branch', done => {
+ vm.mr.targetProjectFullPath = 'root/gitlab-ce';
+
+ vm.$nextTick(() => {
+ const button = vm.$el.querySelector('.js-web-ide');
+
+ expect(button.textContent.trim()).toEqual('Open in Web IDE');
+ expect(button.getAttribute('href')).toEqual(
+ '/-/ide/project/root/gitlab-ce/merge_requests/1?target_project=',
+ );
+
+ done();
+ });
});
- it('renders web ide button with relative URL', () => {
+ it('renders web ide button with relative URL', done => {
gon.relative_url_root = '/gitlab';
+ vm.mr.iid = 2;
- const button = vm.$el.querySelector('.js-web-ide');
+ vm.$nextTick(() => {
+ const button = vm.$el.querySelector('.js-web-ide');
- expect(button.textContent.trim()).toEqual('Web IDE');
- expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
+ expect(button.textContent.trim()).toEqual('Open in Web IDE');
+ expect(button.getAttribute('href')).toEqual(
+ '/gitlab/-/ide/project/root/gitlab-ce/merge_requests/2?target_project=gitlab-org%2Fgitlab-ce',
+ );
+
+ done();
+ });
});
it('renders download dropdown with links', () => {
@@ -253,8 +280,8 @@ describe('MRWidgetHeader', () => {
});
it('renders diverged commits info', () => {
- expect(vm.$el.querySelector('.diverged-commits-count').textContent.trim()).toEqual(
- '(12 commits behind)',
+ expect(vm.$el.querySelector('.diverged-commits-count').textContent).toMatch(
+ /(mr-widget-refactor[\s\S]+?is 12 commits behind[\s\S]+?master)/,
);
});
});
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index 9d2a15ff009..7fd1a2350f7 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -29,8 +29,10 @@ export default {
source_branch: 'daaaa',
source_branch_link: 'daaaa',
source_project_id: 19,
+ source_project_full_path: '/group1/project1',
target_branch: 'master',
target_project_id: 19,
+ target_project_full_path: '/group2/project2',
metrics: {
merged_by: {
name: 'Administrator',
@@ -74,7 +76,7 @@ export default {
path: '/root/acets-app/pipelines/172',
details: {
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
favicon: 'favicon_status_success',
text: 'passed',
label: 'passed',
@@ -89,7 +91,7 @@ export default {
name: 'build',
title: 'build: failed',
status: {
- icon: 'icon_status_failed',
+ icon: 'status_failed',
favicon: 'favicon_status_failed',
text: 'failed',
label: 'failed',
@@ -104,7 +106,7 @@ export default {
name: 'review',
title: 'review: skipped',
status: {
- icon: 'icon_status_skipped',
+ icon: 'status_skipped',
favicon: 'favicon_status_skipped',
text: 'skipped',
label: 'skipped',
diff --git a/spec/javascripts/vue_shared/components/bar_chart_spec.js b/spec/javascripts/vue_shared/components/bar_chart_spec.js
new file mode 100644
index 00000000000..7e91cd6f63f
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/bar_chart_spec.js
@@ -0,0 +1,85 @@
+import Vue from 'vue';
+import BarChart from '~/vue_shared/components/bar_chart.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+function getRandomArbitrary(min, max) {
+ return Math.random() * (max - min) + min;
+}
+
+function generateRandomData(dataNumber) {
+ const randomGraphData = [];
+
+ for (let i = 1; i <= dataNumber; i += 1) {
+ randomGraphData.push({
+ name: `random ${i}`,
+ value: parseInt(getRandomArbitrary(1, 8), 10),
+ });
+ }
+
+ return randomGraphData;
+}
+
+describe('Bar chart component', () => {
+ let barChart;
+ const graphData = generateRandomData(10);
+
+ beforeEach(() => {
+ const BarChartComponent = Vue.extend(BarChart);
+
+ barChart = mountComponent(BarChartComponent, {
+ graphData,
+ yAxisLabel: 'data',
+ });
+ });
+
+ afterEach(() => {
+ barChart.$destroy();
+ });
+
+ it('calculates the padding for even distribution across bars', () => {
+ barChart.vbWidth = 1000;
+ const result = barChart.calculatePadding(30);
+
+ // since padding can't be higher than 1 and lower than 0
+ // for more info: https://github.com/d3/d3-scale#band-scales
+ expect(result).not.toBeLessThan(0);
+ expect(result).not.toBeGreaterThan(1);
+ });
+
+ it('formats the tooltip title', () => {
+ const tooltipTitle = barChart.setTooltipTitle(barChart.graphData[0]);
+
+ expect(tooltipTitle).toContain('random 1:');
+ });
+
+ it('has a translates the bar graphs on across the X axis', () => {
+ barChart.panX = 100;
+
+ expect(barChart.barTranslationTransform).toEqual('translate(100, 0)');
+ });
+
+ it('translates the scroll indicator to the far right side', () => {
+ barChart.vbWidth = 500;
+
+ expect(barChart.scrollIndicatorTransform).toEqual('translate(420, 0)');
+ });
+
+ it('translates the x-axis to the bottom of the viewbox and pan coordinates', () => {
+ barChart.panX = 100;
+ barChart.vbHeight = 250;
+
+ expect(barChart.xAxisLocation).toEqual('translate(100, 250)');
+ });
+
+ it('Contains a total of 4 ticks across the y axis', () => {
+ const ticks = barChart.$el.querySelector('.y-axis').querySelectorAll('.tick').length;
+
+ expect(ticks).toEqual(4);
+ });
+
+ it('rotates the x axis labels a total of 90 degress (CCW)', () => {
+ const xAxisLabel = barChart.$el.querySelector('.x-axis').querySelectorAll('text')[0];
+
+ expect(xAxisLabel.getAttribute('transform')).toEqual('rotate(-90)');
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/ci_icon_spec.js b/spec/javascripts/vue_shared/components/ci_icon_spec.js
index 423bc746a22..b59a7d7544f 100644
--- a/spec/javascripts/vue_shared/components/ci_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/ci_icon_spec.js
@@ -13,7 +13,7 @@ describe('CI Icon component', () => {
it('should render a span element with an svg', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
},
});
@@ -24,7 +24,7 @@ describe('CI Icon component', () => {
it('should render a success status', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
group: 'success',
},
});
@@ -35,7 +35,7 @@ describe('CI Icon component', () => {
it('should render a failed status', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_failed',
+ icon: 'status_failed',
group: 'failed',
},
});
@@ -46,7 +46,7 @@ describe('CI Icon component', () => {
it('should render success with warnings status', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_warning',
+ icon: 'status_warning',
group: 'warning',
},
});
@@ -57,7 +57,7 @@ describe('CI Icon component', () => {
it('should render pending status', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_pending',
+ icon: 'status_pending',
group: 'pending',
},
});
@@ -68,7 +68,7 @@ describe('CI Icon component', () => {
it('should render running status', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_running',
+ icon: 'status_running',
group: 'running',
},
});
@@ -79,7 +79,7 @@ describe('CI Icon component', () => {
it('should render created status', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_created',
+ icon: 'status_created',
group: 'created',
},
});
@@ -90,7 +90,7 @@ describe('CI Icon component', () => {
it('should render skipped status', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_skipped',
+ icon: 'status_skipped',
group: 'skipped',
},
});
@@ -101,7 +101,7 @@ describe('CI Icon component', () => {
it('should render canceled status', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_canceled',
+ icon: 'status_canceled',
group: 'canceled',
},
});
@@ -112,7 +112,7 @@ describe('CI Icon component', () => {
it('should render status for manual action', () => {
vm = mountComponent(Component, {
status: {
- icon: 'icon_status_manual',
+ icon: 'status_manual',
group: 'manual',
},
});
diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
index 97f0fbb04db..ea525b1e44f 100644
--- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js
+++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
@@ -6,31 +6,47 @@ describe('clipboard button', () => {
const Component = Vue.extend(clipboardButton);
let vm;
- beforeEach(() => {
- vm = mountComponent(Component, {
- text: 'copy me',
- title: 'Copy this value into Clipboard!',
- cssClass: 'btn-danger',
- });
- });
-
afterEach(() => {
vm.$destroy();
});
- it('renders a button for clipboard', () => {
- expect(vm.$el.tagName).toEqual('BUTTON');
- expect(vm.$el.getAttribute('data-clipboard-text')).toEqual('copy me');
- expect(vm.$el.querySelector('i').className).toEqual('fa fa-clipboard');
- });
+ describe('without gfm', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ text: 'copy me',
+ title: 'Copy this value into Clipboard!',
+ cssClass: 'btn-danger',
+ });
+ });
- it('should have a tooltip with default values', () => {
- expect(vm.$el.getAttribute('data-original-title')).toEqual('Copy this value into Clipboard!');
- expect(vm.$el.getAttribute('data-placement')).toEqual('top');
- expect(vm.$el.getAttribute('data-container')).toEqual(null);
+ it('renders a button for clipboard', () => {
+ expect(vm.$el.tagName).toEqual('BUTTON');
+ expect(vm.$el.getAttribute('data-clipboard-text')).toEqual('copy me');
+ expect(vm.$el).toHaveSpriteIcon('duplicate');
+ });
+
+ it('should have a tooltip with default values', () => {
+ expect(vm.$el.getAttribute('data-original-title')).toEqual('Copy this value into Clipboard!');
+ expect(vm.$el.getAttribute('data-placement')).toEqual('top');
+ expect(vm.$el.getAttribute('data-container')).toEqual(null);
+ });
+
+ it('should render provided classname', () => {
+ expect(vm.$el.classList).toContain('btn-danger');
+ });
});
- it('should render provided classname', () => {
- expect(vm.$el.classList).toContain('btn-danger');
+ describe('with gfm', () => {
+ it('sets data-clipboard-text with gfm', () => {
+ vm = mountComponent(Component, {
+ text: 'copy me',
+ gfm: '`path/to/file`',
+ title: 'Copy this value into Clipboard!',
+ cssClass: 'btn-danger',
+ });
+ expect(vm.$el.getAttribute('data-clipboard-text')).toEqual(
+ '{"text":"copy me","gfm":"`path/to/file`"}',
+ );
+ });
});
});
diff --git a/spec/javascripts/vue_shared/components/code_block_spec.js b/spec/javascripts/vue_shared/components/code_block_spec.js
new file mode 100644
index 00000000000..6b91a20ff76
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/code_block_spec.js
@@ -0,0 +1,33 @@
+import Vue from 'vue';
+import component from '~/vue_shared/components/code_block.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Code Block', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders a code block with the provided code', () => {
+ const code =
+ "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 \u003ctop (required)\u003e'";
+
+ vm = mountComponent(Component, {
+ code,
+ });
+
+ expect(vm.$el.querySelector('code').textContent).toEqual(code);
+ });
+
+ it('escapes XSS injections', () => {
+ const code = 'CCC&lt;img src=x onerror=alert(document.domain)&gt;';
+
+ vm = mountComponent(Component, {
+ code,
+ });
+
+ expect(vm.$el.querySelector('code').textContent).toEqual(code);
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
index b878286ae3f..dde49b4a5d7 100644
--- a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
+++ b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
@@ -170,8 +170,6 @@ describe('ImageDiffViewer', () => {
vm.$el.querySelector('.view-modes-menu li:nth-child(3)').click();
vm.$nextTick(() => {
- expect(vm.$el.querySelector('.dragger').style.left).toBe('100px');
-
dragSlider(vm.$el.querySelector('.dragger'));
vm.$nextTick(() => {
diff --git a/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
index ba897f4660d..2796cd088c6 100644
--- a/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
+++ b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
@@ -2,15 +2,15 @@ import Vue from 'vue';
import dropdownButtonComponent from '~/vue_shared/components/dropdown/dropdown_button.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper';
const defaultLabel = 'Select';
const customLabel = 'Select project';
-const createComponent = config => {
+const createComponent = (props, slots = {}) => {
const Component = Vue.extend(dropdownButtonComponent);
- return mountComponent(Component, config);
+ return mountComponentWithSlots(Component, { props, slots });
};
describe('DropdownButtonComponent', () => {
@@ -65,5 +65,14 @@ describe('DropdownButtonComponent', () => {
expect(dropdownIconEl).not.toBeNull();
expect(dropdownIconEl.classList.contains('fa-chevron-down')).toBe(true);
});
+
+ it('renders slot, if default slot exists', () => {
+ vm = createComponent({}, {
+ default: ['Lorem Ipsum Dolar'],
+ });
+
+ expect(vm.$el).not.toContainElement('.dropdown-toggle-text');
+ expect(vm.$el).toHaveText('Lorem Ipsum Dolar');
+ });
});
});
diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js
index e4737714312..263824a102a 100644
--- a/spec/javascripts/vue_shared/components/gl_modal_spec.js
+++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js
@@ -29,7 +29,7 @@ describe('GlModal', () => {
describe('without id', () => {
beforeEach(() => {
- vm = mountComponent(modalComponent, { });
+ vm = mountComponent(modalComponent, {});
});
it('does not add an id attribute to the modal', () => {
@@ -83,7 +83,7 @@ describe('GlModal', () => {
});
});
- it('works with data-toggle="modal"', (done) => {
+ it('works with data-toggle="modal"', done => {
setFixtures(`
<button id="modal-button" data-toggle="modal" data-target="#my-modal"></button>
<div id="modal-container"></div>
@@ -91,9 +91,13 @@ describe('GlModal', () => {
const modalContainer = document.getElementById('modal-container');
const modalButton = document.getElementById('modal-button');
- vm = mountComponent(modalComponent, {
- id: 'my-modal',
- }, modalContainer);
+ vm = mountComponent(
+ modalComponent,
+ {
+ id: 'my-modal',
+ },
+ modalContainer,
+ );
$(vm.$el).on('shown.bs.modal', () => done());
modalButton.click();
@@ -103,7 +107,7 @@ describe('GlModal', () => {
const dummyEvent = 'not really an event';
beforeEach(() => {
- vm = mountComponent(modalComponent, { });
+ vm = mountComponent(modalComponent, {});
spyOn(vm, '$emit');
});
@@ -122,11 +126,27 @@ describe('GlModal', () => {
expect(vm.$emit).toHaveBeenCalledWith('submit', dummyEvent);
});
});
+
+ describe('opened', () => {
+ it('emits a open event', () => {
+ vm.opened();
+
+ expect(vm.$emit).toHaveBeenCalledWith('open');
+ });
+ });
+
+ describe('closed', () => {
+ it('emits a closed event', () => {
+ vm.closed();
+
+ expect(vm.$emit).toHaveBeenCalledWith('closed');
+ });
+ });
});
describe('slots', () => {
const slotContent = 'this should go into the slot';
- const modalWithSlot = (slotName) => {
+ const modalWithSlot = slotName => {
let template;
if (slotName) {
template = `
diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
index 65499a2d730..f17818c17c7 100644
--- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js
+++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
@@ -12,7 +12,7 @@ describe('Header CI Component', () => {
props = {
status: {
group: 'failed',
- icon: 'ci-status-failed',
+ icon: 'status_failed',
label: 'failed',
text: 'failed',
details_path: 'path',
diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js
index cc030e29d61..01f4649339e 100644
--- a/spec/javascripts/vue_shared/components/icon_spec.js
+++ b/spec/javascripts/vue_shared/components/icon_spec.js
@@ -10,9 +10,10 @@ describe('Sprite Icon Component', function () {
const IconComponent = Vue.extend(Icon);
icon = mountComponent(IconComponent, {
- name: 'test',
+ name: 'commit',
size: 32,
cssClasses: 'extraclasses',
+ tabIndex: '0',
});
});
@@ -30,7 +31,7 @@ describe('Sprite Icon Component', function () {
it('should have <use> as a child element with the correct href', function () {
expect(icon.$el.firstChild.tagName).toBe('use');
- expect(icon.$el.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_icons}#test`);
+ expect(icon.$el.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_icons}#commit`);
});
it('should properly compute iconSizeClass', function () {
@@ -50,5 +51,17 @@ describe('Sprite Icon Component', function () {
expect(containsSizeClass).toBe(true);
expect(containsCustomClass).toBe(true);
});
+
+ it('`name` validator should return false for non existing icons', () => {
+ expect(Icon.props.name.validator('non_existing_icon_sprite')).toBe(false);
+ });
+
+ it('`name` validator should return false for existing icons', () => {
+ expect(Icon.props.name.validator('commit')).toBe(true);
+ });
+
+ it('should contain `tabindex` attribute on svg element when `tabIndex` prop is defined', () => {
+ expect(icon.$el.getAttribute('tabindex')).toBe('0');
+ });
});
});
diff --git a/spec/javascripts/vue_shared/components/identicon_spec.js b/spec/javascripts/vue_shared/components/identicon_spec.js
index 647680f00f7..0719800c682 100644
--- a/spec/javascripts/vue_shared/components/identicon_spec.js
+++ b/spec/javascripts/vue_shared/components/identicon_spec.js
@@ -25,19 +25,12 @@ describe('IdenticonComponent', () => {
vm.$destroy();
});
- describe('identiconStyles', () => {
- it('should return styles attribute value with `background-color` property', () => {
+ describe('identiconBackgroundClass', () => {
+ it('should return bg class based on entityId', () => {
vm.entityId = 4;
- expect(vm.identiconStyles).toBeDefined();
- expect(vm.identiconStyles.indexOf('background-color: #E0F2F1;') > -1).toBeTruthy();
- });
-
- it('should return styles attribute value with `color` property', () => {
- vm.entityId = 4;
-
- expect(vm.identiconStyles).toBeDefined();
- expect(vm.identiconStyles.indexOf('color: #555;') > -1).toBeTruthy();
+ expect(vm.identiconBackgroundClass).toBeDefined();
+ expect(vm.identiconBackgroundClass).toBe('bg5');
});
});
@@ -58,7 +51,7 @@ describe('IdenticonComponent', () => {
expect(vm.$el.nodeName).toBe('DIV');
expect(vm.$el.classList.contains('identicon')).toBeTruthy();
expect(vm.$el.classList.contains('s40')).toBeTruthy();
- expect(vm.$el.getAttribute('style').indexOf('background-color') > -1).toBeTruthy();
+ expect(vm.$el.classList.contains('bg2')).toBeTruthy();
vm.$destroy();
});
diff --git a/spec/javascripts/vue_shared/components/markdown/header_spec.js b/spec/javascripts/vue_shared/components/markdown/header_spec.js
index 02117638b63..488575df401 100644
--- a/spec/javascripts/vue_shared/components/markdown/header_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/header_spec.js
@@ -51,7 +51,7 @@ describe('Markdown field header component', () => {
spyOn(vm, '$emit');
$(document).triggerHandler('markdown-preview:show', [
- $('<form><textarea class="markdown-area"></textarea></textarea></form>'),
+ $('<form><div class="js-vue-markdown-field"><textarea class="markdown-area"></textarea></div></form>'),
]);
expect(vm.$emit).not.toHaveBeenCalled();
diff --git a/spec/javascripts/vue_shared/components/memory_graph_spec.js b/spec/javascripts/vue_shared/components/memory_graph_spec.js
index 73a69df019e..65d8ed39ade 100644
--- a/spec/javascripts/vue_shared/components/memory_graph_spec.js
+++ b/spec/javascripts/vue_shared/components/memory_graph_spec.js
@@ -113,7 +113,7 @@ describe('MemoryGraph', () => {
const circleEl = el.querySelector('circle');
expect(circleEl).toBeDefined();
expect(circleEl.getAttribute('r')).toBe('1.5');
- expect(circleEl.getAttribute('tranform')).toBe('translate(0 -1)');
+ expect(circleEl.getAttribute('transform')).toBe('translate(0 -1)');
expect(circleEl.getAttribute('cx')).toBe(`${dotX}`);
expect(circleEl.getAttribute('cy')).toBe(`${dotY}`);
done();
diff --git a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js b/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js
index 7e57c51bf29..db665fdaad3 100644
--- a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js
+++ b/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js
@@ -27,7 +27,7 @@ describe('issue placeholder system note component', () => {
userDataMock.path,
);
expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual(
- userDataMock.avatar_url,
+ `${userDataMock.avatar_url}?width=40`,
);
});
});
diff --git a/spec/javascripts/vue_shared/components/notes/system_note_spec.js b/spec/javascripts/vue_shared/components/notes/system_note_spec.js
index aa4c9c4c88c..2a6015fe35f 100644
--- a/spec/javascripts/vue_shared/components/notes/system_note_spec.js
+++ b/spec/javascripts/vue_shared/components/notes/system_note_spec.js
@@ -19,7 +19,7 @@ describe('system note component', () => {
path: '/root',
},
note_html: '<p dir="auto">closed</p>',
- system_note_icon_name: 'icon_status_closed',
+ system_note_icon_name: 'status_closed',
created_at: '2017-08-02T10:51:58.559Z',
},
};
diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
index 8efcb54659d..f1e62069462 100644
--- a/spec/javascripts/vue_shared/components/panel_resizer_spec.js
+++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
@@ -29,7 +29,7 @@ describe('Panel Resizer component', () => {
});
expect(vm.$el.tagName).toEqual('DIV');
- expect(vm.$el.getAttribute('class')).toBe('dragHandle dragleft');
+ expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-left');
expect(vm.$el.getAttribute('style')).toBe('cursor: ew-resize;');
});
@@ -40,7 +40,7 @@ describe('Panel Resizer component', () => {
});
expect(vm.$el.tagName).toEqual('DIV');
- expect(vm.$el.getAttribute('class')).toBe('dragHandle dragright');
+ expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-right');
});
it('drag the resizer', () => {
diff --git a/spec/javascripts/vue_shared/components/project_avatar/default_spec.js b/spec/javascripts/vue_shared/components/project_avatar/default_spec.js
new file mode 100644
index 00000000000..5fed3f4b892
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/project_avatar/default_spec.js
@@ -0,0 +1,58 @@
+import Vue from 'vue';
+import ProjectAvatarDefault from '~/vue_shared/components/project_avatar/default.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { projectData } from 'spec/ide/mock_data';
+import { getFirstCharacterCapitalized } from '~/lib/utils/text_utility';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('ProjectAvatarDefault component', () => {
+ const Component = Vue.extend(ProjectAvatarDefault);
+ let vm;
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ project: projectData,
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders identicon if project has no avatar_url', done => {
+ const expectedText = getFirstCharacterCapitalized(projectData.name);
+
+ vm.project = {
+ ...vm.project,
+ avatar_url: null,
+ };
+
+ vm.$nextTick()
+ .then(() => {
+ const identiconEl = vm.$el.querySelector('.identicon');
+
+ expect(identiconEl).not.toBe(null);
+ expect(identiconEl.textContent.trim()).toEqual(expectedText);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('renders avatar image if project has avatar_url', done => {
+ const avatarUrl = `${TEST_HOST}/images/home/nasa.svg`;
+
+ vm.project = {
+ ...vm.project,
+ avatar_url: avatarUrl,
+ };
+
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.$el).toContainElement('.avatar');
+ expect(vm.$el).not.toContainElement('.identicon');
+ expect(vm.$el.querySelector('img')).toHaveAttr('src', avatarUrl);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
index de3bf667fb3..076d940961d 100644
--- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
+++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
@@ -10,9 +10,9 @@ const createComponent = (config) => {
successLabel: 'Synced',
failureLabel: 'Failed',
neutralLabel: 'Out of sync',
- successCount: 10,
- failureCount: 5,
- totalCount: 20,
+ successCount: 25,
+ failureCount: 10,
+ totalCount: 5000,
}, config);
return mountComponent(Component, defaultConfig);
@@ -32,7 +32,7 @@ describe('StackedProgressBarComponent', () => {
describe('computed', () => {
describe('neutralCount', () => {
it('returns neutralCount based on totalCount, successCount and failureCount', () => {
- expect(vm.neutralCount).toBe(5); // 20 - 10 - 5
+ expect(vm.neutralCount).toBe(4965); // 5000 - 25 - 10
});
});
});
@@ -40,7 +40,11 @@ describe('StackedProgressBarComponent', () => {
describe('methods', () => {
describe('getPercent', () => {
it('returns percentage from provided count based on `totalCount`', () => {
- expect(vm.getPercent(10)).toBe(50);
+ expect(vm.getPercent(500)).toBe(10);
+ });
+
+ it('returns percentage with decimal place from provided count based on `totalCount`', () => {
+ expect(vm.getPercent(10)).toBe(0.2);
});
});
diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
index 656b57d764e..dc7652c77f7 100644
--- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
+++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
@@ -12,7 +12,7 @@ const DEFAULT_PROPS = {
tooltipPlacement: 'bottom',
};
-describe('User Avatar Image Component', function () {
+describe('User Avatar Image Component', function() {
let vm;
let UserAvatarImage;
@@ -20,37 +20,37 @@ describe('User Avatar Image Component', function () {
UserAvatarImage = Vue.extend(userAvatarImage);
});
- describe('Initialization', function () {
- beforeEach(function () {
+ describe('Initialization', function() {
+ beforeEach(function() {
vm = mountComponent(UserAvatarImage, {
...DEFAULT_PROPS,
}).$mount();
});
- it('should return a defined Vue component', function () {
+ it('should return a defined Vue component', function() {
expect(vm).toBeDefined();
});
- it('should have <img> as a child element', function () {
+ it('should have <img> as a child element', function() {
expect(vm.$el.tagName).toBe('IMG');
- expect(vm.$el.getAttribute('src')).toBe(DEFAULT_PROPS.imgSrc);
- expect(vm.$el.getAttribute('data-src')).toBe(DEFAULT_PROPS.imgSrc);
+ expect(vm.$el.getAttribute('src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`);
+ expect(vm.$el.getAttribute('data-src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`);
expect(vm.$el.getAttribute('alt')).toBe(DEFAULT_PROPS.imgAlt);
});
- it('should properly compute tooltipContainer', function () {
+ it('should properly compute tooltipContainer', function() {
expect(vm.tooltipContainer).toBe('body');
});
- it('should properly render tooltipContainer', function () {
+ it('should properly render tooltipContainer', function() {
expect(vm.$el.getAttribute('data-container')).toBe('body');
});
- it('should properly compute avatarSizeClass', function () {
+ it('should properly compute avatarSizeClass', function() {
expect(vm.avatarSizeClass).toBe('s99');
});
- it('should properly render img css', function () {
+ it('should properly render img css', function() {
const { classList } = vm.$el;
const containsAvatar = classList.contains('avatar');
const containsSizeClass = classList.contains('s99');
@@ -64,21 +64,21 @@ describe('User Avatar Image Component', function () {
});
});
- describe('Initialization when lazy', function () {
- beforeEach(function () {
+ describe('Initialization when lazy', function() {
+ beforeEach(function() {
vm = mountComponent(UserAvatarImage, {
...DEFAULT_PROPS,
lazy: true,
}).$mount();
});
- it('should add lazy attributes', function () {
+ it('should add lazy attributes', function() {
const { classList } = vm.$el;
const lazyClass = classList.contains('lazy');
expect(lazyClass).toBe(true);
expect(vm.$el.getAttribute('src')).toBe(placeholderImage);
- expect(vm.$el.getAttribute('data-src')).toBe(DEFAULT_PROPS.imgSrc);
+ expect(vm.$el.getAttribute('data-src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`);
});
});
});
diff --git a/spec/lib/additional_email_headers_interceptor_spec.rb b/spec/lib/additional_email_headers_interceptor_spec.rb
deleted file mode 100644
index b5c1a360ba9..00000000000
--- a/spec/lib/additional_email_headers_interceptor_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'spec_helper'
-
-describe AdditionalEmailHeadersInterceptor do
- let(:mail) do
- ActionMailer::Base.mail(to: 'test@mail.com', from: 'info@mail.com', body: 'hello')
- end
-
- before do
- mail.deliver_now
- end
-
- it 'adds Auto-Submitted header' do
- expect(mail.header['To'].value).to eq('test@mail.com')
- expect(mail.header['From'].value).to eq('info@mail.com')
- expect(mail.header['Auto-Submitted'].value).to eq('auto-generated')
- expect(mail.header['X-Auto-Response-Suppress'].value).to eq('All')
- end
-
- context 'when the same mail object is sent twice' do
- before do
- mail.deliver_now
- end
-
- it 'does not add the Auto-Submitted header twice' do
- expect(mail.header['Auto-Submitted'].value).to eq('auto-generated')
- expect(mail.header['X-Auto-Response-Suppress'].value).to eq('All')
- end
- end
-end
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index 92a27e308d2..c5a854b5660 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -73,37 +73,27 @@ describe Backup::Repository do
end
end
- describe '#delete_all_repositories', :seed_helper do
- shared_examples('delete_all_repositories') do
- before do
- allow(FileUtils).to receive(:mkdir_p).and_call_original
- allow(FileUtils).to receive(:mv).and_call_original
- end
-
- after(:all) do
- ensure_seeds
- end
-
- it 'removes all repositories' do
- # Sanity check: there should be something for us to delete
- expect(list_repositories).to include(File.join(SEED_STORAGE_PATH, TEST_REPO_PATH))
+ describe '#prepare_directories', :seed_helper do
+ before do
+ allow(FileUtils).to receive(:mkdir_p).and_call_original
+ allow(FileUtils).to receive(:mv).and_call_original
+ end
- subject.delete_all_repositories('default', Gitlab.config.repositories.storages['default'])
+ after(:all) do
+ ensure_seeds
+ end
- expect(list_repositories).to be_empty
- end
+ it' removes all repositories' do
+ # Sanity check: there should be something for us to delete
+ expect(list_repositories).to include(File.join(SEED_STORAGE_PATH, TEST_REPO_PATH))
- def list_repositories
- Dir[File.join(SEED_STORAGE_PATH, '*.git')]
- end
- end
+ subject.prepare_directories
- context 'with gitaly' do
- it_behaves_like 'delete_all_repositories'
+ expect(list_repositories).to be_empty
end
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like 'delete_all_repositories'
+ def list_repositories
+ Dir[File.join(SEED_STORAGE_PATH, '*.git')]
end
end
diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
index 41f957c4e00..d06c5535309 100644
--- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
@@ -7,6 +7,20 @@ describe Banzai::Filter::ImageLazyLoadFilter do
%(<img src="#{path}" />)
end
+ def image_with_class(path, class_attr = nil)
+ %(<img src="#{path}" class="#{class_attr}"/>)
+ end
+
+ it 'adds a class attribute' do
+ doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+ expect(doc.at_css('img')['class']).to eq 'lazy'
+ end
+
+ it 'appends to the current class attribute' do
+ doc = filter(image_with_class('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg', 'test'))
+ expect(doc.at_css('img')['class']).to eq 'test lazy'
+ end
+
it 'transforms the image src to a data-src' do
doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('img')['data-src']).to eq '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index ab14d77d552..a515d07b072 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -3,17 +3,61 @@ require 'spec_helper'
describe Banzai::Filter::MarkdownFilter do
include FilterSpecHelper
- context 'code block' do
- it 'adds language to lang attribute when specified' do
- result = filter("```html\nsome code\n```")
+ describe 'markdown engine from context' do
+ it 'defaults to CommonMark' do
+ expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
- expect(result).to start_with("<pre><code lang=\"html\">")
+ filter('test')
end
- it 'does not add language to lang attribute when not specified' do
- result = filter("```\nsome code\n```")
+ it 'uses Redcarpet' do
+ expect_any_instance_of(Banzai::Filter::MarkdownEngines::Redcarpet).to receive(:render).and_return('test')
- expect(result).to start_with("<pre><code>")
+ filter('test', { markdown_engine: :redcarpet })
+ end
+
+ it 'uses CommonMark' do
+ expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
+
+ filter('test', { markdown_engine: :common_mark })
+ end
+ end
+
+ describe 'code block' do
+ context 'using CommonMark' do
+ before do
+ stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
+ end
+
+ it 'adds language to lang attribute when specified' do
+ result = filter("```html\nsome code\n```")
+
+ expect(result).to start_with("<pre><code lang=\"html\">")
+ end
+
+ it 'does not add language to lang attribute when not specified' do
+ result = filter("```\nsome code\n```")
+
+ expect(result).to start_with("<pre><code>")
+ end
+ end
+
+ context 'using Redcarpet' do
+ before do
+ stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :redcarpet)
+ end
+
+ it 'adds language to lang attribute when specified' do
+ result = filter("```html\nsome code\n```")
+
+ expect(result).to start_with("\n<pre><code lang=\"html\">")
+ end
+
+ it 'does not add language to lang attribute when not specified' do
+ result = filter("```\nsome code\n```")
+
+ expect(result).to start_with("\n<pre><code>")
+ end
end
end
end
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index a1dd72c498f..55c41e55437 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -210,6 +210,13 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
.to eq reference
end
+ it 'commit ref tag is valid' do
+ doc = reference_filter("See #{reference}")
+ commit_ref_tag = doc.css('a').first.css('span.gfm.gfm-commit')
+
+ expect(commit_ref_tag.text).to eq(commit.short_id)
+ end
+
it 'has valid text' do
doc = reference_filter("See #{reference}")
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 9a2e521fdcf..919825a6102 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -46,7 +46,7 @@ describe Banzai::Filter::RedactorFilter do
it 'allows permitted Project references' do
user = create(:user)
project = create(:project)
- project.add_master(user)
+ project.add_maintainer(user)
link = reference_link(project: project.id, reference_type: 'test')
doc = filter(link, current_user: user)
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index d930c608b18..0b3c2390304 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -54,6 +54,18 @@ describe Banzai::Filter::SanitizationFilter do
expect(instance.whitelist[:transformers].size).to eq control_count
end
+ it 'customizes the whitelist only once for different instances' do
+ instance1 = described_class.new('Foo1')
+ instance2 = described_class.new('Foo2')
+ control_count = instance1.whitelist[:transformers].size
+
+ instance1.whitelist
+ instance2.whitelist
+
+ expect(instance1.whitelist[:transformers].size).to eq control_count
+ expect(instance2.whitelist[:transformers].size).to eq control_count
+ end
+
it 'sanitizes `class` attribute from all elements' do
act = %q{<pre class="code highlight white c"><code>&lt;span class="k"&gt;def&lt;/span&gt;</code></pre>}
exp = %q{<pre><code>&lt;span class="k"&gt;def&lt;/span&gt;</code></pre>}
diff --git a/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb b/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb
new file mode 100644
index 00000000000..744df3e0b96
--- /dev/null
+++ b/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Pipeline::EmojiPipeline do
+ def parse(text)
+ described_class.to_html(text, {})
+ end
+
+ it 'replaces emoji' do
+ expected_result = "Hello world #{Gitlab::Emoji.gl_emoji_tag('100')}"
+
+ expect(parse('Hello world :100:')).to eq(expected_result)
+ end
+
+ it 'filters out HTML tags' do
+ expected_result = "Hello &lt;b&gt;world&lt;/b&gt; #{Gitlab::Emoji.gl_emoji_tag('100')}"
+
+ expect(parse('Hello <b>world</b> :100:')).to eq(expected_result)
+ end
+end
diff --git a/spec/lib/bitbucket_server/client_spec.rb b/spec/lib/bitbucket_server/client_spec.rb
new file mode 100644
index 00000000000..f926ae963a4
--- /dev/null
+++ b/spec/lib/bitbucket_server/client_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe BitbucketServer::Client do
+ let(:base_uri) { 'https://test:7990/stash/' }
+ let(:options) { { base_uri: base_uri, user: 'bitbucket', password: 'mypassword' } }
+ let(:project) { 'SOME-PROJECT' }
+ let(:repo_slug) { 'my-repo' }
+ let(:headers) { { "Content-Type" => "application/json" } }
+
+ subject { described_class.new(options) }
+
+ describe '#pull_requests' do
+ let(:path) { "/projects/#{project}/repos/#{repo_slug}/pull-requests?state=ALL" }
+
+ it 'requests a collection' do
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :pull_request)
+
+ subject.pull_requests(project, repo_slug)
+ end
+
+ it 'throws an exception when connection fails' do
+ allow(BitbucketServer::Collection).to receive(:new).and_raise(OpenSSL::SSL::SSLError)
+
+ expect { subject.pull_requests(project, repo_slug) }.to raise_error(described_class::ServerError)
+ end
+ end
+
+ describe '#activities' do
+ let(:path) { "/projects/#{project}/repos/#{repo_slug}/pull-requests/1/activities" }
+
+ it 'requests a collection' do
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :activity)
+
+ subject.activities(project, repo_slug, 1)
+ end
+ end
+
+ describe '#repo' do
+ let(:path) { "/projects/#{project}/repos/#{repo_slug}" }
+ let(:url) { "#{base_uri}rest/api/1.0/projects/SOME-PROJECT/repos/my-repo" }
+
+ it 'requests a specific repository' do
+ stub_request(:get, url).to_return(status: 200, headers: headers, body: '{}')
+
+ subject.repo(project, repo_slug)
+
+ expect(WebMock).to have_requested(:get, url)
+ end
+ end
+
+ describe '#repos' do
+ let(:path) { "/repos" }
+
+ it 'requests a collection' do
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :repo)
+
+ subject.repos
+ end
+ end
+
+ describe '#create_branch' do
+ let(:branch) { 'test-branch' }
+ let(:sha) { '12345678' }
+ let(:url) { "#{base_uri}rest/api/1.0/projects/SOME-PROJECT/repos/my-repo/branches" }
+
+ it 'requests Bitbucket to create a branch' do
+ stub_request(:post, url).to_return(status: 204, headers: headers, body: '{}')
+
+ subject.create_branch(project, repo_slug, branch, sha)
+
+ expect(WebMock).to have_requested(:post, url)
+ end
+ end
+
+ describe '#delete_branch' do
+ let(:branch) { 'test-branch' }
+ let(:sha) { '12345678' }
+ let(:url) { "#{base_uri}rest/branch-utils/1.0/projects/SOME-PROJECT/repos/my-repo/branches" }
+
+ it 'requests Bitbucket to create a branch' do
+ stub_request(:delete, url).to_return(status: 204, headers: headers, body: '{}')
+
+ subject.delete_branch(project, repo_slug, branch, sha)
+
+ expect(WebMock).to have_requested(:delete, url)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/connection_spec.rb b/spec/lib/bitbucket_server/connection_spec.rb
new file mode 100644
index 00000000000..b5da4cb1a49
--- /dev/null
+++ b/spec/lib/bitbucket_server/connection_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe BitbucketServer::Connection do
+ let(:options) { { base_uri: 'https://test:7990', user: 'bitbucket', password: 'mypassword' } }
+ let(:payload) { { 'test' => 1 } }
+ let(:headers) { { "Content-Type" => "application/json" } }
+ let(:url) { 'https://test:7990/rest/api/1.0/test?something=1' }
+
+ subject { described_class.new(options) }
+
+ describe '#get' do
+ it 'returns JSON body' do
+ WebMock.stub_request(:get, url).with(headers: { 'Accept' => 'application/json' }).to_return(body: payload.to_json, status: 200, headers: headers)
+
+ expect(subject.get(url, { something: 1 })).to eq(payload)
+ end
+
+ it 'throws an exception if the response is not 200' do
+ WebMock.stub_request(:get, url).with(headers: { 'Accept' => 'application/json' }).to_return(body: payload.to_json, status: 500, headers: headers)
+
+ expect { subject.get(url) }.to raise_error(described_class::ConnectionError)
+ end
+
+ it 'throws an exception if the response is not JSON' do
+ WebMock.stub_request(:get, url).with(headers: { 'Accept' => 'application/json' }).to_return(body: 'bad data', status: 200, headers: headers)
+
+ expect { subject.get(url) }.to raise_error(described_class::ConnectionError)
+ end
+ end
+
+ describe '#post' do
+ let(:headers) { { 'Accept' => 'application/json', 'Content-Type' => 'application/json' } }
+
+ it 'returns JSON body' do
+ WebMock.stub_request(:post, url).with(headers: headers).to_return(body: payload.to_json, status: 200, headers: headers)
+
+ expect(subject.post(url, payload)).to eq(payload)
+ end
+
+ it 'throws an exception if the response is not 200' do
+ WebMock.stub_request(:post, url).with(headers: headers).to_return(body: payload.to_json, status: 500, headers: headers)
+
+ expect { subject.post(url, payload) }.to raise_error(described_class::ConnectionError)
+ end
+ end
+
+ describe '#delete' do
+ let(:headers) { { 'Accept' => 'application/json', 'Content-Type' => 'application/json' } }
+
+ context 'branch API' do
+ let(:branch_path) { '/projects/foo/repos/bar/branches' }
+ let(:branch_url) { 'https://test:7990/rest/branch-utils/1.0/projects/foo/repos/bar/branches' }
+ let(:path) { }
+
+ it 'returns JSON body' do
+ WebMock.stub_request(:delete, branch_url).with(headers: headers).to_return(body: payload.to_json, status: 200, headers: headers)
+
+ expect(subject.delete(:branches, branch_path, payload)).to eq(payload)
+ end
+
+ it 'throws an exception if the response is not 200' do
+ WebMock.stub_request(:delete, branch_url).with(headers: headers).to_return(body: payload.to_json, status: 500, headers: headers)
+
+ expect { subject.delete(:branches, branch_path, payload) }.to raise_error(described_class::ConnectionError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/page_spec.rb b/spec/lib/bitbucket_server/page_spec.rb
new file mode 100644
index 00000000000..cf419a9045b
--- /dev/null
+++ b/spec/lib/bitbucket_server/page_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe BitbucketServer::Page do
+ let(:response) { { 'values' => [{ 'description' => 'Test' }], 'isLastPage' => false, 'nextPageStart' => 2 } }
+
+ before do
+ # Autoloading hack
+ BitbucketServer::Representation::PullRequest.new({})
+ end
+
+ describe '#items' do
+ it 'returns collection of needed objects' do
+ page = described_class.new(response, :pull_request)
+
+ expect(page.items.first).to be_a(BitbucketServer::Representation::PullRequest)
+ expect(page.items.count).to eq(1)
+ end
+ end
+
+ describe '#attrs' do
+ it 'returns attributes' do
+ page = described_class.new(response, :pull_request)
+
+ expect(page.attrs.keys).to include(:isLastPage, :nextPageStart)
+ end
+ end
+
+ describe '#next?' do
+ it 'returns true' do
+ page = described_class.new(response, :pull_request)
+
+ expect(page.next?).to be_truthy
+ end
+
+ it 'returns false' do
+ response['isLastPage'] = true
+ response.delete('nextPageStart')
+ page = described_class.new(response, :pull_request)
+
+ expect(page.next?).to be_falsey
+ end
+ end
+
+ describe '#next' do
+ it 'returns next attribute' do
+ page = described_class.new(response, :pull_request)
+
+ expect(page.next).to eq(2)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/paginator_spec.rb b/spec/lib/bitbucket_server/paginator_spec.rb
new file mode 100644
index 00000000000..2de50eba3c4
--- /dev/null
+++ b/spec/lib/bitbucket_server/paginator_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe BitbucketServer::Paginator do
+ let(:last_page) { double(:page, next?: false, items: ['item_2']) }
+ let(:first_page) { double(:page, next?: true, next: last_page, items: ['item_1']) }
+ let(:connection) { instance_double(BitbucketServer::Connection) }
+
+ describe '#items' do
+ let(:paginator) { described_class.new(connection, 'http://more-data', :pull_request) }
+ let(:page_attrs) { { 'isLastPage' => false, 'nextPageStart' => 1 } }
+
+ it 'returns items and raises StopIteration in the end' do
+ allow(paginator).to receive(:fetch_next_page).and_return(first_page)
+ expect(paginator.items).to match(['item_1'])
+
+ allow(paginator).to receive(:fetch_next_page).and_return(last_page)
+ expect(paginator.items).to match(['item_2'])
+
+ allow(paginator).to receive(:fetch_next_page).and_return(nil)
+ expect { paginator.items }.to raise_error(StopIteration)
+ end
+
+ it 'calls the connection with different offsets' do
+ expect(connection).to receive(:get).with('http://more-data', start: 0, limit: BitbucketServer::Paginator::PAGE_LENGTH).and_return(page_attrs)
+
+ expect(paginator.items).to eq([])
+
+ expect(connection).to receive(:get).with('http://more-data', start: 1, limit: BitbucketServer::Paginator::PAGE_LENGTH).and_return({})
+
+ expect(paginator.items).to eq([])
+
+ expect { paginator.items }.to raise_error(StopIteration)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/activity_spec.rb b/spec/lib/bitbucket_server/representation/activity_spec.rb
new file mode 100644
index 00000000000..15c50e40472
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/activity_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::Activity do
+ let(:activities) { JSON.parse(fixture_file('importers/bitbucket_server/activities.json'))['values'] }
+ let(:inline_comment) { activities.first }
+ let(:comment) { activities[3] }
+ let(:merge_event) { activities[4] }
+
+ describe 'regular comment' do
+ subject { described_class.new(comment) }
+
+ it { expect(subject.comment?).to be_truthy }
+ it { expect(subject.inline_comment?).to be_falsey }
+ it { expect(subject.comment).to be_a(BitbucketServer::Representation::Comment) }
+ it { expect(subject.created_at).to be_a(Time) }
+ end
+
+ describe 'inline comment' do
+ subject { described_class.new(inline_comment) }
+
+ it { expect(subject.comment?).to be_truthy }
+ it { expect(subject.inline_comment?).to be_truthy }
+ it { expect(subject.comment).to be_a(BitbucketServer::Representation::PullRequestComment) }
+ it { expect(subject.created_at).to be_a(Time) }
+ end
+
+ describe 'merge event' do
+ subject { described_class.new(merge_event) }
+
+ it { expect(subject.comment?).to be_falsey }
+ it { expect(subject.inline_comment?).to be_falsey }
+ it { expect(subject.committer_user).to eq('root') }
+ it { expect(subject.committer_email).to eq('test.user@example.com') }
+ it { expect(subject.merge_timestamp).to be_a(Time) }
+ it { expect(subject.created_at).to be_a(Time) }
+ it { expect(subject.merge_commit).to eq('839fa9a2d434eb697815b8fcafaecc51accfdbbc') }
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/comment_spec.rb b/spec/lib/bitbucket_server/representation/comment_spec.rb
new file mode 100644
index 00000000000..53a20a1d80a
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/comment_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::Comment do
+ let(:activities) { JSON.parse(fixture_file('importers/bitbucket_server/activities.json'))['values'] }
+ let(:comment) { activities.first }
+
+ subject { described_class.new(comment) }
+
+ describe '#id' do
+ it { expect(subject.id).to eq(9) }
+ end
+
+ describe '#author_username' do
+ it { expect(subject.author_username).to eq('root' ) }
+ end
+
+ describe '#author_email' do
+ it { expect(subject.author_email).to eq('test.user@example.com' ) }
+ end
+
+ describe '#note' do
+ it { expect(subject.note).to eq('is this a new line?') }
+ end
+
+ describe '#created_at' do
+ it { expect(subject.created_at).to be_a(Time) }
+ end
+
+ describe '#updated_at' do
+ it { expect(subject.created_at).to be_a(Time) }
+ end
+
+ describe '#comments' do
+ it { expect(subject.comments.count).to eq(4) }
+ it { expect(subject.comments).to all( be_a(described_class) ) }
+ it { expect(subject.comments.map(&:note)).to match_array(["Hello world", "Ok", "hello", "hi"]) }
+
+ # The thread should look like:
+ #
+ # is this a new line? (subject)
+ # -> Hello world (first)
+ # -> Ok (third)
+ # -> Hi (fourth)
+ # -> hello (second)
+ it 'comments have the right parent' do
+ first, second, third, fourth = subject.comments[0..4]
+
+ expect(subject.parent_comment).to be_nil
+ expect(first.parent_comment).to eq(subject)
+ expect(second.parent_comment).to eq(subject)
+ expect(third.parent_comment).to eq(first)
+ expect(fourth.parent_comment).to eq(first)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb b/spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb
new file mode 100644
index 00000000000..bd7e3597486
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::PullRequestComment do
+ let(:activities) { JSON.parse(fixture_file('importers/bitbucket_server/activities.json'))['values'] }
+ let(:comment) { activities.second }
+
+ subject { described_class.new(comment) }
+
+ describe '#id' do
+ it { expect(subject.id).to eq(7) }
+ end
+
+ describe '#from_sha' do
+ it { expect(subject.from_sha).to eq('c5f4288162e2e6218180779c7f6ac1735bb56eab') }
+ end
+
+ describe '#to_sha' do
+ it { expect(subject.to_sha).to eq('a4c2164330f2549f67c13f36a93884cf66e976be') }
+ end
+
+ describe '#to?' do
+ it { expect(subject.to?).to be_falsey }
+ end
+
+ describe '#from?' do
+ it { expect(subject.from?).to be_truthy }
+ end
+
+ describe '#added?' do
+ it { expect(subject.added?).to be_falsey }
+ end
+
+ describe '#removed?' do
+ it { expect(subject.removed?).to be_falsey }
+ end
+
+ describe '#new_pos' do
+ it { expect(subject.new_pos).to eq(11) }
+ end
+
+ describe '#old_pos' do
+ it { expect(subject.old_pos).to eq(9) }
+ end
+
+ describe '#file_path' do
+ it { expect(subject.file_path).to eq('CHANGELOG.md') }
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/pull_request_spec.rb b/spec/lib/bitbucket_server/representation/pull_request_spec.rb
new file mode 100644
index 00000000000..4b8afdb006b
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/pull_request_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::PullRequest do
+ let(:sample_data) { JSON.parse(fixture_file('importers/bitbucket_server/pull_request.json')) }
+
+ subject { described_class.new(sample_data) }
+
+ describe '#author' do
+ it { expect(subject.author).to eq('root') }
+ end
+
+ describe '#author_email' do
+ it { expect(subject.author_email).to eq('joe.montana@49ers.com') }
+ end
+
+ describe '#description' do
+ it { expect(subject.description).to eq('Test') }
+ end
+
+ describe '#iid' do
+ it { expect(subject.iid).to eq(7) }
+ end
+
+ describe '#state' do
+ it { expect(subject.state).to eq('merged') }
+
+ context 'declined pull requests' do
+ before do
+ sample_data['state'] = 'DECLINED'
+ end
+
+ it 'returns closed' do
+ expect(subject.state).to eq('closed')
+ end
+ end
+
+ context 'open pull requests' do
+ before do
+ sample_data['state'] = 'OPEN'
+ end
+
+ it 'returns open' do
+ expect(subject.state).to eq('opened')
+ end
+ end
+ end
+
+ describe '#merged?' do
+ it { expect(subject.merged?).to be_truthy }
+ end
+
+ describe '#created_at' do
+ it { expect(subject.created_at.to_i).to eq(sample_data['createdDate'] / 1000) }
+ end
+
+ describe '#updated_at' do
+ it { expect(subject.updated_at.to_i).to eq(sample_data['updatedDate'] / 1000) }
+ end
+
+ describe '#title' do
+ it { expect(subject.title).to eq('Added a new line') }
+ end
+
+ describe '#source_branch_name' do
+ it { expect(subject.source_branch_name).to eq('refs/heads/root/CODE_OF_CONDUCTmd-1530600625006') }
+ end
+
+ describe '#source_branch_sha' do
+ it { expect(subject.source_branch_sha).to eq('074e2b4dddc5b99df1bf9d4a3f66cfc15481fdc8') }
+ end
+
+ describe '#target_branch_name' do
+ it { expect(subject.target_branch_name).to eq('refs/heads/master') }
+ end
+
+ describe '#target_branch_sha' do
+ it { expect(subject.target_branch_sha).to eq('839fa9a2d434eb697815b8fcafaecc51accfdbbc') }
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/repo_spec.rb b/spec/lib/bitbucket_server/representation/repo_spec.rb
new file mode 100644
index 00000000000..3ac1030fbb0
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/repo_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::Repo do
+ let(:sample_data) do
+ <<~DATA
+ {
+ "slug": "rouge",
+ "id": 1,
+ "name": "rouge",
+ "scmId": "git",
+ "state": "AVAILABLE",
+ "statusMessage": "Available",
+ "forkable": true,
+ "project": {
+ "key": "TEST",
+ "id": 1,
+ "name": "test",
+ "description": "Test",
+ "public": false,
+ "type": "NORMAL",
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/projects/TEST"
+ }
+ ]
+ }
+ },
+ "public": false,
+ "links": {
+ "clone": [
+ {
+ "href": "http://root@localhost:7990/scm/test/rouge.git",
+ "name": "http"
+ },
+ {
+ "href": "ssh://git@localhost:7999/test/rouge.git",
+ "name": "ssh"
+ }
+ ],
+ "self": [
+ {
+ "href": "http://localhost:7990/projects/TEST/repos/rouge/browse"
+ }
+ ]
+ }
+ }
+ DATA
+ end
+
+ subject { described_class.new(JSON.parse(sample_data)) }
+
+ describe '#project_key' do
+ it { expect(subject.project_key).to eq('TEST') }
+ end
+
+ describe '#project_name' do
+ it { expect(subject.project_name).to eq('test') }
+ end
+
+ describe '#slug' do
+ it { expect(subject.slug).to eq('rouge') }
+ end
+
+ describe '#browse_url' do
+ it { expect(subject.browse_url).to eq('http://localhost:7990/projects/TEST/repos/rouge/browse') }
+ end
+
+ describe '#clone_url' do
+ it { expect(subject.clone_url).to eq('http://root@localhost:7990/scm/test/rouge.git') }
+ end
+
+ describe '#description' do
+ it { expect(subject.description).to eq('Test') }
+ end
+
+ describe '#full_name' do
+ it { expect(subject.full_name).to eq('test/rouge') }
+ end
+end
diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/disable_email_interceptor_spec.rb
deleted file mode 100644
index 3652d928c43..00000000000
--- a/spec/lib/disable_email_interceptor_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'spec_helper'
-
-describe DisableEmailInterceptor do
- before do
- Mail.register_interceptor(described_class)
- end
-
- it 'does not send emails' do
- allow(Gitlab.config.gitlab).to receive(:email_enabled).and_return(false)
- expect { deliver_mail }.not_to change(ActionMailer::Base.deliveries, :count)
- end
-
- after do
- # Removing interceptor from the list because unregister_interceptor is
- # implemented in later version of mail gem
- # See: https://github.com/mikel/mail/pull/705
- Mail.unregister_interceptor(described_class)
- end
-
- def deliver_mail
- key = create :personal_key
- Notify.new_ssh_key_email(key.id)
- end
-end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index e13406d1972..8947e2ac4fb 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -203,4 +203,30 @@ describe ExtractsPath do
expect(extract_ref_without_atom('foo.atom')).to eq(nil)
end
end
+
+ describe '#lfs_blob_ids' do
+ shared_examples '#lfs_blob_ids' do
+ let(:tag) { @project.repository.add_tag(@project.owner, 'my-annotated-tag', 'master', 'test tag') }
+ let(:ref) { tag.target }
+ let(:params) { { ref: ref, path: 'README.md' } }
+
+ before do
+ @project = create(:project, :repository)
+ end
+
+ it 'handles annotated tags' do
+ assign_ref_vars
+
+ expect(lfs_blob_ids).to eq([])
+ end
+ end
+
+ context 'when gitaly is enabled' do
+ it_behaves_like '#lfs_blob_ids'
+ end
+
+ context 'when gitaly is disabled', :skip_gitaly_mock do
+ it_behaves_like '#lfs_blob_ids'
+ end
+ end
end
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 6eb10497428..f313e675654 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -39,18 +39,36 @@ describe Feature do
end
describe '.persisted?' do
- it 'returns true for a persisted feature' do
- Feature::FlipperFeature.create!(key: 'foo')
+ context 'when the feature is persisted' do
+ it 'returns true when feature name is a string' do
+ Feature::FlipperFeature.create!(key: 'foo')
+
+ feature = double(:feature, name: 'foo')
+
+ expect(described_class.persisted?(feature)).to eq(true)
+ end
+
+ it 'returns true when feature name is a symbol' do
+ Feature::FlipperFeature.create!(key: 'foo')
- feature = double(:feature, name: 'foo')
+ feature = double(:feature, name: :foo)
- expect(described_class.persisted?(feature)).to eq(true)
+ expect(described_class.persisted?(feature)).to eq(true)
+ end
end
- it 'returns false for a feature that is not persisted' do
- feature = double(:feature, name: 'foo')
+ context 'when the feature is not persisted' do
+ it 'returns false when feature name is a string' do
+ feature = double(:feature, name: 'foo')
+
+ expect(described_class.persisted?(feature)).to eq(false)
+ end
- expect(described_class.persisted?(feature)).to eq(false)
+ it 'returns false when feature name is a symbol' do
+ feature = double(:feature, name: :bar)
+
+ expect(described_class.persisted?(feature)).to eq(false)
+ end
end
end
diff --git a/spec/lib/gitlab/auth/activity_spec.rb b/spec/lib/gitlab/auth/activity_spec.rb
new file mode 100644
index 00000000000..07854cb1eba
--- /dev/null
+++ b/spec/lib/gitlab/auth/activity_spec.rb
@@ -0,0 +1,30 @@
+require 'fast_spec_helper'
+
+describe Gitlab::Auth::Activity do
+ describe '.each_counter' do
+ it 'has all static counters defined' do
+ described_class.each_counter do |counter|
+ expect(described_class).to respond_to(counter)
+ end
+ end
+
+ it 'has all static incrementers defined' do
+ described_class.each_counter do |counter|
+ expect(described_class).to respond_to("#{counter}_increment!")
+ end
+ end
+
+ it 'has all counters starting with `user_`' do
+ described_class.each_counter do |counter|
+ expect(counter).to start_with('user_')
+ end
+ end
+
+ it 'yields counter method, name and description' do
+ described_class.each_counter do |method, name, description|
+ expect(method).to eq "#{name}_counter"
+ expect(description).to start_with('Counter of')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb b/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb
index 43b68e69131..f39863fdda1 100644
--- a/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb
+++ b/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb
@@ -1,62 +1,30 @@
require 'spec_helper'
describe Gitlab::Auth::BlockedUserTracker do
- set(:user) { create(:user) }
+ describe '#log_blocked_user_activity!' do
+ context 'when user is not blocked' do
+ it 'does not log blocked user activity' do
+ expect_any_instance_of(SystemHooksService)
+ .not_to receive(:execute_hooks_for)
+ expect(Gitlab::AppLogger).not_to receive(:info)
- describe '.log_if_user_blocked' do
- it 'does not log if user failed to login due to undefined reason' do
- expect_any_instance_of(SystemHooksService).not_to receive(:execute_hooks_for)
+ user = create(:user)
- expect(described_class.log_if_user_blocked({})).to be_nil
- end
-
- it 'gracefully handles malformed environment variables' do
- env = { 'warden.options' => 'test' }
-
- expect(described_class.log_if_user_blocked(env)).to be_nil
- end
-
- context 'failed login due to blocked user' do
- let(:base_env) { { 'warden.options' => { message: User::BLOCKED_MESSAGE } } }
- let(:env) { base_env.merge(request_env) }
-
- subject { described_class.log_if_user_blocked(env) }
-
- before do
- expect_any_instance_of(SystemHooksService).to receive(:execute_hooks_for).with(user, :failed_login)
- end
-
- context 'via GitLab login' do
- let(:request_env) { { described_class::ACTIVE_RECORD_REQUEST_PARAMS => { 'user' => { 'login' => user.username } } } }
-
- it 'logs a blocked user' do
- user.block!
-
- expect(subject).to be_truthy
- end
-
- it 'logs a blocked user by e-mail' do
- user.block!
- env[described_class::ACTIVE_RECORD_REQUEST_PARAMS]['user']['login'] = user.email
-
- expect(subject).to be_truthy
- end
+ described_class.new(user, spy('auth')).log_activity!
end
+ end
- context 'via LDAP login' do
- let(:request_env) { { described_class::ACTIVE_RECORD_REQUEST_PARAMS => { 'username' => user.username } } }
-
- it 'logs a blocked user' do
- user.block!
-
- expect(subject).to be_truthy
- end
+ context 'when user is not blocked' do
+ it 'logs blocked user activity' do
+ user = create(:user, :blocked)
- it 'logs a LDAP blocked user' do
- user.ldap_block!
+ expect_any_instance_of(SystemHooksService)
+ .to receive(:execute_hooks_for)
+ .with(user, :failed_login)
+ expect(Gitlab::AppLogger).to receive(:info)
+ .with(/Failed login for blocked user/)
- expect(subject).to be_truthy
- end
+ described_class.new(user, spy('auth')).log_activity!
end
end
end
diff --git a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
index a251ab323d8..1969aed51da 100644
--- a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
+++ b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
@@ -1,31 +1,35 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180619121030 do
+describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, :sidekiq, schema: 20180619121030 do
describe '#perform' do
context 'when diff files can be deleted' do
let(:merge_request) { create(:merge_request, :merged) }
- let(:merge_request_diff) do
+ let!(:merge_request_diff) do
merge_request.create_merge_request_diff
merge_request.merge_request_diffs.first
end
+ let(:perform) do
+ described_class.new.perform(MergeRequestDiff.pluck(:id))
+ end
+
it 'deletes all merge request diff files' do
- expect { described_class.new.perform(merge_request_diff.id) }
+ expect { perform }
.to change { merge_request_diff.merge_request_diff_files.count }
.from(20).to(0)
end
it 'updates state to without_files' do
- expect { described_class.new.perform(merge_request_diff.id) }
+ expect { perform }
.to change { merge_request_diff.reload.state }
.from('collected').to('without_files')
end
it 'rollsback if something goes wrong' do
- expect(MergeRequestDiffFile).to receive_message_chain(:where, :delete_all)
+ expect(described_class::MergeRequestDiffFile).to receive_message_chain(:where, :delete_all)
.and_raise
- expect { described_class.new.perform(merge_request_diff.id) }
+ expect { perform }
.to raise_error
merge_request_diff.reload
@@ -35,35 +39,35 @@ describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180
end
end
- it 'deletes no merge request diff files when MR is not merged' do
- merge_request = create(:merge_request, :opened)
- merge_request.create_merge_request_diff
- merge_request_diff = merge_request.merge_request_diffs.first
-
- expect { described_class.new.perform(merge_request_diff.id) }
- .not_to change { merge_request_diff.merge_request_diff_files.count }
- .from(20)
- end
-
- it 'deletes no merge request diff files when diff is marked as "without_files"' do
+ it 'reschedules itself when should_wait_deadtuple_vacuum' do
merge_request = create(:merge_request, :merged)
- merge_request.create_merge_request_diff
- merge_request_diff = merge_request.merge_request_diffs.first
+ first_diff = merge_request.merge_request_diff
+ second_diff = merge_request.create_merge_request_diff
- merge_request_diff.clean!
+ Sidekiq::Testing.fake! do
+ worker = described_class.new
+ allow(worker).to receive(:should_wait_deadtuple_vacuum?) { true }
- expect { described_class.new.perform(merge_request_diff.id) }
- .not_to change { merge_request_diff.merge_request_diff_files.count }
- .from(20)
+ worker.perform([first_diff.id, second_diff.id])
+
+ expect(described_class.name.demodulize).to be_scheduled_delayed_migration(5.minutes, [first_diff.id, second_diff.id])
+ expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ end
end
+ end
- it 'deletes no merge request diff files when diff is the latest' do
- merge_request = create(:merge_request, :merged)
- merge_request_diff = merge_request.merge_request_diff
+ describe '#should_wait_deadtuple_vacuum?' do
+ it 'returns true when hitting merge_request_diff_files hits DEAD_TUPLES_THRESHOLD', :postgresql do
+ worker = described_class.new
+ threshold_query_result = [{ "n_dead_tup" => described_class::DEAD_TUPLES_THRESHOLD.to_s }]
+ normal_query_result = [{ "n_dead_tup" => '3' }]
+
+ allow(worker)
+ .to receive(:execute_statement)
+ .with(/SELECT n_dead_tup */)
+ .and_return(threshold_query_result, normal_query_result)
- expect { described_class.new.perform(merge_request_diff.id) }
- .not_to change { merge_request_diff.merge_request_diff_files.count }
- .from(20)
+ expect(worker.should_wait_deadtuple_vacuum?).to be(true)
end
end
end
diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
index 211e3aaa94b..0735ebd6dcb 100644
--- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
+++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
@@ -9,6 +9,11 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:merge_request) { merge_requests.create!(iid: 1, target_project_id: project.id, source_project_id: project.id, target_branch: 'feature', source_branch: 'master').becomes(MergeRequest) }
let(:merge_request_diff) { MergeRequest.find(merge_request.id).create_merge_request_diff }
let(:updated_merge_request_diff) { MergeRequestDiff.find(merge_request_diff.id) }
+ let(:rugged) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged
+ end
+ end
before do
allow_any_instance_of(MergeRequestDiff)
@@ -299,11 +304,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:commits) { merge_request_diff.commits.map(&:to_hash) }
let(:first_commit) { project.repository.commit(merge_request_diff.head_commit_sha) }
let(:expected_commits) { commits }
- let(:diffs) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- first_commit.rugged_diff_from_parent.patches
- end
- end
+ let(:diffs) { rugged_diff(first_commit.sha).patches }
let(:expected_diffs) { [] }
include_examples 'updated MR diff'
@@ -313,14 +314,15 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:commits) { merge_request_diff.commits.map(&:to_hash) }
let(:first_commit) { project.repository.commit(merge_request_diff.head_commit_sha) }
let(:expected_commits) { commits }
- let(:diffs) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- first_commit.rugged_diff_from_parent.deltas
- end
- end
+ let(:diffs) { rugged_diff(first_commit.sha).deltas }
let(:expected_diffs) { [] }
include_examples 'updated MR diff'
end
+
+ def rugged_diff(commit_sha)
+ rugged_commit = rugged.lookup(commit_sha)
+ rugged_commit.parents[0].diff(rugged_commit)
+ end
end
end
diff --git a/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb b/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb
new file mode 100644
index 00000000000..20af63bc6c8
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb
@@ -0,0 +1,109 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::FixCrossProjectLabelLinks, :migration, schema: 20180702120647 do
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:issues_table) { table(:issues) }
+ let(:merge_requests_table) { table(:merge_requests) }
+ let(:labels_table) { table(:labels) }
+ let(:label_links_table) { table(:label_links) }
+
+ let!(:group1) { namespaces_table.create(id: 10, type: 'Group', name: 'group1', path: 'group1') }
+ let!(:group2) { namespaces_table.create(id: 20, type: 'Group', name: 'group2', path: 'group2') }
+
+ let!(:project1) { projects_table.create(id: 1, name: 'project1', path: 'group1/project1', namespace_id: 10) }
+ let!(:project2) { projects_table.create(id: 3, name: 'project2', path: 'group1/project2', namespace_id: 20) }
+
+ let!(:label1) { labels_table.create(id: 1, title: 'bug', color: 'red', group_id: 10, type: 'GroupLabel') }
+ let!(:label2) { labels_table.create(id: 2, title: 'bug', color: 'red', group_id: 20, type: 'GroupLabel') }
+
+ def create_merge_request(id, project_id)
+ merge_requests_table.create(id: id,
+ target_project_id: project_id,
+ target_branch: 'master',
+ source_project_id: project_id,
+ source_branch: 'mr name',
+ title: "mr name#{id}")
+ end
+
+ def create_issue(id, project_id)
+ issues_table.create(id: id, title: "issue#{id}", project_id: project_id)
+ end
+
+ def create_resource(target_type, id, project_id)
+ target_type == 'Issue' ? create_issue(id, project_id) : create_merge_request(id, project_id)
+ end
+
+ shared_examples_for 'resource with cross-project labels' do
+ it 'updates only cross-project label links which exist in the local project or group' do
+ create_resource(target_type, 1, 1)
+ create_resource(target_type, 2, 3)
+ labels_table.create(id: 3, title: 'bug', color: 'red', project_id: 3, type: 'ProjectLabel')
+ link = label_links_table.create(label_id: 2, target_type: target_type, target_id: 1)
+ link2 = label_links_table.create(label_id: 3, target_type: target_type, target_id: 2)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(1)
+ expect(link2.reload.label_id).to eq(3)
+ end
+
+ it 'ignores cross-project label links if label color is different' do
+ labels_table.create(id: 3, title: 'bug', color: 'green', group_id: 20, type: 'GroupLabel')
+ create_resource(target_type, 1, 1)
+ link = label_links_table.create(label_id: 3, target_type: target_type, target_id: 1)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(3)
+ end
+
+ it 'ignores cross-project label links if label name is different' do
+ labels_table.create(id: 3, title: 'bug1', color: 'red', group_id: 20, type: 'GroupLabel')
+ create_resource(target_type, 1, 1)
+ link = label_links_table.create(label_id: 3, target_type: target_type, target_id: 1)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(3)
+ end
+
+ context 'with nested group' do
+ before do
+ namespaces_table.create(id: 11, type: 'Group', name: 'subgroup1', path: 'group1/subgroup1', parent_id: 10)
+ projects_table.create(id: 2, name: 'subproject1', path: 'group1/subgroup1/subproject1', namespace_id: 11)
+ create_resource(target_type, 1, 2)
+ end
+
+ it 'ignores label links referencing ancestor group labels', :nested_groups do
+ labels_table.create(id: 4, title: 'bug', color: 'red', project_id: 2, type: 'ProjectLabel')
+ label_links_table.create(label_id: 4, target_type: target_type, target_id: 1)
+ link = label_links_table.create(label_id: 1, target_type: target_type, target_id: 1)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(1)
+ end
+
+ it 'checks also issues and MRs in subgroups', :nested_groups do
+ link = label_links_table.create(label_id: 2, target_type: target_type, target_id: 1)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(1)
+ end
+ end
+ end
+
+ context 'resource is Issue' do
+ it_behaves_like 'resource with cross-project labels' do
+ let(:target_type) { 'Issue' }
+ end
+ end
+
+ context 'resource is Merge Request' do
+ it_behaves_like 'resource with cross-project labels' do
+ let(:target_type) { 'MergeRequest' }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb b/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb
index ee60e498b59..2e77e80ee46 100644
--- a/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb
+++ b/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::BackgroundMigration::MovePersonalSnippetFiles do
let(:snippet) do
snippet = create(:personal_snippet)
create_upload_for_snippet(snippet)
- snippet.update_attributes!(description: markdown_linking_file(snippet))
+ snippet.update!(description: markdown_linking_file(snippet))
snippet
end
diff --git a/spec/lib/gitlab/background_migration/remove_restricted_todos_spec.rb b/spec/lib/gitlab/background_migration/remove_restricted_todos_spec.rb
new file mode 100644
index 00000000000..dae754112dc
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/remove_restricted_todos_spec.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::RemoveRestrictedTodos, :migration, schema: 20180704204006 do
+ let(:projects) { table(:projects) }
+ let(:users) { table(:users) }
+ let(:todos) { table(:todos) }
+ let(:issues) { table(:issues) }
+ let(:assignees) { table(:issue_assignees) }
+ let(:project_authorizations) { table(:project_authorizations) }
+ let(:project_features) { table(:project_features) }
+
+ let(:todo_params) { { author_id: 1, target_type: 'Issue', action: 1, state: :pending } }
+
+ before do
+ users.create(id: 1, email: 'user@example.com', projects_limit: 10)
+ users.create(id: 2, email: 'reporter@example.com', projects_limit: 10)
+ users.create(id: 3, email: 'guest@example.com', projects_limit: 10)
+
+ projects.create!(id: 1, name: 'project-1', path: 'project-1', visibility_level: 0, namespace_id: 1)
+ projects.create!(id: 2, name: 'project-2', path: 'project-2', visibility_level: 0, namespace_id: 1)
+
+ issues.create(id: 1, project_id: 1)
+ issues.create(id: 2, project_id: 2)
+
+ project_authorizations.create(user_id: 2, project_id: 2, access_level: 20) # reporter
+ project_authorizations.create(user_id: 3, project_id: 2, access_level: 10) # guest
+
+ todos.create(todo_params.merge(user_id: 1, project_id: 1, target_id: 1)) # out of project ids range
+ todos.create(todo_params.merge(user_id: 1, project_id: 2, target_id: 2)) # non member
+ todos.create(todo_params.merge(user_id: 2, project_id: 2, target_id: 2)) # reporter
+ todos.create(todo_params.merge(user_id: 3, project_id: 2, target_id: 2)) # guest
+ end
+
+ subject { described_class.new.perform(2, 5) }
+
+ context 'when a project is private' do
+ it 'removes todos of users without project access' do
+ expect { subject }.to change { Todo.count }.from(4).to(3)
+ end
+
+ context 'with a confidential issue' do
+ it 'removes todos of users without project access and guests for confidential issues' do
+ issues.create(id: 3, project_id: 2, confidential: true)
+ issues.create(id: 4, project_id: 1, confidential: true) # not in the batch
+ todos.create(todo_params.merge(user_id: 3, project_id: 2, target_id: 3))
+ todos.create(todo_params.merge(user_id: 2, project_id: 2, target_id: 3))
+ todos.create(todo_params.merge(user_id: 1, project_id: 1, target_id: 4))
+
+ expect { subject }.to change { Todo.count }.from(7).to(5)
+ end
+ end
+ end
+
+ context 'when a project is public' do
+ before do
+ projects.find(2).update_attribute(:visibility_level, 20)
+ end
+
+ context 'when all features have the same visibility as the project, no confidential issues' do
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'with confidential issues' do
+ before do
+ users.create(id: 4, email: 'author@example.com', projects_limit: 10)
+ users.create(id: 5, email: 'assignee@example.com', projects_limit: 10)
+ issues.create(id: 3, project_id: 2, confidential: true, author_id: 4)
+ assignees.create(user_id: 5, issue_id: 3)
+
+ todos.create(todo_params.merge(user_id: 1, project_id: 2, target_id: 3)) # to be deleted
+ todos.create(todo_params.merge(user_id: 2, project_id: 2, target_id: 3)) # authorized user
+ todos.create(todo_params.merge(user_id: 3, project_id: 2, target_id: 3)) # to be deleted guest
+ todos.create(todo_params.merge(user_id: 4, project_id: 2, target_id: 3)) # conf issue author
+ todos.create(todo_params.merge(user_id: 5, project_id: 2, target_id: 3)) # conf issue assignee
+ end
+
+ it 'removes confidential issue todos for non authorized users' do
+ expect { subject }.to change { Todo.count }.from(9).to(7)
+ end
+ end
+
+ context 'features visibility restrictions' do
+ before do
+ todo_params.merge!(project_id: 2, user_id: 1, target_id: 3)
+ todos.create(todo_params.merge(user_id: 1, target_id: 3, target_type: 'MergeRequest'))
+ todos.create(todo_params.merge(user_id: 1, target_id: 3, target_type: 'Commit'))
+ end
+
+ context 'when issues are restricted to project members' do
+ before do
+ project_features.create(issues_access_level: 10, project_id: 2)
+ end
+
+ it 'removes non members issue todos' do
+ expect { subject }.to change { Todo.count }.from(6).to(5)
+ end
+ end
+
+ context 'when merge requests are restricted to project members' do
+ before do
+ project_features.create(merge_requests_access_level: 10, project_id: 2)
+ end
+
+ it 'removes non members issue todos' do
+ expect { subject }.to change { Todo.count }.from(6).to(5)
+ end
+ end
+
+ context 'when repository and merge requests are restricted to project members' do
+ before do
+ project_features.create(repository_access_level: 10, merge_requests_access_level: 10, project_id: 2)
+ end
+
+ it 'removes non members commit and merge requests todos' do
+ expect { subject }.to change { Todo.count }.from(6).to(4)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb
new file mode 100644
index 00000000000..ec8ba0ce127
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::ScheduleDiffFilesDeletion, :migration, :sidekiq, schema: 20180619121030 do
+ describe '#perform' do
+ let(:merge_request_diffs) { table(:merge_request_diffs) }
+ let(:merge_requests) { table(:merge_requests) }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+
+ before do
+ stub_const("#{described_class.name}::DIFF_BATCH_SIZE", 3)
+
+ namespaces.create!(id: 1, name: 'gitlab', path: 'gitlab')
+ projects.create!(id: 1, namespace_id: 1, name: 'gitlab', path: 'gitlab')
+
+ merge_requests.create!(id: 1, target_project_id: 1, source_project_id: 1, target_branch: 'feature', source_branch: 'master', state: 'merged')
+
+ merge_request_diffs.create!(id: 1, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 2, merge_request_id: 1, state: 'empty')
+ merge_request_diffs.create!(id: 3, merge_request_id: 1, state: 'without_files')
+ merge_request_diffs.create!(id: 4, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 5, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 6, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 7, merge_request_id: 1, state: 'collected')
+
+ merge_requests.update(1, latest_merge_request_diff_id: 7)
+ end
+
+ it 'correctly schedules diff file deletion workers' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ described_class.new.perform
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, [1, 4, 5])
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, [6])
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
index 468f6ff6d24..6e21c846c0a 100644
--- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
@@ -1,10 +1,11 @@
require 'spec_helper'
-describe Gitlab::BareRepositoryImport::Importer, repository: true do
+describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
let!(:admin) { create(:admin) }
let!(:base_dir) { Dir.mktmpdir + '/' }
let(:bare_repository) { Gitlab::BareRepositoryImport::Repository.new(base_dir, File.join(base_dir, "#{project_path}.git")) }
let(:gitlab_shell) { Gitlab::Shell.new }
+ let(:source_project) { TEST_REPO_PATH }
subject(:importer) { described_class.new(admin, bare_repository) }
@@ -17,16 +18,11 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
after do
FileUtils.rm_rf(base_dir)
+ TestEnv.clean_test_path
+ ensure_seeds
Rainbow.enabled = @rainbow
end
- around do |example|
- # TODO migrate BareRepositoryImport https://gitlab.com/gitlab-org/gitaly/issues/953
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
shared_examples 'importing a repository' do
describe '.execute' do
it 'creates a project for a repository in storage' do
@@ -86,8 +82,8 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
importer.create_project_if_needed
end
- it 'creates the Git repo on disk with the proper symlink for hooks' do
- create_bare_repository("#{project_path}.git")
+ it 'creates the Git repo on disk' do
+ prepare_repository("#{project_path}.git", source_project)
importer.create_project_if_needed
@@ -97,9 +93,6 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
expect(gitlab_shell.exists?(project.repository_storage, repo_path)).to be(true)
expect(gitlab_shell.exists?(project.repository_storage, hook_path)).to be(true)
-
- full_hook_path = File.join(project.repository.path_to_repo, 'hooks')
- expect(File.readlink(full_hook_path)).to eq(Gitlab.config.gitlab_shell.hooks_path)
end
context 'hashed storage enabled' do
@@ -148,7 +141,7 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
end
it 'creates the Git repo in disk' do
- create_bare_repository("#{project_path}.git")
+ prepare_repository("#{project_path}.git", source_project)
importer.create_project_if_needed
@@ -158,23 +151,23 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
expect(gitlab_shell.exists?(project.repository_storage, project.disk_path + '.wiki.git')).to be(true)
end
- it 'moves an existing project to the correct path' do
+ context 'with a repository already on disk' do
+ let!(:base_dir) { TestEnv.repos_path }
# This is a quick way to get a valid repository instead of copying an
# existing one. Since it's not persisted, the importer will try to
# create the project.
- project = build(:project, :legacy_storage, :repository)
- original_commit_count = project.repository.commit_count
-
- legacy_path = Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path
+ let(:project) { build(:project, :legacy_storage, :repository) }
+ let(:project_path) { project.full_path }
- bare_repo = Gitlab::BareRepositoryImport::Repository.new(legacy_path, project.repository.path)
- gitlab_importer = described_class.new(admin, bare_repo)
+ it 'moves an existing project to the correct path' do
+ original_commit_count = project.repository.commit_count
- expect(gitlab_importer).to receive(:create_project).and_call_original
+ expect(importer).to receive(:create_project).and_call_original
- new_project = gitlab_importer.create_project_if_needed
+ new_project = importer.create_project_if_needed
- expect(new_project.repository.commit_count).to eq(original_commit_count)
+ expect(new_project.repository.commit_count).to eq(original_commit_count)
+ end
end
end
@@ -185,8 +178,8 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
it_behaves_like 'importing a repository'
it 'creates the Wiki git repo in disk' do
- create_bare_repository("#{project_path}.git")
- create_bare_repository("#{project_path}.wiki.git")
+ prepare_repository("#{project_path}.git", source_project)
+ prepare_repository("#{project_path}.wiki.git", source_project)
expect(Projects::CreateService).to receive(:new).with(admin, hash_including(skip_wiki: true,
import_type: 'bare_repository')).and_call_original
@@ -213,8 +206,13 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
end
end
- def create_bare_repository(project_path)
+ def prepare_repository(project_path, source_project)
repo_path = File.join(base_dir, project_path)
- Gitlab::Git::Repository.create(repo_path, bare: true)
+
+ return create_bare_repository(repo_path) unless source_project
+
+ cmd = %W(#{Gitlab.config.git.bin_path} clone --bare #{source_project} #{repo_path})
+
+ system(git_env, *cmd, chdir: SEED_STORAGE_PATH, out: '/dev/null', err: '/dev/null')
end
end
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index 05c232d22cf..7a681bc6610 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -69,6 +69,7 @@ describe Gitlab::BitbucketImport::Importer do
let(:project) do
create(
:project,
+ :repository,
import_source: project_identifier,
import_url: "https://bitbucket.org/#{project_identifier}.git",
import_data_attributes: { credentials: data }
@@ -85,10 +86,84 @@ describe Gitlab::BitbucketImport::Importer do
}
end
+ let(:sample) { RepoHelpers.sample_compare }
+
before do
allow(importer).to receive(:gitlab_shell) { gitlab_shell }
end
+ subject { described_class.new(project) }
+
+ describe '#import_pull_requests' do
+ before do
+ allow(subject).to receive(:import_wiki)
+ allow(subject).to receive(:import_issues)
+
+ pull_request = instance_double(
+ Bitbucket::Representation::PullRequest,
+ iid: 10,
+ source_branch_sha: sample.commits.last,
+ source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
+ target_branch_sha: sample.commits.first,
+ target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
+ title: 'This is a title',
+ description: 'This is a test pull request',
+ state: 'merged',
+ author: 'other',
+ created_at: Time.now,
+ updated_at: Time.now)
+
+ # https://gitlab.com/gitlab-org/gitlab-test/compare/c1acaa58bbcbc3eafe538cb8274ba387047b69f8...5937ac0a7beb003549fc5fd26fc247ad
+ @inline_note = instance_double(
+ Bitbucket::Representation::PullRequestComment,
+ iid: 2,
+ file_path: '.gitmodules',
+ old_pos: nil,
+ new_pos: 4,
+ note: 'Hello world',
+ author: 'root',
+ created_at: Time.now,
+ updated_at: Time.now,
+ inline?: true,
+ has_parent?: false)
+
+ @reply = instance_double(
+ Bitbucket::Representation::PullRequestComment,
+ iid: 3,
+ file_path: '.gitmodules',
+ note: 'Hello world',
+ author: 'root',
+ created_at: Time.now,
+ updated_at: Time.now,
+ inline?: true,
+ has_parent?: true,
+ parent_id: 2)
+
+ comments = [@inline_note, @reply]
+
+ allow(subject.client).to receive(:repo)
+ allow(subject.client).to receive(:pull_requests).and_return([pull_request])
+ allow(subject.client).to receive(:pull_request_comments).with(anything, pull_request.iid).and_return(comments)
+ end
+
+ it 'imports threaded discussions' do
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.notes.count).to eq(2)
+ expect(merge_request.notes.map(&:discussion_id).uniq.count).to eq(1)
+
+ notes = merge_request.notes.order(:id).to_a
+ start_note = notes.first
+ expect(start_note).to be_a(DiffNote)
+ expect(start_note.note).to eq(@inline_note.note)
+
+ reply_note = notes.last
+ expect(reply_note).to be_a(DiffNote)
+ expect(reply_note.note).to eq(@reply.note)
+ end
+ end
+
context 'issues statuses' do
before do
# HACK: Bitbucket::Representation.const_get('Issue') seems to return ::Issue without this
diff --git a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
new file mode 100644
index 00000000000..70423823b89
--- /dev/null
+++ b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
@@ -0,0 +1,291 @@
+require 'spec_helper'
+
+describe Gitlab::BitbucketServerImport::Importer do
+ include ImportSpecHelper
+
+ let(:project) { create(:project, :repository, import_url: 'http://my-bitbucket') }
+ let(:now) { Time.now.utc.change(usec: 0) }
+ let(:project_key) { 'TEST' }
+ let(:repo_slug) { 'rouge' }
+ let(:sample) { RepoHelpers.sample_compare }
+
+ subject { described_class.new(project, recover_missing_commits: true) }
+
+ before do
+ data = project.create_or_update_import_data(
+ data: { project_key: project_key, repo_slug: repo_slug },
+ credentials: { base_uri: 'http://my-bitbucket', user: 'bitbucket', password: 'test' }
+ )
+ data.save
+ project.save
+ end
+
+ describe '#import_repository' do
+ before do
+ expect(subject).to receive(:import_pull_requests)
+ expect(subject).to receive(:delete_temp_branches)
+ end
+
+ it 'adds a remote' do
+ expect(project.repository).to receive(:fetch_as_mirror)
+ .with('http://bitbucket:test@my-bitbucket',
+ refmap: [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head'],
+ remote_name: 'bitbucket_server')
+
+ subject.execute
+ end
+ end
+
+ describe '#import_pull_requests' do
+ before do
+ allow(subject).to receive(:import_repository)
+ allow(subject).to receive(:delete_temp_branches)
+ allow(subject).to receive(:restore_branches)
+
+ pull_request = instance_double(
+ BitbucketServer::Representation::PullRequest,
+ iid: 10,
+ source_branch_sha: sample.commits.last,
+ source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
+ target_branch_sha: sample.commits.first,
+ target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
+ title: 'This is a title',
+ description: 'This is a test pull request',
+ state: 'merged',
+ author: 'Test Author',
+ author_email: project.owner.email,
+ created_at: Time.now,
+ updated_at: Time.now,
+ merged?: true)
+
+ allow(subject.client).to receive(:pull_requests).and_return([pull_request])
+
+ @merge_event = instance_double(
+ BitbucketServer::Representation::Activity,
+ comment?: false,
+ merge_event?: true,
+ committer_email: project.owner.email,
+ merge_timestamp: now,
+ merge_commit: '12345678'
+ )
+
+ @pr_note = instance_double(
+ BitbucketServer::Representation::Comment,
+ note: 'Hello world',
+ author_email: 'unknown@gmail.com',
+ author_username: 'The Flash',
+ comments: [],
+ created_at: now,
+ updated_at: now,
+ parent_comment: nil)
+
+ @pr_comment = instance_double(
+ BitbucketServer::Representation::Activity,
+ comment?: true,
+ inline_comment?: false,
+ merge_event?: false,
+ comment: @pr_note)
+ end
+
+ it 'imports merge event' do
+ expect(subject.client).to receive(:activities).and_return([@merge_event])
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.metrics.merged_by).to eq(project.owner)
+ expect(merge_request.metrics.merged_at).to eq(@merge_event.merge_timestamp)
+ expect(merge_request.merge_commit_sha).to eq('12345678')
+ end
+
+ it 'imports comments' do
+ expect(subject.client).to receive(:activities).and_return([@pr_comment])
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.notes.count).to eq(1)
+ note = merge_request.notes.first
+ expect(note.note).to end_with(@pr_note.note)
+ expect(note.author).to eq(project.owner)
+ expect(note.created_at).to eq(@pr_note.created_at)
+ expect(note.updated_at).to eq(@pr_note.created_at)
+ end
+
+ it 'imports threaded discussions' do
+ reply = instance_double(
+ BitbucketServer::Representation::PullRequestComment,
+ author_email: 'someuser@gitlab.com',
+ author_username: 'Batman',
+ note: 'I agree',
+ created_at: now,
+ updated_at: now)
+
+ # https://gitlab.com/gitlab-org/gitlab-test/compare/c1acaa58bbcbc3eafe538cb8274ba387047b69f8...5937ac0a7beb003549fc5fd26fc247ad
+ inline_note = instance_double(
+ BitbucketServer::Representation::PullRequestComment,
+ file_type: 'ADDED',
+ from_sha: sample.commits.first,
+ to_sha: sample.commits.last,
+ file_path: '.gitmodules',
+ old_pos: nil,
+ new_pos: 4,
+ note: 'Hello world',
+ author_email: 'unknown@gmail.com',
+ author_username: 'Superman',
+ comments: [reply],
+ created_at: now,
+ updated_at: now,
+ parent_comment: nil)
+
+ allow(reply).to receive(:parent_comment).and_return(inline_note)
+
+ inline_comment = instance_double(
+ BitbucketServer::Representation::Activity,
+ comment?: true,
+ inline_comment?: true,
+ merge_event?: false,
+ comment: inline_note)
+
+ expect(subject.client).to receive(:activities).and_return([inline_comment])
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.notes.count).to eq(2)
+ expect(merge_request.notes.map(&:discussion_id).uniq.count).to eq(1)
+
+ notes = merge_request.notes.order(:id).to_a
+ start_note = notes.first
+ expect(start_note.type).to eq('DiffNote')
+ expect(start_note.note).to end_with(inline_note.note)
+ expect(start_note.created_at).to eq(inline_note.created_at)
+ expect(start_note.updated_at).to eq(inline_note.updated_at)
+ expect(start_note.position.base_sha).to eq(inline_note.from_sha)
+ expect(start_note.position.start_sha).to eq(inline_note.from_sha)
+ expect(start_note.position.head_sha).to eq(inline_note.to_sha)
+ expect(start_note.position.old_line).to be_nil
+ expect(start_note.position.new_line).to eq(inline_note.new_pos)
+
+ reply_note = notes.last
+ # Make sure author and reply context is included
+ expect(reply_note.note).to start_with("*By #{reply.author_username} (#{reply.author_email})*\n\n")
+ expect(reply_note.note).to end_with("> #{inline_note.note}\n\n#{reply.note}")
+ expect(reply_note.author).to eq(project.owner)
+ expect(reply_note.created_at).to eq(reply.created_at)
+ expect(reply_note.updated_at).to eq(reply.created_at)
+ expect(reply_note.position.base_sha).to eq(inline_note.from_sha)
+ expect(reply_note.position.start_sha).to eq(inline_note.from_sha)
+ expect(reply_note.position.head_sha).to eq(inline_note.to_sha)
+ expect(reply_note.position.old_line).to be_nil
+ expect(reply_note.position.new_line).to eq(inline_note.new_pos)
+ end
+
+ it 'falls back to comments if diff comments fail to validate' do
+ reply = instance_double(
+ BitbucketServer::Representation::Comment,
+ author_email: 'someuser@gitlab.com',
+ author_username: 'Aquaman',
+ note: 'I agree',
+ created_at: now,
+ updated_at: now)
+
+ # https://gitlab.com/gitlab-org/gitlab-test/compare/c1acaa58bbcbc3eafe538cb8274ba387047b69f8...5937ac0a7beb003549fc5fd26fc247ad
+ inline_note = instance_double(
+ BitbucketServer::Representation::PullRequestComment,
+ file_type: 'REMOVED',
+ from_sha: sample.commits.first,
+ to_sha: sample.commits.last,
+ file_path: '.gitmodules',
+ old_pos: 8,
+ new_pos: 9,
+ note: 'This is a note with an invalid line position.',
+ author_email: project.owner.email,
+ author_username: 'Owner',
+ comments: [reply],
+ created_at: now,
+ updated_at: now,
+ parent_comment: nil)
+
+ inline_comment = instance_double(
+ BitbucketServer::Representation::Activity,
+ comment?: true,
+ inline_comment?: true,
+ merge_event?: false,
+ comment: inline_note)
+
+ allow(reply).to receive(:parent_comment).and_return(inline_note)
+
+ expect(subject.client).to receive(:activities).and_return([inline_comment])
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.notes.count).to eq(2)
+ notes = merge_request.notes
+
+ expect(notes.first.note).to start_with('*Comment on .gitmodules')
+ expect(notes.second.note).to start_with('*Comment on .gitmodules')
+ end
+ end
+
+ describe 'inaccessible branches' do
+ let(:id) { 10 }
+ let(:temp_branch_from) { "gitlab/import/pull-request/#{id}/from" }
+ let(:temp_branch_to) { "gitlab/import/pull-request/#{id}/to" }
+
+ before do
+ pull_request = instance_double(
+ BitbucketServer::Representation::PullRequest,
+ iid: id,
+ source_branch_sha: '12345678',
+ source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
+ target_branch_sha: '98765432',
+ target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
+ title: 'This is a title',
+ description: 'This is a test pull request',
+ state: 'merged',
+ author: 'Test Author',
+ author_email: project.owner.email,
+ created_at: Time.now,
+ updated_at: Time.now,
+ merged?: true)
+
+ expect(subject.client).to receive(:pull_requests).and_return([pull_request])
+ expect(subject.client).to receive(:activities).and_return([])
+ expect(subject).to receive(:import_repository).twice
+ end
+
+ it '#restore_branches' do
+ expect(subject).to receive(:restore_branches).and_call_original
+ expect(subject).to receive(:delete_temp_branches)
+ expect(subject.client).to receive(:create_branch)
+ .with(project_key, repo_slug,
+ temp_branch_from,
+ '12345678')
+ expect(subject.client).to receive(:create_branch)
+ .with(project_key, repo_slug,
+ temp_branch_to,
+ '98765432')
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+ end
+
+ it '#delete_temp_branches' do
+ expect(subject.client).to receive(:create_branch).twice
+ expect(subject).to receive(:delete_temp_branches).and_call_original
+ expect(subject.client).to receive(:delete_branch)
+ .with(project_key, repo_slug,
+ temp_branch_from,
+ '12345678')
+ expect(subject.client).to receive(:delete_branch)
+ .with(project_key, repo_slug,
+ temp_branch_to,
+ '98765432')
+ expect(project.repository).to receive(:delete_branch).with(temp_branch_from)
+ expect(project.repository).to receive(:delete_branch).with(temp_branch_to)
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb
index 1cb8143a9e9..4df426c54ae 100644
--- a/spec/lib/gitlab/checks/change_access_spec.rb
+++ b/spec/lib/gitlab/checks/change_access_spec.rb
@@ -54,7 +54,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'as maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'deletion' do
@@ -132,6 +132,16 @@ describe Gitlab::Checks::ChangeAccess do
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to protected branches on this project.')
end
+ context 'when project repository is empty' do
+ let(:project) { create(:project) }
+
+ it 'raises an error if the user is not allowed to push to protected branches' do
+ expect(user_access).to receive(:can_push_to_branch?).and_return(false)
+
+ expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /Ask a project Owner or Maintainer to create a default branch/)
+ end
+ end
+
context 'branch deletion' do
let(:newrev) { '0000000000000000000000000000000000000000' }
let(:ref) { 'refs/heads/feature' }
@@ -144,7 +154,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'if the user is allowed to delete protected branches' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'through the web interface' do
diff --git a/spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb b/spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb
new file mode 100644
index 00000000000..384329dda18
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Artifacts::GzipFileAdapter do
+ describe '#initialize' do
+ context 'when stream is passed' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
+
+ it 'initialized' do
+ expect { described_class.new(stream) }.not_to raise_error
+ end
+ end
+
+ context 'when stream is not passed' do
+ let(:stream) { nil }
+
+ it 'raises an error' do
+ expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError)
+ end
+ end
+ end
+
+ describe '#each_blob' do
+ let(:adapter) { described_class.new(stream) }
+
+ context 'when stream is gzip file' do
+ context 'when gzip file contains one file' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
+
+ it 'iterates content and file_name' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_with_args(fixture_file('junit/junit.xml'), 'rspec.xml')
+ end
+ end
+
+ context 'when gzip file contains three files' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit_with_three_testsuites.xml.gz'), 'rb') }
+
+ it 'iterates content and file_name' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_successive_args(
+ [fixture_file('junit/junit_with_three_testsuites_1.xml'), 'rspec-3.xml'],
+ [fixture_file('junit/junit_with_three_testsuites_2.xml'), 'rspec-1.xml'],
+ [fixture_file('junit/junit_with_three_testsuites_3.xml'), 'rspec-2.xml'])
+ end
+ end
+ end
+
+ context 'when stream is zip file' do
+ let(:stream) { File.open(expand_fixture_path('ci_build_artifacts.zip'), 'rb') }
+
+ it 'raises an error' do
+ expect { |b| adapter.each_blob(&b) }.to raise_error(described_class::InvalidStreamError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
index 6a52ae01b2f..e327399d82d 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -2,13 +2,21 @@ require 'spec_helper'
describe Gitlab::Ci::Build::Artifacts::Metadata do
def metadata(path = '', **opts)
- described_class.new(metadata_file_path, path, **opts)
+ described_class.new(metadata_file_stream, path, **opts)
end
let(:metadata_file_path) do
Rails.root + 'spec/fixtures/ci_build_artifacts_metadata.gz'
end
+ let(:metadata_file_stream) do
+ File.open(metadata_file_path) if metadata_file_path
+ end
+
+ after do
+ metadata_file_stream&.close
+ end
+
context 'metadata file exists' do
describe '#find_entries! empty string' do
subject { metadata('').find_entries! }
@@ -86,11 +94,21 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
end
context 'metadata file does not exist' do
- let(:metadata_file_path) { '' }
+ let(:metadata_file_path) { nil }
+
+ describe '#find_entries!' do
+ it 'raises error' do
+ expect { metadata.find_entries! }.to raise_error(described_class::InvalidStreamError, /Invalid stream/)
+ end
+ end
+ end
+
+ context 'metadata file is invalid' do
+ let(:metadata_file_path) { Rails.root + 'spec/fixtures/ci_build_artifacts.zip' }
describe '#find_entries!' do
it 'raises error' do
- expect { metadata.find_entries! }.to raise_error(Errno::ENOENT)
+ expect { metadata.find_entries! }.to raise_error(described_class::InvalidStreamError, /not in gzip format/)
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
index 5c31423fdee..d48aac15f28 100644
--- a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
@@ -18,6 +18,14 @@ describe Gitlab::Ci::Config::Entry::Artifacts do
expect(entry).to be_valid
end
end
+
+ context "when value includes 'reports' keyword" do
+ let(:config) { { paths: %w[public/], reports: { junit: 'junit.xml' } } }
+
+ it 'returns general artifact and report-type artifacts configuration' do
+ expect(entry.value).to eq config
+ end
+ end
end
context 'when entry value is not correct' do
@@ -39,6 +47,15 @@ describe Gitlab::Ci::Config::Entry::Artifacts do
.to include 'artifacts config contains unknown keys: test'
end
end
+
+ context "when 'reports' keyword is not hash" do
+ let(:config) { { paths: %w[public/], reports: 'junit.xml' } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'artifacts reports should be a hash'
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/commands_spec.rb b/spec/lib/gitlab/ci/config/entry/commands_spec.rb
index afa4a089418..8934aeb83db 100644
--- a/spec/lib/gitlab/ci/config/entry/commands_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/commands_spec.rb
@@ -41,8 +41,7 @@ describe Gitlab::Ci::Config::Entry::Commands do
describe '#errors' do
it 'saves errors' do
expect(entry.errors)
- .to include 'commands config should be a ' \
- 'string or an array of strings'
+ .to include 'commands config should be an array of strings or a string'
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
new file mode 100644
index 00000000000..b3a3a6bee1d
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Entry::Reports do
+ let(:entry) { described_class.new(config) }
+
+ describe 'validation' do
+ context 'when entry config value is correct' do
+ let(:config) { { junit: %w[junit.xml] } }
+
+ describe '#value' do
+ it 'returns artifacs configuration' do
+ expect(entry.value).to eq config
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ context 'when value is not array' do
+ let(:config) { { junit: 'junit.xml' } }
+
+ it 'converts to array' do
+ expect(entry.value).to eq({ junit: ['junit.xml'] } )
+ end
+ end
+ end
+
+ context 'when entry value is not correct' do
+ describe '#errors' do
+ context 'when value of attribute is invalid' do
+ let(:config) { { junit: 10 } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'reports junit should be an array of strings or a string'
+ end
+ end
+
+ context 'when there is an unknown key present' do
+ let(:config) { { codeclimate: 'codeclimate.json' } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'reports config contains unknown keys: codeclimate'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/junit_spec.rb b/spec/lib/gitlab/ci/parsers/junit_spec.rb
new file mode 100644
index 00000000000..f7ec86f5385
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/junit_spec.rb
@@ -0,0 +1,118 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Parsers::Junit do
+ describe '#parse!' do
+ subject { described_class.new.parse!(junit, test_suite) }
+
+ let(:test_suite) { Gitlab::Ci::Reports::TestSuite.new('rspec') }
+ let(:test_cases) { flattened_test_cases(test_suite) }
+
+ context 'when data is JUnit style XML' do
+ context 'when there are no test cases' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite></testsuite>
+ EOF
+ end
+
+ it 'raises an error and does not add any test cases' do
+ expect { subject }.to raise_error(described_class::JunitParserError)
+
+ expect(test_cases.count).to eq(0)
+ end
+ end
+
+ context 'when there is a test case' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ </testsuite>
+ EOF
+ end
+
+ it 'parses XML and adds a test case to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ end
+ end
+
+ context 'when there are two test cases' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ <testcase classname='Calculator' name='sumTest2' time='0.02'></testcase>
+ </testsuite>
+ EOF
+ end
+
+ it 'parses XML and adds test cases to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ expect(test_cases[1].classname).to eq('Calculator')
+ expect(test_cases[1].name).to eq('sumTest2')
+ expect(test_cases[1].execution_time).to eq(0.02)
+ end
+ end
+
+ context 'when there are two test suites' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuites>
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ <testcase classname='Calculator' name='sumTest2' time='0.02'></testcase>
+ </testsuite>
+ <testsuite>
+ <testcase classname='Statemachine' name='happy path' time='100'></testcase>
+ <testcase classname='Statemachine' name='unhappy path' time='200'></testcase>
+ </testsuite>
+ </testsuites>
+ EOF
+ end
+
+ it 'parses XML and adds test cases to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ expect(test_cases[1].classname).to eq('Calculator')
+ expect(test_cases[1].name).to eq('sumTest2')
+ expect(test_cases[1].execution_time).to eq(0.02)
+ expect(test_cases[2].classname).to eq('Statemachine')
+ expect(test_cases[2].name).to eq('happy path')
+ expect(test_cases[2].execution_time).to eq(100)
+ expect(test_cases[3].classname).to eq('Statemachine')
+ expect(test_cases[3].name).to eq('unhappy path')
+ expect(test_cases[3].execution_time).to eq(200)
+ end
+ end
+ end
+
+ context 'when data is not JUnit style XML' do
+ let(:junit) { { testsuite: 'abc' }.to_json }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(described_class::JunitParserError)
+ end
+ end
+
+ private
+
+ def flattened_test_cases(test_suite)
+ test_suite.test_cases.map do |status, value|
+ value.map do |key, test_case|
+ test_case
+ end
+ end.flatten
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers_spec.rb b/spec/lib/gitlab/ci/parsers_spec.rb
new file mode 100644
index 00000000000..2fa83c4abae
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Parsers do
+ describe '.fabricate!' do
+ subject { described_class.fabricate!(file_type) }
+
+ context 'when file_type exists' do
+ let(:file_type) { 'junit' }
+
+ it 'fabricates the class' do
+ is_expected.to be_a(described_class::Junit)
+ end
+ end
+
+ context 'when file_type does not exist' do
+ let(:file_type) { 'undefined' }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(NameError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
index a973ccda8de..8ba56d73838 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
@@ -99,9 +99,9 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it { is_expected.to be_truthy }
diff --git a/spec/lib/gitlab/ci/reports/test_case_spec.rb b/spec/lib/gitlab/ci/reports/test_case_spec.rb
new file mode 100644
index 00000000000..6932f79f0ce
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_case_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestCase do
+ describe '#initialize' do
+ let(:test_case) { described_class.new(**params)}
+
+ context 'when both classname and name are given' do
+ context 'when test case is passed' do
+ let(:params) do
+ {
+ name: 'test-1',
+ classname: 'trace',
+ file: 'spec/trace_spec.rb',
+ execution_time: 1.23,
+ status: described_class::STATUS_SUCCESS,
+ system_output: nil
+ }
+ end
+
+ it 'initializes an instance' do
+ expect { test_case }.not_to raise_error
+
+ expect(test_case.name).to eq('test-1')
+ expect(test_case.classname).to eq('trace')
+ expect(test_case.file).to eq('spec/trace_spec.rb')
+ expect(test_case.execution_time).to eq(1.23)
+ expect(test_case.status).to eq(described_class::STATUS_SUCCESS)
+ expect(test_case.system_output).to be_nil
+ end
+ end
+
+ context 'when test case is failed' do
+ let(:params) do
+ {
+ name: 'test-1',
+ classname: 'trace',
+ file: 'spec/trace_spec.rb',
+ execution_time: 1.23,
+ status: described_class::STATUS_FAILED,
+ system_output: "Failure/Error: is_expected.to eq(300) expected: 300 got: -100"
+ }
+ end
+
+ it 'initializes an instance' do
+ expect { test_case }.not_to raise_error
+
+ expect(test_case.name).to eq('test-1')
+ expect(test_case.classname).to eq('trace')
+ expect(test_case.file).to eq('spec/trace_spec.rb')
+ expect(test_case.execution_time).to eq(1.23)
+ expect(test_case.status).to eq(described_class::STATUS_FAILED)
+ expect(test_case.system_output)
+ .to eq('Failure/Error: is_expected.to eq(300) expected: 300 got: -100')
+ end
+ end
+ end
+
+ context 'when classname is missing' do
+ let(:params) do
+ {
+ name: 'test-1',
+ file: 'spec/trace_spec.rb',
+ execution_time: 1.23,
+ status: described_class::STATUS_SUCCESS,
+ system_output: nil
+ }
+ end
+
+ it 'raises an error' do
+ expect { test_case }.to raise_error(ArgumentError)
+ end
+ end
+
+ context 'when name is missing' do
+ let(:params) do
+ {
+ classname: 'trace',
+ file: 'spec/trace_spec.rb',
+ execution_time: 1.23,
+ status: described_class::STATUS_SUCCESS,
+ system_output: nil
+ }
+ end
+
+ it 'raises an error' do
+ expect { test_case }.to raise_error(ArgumentError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb
new file mode 100644
index 00000000000..71c61e0345f
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb
@@ -0,0 +1,134 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestReportsComparer do
+ include TestReportsHelper
+
+ let(:comparer) { described_class.new(base_reports, head_reports) }
+ let(:base_reports) { Gitlab::Ci::Reports::TestReports.new }
+ let(:head_reports) { Gitlab::Ci::Reports::TestReports.new }
+
+ describe '#suite_comparers' do
+ subject { comparer.suite_comparers }
+
+ context 'when head and base reports include two test suites' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns test suite comparers with specified values' do
+ expect(subject[0]).to be_a(Gitlab::Ci::Reports::TestSuiteComparer)
+ expect(subject[0].name).to eq('rspec')
+ expect(subject[0].head_suite).to eq(head_reports.get_suite('rspec'))
+ expect(subject[0].base_suite).to eq(base_reports.get_suite('rspec'))
+ expect(subject[1]).to be_a(Gitlab::Ci::Reports::TestSuiteComparer)
+ expect(subject[1].name).to eq('junit')
+ expect(subject[1].head_suite).to eq(head_reports.get_suite('junit'))
+ expect(subject[1].base_suite).to eq(base_reports.get_suite('junit'))
+ end
+ end
+ end
+
+ describe '#total_status' do
+ subject { comparer.total_status }
+
+ context 'when all tests cases are success in head suites' do
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns the total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ context 'when there is a failed test case in head suites' do
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns the total status in head suite' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+ end
+
+ describe '#total_count' do
+ subject { comparer.total_count }
+
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns the total test counts in head suites' do
+ is_expected.to eq(2)
+ end
+ end
+
+ describe '#resolved_count' do
+ subject { comparer.resolved_count }
+
+ context 'when there is a resolved test case in head suites' do
+ let(:create_test_case_java_resolved) do
+ create_test_case_java_failed.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_resolved)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(1)
+ end
+ end
+
+ context 'when there are no resolved test cases in head suites' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(0)
+ end
+ end
+ end
+
+ describe '#failed_count' do
+ subject { comparer.failed_count }
+
+ context 'when there is a failed test case in head suites' do
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(1)
+ end
+ end
+
+ context 'when there are no failed test cases in head suites' do
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_rspec_success)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(0)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/test_reports_spec.rb b/spec/lib/gitlab/ci/reports/test_reports_spec.rb
new file mode 100644
index 00000000000..74ff134b239
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_reports_spec.rb
@@ -0,0 +1,132 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestReports do
+ include TestReportsHelper
+
+ let(:test_reports) { described_class.new }
+
+ describe '#get_suite' do
+ subject { test_reports.get_suite(suite_name) }
+
+ context 'when suite name is rspec' do
+ let(:suite_name) { 'rspec' }
+
+ it { expect(subject.name).to eq('rspec') }
+
+ it 'initializes a new test suite and returns it' do
+ expect(Gitlab::Ci::Reports::TestSuite).to receive(:new).and_call_original
+
+ is_expected.to be_a(Gitlab::Ci::Reports::TestSuite)
+ end
+
+ context 'when suite name is already allocated' do
+ before do
+ subject
+ end
+
+ it 'does not initialize a new test suite' do
+ expect(Gitlab::Ci::Reports::TestSuite).not_to receive(:new)
+
+ is_expected.to be_a(Gitlab::Ci::Reports::TestSuite)
+ end
+ end
+ end
+ end
+
+ describe '#total_time' do
+ subject { test_reports.total_time }
+
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns the total time' do
+ is_expected.to eq(6.66)
+ end
+ end
+
+ describe '#total_count' do
+ subject { test_reports.total_count }
+
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns the total count' do
+ is_expected.to eq(2)
+ end
+ end
+
+ describe '#total_status' do
+ subject { test_reports.total_status }
+
+ context 'when all test cases succeeded' do
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns correct total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ context 'when there is a failed test case' do
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns correct total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+
+ context 'when there is a skipped test case' do
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_skipped)
+ end
+
+ it 'returns correct total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ context 'when there is an error test case' do
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_error)
+ end
+
+ it 'returns correct total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+ end
+
+ Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
+ describe "##{status_type}_count" do
+ subject { test_reports.public_send("#{status_type}_count") }
+
+ context "when #{status_type} test case exists" do
+ before do
+ test_reports.get_suite('rspec').add_test_case(public_send("create_test_case_rspec_#{status_type}"))
+ test_reports.get_suite('junit').add_test_case(public_send("create_test_case_java_#{status_type}"))
+ end
+
+ it 'returns the count' do
+ is_expected.to eq(2)
+ end
+ end
+
+ context "when #{status_type} test case do not exist" do
+ it 'returns nothing' do
+ is_expected.to be(0)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb
new file mode 100644
index 00000000000..6ab16e5518d
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb
@@ -0,0 +1,225 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestSuiteComparer do
+ include TestReportsHelper
+
+ let(:comparer) { described_class.new(name, base_suite, head_suite) }
+ let(:name) { 'rpsec' }
+ let(:base_suite) { Gitlab::Ci::Reports::TestSuite.new(name) }
+ let(:head_suite) { Gitlab::Ci::Reports::TestSuite.new(name) }
+ let(:test_case_success) { create_test_case_rspec_success }
+ let(:test_case_failed) { create_test_case_rspec_failed }
+
+ let(:test_case_resolved) do
+ create_test_case_rspec_failed.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ describe '#new_failures' do
+ subject { comparer.new_failures }
+
+ context 'when head sutie has a newly failed test case which does not exist in base' do
+ before do
+ base_suite.add_test_case(test_case_success)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the failed test case' do
+ is_expected.to eq([test_case_failed])
+ end
+ end
+
+ context 'when head sutie still has a failed test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when head sutie has a success test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_resolved)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '#existing_failures' do
+ subject { comparer.existing_failures }
+
+ context 'when head sutie has a newly failed test case which does not exist in base' do
+ before do
+ base_suite.add_test_case(test_case_success)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the failed test case' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when head sutie still has a failed test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to eq([test_case_failed])
+ end
+ end
+
+ context 'when head sutie has a success test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_resolved)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '#resolved_failures' do
+ subject { comparer.resolved_failures }
+
+ context 'when head sutie has a newly failed test case which does not exist in base' do
+ before do
+ base_suite.add_test_case(test_case_success)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the failed test case' do
+ is_expected.to be_empty
+ end
+
+ it 'returns the correct resolved count' do
+ expect(comparer.resolved_count).to eq(0)
+ end
+ end
+
+ context 'when head sutie still has a failed test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to be_empty
+ end
+
+ it 'returns the correct resolved count' do
+ expect(comparer.resolved_count).to eq(0)
+ end
+ end
+
+ context 'when head sutie has a success test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_resolved)
+ end
+
+ it 'does not return the resolved test case' do
+ is_expected.to eq([test_case_resolved])
+ end
+
+ it 'returns the correct resolved count' do
+ expect(comparer.resolved_count).to eq(1)
+ end
+ end
+ end
+
+ describe '#total_count' do
+ subject { comparer.total_count }
+
+ before do
+ head_suite.add_test_case(test_case_success)
+ end
+
+ it 'returns the total test counts in head suite' do
+ is_expected.to eq(1)
+ end
+ end
+
+ describe '#failed_count' do
+ subject { comparer.failed_count }
+
+ context 'when there are a new failure and an existing failure' do
+ let(:test_case_1_success) { create_test_case_rspec_success }
+ let(:test_case_2_failed) { create_test_case_rspec_failed }
+
+ let(:test_case_1_failed) do
+ create_test_case_rspec_success.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+
+ before do
+ base_suite.add_test_case(test_case_1_success)
+ base_suite.add_test_case(test_case_2_failed)
+ head_suite.add_test_case(test_case_1_failed)
+ head_suite.add_test_case(test_case_2_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(2)
+ end
+ end
+
+ context 'when there is a new failure' do
+ before do
+ base_suite.add_test_case(test_case_success)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(1)
+ end
+ end
+
+ context 'when there is an existing failure' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(1)
+ end
+ end
+ end
+
+ describe '#total_status' do
+ subject { comparer.total_status }
+
+ context 'when all test cases in head suite are success' do
+ before do
+ head_suite.add_test_case(test_case_success)
+ end
+
+ it 'returns the total status in head suite' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ context 'when there is a failed test case in head suite' do
+ before do
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the total status in head suite' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/test_suite_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_spec.rb
new file mode 100644
index 00000000000..cd34dbaf62f
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_suite_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestSuite do
+ include TestReportsHelper
+
+ let(:test_suite) { described_class.new('Rspec') }
+ let(:test_case_success) { create_test_case_rspec_success }
+ let(:test_case_failed) { create_test_case_rspec_failed }
+ let(:test_case_skipped) { create_test_case_rspec_skipped }
+ let(:test_case_error) { create_test_case_rspec_error }
+
+ it { expect(test_suite.name).to eq('Rspec') }
+
+ describe '#add_test_case' do
+ context 'when status of the test case is success' do
+ it 'stores data correctly' do
+ test_suite.add_test_case(test_case_success)
+
+ expect(test_suite.test_cases[test_case_success.status][test_case_success.key])
+ .to eq(test_case_success)
+ expect(test_suite.total_time).to eq(1.11)
+ end
+ end
+
+ context 'when status of the test case is failed' do
+ it 'stores data correctly' do
+ test_suite.add_test_case(test_case_failed)
+
+ expect(test_suite.test_cases[test_case_failed.status][test_case_failed.key])
+ .to eq(test_case_failed)
+ expect(test_suite.total_time).to eq(2.22)
+ end
+ end
+
+ context 'when two test cases are added' do
+ it 'sums up total time' do
+ test_suite.add_test_case(test_case_success)
+ test_suite.add_test_case(test_case_failed)
+
+ expect(test_suite.total_time).to eq(3.33)
+ end
+ end
+ end
+
+ describe '#total_count' do
+ subject { test_suite.total_count }
+
+ before do
+ test_suite.add_test_case(test_case_success)
+ test_suite.add_test_case(test_case_failed)
+ end
+
+ it { is_expected.to eq(2) }
+ end
+
+ describe '#total_status' do
+ subject { test_suite.total_status }
+
+ context 'when all test cases succeeded' do
+ before do
+ test_suite.add_test_case(test_case_success)
+ end
+
+ it { is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS) }
+ end
+
+ context 'when a test case failed' do
+ before do
+ test_suite.add_test_case(test_case_success)
+ test_suite.add_test_case(test_case_failed)
+ end
+
+ it { is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED) }
+ end
+ end
+
+ Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
+ describe "##{status_type}" do
+ subject { test_suite.public_send("#{status_type}") }
+
+ context "when #{status_type} test case exists" do
+ before do
+ test_suite.add_test_case(public_send("test_case_#{status_type}"))
+ end
+
+ it 'returns all success test cases' do
+ is_expected.to eq( { public_send("test_case_#{status_type}").key => public_send("test_case_#{status_type}") })
+ end
+ end
+
+ context "when #{status_type} test case do not exist" do
+ it 'returns nothing' do
+ is_expected.to be_empty
+ end
+ end
+ end
+ end
+
+ Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
+ describe "##{status_type}_count" do
+ subject { test_suite.public_send("#{status_type}_count") }
+
+ context "when #{status_type} test case exists" do
+ before do
+ test_suite.add_test_case(public_send("test_case_#{status_type}"))
+ end
+
+ it 'returns the count' do
+ is_expected.to eq(1)
+ end
+ end
+
+ context "when #{status_type} test case do not exist" do
+ it 'returns nothing' do
+ is_expected.to be(0)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/failed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_spec.rb
index cadb424ea2c..b6676b40fd3 100644
--- a/spec/lib/gitlab/ci/status/build/failed_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/failed_spec.rb
@@ -80,4 +80,31 @@ describe Gitlab::Ci::Status::Build::Failed do
end
end
end
+
+ describe 'covers all failure reasons' do
+ let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
+ let(:tooltip) { subject.status_tooltip }
+
+ CommitStatus.failure_reasons.keys.each do |failure_reason|
+ context failure_reason do
+ before do
+ build.failure_reason = failure_reason
+ end
+
+ it "is a valid status" do
+ expect { tooltip }.not_to raise_error
+ end
+ end
+ end
+
+ context 'invalid failure message' do
+ before do
+ expect(build).to receive(:failure_reason) { 'invalid failure message' }
+ end
+
+ it "is an invalid status" do
+ expect { tooltip }.to raise_error(/key not found:/)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb
index e2bb378f663..02f8c4c114b 100644
--- a/spec/lib/gitlab/ci/status/build/play_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/play_spec.rb
@@ -46,7 +46,7 @@ describe Gitlab::Ci::Status::Build::Play do
context 'when user can not push to the branch' do
before do
build.project.add_developer(user)
- create(:protected_branch, :masters_can_push,
+ create(:protected_branch, :maintainers_can_push,
name: build.ref, project: project)
end
diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb
index 6ec35f8da7e..bb2d0a2c75c 100644
--- a/spec/lib/gitlab/ci/status/stage/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::Ci::Status::Stage::Common do
context 'when user has permission to read pipeline' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'has details' do
diff --git a/spec/lib/gitlab/ci/trace/http_io_spec.rb b/spec/lib/gitlab/ci/trace/http_io_spec.rb
deleted file mode 100644
index 5474e2f518c..00000000000
--- a/spec/lib/gitlab/ci/trace/http_io_spec.rb
+++ /dev/null
@@ -1,315 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Ci::Trace::HttpIO do
- include HttpIOHelpers
-
- let(:http_io) { described_class.new(url, size) }
- let(:url) { remote_trace_url }
- let(:size) { remote_trace_size }
-
- describe '#close' do
- subject { http_io.close }
-
- it { is_expected.to be_nil }
- end
-
- describe '#binmode' do
- subject { http_io.binmode }
-
- it { is_expected.to be_nil }
- end
-
- describe '#binmode?' do
- subject { http_io.binmode? }
-
- it { is_expected.to be_truthy }
- end
-
- describe '#path' do
- subject { http_io.path }
-
- it { is_expected.to be_nil }
- end
-
- describe '#url' do
- subject { http_io.url }
-
- it { is_expected.to eq(url) }
- end
-
- describe '#seek' do
- subject { http_io.seek(pos, where) }
-
- context 'when moves pos to end of the file' do
- let(:pos) { 0 }
- let(:where) { IO::SEEK_END }
-
- it { is_expected.to eq(size) }
- end
-
- context 'when moves pos to middle of the file' do
- let(:pos) { size / 2 }
- let(:where) { IO::SEEK_SET }
-
- it { is_expected.to eq(size / 2) }
- end
-
- context 'when moves pos around' do
- it 'matches the result' do
- expect(http_io.seek(0)).to eq(0)
- expect(http_io.seek(100, IO::SEEK_CUR)).to eq(100)
- expect { http_io.seek(size + 1, IO::SEEK_CUR) }.to raise_error('new position is outside of file')
- end
- end
- end
-
- describe '#eof?' do
- subject { http_io.eof? }
-
- context 'when current pos is at end of the file' do
- before do
- http_io.seek(size, IO::SEEK_SET)
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'when current pos is not at end of the file' do
- before do
- http_io.seek(0, IO::SEEK_SET)
- end
-
- it { is_expected.to be_falsey }
- end
- end
-
- describe '#each_line' do
- subject { http_io.each_line }
-
- let(:string_io) { StringIO.new(remote_trace_body) }
-
- before do
- stub_remote_trace_206
- end
-
- it 'yields lines' do
- expect { |b| http_io.each_line(&b) }.to yield_successive_args(*string_io.each_line.to_a)
- end
-
- context 'when buckets on GCS' do
- context 'when BUFFER_SIZE is larger than file size' do
- before do
- stub_remote_trace_200
- set_larger_buffer_size_than(size)
- end
-
- it 'calls get_chunk only once' do
- expect_any_instance_of(Net::HTTP).to receive(:request).once.and_call_original
-
- http_io.each_line { |line| }
- end
- end
- end
- end
-
- describe '#read' do
- subject { http_io.read(length) }
-
- context 'when there are no network issue' do
- before do
- stub_remote_trace_206
- end
-
- context 'when read whole size' do
- let(:length) { nil }
-
- context 'when BUFFER_SIZE is smaller than file size' do
- before do
- set_smaller_buffer_size_than(size)
- end
-
- it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
- end
- end
-
- context 'when BUFFER_SIZE is larger than file size' do
- before do
- set_larger_buffer_size_than(size)
- end
-
- it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
- end
- end
- end
-
- context 'when read only first 100 bytes' do
- let(:length) { 100 }
-
- context 'when BUFFER_SIZE is smaller than file size' do
- before do
- set_smaller_buffer_size_than(size)
- end
-
- it 'reads a trace' do
- is_expected.to eq(remote_trace_body[0, length])
- end
- end
-
- context 'when BUFFER_SIZE is larger than file size' do
- before do
- set_larger_buffer_size_than(size)
- end
-
- it 'reads a trace' do
- is_expected.to eq(remote_trace_body[0, length])
- end
- end
- end
-
- context 'when tries to read oversize' do
- let(:length) { size + 1000 }
-
- context 'when BUFFER_SIZE is smaller than file size' do
- before do
- set_smaller_buffer_size_than(size)
- end
-
- it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
- end
- end
-
- context 'when BUFFER_SIZE is larger than file size' do
- before do
- set_larger_buffer_size_than(size)
- end
-
- it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
- end
- end
- end
-
- context 'when tries to read 0 bytes' do
- let(:length) { 0 }
-
- context 'when BUFFER_SIZE is smaller than file size' do
- before do
- set_smaller_buffer_size_than(size)
- end
-
- it 'reads a trace' do
- is_expected.to be_empty
- end
- end
-
- context 'when BUFFER_SIZE is larger than file size' do
- before do
- set_larger_buffer_size_than(size)
- end
-
- it 'reads a trace' do
- is_expected.to be_empty
- end
- end
- end
- end
-
- context 'when there is anetwork issue' do
- let(:length) { nil }
-
- before do
- stub_remote_trace_500
- end
-
- it 'reads a trace' do
- expect { subject }.to raise_error(Gitlab::Ci::Trace::HttpIO::FailedToGetChunkError)
- end
- end
- end
-
- describe '#readline' do
- subject { http_io.readline }
-
- let(:string_io) { StringIO.new(remote_trace_body) }
-
- before do
- stub_remote_trace_206
- end
-
- shared_examples 'all line matching' do
- it 'reads a line' do
- (0...remote_trace_body.lines.count).each do
- expect(http_io.readline).to eq(string_io.readline)
- end
- end
- end
-
- context 'when there is anetwork issue' do
- let(:length) { nil }
-
- before do
- stub_remote_trace_500
- end
-
- it 'reads a trace' do
- expect { subject }.to raise_error(Gitlab::Ci::Trace::HttpIO::FailedToGetChunkError)
- end
- end
-
- context 'when BUFFER_SIZE is smaller than file size' do
- before do
- set_smaller_buffer_size_than(size)
- end
-
- it_behaves_like 'all line matching'
- end
-
- context 'when BUFFER_SIZE is larger than file size' do
- before do
- set_larger_buffer_size_than(size)
- end
-
- it_behaves_like 'all line matching'
- end
-
- context 'when pos is at middle of the file' do
- before do
- set_smaller_buffer_size_than(size)
-
- http_io.seek(size / 2)
- string_io.seek(size / 2)
- end
-
- it 'reads from pos' do
- expect(http_io.readline).to eq(string_io.readline)
- end
- end
- end
-
- describe '#write' do
- subject { http_io.write(nil) }
-
- it { expect { subject }.to raise_error(NotImplementedError) }
- end
-
- describe '#truncate' do
- subject { http_io.truncate(nil) }
-
- it { expect { subject }.to raise_error(NotImplementedError) }
- end
-
- describe '#flush' do
- subject { http_io.flush }
-
- it { expect { subject }.to raise_error(NotImplementedError) }
- end
-
- describe '#present?' do
- subject { http_io.present? }
-
- it { is_expected.to be_truthy }
- end
-end
diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
index adb3ff4321f..46874662edd 100644
--- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
@@ -75,6 +75,14 @@ describe Gitlab::Ci::Variables::Collection::Item do
expect(resource).to eq variable
end
+ it 'supports using a hash with stringified values' do
+ variable = { 'key' => 'VARIABLE', 'value' => 'my value' }
+
+ resource = described_class.fabricate(variable)
+
+ expect(resource).to eq(key: 'VARIABLE', value: 'my value')
+ end
+
it 'supports using an active record resource' do
variable = create(:ci_variable, key: 'CI_VAR', value: '123')
resource = described_class.fabricate(variable)
diff --git a/spec/lib/gitlab/cleanup/project_uploads_spec.rb b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
new file mode 100644
index 00000000000..37b38776775
--- /dev/null
+++ b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
@@ -0,0 +1,278 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Cleanup::ProjectUploads do
+ subject { described_class.new(logger: logger) }
+ let(:logger) { double(:logger) }
+
+ before do
+ allow(logger).to receive(:info).at_least(1).times
+ allow(logger).to receive(:debug).at_least(1).times
+ end
+
+ describe '#run!' do
+ shared_examples_for 'moves the file' do
+ shared_examples_for 'a real run' do
+ let(:args) { [dry_run: false] }
+
+ it 'moves the file to its proper location' do
+ subject.run!(*args)
+
+ expect(File.exist?(path)).to be_falsey
+ expect(File.exist?(new_path)).to be_truthy
+ end
+
+ it 'logs action as done' 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)
+ end
+ end
+
+ shared_examples_for 'a dry run' do
+ it 'does not move the file' do
+ subject.run!(*args)
+
+ expect(File.exist?(path)).to be_truthy
+ expect(File.exist?(new_path)).to be_falsey
+ end
+
+ it 'logs action as able to be done' 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)
+ end
+ end
+
+ context 'when dry_run is false' do
+ let(:args) { [dry_run: false] }
+
+ it_behaves_like 'a real run'
+ end
+
+ context 'when dry_run is nil' do
+ let(:args) { [dry_run: nil] }
+
+ it_behaves_like 'a real run'
+ end
+
+ context 'when dry_run is true' do
+ let(:args) { [dry_run: true] }
+
+ it_behaves_like 'a dry run'
+ end
+
+ context 'with dry_run not specified' do
+ let(:args) { [] }
+
+ it_behaves_like 'a dry run'
+ end
+ end
+
+ shared_examples_for 'moves the file to lost and found' do
+ let(:action) { "move to lost and found #{path} -> #{new_path}" }
+
+ it_behaves_like 'moves the file'
+ end
+
+ shared_examples_for 'fixes the file' do
+ let(:action) { "fix #{path} -> #{new_path}" }
+
+ it_behaves_like 'moves the file'
+ end
+
+ context 'orphaned project upload file' do
+ context 'when an upload record matching the secret and filename is found' do
+ context 'when the project is still in legacy storage' do
+ let(:orphaned) { create(:upload, :issuable_upload, :with_file, model: create(:project, :legacy_storage)) }
+ let(:new_path) { orphaned.absolute_path }
+ let(:path) { File.join(FileUploader.root, 'some', 'wrong', 'location', orphaned.path) }
+
+ before do
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.mv(new_path, path)
+ end
+
+ it_behaves_like 'fixes the file'
+ end
+
+ context 'when the project was moved to hashed storage' do
+ let(:orphaned) { create(:upload, :issuable_upload, :with_file) }
+ let(:new_path) { orphaned.absolute_path }
+ let(:path) { File.join(FileUploader.root, 'some', 'wrong', 'location', orphaned.path) }
+
+ before do
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.mv(new_path, path)
+ end
+
+ it_behaves_like 'fixes the file'
+ end
+
+ context 'when the project is missing (the upload *record* is an orphan)' do
+ let(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) }
+ let!(:path) { orphaned.absolute_path }
+ let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', orphaned.model.full_path, orphaned.path) }
+
+ before do
+ orphaned.model.delete
+ end
+
+ it_behaves_like 'moves the file to lost and found'
+ end
+
+ # We will probably want to add logic (Reschedule background upload) to
+ # cover Case 2 in https://gitlab.com/gitlab-org/gitlab-ce/issues/46535#note_75355104
+ context 'when the file should be in object storage' do
+ context 'when the file otherwise has the correct local path' do
+ let!(:orphaned) { create(:upload, :issuable_upload, :object_storage, model: build(:project, :legacy_storage)) }
+ let!(:path) { File.join(FileUploader.root, orphaned.model.full_path, orphaned.path) }
+
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.touch(path)
+ end
+
+ it 'does not move the file' do
+ expect(File.exist?(path)).to be_truthy
+
+ subject.run!(dry_run: false)
+
+ expect(File.exist?(path)).to be_truthy
+ end
+ end
+
+ # E.g. the upload file was orphaned, and then uploads were migrated to
+ # object storage
+ context 'when the file has the wrong local path' do
+ let!(:orphaned) { create(:upload, :issuable_upload, :object_storage, model: build(:project, :legacy_storage)) }
+ let!(:path) { File.join(FileUploader.root, 'wrong', orphaned.path) }
+ let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', 'wrong', orphaned.path) }
+
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.touch(path)
+ end
+
+ it_behaves_like 'moves the file to lost and found'
+ end
+ end
+ end
+
+ context 'when a matching upload record can not be found' do
+ context 'when the file path fits the known pattern' do
+ let!(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) }
+ let!(:path) { orphaned.absolute_path }
+ let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', orphaned.model.full_path, orphaned.path) }
+
+ before do
+ orphaned.delete
+ end
+
+ it_behaves_like 'moves the file to lost and found'
+ end
+
+ context 'when the file path does not fit the known pattern' do
+ let!(:invalid_path) { File.join('group', 'file.jpg') }
+ let!(:path) { File.join(FileUploader.root, invalid_path) }
+ let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', invalid_path) }
+
+ before do
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.touch(path)
+ end
+
+ after do
+ File.delete(path) if File.exist?(path)
+ end
+
+ it_behaves_like 'moves the file to lost and found'
+ end
+ end
+ end
+
+ context 'non-orphaned project upload file' do
+ it 'does not move the file' do
+ tracked = create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage))
+ tracked_path = tracked.absolute_path
+
+ expect(logger).not_to receive(:info).with(/move|fix/i)
+ expect(File.exist?(tracked_path)).to be_truthy
+
+ subject.run!(dry_run: false)
+
+ expect(File.exist?(tracked_path)).to be_truthy
+ end
+ end
+
+ context 'ignorable cases' do
+ # Because we aren't concerned about these, and can save a lot of
+ # processing time by ignoring them. If we wish to cleanup hashed storage
+ # directories, it should simply require removing this test and modifying
+ # the find command.
+ context 'when the file is already in hashed storage' do
+ let(:project) { create(:project) }
+
+ before do
+ expect(logger).not_to receive(:info).with(/move|fix/i)
+ end
+
+ it 'does not move even an orphan file' do
+ orphaned = create(:upload, :issuable_upload, :with_file, model: project)
+ path = orphaned.absolute_path
+ orphaned.delete
+
+ expect(File.exist?(path)).to be_truthy
+
+ subject.run!(dry_run: false)
+
+ expect(File.exist?(path)).to be_truthy
+ end
+ end
+
+ it 'does not move any non-project (FileUploader) uploads' do
+ paths = []
+ orphaned1 = create(:upload, :personal_snippet_upload, :with_file)
+ orphaned2 = create(:upload, :namespace_upload, :with_file)
+ orphaned3 = create(:upload, :attachment_upload, :with_file)
+ paths << orphaned1.absolute_path
+ paths << orphaned2.absolute_path
+ paths << orphaned3.absolute_path
+ Upload.delete_all
+
+ expect(logger).not_to receive(:info).with(/move|fix/i)
+ paths.each do |path|
+ expect(File.exist?(path)).to be_truthy
+ end
+
+ subject.run!(dry_run: false)
+
+ paths.each do |path|
+ expect(File.exist?(path)).to be_truthy
+ end
+ end
+
+ it 'does not move any uploads in tmp (which would interfere with ongoing upload activity)' do
+ path = File.join(FileUploader.root, 'tmp', 'foo.jpg')
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.touch(path)
+
+ expect(logger).not_to receive(:info).with(/move|fix/i)
+ expect(File.exist?(path)).to be_truthy
+
+ subject.run!(dry_run: false)
+
+ expect(File.exist?(path)).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cleanup/remote_uploads_spec.rb b/spec/lib/gitlab/cleanup/remote_uploads_spec.rb
new file mode 100644
index 00000000000..8d03baeb07b
--- /dev/null
+++ b/spec/lib/gitlab/cleanup/remote_uploads_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Cleanup::RemoteUploads do
+ context 'when object_storage is enabled' do
+ let(:connection) { double }
+ let(:directory) { double }
+ let!(:uploads) do
+ [
+ create(:upload, path: 'dir/file1', store: ObjectStorage::Store::REMOTE),
+ create(:upload, path: 'dir/file2', store: ObjectStorage::Store::LOCAL)
+ ]
+ end
+ let(:remote_files) do
+ [
+ double(key: 'dir/file1'),
+ double(key: 'dir/file2'),
+ double(key: 'dir/file3'),
+ double(key: 'lost_and_found/dir/file3')
+ ]
+ end
+
+ before do
+ stub_uploads_object_storage(FileUploader)
+
+ expect(::Fog::Storage).to receive(:new).and_return(connection)
+
+ expect(connection).to receive(:directories).and_return(double(get: directory))
+ expect(directory).to receive(:files).and_return(remote_files)
+ end
+
+ context 'when dry_run is set to false' do
+ subject { described_class.new.run!(dry_run: false) }
+
+ it 'moves files that are not in uploads table' do
+ expect(remote_files[0]).not_to receive(:copy)
+ expect(remote_files[0]).not_to receive(:destroy)
+ expect(remote_files[1]).to receive(:copy)
+ expect(remote_files[1]).to receive(:destroy)
+ expect(remote_files[2]).to receive(:copy)
+ expect(remote_files[2]).to receive(:destroy)
+ expect(remote_files[3]).not_to receive(:copy)
+ expect(remote_files[3]).not_to receive(:destroy)
+
+ subject
+ end
+ end
+
+ context 'when dry_run is set to true' do
+ subject { described_class.new.run!(dry_run: true) }
+
+ it 'does not move filese' do
+ expect(remote_files[0]).not_to receive(:copy)
+ expect(remote_files[0]).not_to receive(:destroy)
+ expect(remote_files[1]).not_to receive(:copy)
+ expect(remote_files[1]).not_to receive(:destroy)
+ expect(remote_files[2]).not_to receive(:copy)
+ expect(remote_files[2]).not_to receive(:destroy)
+ expect(remote_files[3]).not_to receive(:copy)
+ expect(remote_files[3]).not_to receive(:destroy)
+
+ subject
+ end
+ end
+ end
+
+ context 'when object_storage is not enabled' do
+ it 'does not connect to any storage' do
+ expect(::Fog::Storage).not_to receive(:new)
+
+ subject
+ end
+ end
+end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 8d4862932b2..1f35d1e4880 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::ClosingIssueExtractor do
before do
project.add_developer(project.creator)
project.add_developer(project2.creator)
- project2.add_master(project.creator)
+ project2.add_maintainer(project.creator)
end
describe "#closed_by_message" do
@@ -298,7 +298,7 @@ describe Gitlab::ClosingIssueExtractor do
context 'with an external issue tracker reference' do
it 'extracts the referenced issue' do
jira_project = create(:jira_project, name: 'JIRA_EXT1')
- jira_project.add_master(jira_project.creator)
+ jira_project.add_maintainer(jira_project.creator)
jira_issue = ExternalIssue.new("#{jira_project.name}-1", project: jira_project)
closing_issue_extractor = described_class.new(jira_project, jira_project.creator)
message = "Resolve #{jira_issue.to_reference}"
@@ -379,6 +379,20 @@ describe Gitlab::ClosingIssueExtractor do
.to match_array([issue, other_issue, third_issue])
end
+ it 'allows non-comma-separated issue numbers in single line message' do
+ message = "Closes #{reference} #{reference2} #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
+ it 'allows mixed comma-separated and non-comma-separated issue numbers in single line message' do
+ message = "Closes #{reference}, #{reference2} and #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
it 'fetches issues in multi-line message' do
message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
diff --git a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
index 6de4bd3dc7c..f670c7f6c75 100644
--- a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
@@ -36,9 +36,9 @@ describe Gitlab::CycleAnalytics::Permissions do
end
end
- context 'user is master' do
+ context 'user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'has permissions to issue stage' do
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index b411aaa19da..0a8c77b0ad9 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -281,7 +281,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
it "doesn't break when the namespace was renamed" do
subject.rename_namespace(namespace)
- namespace.update_attributes!(path: 'renamed-afterwards')
+ namespace.update!(path: 'renamed-afterwards')
expect { subject.revert_renames }.not_to raise_error
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
index b4896d69077..d4d7a83921c 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
@@ -169,7 +169,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :de
it "doesn't break when the project was renamed" do
subject.rename_project(project)
- project.update_attributes!(path: 'renamed-afterwards')
+ project.update!(path: 'renamed-afterwards')
expect { subject.revert_renames }.not_to raise_error
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 8bb246aa4bd..7d76519dddd 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -65,6 +65,28 @@ describe Gitlab::Database do
end
end
+ describe '.postgresql_9_or_less?' do
+ it 'returns false when using MySQL' do
+ allow(described_class).to receive(:postgresql?).and_return(false)
+
+ expect(described_class.postgresql_9_or_less?).to eq(false)
+ end
+
+ it 'returns true when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.postgresql_9_or_less?).to eq(true)
+ end
+
+ it 'returns false when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.postgresql_9_or_less?).to eq(false)
+ end
+ end
+
describe '.join_lateral_supported?' do
it 'returns false when using MySQL' do
allow(described_class).to receive(:postgresql?).and_return(false)
@@ -109,6 +131,70 @@ describe Gitlab::Database do
end
end
+ describe '.pg_wal_lsn_diff' do
+ it 'returns old name when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.pg_wal_lsn_diff).to eq('pg_xlog_location_diff')
+ end
+
+ it 'returns new name when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.pg_wal_lsn_diff).to eq('pg_wal_lsn_diff')
+ end
+ end
+
+ describe '.pg_current_wal_insert_lsn' do
+ it 'returns old name when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_xlog_insert_location')
+ end
+
+ it 'returns new name when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_wal_insert_lsn')
+ end
+ end
+
+ describe '.pg_last_wal_receive_lsn' do
+ it 'returns old name when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_xlog_receive_location')
+ end
+
+ it 'returns new name when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_wal_receive_lsn')
+ end
+ end
+
+ describe '.pg_last_wal_replay_lsn' do
+ it 'returns old name when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_xlog_replay_location')
+ end
+
+ it 'returns new name when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_wal_replay_lsn')
+ end
+ end
+
describe '.nulls_last_order' do
context 'when using PostgreSQL' do
before do
@@ -357,6 +443,35 @@ describe Gitlab::Database do
end
end
+ describe '.db_read_only?' do
+ context 'when using PostgreSQL' do
+ before do
+ allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original
+ expect(described_class).to receive(:postgresql?).and_return(true)
+ end
+
+ it 'detects a read only database' do
+ allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "t" }])
+
+ expect(described_class.db_read_only?).to be_truthy
+ end
+
+ it 'detects a read write database' do
+ allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "f" }])
+
+ expect(described_class.db_read_only?).to be_falsey
+ end
+ end
+
+ context 'when using MySQL' do
+ before do
+ expect(described_class).to receive(:postgresql?).and_return(false)
+ end
+
+ it { expect(described_class.db_read_only?).to be_falsey }
+ end
+ end
+
describe '#sanitize_timestamp' do
let(:max_timestamp) { Time.at((1 << 31) - 1) }
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 f48ee8924e8..79287021981 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
@@ -20,6 +20,15 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
diff_files
end
+ it 'it uses a different cache key if diff line keys change' do
+ mr_diff = described_class.new(merge_request.merge_request_diff, diff_options: nil)
+ key = mr_diff.cache_key
+
+ stub_const('Gitlab::Diff::Line::SERIALIZE_KEYS', [:foo])
+
+ expect(mr_diff.cache_key).not_to eq(key)
+ end
+
shared_examples 'initializes a DiffCollection' do
it 'returns a valid instance of a DiffCollection' do
expect(diff_files).to be_a(Gitlab::Git::DiffCollection)
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 5dfbb8e71f8..ebeb05d6e02 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -26,6 +26,21 @@ describe Gitlab::Diff::File do
end
end
+ describe '#diff_lines_for_serializer' do
+ it 'includes bottom match line if not in the end' do
+ expect(diff_file.diff_lines_for_serializer.last.type).to eq('match')
+ end
+
+ context 'when deleted' do
+ let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') }
+ let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') }
+
+ it 'does not include bottom match line' do
+ expect(diff_file.diff_lines_for_serializer.last.type).not_to eq('match')
+ end
+ end
+ end
+
describe '#mode_changed?' do
it { expect(diff_file.mode_changed?).to be_falsey }
end
diff --git a/spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb b/spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb
new file mode 100644
index 00000000000..ae61ece8029
--- /dev/null
+++ b/spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe Gitlab::Email::Hook::AdditionalHeadersInterceptor do
+ let(:mail) do
+ ActionMailer::Base.mail(to: 'test@mail.com', from: 'info@mail.com', body: 'hello')
+ end
+
+ before do
+ mail.deliver_now
+ end
+
+ it 'adds Auto-Submitted header' do
+ expect(mail.header['To'].value).to eq('test@mail.com')
+ expect(mail.header['From'].value).to eq('info@mail.com')
+ expect(mail.header['Auto-Submitted'].value).to eq('auto-generated')
+ expect(mail.header['X-Auto-Response-Suppress'].value).to eq('All')
+ end
+
+ context 'when the same mail object is sent twice' do
+ before do
+ mail.deliver_now
+ end
+
+ it 'does not add the Auto-Submitted header twice' do
+ expect(mail.header['Auto-Submitted'].value).to eq('auto-generated')
+ expect(mail.header['X-Auto-Response-Suppress'].value).to eq('All')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb b/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb
new file mode 100644
index 00000000000..4497d4002da
--- /dev/null
+++ b/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::Email::Hook::DeliveryMetricsObserver do
+ let(:email) do
+ ActionMailer::Base.mail(to: 'test@example.com',
+ from: 'info@example.com',
+ body: 'hello')
+ end
+
+ context 'when email has been delivered' do
+ it 'increments both email delivery metrics' do
+ expect(described_class.delivery_attempts_counter).to receive(:increment)
+ expect(described_class.delivered_emails_counter).to receive(:increment)
+
+ email.deliver_now
+ end
+ end
+
+ context 'when email has not been delivered due to an error' do
+ before do
+ allow(email.delivery_method).to receive(:deliver!)
+ .and_raise(StandardError, 'Some SMTP error')
+ end
+
+ it 'increments only delivery attempt metric' do
+ expect(described_class.delivery_attempts_counter)
+ .to receive(:increment)
+ expect(described_class.delivered_emails_counter)
+ .not_to receive(:increment)
+
+ expect { email.deliver_now }
+ .to raise_error(StandardError, 'Some SMTP error')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
new file mode 100644
index 00000000000..91aa3bc7c2e
--- /dev/null
+++ b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Gitlab::Email::Hook::DisableEmailInterceptor do
+ before do
+ Mail.register_interceptor(described_class)
+ end
+
+ it 'does not send emails' do
+ allow(Gitlab.config.gitlab).to receive(:email_enabled).and_return(false)
+ expect { deliver_mail }.not_to change(ActionMailer::Base.deliveries, :count)
+ end
+
+ after do
+ # Removing interceptor from the list because unregister_interceptor is
+ # implemented in later version of mail gem
+ # See: https://github.com/mikel/mail/pull/705
+ Mail.unregister_interceptor(described_class)
+ end
+
+ def deliver_mail
+ key = create :personal_key
+ Notify.new_ssh_key_email(key.id)
+ end
+end
diff --git a/spec/lib/gitlab/exclusive_lease_helpers_spec.rb b/spec/lib/gitlab/exclusive_lease_helpers_spec.rb
new file mode 100644
index 00000000000..2e3656b52fb
--- /dev/null
+++ b/spec/lib/gitlab/exclusive_lease_helpers_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe Gitlab::ExclusiveLeaseHelpers, :clean_gitlab_redis_shared_state do
+ include ::ExclusiveLeaseHelpers
+
+ let(:class_instance) { (Class.new { include ::Gitlab::ExclusiveLeaseHelpers }).new }
+ let(:unique_key) { SecureRandom.hex(10) }
+
+ describe '#in_lock' do
+ subject { class_instance.in_lock(unique_key, **options) { } }
+
+ let(:options) { {} }
+
+ context 'when the lease is not obtained yet' do
+ before do
+ stub_exclusive_lease(unique_key, 'uuid')
+ end
+
+ it 'calls the given block' do
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ end
+
+ it 'calls the given block continuously' do
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ end
+
+ it 'cancels the exclusive lease after the block' do
+ expect_to_cancel_exclusive_lease(unique_key, 'uuid')
+
+ subject
+ end
+ end
+
+ context 'when the lease is obtained already' do
+ let!(:lease) { stub_exclusive_lease_taken(unique_key) }
+
+ it 'retries to obtain a lease and raises an error' do
+ expect(lease).to receive(:try_obtain).exactly(11).times
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+
+ context 'when ttl is specified' do
+ let(:options) { { ttl: 10.minutes } }
+
+ it 'receives the specified argument' do
+ expect(Gitlab::ExclusiveLease).to receive(:new).with(unique_key, { timeout: 10.minutes } )
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+
+ context 'when retry count is specified' do
+ let(:options) { { retries: 3 } }
+
+ it 'retries for the specified times' do
+ expect(lease).to receive(:try_obtain).exactly(4).times
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+
+ context 'when sleep second is specified' do
+ let(:options) { { retries: 0, sleep_sec: 0.05.seconds } }
+
+ it 'receives the specified argument' do
+ expect(class_instance).to receive(:sleep).with(0.05.seconds).once
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index 13df8531b63..ef52a25f47e 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -20,37 +20,55 @@ describe Gitlab::Gfm::UploadsRewriter do
"Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}"
end
- describe '#rewrite' do
- let!(:new_text) { rewriter.rewrite(new_project) }
+ shared_examples "files are accessible" do
+ describe '#rewrite' do
+ let!(:new_text) { rewriter.rewrite(new_project) }
- let(:old_files) { [image_uploader, zip_uploader].map(&:file) }
- let(:new_files) do
- described_class.new(new_text, new_project, user).files
- end
+ let(:old_files) { [image_uploader, zip_uploader] }
+ let(:new_files) do
+ described_class.new(new_text, new_project, user).files
+ end
- let(:old_paths) { old_files.map(&:path) }
- let(:new_paths) { new_files.map(&:path) }
+ let(:old_paths) { old_files.map(&:path) }
+ let(:new_paths) { new_files.map(&:path) }
- it 'rewrites content' do
- expect(new_text).not_to eq text
- expect(new_text.length).to eq text.length
- end
+ it 'rewrites content' do
+ expect(new_text).not_to eq text
+ expect(new_text.length).to eq text.length
+ end
- it 'copies files' do
- expect(new_files).to all(exist)
- expect(old_paths).not_to match_array new_paths
- expect(old_paths).to all(include(old_project.disk_path))
- expect(new_paths).to all(include(new_project.disk_path))
- end
+ it 'copies files' do
+ expect(new_files).to all(exist)
+ expect(old_paths).not_to match_array new_paths
+ expect(old_paths).to all(include(old_project.disk_path))
+ expect(new_paths).to all(include(new_project.disk_path))
+ end
- it 'does not remove old files' do
- expect(old_files).to all(exist)
+ it 'does not remove old files' do
+ expect(old_files).to all(exist)
+ end
+
+ it 'generates a new secret for each file' do
+ expect(new_paths).not_to include image_uploader.secret
+ expect(new_paths).not_to include zip_uploader.secret
+ end
end
+ end
- it 'generates a new secret for each file' do
- expect(new_paths).not_to include image_uploader.secret
- expect(new_paths).not_to include zip_uploader.secret
+ context "file are stored locally" do
+ include_examples "files are accessible"
+ end
+
+ context "files are stored remotely" do
+ before do
+ stub_uploads_object_storage(FileUploader)
+
+ old_files.each do |file|
+ file.migrate!(ObjectStorage::Store::REMOTE)
+ end
end
+
+ include_examples "files are accessible"
end
describe '#needs_rewrite?' do
diff --git a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
index 5d22dcfb508..ca067a29174 100644
--- a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
+++ b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::AttributesAtRefParser, seed_helper: true do
+describe Gitlab::Git::AttributesAtRefParser, :seed_helper do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
diff --git a/spec/lib/gitlab/git/attributes_parser_spec.rb b/spec/lib/gitlab/git/attributes_parser_spec.rb
index 2d103123998..18ebfef38f0 100644
--- a/spec/lib/gitlab/git/attributes_parser_spec.rb
+++ b/spec/lib/gitlab/git/attributes_parser_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::AttributesParser, seed_helper: true do
+describe Gitlab::Git::AttributesParser, :seed_helper do
let(:attributes_path) { File.join(SEED_STORAGE_PATH, 'with-git-attributes.git', 'info', 'attributes') }
let(:data) { File.read(attributes_path) }
diff --git a/spec/lib/gitlab/git/blame_spec.rb b/spec/lib/gitlab/git/blame_spec.rb
index ba790b717ae..e704d1c673c 100644
--- a/spec/lib/gitlab/git/blame_spec.rb
+++ b/spec/lib/gitlab/git/blame_spec.rb
@@ -1,7 +1,7 @@
# coding: utf-8
require "spec_helper"
-describe Gitlab::Git::Blame, seed_helper: true do
+describe Gitlab::Git::Blame, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:blame) do
Gitlab::Git::Blame.new(repository, SeedRepo::Commit::ID, "CONTRIBUTING.md")
diff --git a/spec/lib/gitlab/git/blob_snippet_spec.rb b/spec/lib/gitlab/git/blob_snippet_spec.rb
index d6d365f6492..6effec8295c 100644
--- a/spec/lib/gitlab/git/blob_snippet_spec.rb
+++ b/spec/lib/gitlab/git/blob_snippet_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe Gitlab::Git::BlobSnippet, seed_helper: true do
+describe Gitlab::Git::BlobSnippet, :seed_helper do
describe '#data' do
context 'empty lines' do
let(:snippet) { Gitlab::Git::BlobSnippet.new('master', nil, nil, nil) }
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index b6061df349d..ea49502ae2e 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe Gitlab::Git::Blob, seed_helper: true do
+describe Gitlab::Git::Blob, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
describe 'initialize' do
@@ -178,77 +178,67 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
describe '.batch' do
- shared_examples 'loading blobs in batch' do
- let(:blob_references) do
- [
- [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
- [SeedRepo::Commit::ID, 'six']
- ]
- end
+ let(:blob_references) do
+ [
+ [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
+ [SeedRepo::Commit::ID, 'six']
+ ]
+ end
- subject { described_class.batch(repository, blob_references) }
+ subject { described_class.batch(repository, blob_references) }
- it { expect(subject.size).to eq(blob_references.size) }
+ it { expect(subject.size).to eq(blob_references.size) }
- context 'first blob' do
- let(:blob) { subject[0] }
+ context 'first blob' do
+ let(:blob) { subject[0] }
- it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
- it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
- it { expect(blob.path).to eq("files/ruby/popen.rb") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
- it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
- it { expect(blob.size).to eq(669) }
- it { expect(blob.mode).to eq("100644") }
- end
+ it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
+ it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
+ it { expect(blob.path).to eq("files/ruby/popen.rb") }
+ it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
+ it { expect(blob.size).to eq(669) }
+ it { expect(blob.mode).to eq("100644") }
+ end
- context 'second blob' do
- let(:blob) { subject[1] }
+ context 'second blob' do
+ let(:blob) { subject[1] }
- it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
- it { expect(blob.data).to eq('') }
- it 'does not mark the blob as binary' do
- expect(blob).not_to be_binary
- end
+ it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
+ it { expect(blob.data).to eq('') }
+ it 'does not mark the blob as binary' do
+ expect(blob).not_to be_binary
end
+ end
- context 'limiting' do
- subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
+ context 'limiting' do
+ subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
- context 'positive' do
- let(:blob_size_limit) { 10 }
+ context 'positive' do
+ let(:blob_size_limit) { 10 }
- it { expect(subject.first.data.size).to eq(10) }
- end
+ it { expect(subject.first.data.size).to eq(10) }
+ end
- context 'zero' do
- let(:blob_size_limit) { 0 }
+ context 'zero' do
+ let(:blob_size_limit) { 0 }
- it 'only loads the metadata' do
- expect(subject.first.size).not_to be(0)
- expect(subject.first.data).to eq('')
- end
+ it 'only loads the metadata' do
+ expect(subject.first.size).not_to be(0)
+ expect(subject.first.data).to eq('')
end
+ end
- context 'negative' do
- let(:blob_size_limit) { -1 }
+ context 'negative' do
+ let(:blob_size_limit) { -1 }
- it 'ignores MAX_DATA_DISPLAY_SIZE' do
- stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
+ it 'ignores MAX_DATA_DISPLAY_SIZE' do
+ stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
- expect(subject.first.data.size).to eq(669)
- end
+ expect(subject.first.data.size).to eq(669)
end
end
end
-
- context 'when Gitaly list_blobs_by_sha_path feature is enabled' do
- it_behaves_like 'loading blobs in batch'
- end
-
- context 'when Gitaly list_blobs_by_sha_path feature is disabled', :disable_gitaly do
- it_behaves_like 'loading blobs in batch'
- end
end
describe '.batch_metadata' do
@@ -294,58 +284,48 @@ describe Gitlab::Git::Blob, seed_helper: true do
)
end
- shared_examples 'fetching batch of LFS pointers' do
- it 'returns a list of Gitlab::Git::Blob' do
- blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
-
- expect(blobs.count).to eq(1)
- expect(blobs).to all( be_a(Gitlab::Git::Blob) )
- expect(blobs).to be_an(Array)
- end
-
- it 'accepts blob IDs as a lazy enumerator' do
- blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
-
- expect(blobs.count).to eq(1)
- expect(blobs).to all( be_a(Gitlab::Git::Blob) )
- end
+ it 'returns a list of Gitlab::Git::Blob' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
- it 'handles empty list of IDs gracefully' do
- blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
- blobs_2 = described_class.batch_lfs_pointers(repository, [])
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ expect(blobs).to be_an(Array)
+ end
- expect(blobs_1).to eq([])
- expect(blobs_2).to eq([])
- end
+ it 'accepts blob IDs as a lazy enumerator' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
- it 'silently ignores tree objects' do
- blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ end
- expect(blobs).to eq([])
- end
+ it 'handles empty list of IDs gracefully' do
+ blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
+ blobs_2 = described_class.batch_lfs_pointers(repository, [])
- it 'silently ignores non lfs objects' do
- blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
+ expect(blobs_1).to eq([])
+ expect(blobs_2).to eq([])
+ end
- expect(blobs).to eq([])
- end
+ it 'silently ignores tree objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
- it 'avoids loading large blobs into memory' do
- # This line could call `lookup` on `repository`, so do here before mocking.
- non_lfs_blob_id = non_lfs_blob.id
+ expect(blobs).to eq([])
+ end
- expect(repository).not_to receive(:lookup)
+ it 'silently ignores non lfs objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
- described_class.batch_lfs_pointers(repository, [non_lfs_blob_id])
- end
+ expect(blobs).to eq([])
end
- context 'when Gitaly batch_lfs_pointers is enabled' do
- it_behaves_like 'fetching batch of LFS pointers'
- end
+ it 'avoids loading large blobs into memory' do
+ # This line could call `lookup` on `repository`, so do here before mocking.
+ non_lfs_blob_id = non_lfs_blob.id
+
+ expect(repository).not_to receive(:lookup)
- context 'when Gitaly batch_lfs_pointers is disabled', :disable_gitaly do
- it_behaves_like 'fetching batch of LFS pointers'
+ described_class.batch_lfs_pointers(repository, [non_lfs_blob_id])
end
end
@@ -532,8 +512,8 @@ describe Gitlab::Git::Blob, seed_helper: true do
subject { blob.load_all_data!(repository) }
it 'loads missing data' do
- expect(Gitlab::GitalyClient).to receive(:migrate)
- .with(:git_blob_load_all_data).and_return(full_data)
+ expect(repository.gitaly_blob_client).to receive(:get_blob)
+ .and_return(double(:response, data: full_data))
subject
@@ -544,8 +524,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
let(:blob) { Gitlab::Git::Blob.new(name: 'test', size: 4, data: full_data) }
it "doesn't perform any loading" do
- expect(Gitlab::GitalyClient).not_to receive(:migrate)
- .with(:git_blob_load_all_data)
+ expect(repository.gitaly_blob_client).not_to receive(:get_blob)
subject
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index ec1a684cfbc..79ccbb79966 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -1,7 +1,12 @@
require "spec_helper"
-describe Gitlab::Git::Branch, seed_helper: true do
+describe Gitlab::Git::Branch, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:rugged) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged
+ end
+ end
subject { repository.branches }
@@ -124,6 +129,7 @@ describe Gitlab::Git::Branch, seed_helper: true do
it { expect(repository.branches.size).to eq(SeedRepo::Repo::BRANCHES.size) }
def create_commit
- repository.create_commit(params.merge(committer: committer.merge(time: Time.now)))
+ params[:message].delete!("\r")
+ Rugged::Commit.create(rugged, params.merge(committer: committer.merge(time: Time.now)))
end
end
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index ee74c2769eb..2718a3c5e49 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Commit, seed_helper: true do
+describe Gitlab::Git::Commit, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) }
let(:rugged_commit) do
@@ -27,7 +27,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
}
@parents = [repo.head.target]
- @gitlab_parents = @parents.map { |c| described_class.decorate(repository, c) }
+ @gitlab_parents = @parents.map { |c| described_class.find(repository, c.oid) }
@tree = @parents.first.tree
sha = Rugged::Commit.create(
@@ -41,7 +41,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
)
@raw_commit = repo.lookup(sha)
- @commit = described_class.new(repository, @raw_commit)
+ @commit = described_class.find(repository, sha)
end
it { expect(@commit.short_id).to eq(@raw_commit.oid[0..10]) }
@@ -488,13 +488,15 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe '#init_from_rugged' do
- let(:gitlab_commit) { described_class.new(repository, rugged_commit) }
- subject { gitlab_commit }
+ skip 'move this test to gitaly-ruby' do
+ describe '#init_from_rugged' do
+ let(:gitlab_commit) { described_class.new(repository, rugged_commit) }
+ subject { gitlab_commit }
- describe '#id' do
- subject { super().id }
- it { is_expected.to eq(SeedRepo::Commit::ID) }
+ describe '#id' do
+ subject { super().id }
+ it { is_expected.to eq(SeedRepo::Commit::ID) }
+ end
end
end
diff --git a/spec/lib/gitlab/git/committer_with_hooks_spec.rb b/spec/lib/gitlab/git/committer_with_hooks_spec.rb
index 2100690f873..c7626058acd 100644
--- a/spec/lib/gitlab/git/committer_with_hooks_spec.rb
+++ b/spec/lib/gitlab/git/committer_with_hooks_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::CommitterWithHooks, seed_helper: true do
+describe Gitlab::Git::CommitterWithHooks, :seed_helper do
# TODO https://gitlab.com/gitlab-org/gitaly/issues/1234
skip 'needs to be moved to gitaly-ruby test suite' do
shared_examples 'calling wiki hooks' do
diff --git a/spec/lib/gitlab/git/compare_spec.rb b/spec/lib/gitlab/git/compare_spec.rb
index b6a42e422b5..7cc6f52f8ee 100644
--- a/spec/lib/gitlab/git/compare_spec.rb
+++ b/spec/lib/gitlab/git/compare_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Compare, seed_helper: true do
+describe Gitlab::Git::Compare, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:compare) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: false) }
let(:compare_straight) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: true) }
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index 65edc750f39..81658874be7 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::DiffCollection, seed_helper: true do
+describe Gitlab::Git::DiffCollection, :seed_helper do
subject do
Gitlab::Git::DiffCollection.new(
iterator,
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 3bb0b5be15b..87d9fcee39e 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Diff, seed_helper: true do
+describe Gitlab::Git::Diff, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
before do
@@ -27,6 +27,7 @@ EOT
too_large: false
}
+ # TODO use a Gitaly diff object instead
@rugged_diff = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths:
[".gitmodules"]).patches.first
@@ -266,8 +267,12 @@ EOT
describe '#submodule?' do
before do
- commit = repository.lookup('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- @diffs = commit.parents[0].diff(commit).patches
+ # TODO use a Gitaly diff object instead
+ rugged_commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.rev_parse('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+ end
+
+ @diffs = rugged_commit.parents[0].diff(rugged_commit).patches
end
it { expect(described_class.new(@diffs[0]).submodule?).to eq(false) }
diff --git a/spec/lib/gitlab/git/hooks_service_spec.rb b/spec/lib/gitlab/git/hooks_service_spec.rb
index 9337aa39e13..55ffced36ac 100644
--- a/spec/lib/gitlab/git/hooks_service_spec.rb
+++ b/spec/lib/gitlab/git/hooks_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::HooksService, seed_helper: true do
+describe Gitlab::Git::HooksService, :seed_helper do
let(:gl_id) { 'user-456' }
let(:gl_username) { 'janedoe' }
let(:user) { Gitlab::Git::User.new(gl_username, 'Jane Doe', 'janedoe@example.com', gl_id) }
diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb
index 16e6bd35449..c4edd6961e1 100644
--- a/spec/lib/gitlab/git/index_spec.rb
+++ b/spec/lib/gitlab/git/index_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-describe Gitlab::Git::Index, seed_helper: true do
+describe Gitlab::Git::Index, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:index) { described_class.new(repository) }
before do
- index.read_tree(repository.lookup('master').tree)
+ index.read_tree(lookup('master').tree)
end
around do |example|
@@ -30,7 +30,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
expect(entry).not_to be_nil
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
end
@@ -54,7 +54,7 @@ describe Gitlab::Git::Index, seed_helper: true do
index.create(options)
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
+ expect(lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
end
end
@@ -68,7 +68,7 @@ describe Gitlab::Git::Index, seed_helper: true do
index.create(options)
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq("Hello,\nWorld")
+ expect(lookup(entry[:oid]).content).to eq("Hello,\nWorld")
end
end
end
@@ -135,7 +135,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
it 'preserves file mode' do
@@ -190,7 +190,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
expect(entry).not_to be_nil
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
it 'preserves file mode' do
@@ -232,4 +232,8 @@ describe Gitlab::Git::Index, seed_helper: true do
end
end
end
+
+ def lookup(revision)
+ repository.rugged.rev_parse(revision)
+ end
end
diff --git a/spec/lib/gitlab/git/popen_spec.rb b/spec/lib/gitlab/git/popen_spec.rb
index b033ede9062..074e66d2a5d 100644
--- a/spec/lib/gitlab/git/popen_spec.rb
+++ b/spec/lib/gitlab/git/popen_spec.rb
@@ -2,6 +2,9 @@ require 'spec_helper'
describe 'Gitlab::Git::Popen' do
let(:path) { Rails.root.join('tmp').to_s }
+ let(:test_string) { 'The quick brown fox jumped over the lazy dog' }
+ # The pipe buffer is typically 64K. This string is about 440K.
+ let(:spew_command) { ['bash', '-c', "for i in {1..10000}; do echo '#{test_string}' 1>&2; done"] }
let(:klass) do
Class.new(Object) do
@@ -70,6 +73,15 @@ describe 'Gitlab::Git::Popen' do
end
end
end
+
+ context 'with a process that writes a lot of data to stderr' do
+ it 'returns zero' do
+ output, status = klass.new.popen(spew_command, path)
+
+ expect(output).to include(test_string)
+ expect(status).to eq(0)
+ end
+ end
end
context 'popen_with_timeout' do
@@ -85,6 +97,17 @@ describe 'Gitlab::Git::Popen' do
it { expect(output).to include('tests') }
end
+ context 'multi-line string' do
+ let(:test_string) { "this is 1 line\n2nd line\n3rd line\n" }
+ let(:result) { klass.new.popen_with_timeout(['echo', test_string], timeout, path) }
+ let(:output) { result.first }
+ let(:status) { result.last }
+
+ it { expect(status).to be_zero }
+ # echo adds its own line
+ it { expect(output).to eq(test_string + "\n") }
+ end
+
context 'non-zero status' do
let(:result) { klass.new.popen_with_timeout(%w(cat NOTHING), timeout, path) }
let(:output) { result.first }
@@ -110,6 +133,13 @@ describe 'Gitlab::Git::Popen' do
it "handles processes that do not shutdown correctly" do
expect { klass.new.popen_with_timeout(['bash', '-c', "trap -- '' SIGTERM; sleep 1000"], timeout, path) }.to raise_error(Timeout::Error)
end
+
+ it 'handles process that writes a lot of data to stderr' do
+ output, status = klass.new.popen_with_timeout(spew_command, timeout, path)
+
+ expect(output).to include(test_string)
+ expect(status).to eq(0)
+ end
end
context 'timeout period' do
diff --git a/spec/lib/gitlab/git/remote_repository_spec.rb b/spec/lib/gitlab/git/remote_repository_spec.rb
index eb148cc3804..53ed7c5a13a 100644
--- a/spec/lib/gitlab/git/remote_repository_spec.rb
+++ b/spec/lib/gitlab/git/remote_repository_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::RemoteRepository, seed_helper: true do
+describe Gitlab::Git::RemoteRepository, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
subject { described_class.new(repository) }
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 6ec4b90d70c..35a6fc94753 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1,7 +1,7 @@
# coding: utf-8
require "spec_helper"
-describe Gitlab::Git::Repository, seed_helper: true do
+describe Gitlab::Git::Repository, :seed_helper do
include Gitlab::EncodingHelper
using RSpec::Parameterized::TableSyntax
@@ -321,90 +321,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context '#submodules' do
- around do |example|
- # TODO #submodules will be removed, has been migrated to gitaly
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
-
- context 'where repo has submodules' do
- let(:submodules) { repository.send(:submodules, 'master') }
- let(:submodule) { submodules.first }
-
- it { expect(submodules).to be_kind_of Hash }
- it { expect(submodules.empty?).to be_falsey }
-
- it 'should have valid data' do
- expect(submodule).to eq([
- "six", {
- "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "name" => "six",
- "url" => "git://github.com/randx/six.git"
- }
- ])
- end
-
- it 'should handle nested submodules correctly' do
- nested = submodules['nested/six']
- expect(nested['name']).to eq('nested/six')
- expect(nested['url']).to eq('git://github.com/randx/six.git')
- expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
- end
-
- it 'should handle deeply nested submodules correctly' do
- nested = submodules['deeper/nested/six']
- expect(nested['name']).to eq('deeper/nested/six')
- expect(nested['url']).to eq('git://github.com/randx/six.git')
- expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
- end
-
- it 'should not have an entry for an invalid submodule' do
- expect(submodules).not_to have_key('invalid/path')
- end
-
- it 'should not have an entry for an uncommited submodule dir' do
- submodules = repository.send(:submodules, 'fix-existing-submodule-dir')
- expect(submodules).not_to have_key('submodule-existing-dir')
- end
-
- it 'should handle tags correctly' do
- submodules = repository.send(:submodules, 'v1.2.1')
-
- expect(submodules.first).to eq([
- "six", {
- "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "name" => "six",
- "url" => "git://github.com/randx/six.git"
- }
- ])
- end
-
- it 'should not break on invalid syntax' do
- allow(repository).to receive(:blob_content).and_return(<<-GITMODULES.strip_heredoc)
- [submodule "six"]
- path = six
- url = git://github.com/randx/six.git
-
- [submodule]
- foo = bar
- GITMODULES
-
- expect(submodules).to have_key('six')
- end
- end
-
- context 'where repo doesn\'t have submodules' do
- let(:submodules) { repository.send(:submodules, '6d39438') }
- it 'should return an empty hash' do
- expect(submodules).to be_empty
- end
- end
- end
-
describe '#commit_count' do
shared_examples 'simple commit counting' do
it { expect(repository.commit_count("master")).to eq(25) }
@@ -611,38 +527,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe "#remove_remote" do
- before(:all) do
- @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- @repo.remove_remote("expendable")
- end
-
- it "should remove the remote" do
- expect(@repo.rugged.remotes).not_to include("expendable")
- end
-
- after(:all) do
- ensure_seeds
- end
- end
-
- describe "#remote_update" do
- before(:all) do
- @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- @repo.remote_update("expendable", url: TEST_NORMAL_REPO_PATH)
- end
-
- it "should add the remote" do
- expect(@repo.rugged.remotes["expendable"].url).to(
- eq(TEST_NORMAL_REPO_PATH)
- )
- end
-
- after(:all) do
- ensure_seeds
- end
- end
-
describe '#fetch_repository_as_mirror' do
let(:new_repository) do
Gitlab::Git::Repository.new('default', 'my_project.git', '')
@@ -699,58 +583,24 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#remote_tags' do
- let(:remote_name) { 'upstream' }
- let(:target_commit_id) { SeedRepo::Commit::ID }
- let(:tag_name) { 'v0.0.1' }
- let(:tag_message) { 'My tag' }
- let(:remote_repository) do
- Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- end
-
- around do |example|
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- subject { repository.remote_tags(remote_name) }
-
- before do
- remote_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { remote_repository.path }
- repository.add_remote(remote_name, remote_repository_path)
- remote_repository.add_tag(tag_name, user: user, target: target_commit_id)
- end
-
- after do
- ensure_seeds
- end
-
- it 'gets the remote tags' do
- expect(subject.first).to be_an_instance_of(Gitlab::Git::Tag)
- expect(subject.first.name).to eq(tag_name)
- expect(subject.first.dereferenced_target.id).to eq(target_commit_id)
- end
- end
-
describe "#log" do
shared_examples 'repository log' do
let(:commit_with_old_name) do
- Gitlab::Git::Commit.decorate(repository, @commit_with_old_name_id)
+ Gitlab::Git::Commit.find(repository, @commit_with_old_name_id)
end
let(:commit_with_new_name) do
- Gitlab::Git::Commit.decorate(repository, @commit_with_new_name_id)
+ Gitlab::Git::Commit.find(repository, @commit_with_new_name_id)
end
let(:rename_commit) do
- Gitlab::Git::Commit.decorate(repository, @rename_commit_id)
+ Gitlab::Git::Commit.find(repository, @rename_commit_id)
end
before(:context) do
# Add new commits so that there's a renamed file in the commit history
repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- @commit_with_old_name_id = new_commit_edit_old_file(repo)
- @rename_commit_id = new_commit_move_file(repo)
- @commit_with_new_name_id = new_commit_edit_new_file(repo)
+ @commit_with_old_name_id = new_commit_edit_old_file(repo).oid
+ @rename_commit_id = new_commit_move_file(repo).oid
+ @commit_with_new_name_id = new_commit_edit_new_file(repo).oid
end
after(:context) do
@@ -952,8 +802,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
def commit_files(commit)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- commit.rugged_diff_from_parent.deltas.flat_map do |delta|
- [delta.old_file[:path], delta.new_file[:path]].uniq.compact
+ commit.deltas.flat_map do |delta|
+ [delta.old_path, delta.new_path].uniq.compact
end
end
end
@@ -990,10 +840,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
context 'when Gitaly find_commits feature is enabled' do
it_behaves_like 'repository log'
end
-
- context 'when Gitaly find_commits feature is disabled', :disable_gitaly do
- it_behaves_like 'repository log'
- end
end
describe '#count_commits_between' do
@@ -1157,6 +1003,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
@repo.rugged.config['core.autocrlf'] = true
end
+ around do |example|
+ # OK because autocrlf is only used in gitaly-ruby
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ example.run
+ end
+ end
+
it 'return the value of the autocrlf option' do
expect(@repo.autocrlf).to be(true)
end
@@ -1172,6 +1025,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
@repo.rugged.config['core.autocrlf'] = false
end
+ around do |example|
+ # OK because autocrlf= is only used in gitaly-ruby
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ example.run
+ end
+ end
+
it 'should set the autocrlf option to the provided option' do
@repo.autocrlf = :input
@@ -1186,50 +1046,17 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#find_branch' do
- shared_examples 'finding a branch' do
- it 'should return a Branch for master' do
- branch = repository.find_branch('master')
-
- expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
- expect(branch.name).to eq('master')
- end
-
- it 'should handle non-existent branch' do
- branch = repository.find_branch('this-is-garbage')
+ it 'should return a Branch for master' do
+ branch = repository.find_branch('master')
- expect(branch).to eq(nil)
- end
- end
-
- context 'when Gitaly find_branch feature is enabled' do
- it_behaves_like 'finding a branch'
+ expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
+ expect(branch.name).to eq('master')
end
- context 'when Gitaly find_branch feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'finding a branch'
-
- context 'force_reload is true' do
- it 'should reload Rugged::Repository' do
- expect(Rugged::Repository).to receive(:new).twice.and_call_original
+ it 'should handle non-existent branch' do
+ branch = repository.find_branch('this-is-garbage')
- repository.find_branch('master')
- branch = repository.find_branch('master', force_reload: true)
-
- expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
- expect(branch.name).to eq('master')
- end
- end
-
- context 'force_reload is false' do
- it 'should not reload Rugged::Repository' do
- expect(Rugged::Repository).to receive(:new).once.and_call_original
-
- branch = repository.find_branch('master', force_reload: false)
-
- expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
- expect(branch.name).to eq('master')
- end
- end
+ expect(branch).to eq(nil)
end
end
@@ -1402,94 +1229,84 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe "#copy_gitattributes" do
- shared_examples 'applying git attributes' do
- let(:attributes_path) { File.join(SEED_STORAGE_PATH, TEST_REPO_PATH, 'info/attributes') }
+ let(:attributes_path) { File.join(SEED_STORAGE_PATH, TEST_REPO_PATH, 'info/attributes') }
- after do
- FileUtils.rm_rf(attributes_path) if Dir.exist?(attributes_path)
- end
-
- it "raises an error with invalid ref" do
- expect { repository.copy_gitattributes("invalid") }.to raise_error(Gitlab::Git::Repository::InvalidRef)
- end
-
- context 'when forcing encoding issues' do
- let(:branch_name) { "ʕ•ᴥ•ʔ" }
+ after do
+ FileUtils.rm_rf(attributes_path) if Dir.exist?(attributes_path)
+ end
- before do
- repository.create_branch(branch_name, "master")
- end
+ it "raises an error with invalid ref" do
+ expect { repository.copy_gitattributes("invalid") }.to raise_error(Gitlab::Git::Repository::InvalidRef)
+ end
- after do
- repository.rm_branch(branch_name, user: build(:admin))
- end
+ context 'when forcing encoding issues' do
+ let(:branch_name) { "ʕ•ᴥ•ʔ" }
- it "doesn't raise with a valid unicode ref" do
- expect { repository.copy_gitattributes(branch_name) }.not_to raise_error
+ before do
+ repository.create_branch(branch_name, "master")
+ end
- repository
- end
+ after do
+ repository.rm_branch(branch_name, user: build(:admin))
end
- context "with no .gitattrbutes" do
- before do
- repository.copy_gitattributes("master")
- end
+ it "doesn't raise with a valid unicode ref" do
+ expect { repository.copy_gitattributes(branch_name) }.not_to raise_error
- it "does not have an info/attributes" do
- expect(File.exist?(attributes_path)).to be_falsey
- end
+ repository
end
+ end
- context "with .gitattrbutes" do
- before do
- repository.copy_gitattributes("gitattributes")
- end
+ context "with no .gitattrbutes" do
+ before do
+ repository.copy_gitattributes("master")
+ end
- it "has an info/attributes" do
- expect(File.exist?(attributes_path)).to be_truthy
- end
+ it "does not have an info/attributes" do
+ expect(File.exist?(attributes_path)).to be_falsey
+ end
+ end
- it "has the same content in info/attributes as .gitattributes" do
- contents = File.open(attributes_path, "rb") { |f| f.read }
- expect(contents).to eq("*.md binary\n")
- end
+ context "with .gitattrbutes" do
+ before do
+ repository.copy_gitattributes("gitattributes")
end
- context "with updated .gitattrbutes" do
- before do
- repository.copy_gitattributes("gitattributes")
- repository.copy_gitattributes("gitattributes-updated")
- end
+ it "has an info/attributes" do
+ expect(File.exist?(attributes_path)).to be_truthy
+ end
- it "has an info/attributes" do
- expect(File.exist?(attributes_path)).to be_truthy
- end
+ it "has the same content in info/attributes as .gitattributes" do
+ contents = File.open(attributes_path, "rb") { |f| f.read }
+ expect(contents).to eq("*.md binary\n")
+ end
+ end
- it "has the updated content in info/attributes" do
- contents = File.read(attributes_path)
- expect(contents).to eq("*.txt binary\n")
- end
+ context "with updated .gitattrbutes" do
+ before do
+ repository.copy_gitattributes("gitattributes")
+ repository.copy_gitattributes("gitattributes-updated")
end
- context "with no .gitattrbutes in HEAD but with previous info/attributes" do
- before do
- repository.copy_gitattributes("gitattributes")
- repository.copy_gitattributes("master")
- end
+ it "has an info/attributes" do
+ expect(File.exist?(attributes_path)).to be_truthy
+ end
- it "does not have an info/attributes" do
- expect(File.exist?(attributes_path)).to be_falsey
- end
+ it "has the updated content in info/attributes" do
+ contents = File.read(attributes_path)
+ expect(contents).to eq("*.txt binary\n")
end
end
- context 'when gitaly is enabled' do
- it_behaves_like 'applying git attributes'
- end
+ context "with no .gitattrbutes in HEAD but with previous info/attributes" do
+ before do
+ repository.copy_gitattributes("gitattributes")
+ repository.copy_gitattributes("master")
+ end
- context 'when gitaly is disabled', :disable_gitaly do
- it_behaves_like 'applying git attributes'
+ it "does not have an info/attributes" do
+ expect(File.exist?(attributes_path)).to be_falsey
+ end
end
end
@@ -1567,31 +1384,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#batch_existence' do
- let(:refs) { ['deadbeef', SeedRepo::RubyBlob::ID, '909e6157199'] }
-
- around do |example|
- # TODO #batch_existence isn't used anywhere, can we remove it?
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- it 'returns existing refs back' do
- result = repository.batch_existence(refs)
-
- expect(result).to eq([SeedRepo::RubyBlob::ID])
- end
-
- context 'existing: true' do
- it 'inverts meaning and returns non-existing refs' do
- result = repository.batch_existence(refs, existing: false)
-
- expect(result).to eq(%w(deadbeef 909e6157199))
- end
- end
- end
-
describe '#local_branches' do
before(:all) do
@repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
@@ -1726,59 +1518,51 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#fetch_source_branch!' do
- shared_examples '#fetch_source_branch!' do
- let(:local_ref) { 'refs/merge-requests/1/head' }
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
- let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
-
- after do
- ensure_seeds
- end
+ let(:local_ref) { 'refs/merge-requests/1/head' }
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
- context 'when the branch exists' do
- context 'when the commit does not exist locally' do
- let(:source_branch) { 'new-branch-for-fetch-source-branch' }
- let(:source_rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { source_repository.rugged } }
- let(:new_oid) { new_commit_edit_old_file(source_rugged).oid }
+ after do
+ ensure_seeds
+ end
- before do
- source_rugged.branches.create(source_branch, new_oid)
- end
+ context 'when the branch exists' do
+ context 'when the commit does not exist locally' do
+ let(:source_branch) { 'new-branch-for-fetch-source-branch' }
+ let(:source_rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { source_repository.rugged } }
+ let(:new_oid) { new_commit_edit_old_file(source_rugged).oid }
- it 'writes the ref' do
- expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
- expect(repository.commit(local_ref).sha).to eq(new_oid)
- end
+ before do
+ source_rugged.branches.create(source_branch, new_oid)
end
- context 'when the commit exists locally' do
- let(:source_branch) { 'master' }
- let(:expected_oid) { SeedRepo::LastCommit::ID }
-
- it 'writes the ref' do
- # Sanity check: the commit should already exist
- expect(repository.commit(expected_oid)).not_to be_nil
-
- expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
- expect(repository.commit(local_ref).sha).to eq(expected_oid)
- end
+ it 'writes the ref' do
+ expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
+ expect(repository.commit(local_ref).sha).to eq(new_oid)
end
end
- context 'when the branch does not exist' do
- let(:source_branch) { 'definitely-not-master' }
+ context 'when the commit exists locally' do
+ let(:source_branch) { 'master' }
+ let(:expected_oid) { SeedRepo::LastCommit::ID }
- it 'does not write the ref' do
- expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(false)
- expect(repository.commit(local_ref)).to be_nil
+ it 'writes the ref' do
+ # Sanity check: the commit should already exist
+ expect(repository.commit(expected_oid)).not_to be_nil
+
+ expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
+ expect(repository.commit(local_ref).sha).to eq(expected_oid)
end
end
end
- it_behaves_like '#fetch_source_branch!'
+ context 'when the branch does not exist' do
+ let(:source_branch) { 'definitely-not-master' }
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#fetch_source_branch!'
+ it 'does not write the ref' do
+ expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(false)
+ expect(repository.commit(local_ref)).to be_nil
+ end
end
end
@@ -1866,6 +1650,54 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe '#set_config' do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+ let(:rugged) { repository_rugged }
+ let(:entries) do
+ {
+ 'test.foo1' => 'bla bla',
+ 'test.foo2' => 1234,
+ 'test.foo3' => true
+ }
+ end
+
+ it 'can set config settings' do
+ expect(repository.set_config(entries)).to be_nil
+
+ expect(rugged.config['test.foo1']).to eq('bla bla')
+ expect(rugged.config['test.foo2']).to eq('1234')
+ expect(rugged.config['test.foo3']).to eq('true')
+ end
+
+ after do
+ entries.keys.each { |k| rugged.config.delete(k) }
+ end
+ end
+
+ describe '#delete_config' do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+ let(:rugged) { repository_rugged }
+ let(:entries) do
+ {
+ 'test.foo1' => 'bla bla',
+ 'test.foo2' => 1234,
+ 'test.foo3' => true
+ }
+ end
+
+ it 'can delete config settings' do
+ entries.each do |key, value|
+ rugged.config[key] = value
+ end
+
+ expect(repository.delete_config(*%w[does.not.exist test.foo1 test.foo2])).to be_nil
+
+ config_keys = rugged.config.each_key.to_a
+ expect(config_keys).not_to include('test.foo1')
+ expect(config_keys).not_to include('test.foo2')
+ end
+ end
+
describe '#merge' do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
@@ -1971,21 +1803,15 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context 'with gitaly' do
- it "calls Gitaly's OperationService" do
- expect_any_instance_of(Gitlab::GitalyClient::OperationService)
- .to receive(:user_ff_branch).with(user, source_sha, target_branch)
- .and_return(nil)
-
- subject
- end
+ it "calls Gitaly's OperationService" do
+ expect_any_instance_of(Gitlab::GitalyClient::OperationService)
+ .to receive(:user_ff_branch).with(user, source_sha, target_branch)
+ .and_return(nil)
- it_behaves_like '#ff_merge'
+ subject
end
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#ff_merge'
- end
+ it_behaves_like '#ff_merge'
end
describe '#delete_all_refs_except' do
@@ -2018,54 +1844,61 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
end
+ let(:rugged) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged }
+ end
let(:remote_name) { 'my-remote' }
+ let(:url) { 'http://my-repo.git' }
after do
ensure_seeds
end
describe '#add_remote' do
- let(:url) { 'http://my-repo.git' }
let(:mirror_refmap) { '+refs/*:refs/*' }
- it 'creates a new remote via Gitaly' do
- expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
- .to receive(:add_remote).with(remote_name, url, mirror_refmap)
+ shared_examples 'add_remote' do
+ it 'added the remote' do
+ begin
+ rugged.remotes.delete(remote_name)
+ rescue Rugged::ConfigError
+ end
+
+ repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
- repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
+ expect(rugged.remotes[remote_name]).not_to be_nil
+ expect(rugged.config["remote.#{remote_name}.mirror"]).to eq('true')
+ expect(rugged.config["remote.#{remote_name}.prune"]).to eq('true')
+ expect(rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap)
+ end
end
- context 'with Gitaly disabled', :skip_gitaly_mock do
- it 'creates a new remote via Rugged' do
- expect_any_instance_of(Rugged::RemoteCollection).to receive(:create)
- .with(remote_name, url)
- expect_any_instance_of(Rugged::Config).to receive(:[]=)
- .with("remote.#{remote_name}.mirror", true)
- expect_any_instance_of(Rugged::Config).to receive(:[]=)
- .with("remote.#{remote_name}.prune", true)
- expect_any_instance_of(Rugged::Config).to receive(:[]=)
- .with("remote.#{remote_name}.fetch", mirror_refmap)
+ context 'using Gitaly' do
+ it_behaves_like 'add_remote'
+ end
- repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
- end
+ context 'with Gitaly disabled', :disable_gitaly do
+ it_behaves_like 'add_remote'
end
end
describe '#remove_remote' do
- it 'removes the remote via Gitaly' do
- expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
- .to receive(:remove_remote).with(remote_name)
+ shared_examples 'remove_remote' do
+ it 'removes the remote' do
+ rugged.remotes.create(remote_name, url)
+
+ repository.remove_remote(remote_name)
- repository.remove_remote(remote_name)
+ expect(rugged.remotes[remote_name]).to be_nil
+ end
end
- context 'with Gitaly disabled', :skip_gitaly_mock do
- it 'removes the remote via Rugged' do
- expect_any_instance_of(Rugged::RemoteCollection).to receive(:delete)
- .with(remote_name)
+ context 'using Gitaly' do
+ it_behaves_like 'remove_remote'
+ end
- repository.remove_remote(remote_name)
- end
+ context 'with Gitaly disabled', :disable_gitaly do
+ it_behaves_like 'remove_remote'
end
end
end
@@ -2257,20 +2090,25 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:worktree_path) { File.join(repository_path, 'worktrees', 'delete-me') }
it 'cleans up the files' do
- repository.with_worktree(worktree_path, 'master', env: ENV) do
- FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
- # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
- # but the HEAD must be 40 characters long or git will ignore it.
- File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
+ create_worktree = %W[git -C #{repository_path} worktree add --detach #{worktree_path} master]
+ raise 'preparation failed' unless system(*create_worktree, err: '/dev/null')
- # git 2.16 fails with "fatal: bad object HEAD"
- expect { repository.rev_list(including: :all) }.to raise_error(Gitlab::Git::Repository::GitError)
+ FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
+ # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
+ # but the HEAD must be 40 characters long or git will ignore it.
+ File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
- repository.clean_stale_repository_files
+ # git 2.16 fails with "fatal: bad object HEAD"
+ expect(rev_list_all).to be false
- expect { repository.rev_list(including: :all) }.not_to raise_error
- expect(File.exist?(worktree_path)).to be_falsey
- end
+ repository.clean_stale_repository_files
+
+ expect(rev_list_all).to be true
+ expect(File.exist?(worktree_path)).to be_falsey
+ end
+
+ def rev_list_all
+ system(*%W[git -C #{repository_path} rev-list --all], out: '/dev/null', err: '/dev/null')
end
it 'increments a counter upon an error' do
@@ -2308,92 +2146,95 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
end
end
+ end
- describe '#squash' do
- let(:squash_id) { '1' }
- let(:branch_name) { 'fix' }
- let(:start_sha) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
- let(:end_sha) { '12d65c8dd2b2676fa3ac47d955accc085a37a9c1' }
+ describe '#squash' do
+ let(:squash_id) { '1' }
+ let(:branch_name) { 'fix' }
+ let(:start_sha) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ let(:end_sha) { '12d65c8dd2b2676fa3ac47d955accc085a37a9c1' }
- subject do
- opts = {
- branch: branch_name,
- start_sha: start_sha,
- end_sha: end_sha,
- author: user,
- message: 'Squash commit message'
- }
+ subject do
+ opts = {
+ branch: branch_name,
+ start_sha: start_sha,
+ end_sha: end_sha,
+ author: user,
+ message: 'Squash commit message'
+ }
+
+ repository.squash(user, squash_id, opts)
+ end
+
+ # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
+ skip 'sparse checkout' do
+ let(:expected_files) { %w(files files/js files/js/application.js) }
+
+ it 'checks out only the files in the diff' do
+ allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
+ m.call(*args) do
+ worktree_path = args[0]
+ files_pattern = File.join(worktree_path, '**', '*')
+ expected = expected_files.map do |path|
+ File.expand_path(path, worktree_path)
+ end
+
+ expect(Dir[files_pattern]).to eq(expected)
+ end
+ end
- repository.squash(user, squash_id, opts)
+ subject
end
- context 'sparse checkout', :skip_gitaly_mock do
- let(:expected_files) { %w(files files/js files/js/application.js) }
+ context 'when the diff contains a rename' do
+ let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged }
+ let(:end_sha) { new_commit_move_file(repo).oid }
+
+ after do
+ # Erase our commits so other tests get the original repo
+ repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
+ repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID)
+ end
- it 'checks out only the files in the diff' do
+ it 'does not include the renamed file in the sparse checkout' do
allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
m.call(*args) do
worktree_path = args[0]
files_pattern = File.join(worktree_path, '**', '*')
- expected = expected_files.map do |path|
- File.expand_path(path, worktree_path)
- end
- expect(Dir[files_pattern]).to eq(expected)
+ expect(Dir[files_pattern]).not_to include('CHANGELOG')
+ expect(Dir[files_pattern]).not_to include('encoding/CHANGELOG')
end
end
subject
end
-
- context 'when the diff contains a rename' do
- let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged }
- let(:end_sha) { new_commit_move_file(repo).oid }
-
- after do
- # Erase our commits so other tests get the original repo
- repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID)
- end
-
- it 'does not include the renamed file in the sparse checkout' do
- allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
- m.call(*args) do
- worktree_path = args[0]
- files_pattern = File.join(worktree_path, '**', '*')
-
- expect(Dir[files_pattern]).not_to include('CHANGELOG')
- expect(Dir[files_pattern]).not_to include('encoding/CHANGELOG')
- end
- end
-
- subject
- end
- end
end
+ end
- context 'with an ASCII-8BIT diff', :skip_gitaly_mock do
- let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" }
+ # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
+ skip 'with an ASCII-8BIT diff' do
+ let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" }
- it 'applies a ASCII-8BIT diff' do
- allow(repository).to receive(:run_git!).and_call_original
- allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
+ it 'applies a ASCII-8BIT diff' do
+ allow(repository).to receive(:run_git!).and_call_original
+ allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
- expect(subject).to match(/\h{40}/)
- end
+ expect(subject).to match(/\h{40}/)
end
+ end
- context 'with trailing whitespace in an invalid patch', :skip_gitaly_mock do
- let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" }
+ # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
+ skip 'with trailing whitespace in an invalid patch' do
+ let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" }
- it 'does not include whitespace warnings in the error' do
- allow(repository).to receive(:run_git!).and_call_original
- allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
+ it 'does not include whitespace warnings in the error' do
+ allow(repository).to receive(:run_git!).and_call_original
+ allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
- expect { subject }.to raise_error do |error|
- expect(error).to be_a(described_class::GitError)
- expect(error.message).not_to include('trailing whitespace')
- end
+ expect { subject }.to raise_error do |error|
+ expect(error).to be_a(described_class::GitError)
+ expect(error.message).not_to include('trailing whitespace')
end
end
end
diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb
deleted file mode 100644
index b752c3e8341..00000000000
--- a/spec/lib/gitlab/git/rev_list_spec.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::RevList do
- let(:repository) { create(:project, :repository).repository.raw }
- let(:rev_list) { described_class.new(repository, newrev: 'newrev') }
-
- def args_for_popen(args_list)
- [Gitlab.config.git.bin_path, 'rev-list', *args_list]
- end
-
- def stub_popen_rev_list(*additional_args, with_lazy_block: true, output:)
- repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.path }
-
- params = [
- args_for_popen(additional_args),
- repo_path,
- {},
- hash_including(lazy_block: with_lazy_block ? anything : nil)
- ]
-
- expect(repository).to receive(:popen).with(*params) do |*_, lazy_block:|
- output = lazy_block.call(output.lines.lazy.map(&:chomp)) if with_lazy_block
-
- [output, 0]
- end
- end
-
- context "#new_refs" do
- it 'calls out to `popen`' do
- stub_popen_rev_list('newrev', '--not', '--all', with_lazy_block: false, output: "sha1\nsha2")
-
- expect(rev_list.new_refs).to eq(%w[sha1 sha2])
- end
- end
-
- context '#new_objects' do
- it 'fetches list of newly pushed objects using rev-list' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.new_objects(&b) }.to yield_with_args(%w[sha1 sha2])
- end
-
- it 'can skip pathless objects' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2 path/to/file")
-
- expect { |b| rev_list.new_objects(require_path: true, &b) }.to yield_with_args(%w[sha2])
- end
-
- it 'can handle non utf-8 paths' do
- non_utf_char = [0x89].pack("c*").force_encoding("UTF-8")
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha2 πå†h/†ø/ƒîlé#{non_utf_char}\nsha1")
-
- rev_list.new_objects(require_path: true) do |object_ids|
- expect(object_ids.force).to eq(%w[sha2])
- end
- end
-
- it 'can yield a lazy enumerator' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
-
- rev_list.new_objects do |object_ids|
- expect(object_ids).to be_a Enumerator::Lazy
- end
- end
-
- it 'returns the result of the block when given' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
-
- objects = rev_list.new_objects do |object_ids|
- object_ids.first
- end
-
- expect(objects).to eq 'sha1'
- end
-
- it 'can accept list of references to exclude' do
- stub_popen_rev_list('newrev', '--not', 'master', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.new_objects(not_in: ['master'], &b) }.to yield_with_args(%w[sha1 sha2])
- end
-
- it 'handles empty list of references to exclude as listing all known objects' do
- stub_popen_rev_list('newrev', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.new_objects(not_in: [], &b) }.to yield_with_args(%w[sha1 sha2])
- end
- end
-
- context '#all_objects' do
- it 'fetches list of all pushed objects using rev-list' do
- stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2])
- end
- end
-end
diff --git a/spec/lib/gitlab/git/tag_spec.rb b/spec/lib/gitlab/git/tag_spec.rb
index be2f5bfb819..2d9db576a6c 100644
--- a/spec/lib/gitlab/git/tag_spec.rb
+++ b/spec/lib/gitlab/git/tag_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Tag, seed_helper: true do
+describe Gitlab::Git::Tag, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
shared_examples 'Gitlab::Git::Repository#tags' do
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 001e406a930..3792d6bf67b 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Tree, seed_helper: true do
+describe Gitlab::Git::Tree, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
context :repo do
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index b63658e1b3b..c5666e4ec61 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -6,6 +6,31 @@ describe Gitlab::Git::Wiki do
let(:project_wiki) { ProjectWiki.new(project, user) }
subject { project_wiki.wiki }
+ describe '#pages' do
+ before do
+ create_page('page1', 'content')
+ create_page('page2', 'content2')
+ end
+
+ after do
+ destroy_page('page1')
+ destroy_page('page2')
+ end
+
+ it 'returns all the pages' do
+ expect(subject.pages.count).to eq(2)
+ expect(subject.pages.first.title).to eq 'page1'
+ expect(subject.pages.last.title).to eq 'page2'
+ end
+
+ it 'returns only one page' do
+ pages = subject.pages(limit: 1)
+
+ expect(pages.count).to eq(1)
+ expect(pages.first.title).to eq 'page1'
+ end
+ end
+
describe '#page' do
before do
create_page('page1', 'content')
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index ff32025253a..dbd64c4bec0 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -13,14 +13,6 @@ describe Gitlab::GitAccess do
let(:authentication_abilities) { %i[read_project download_code push_code] }
let(:redirected_path) { nil }
let(:auth_result_type) { nil }
-
- let(:access) do
- described_class.new(actor, project,
- protocol, authentication_abilities: authentication_abilities,
- namespace_path: namespace_path, project_path: project_path,
- redirected_path: redirected_path, auth_result_type: auth_result_type)
- end
-
let(:changes) { '_any' }
let(:push_access_check) { access.check('git-receive-pack', changes) }
let(:pull_access_check) { access.check('git-upload-pack', changes) }
@@ -48,7 +40,7 @@ describe Gitlab::GitAccess do
before do
disable_protocol('http')
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'blocks http push and pull' do
@@ -113,7 +105,7 @@ describe Gitlab::GitAccess do
context 'when actor is a User' do
context 'when the User can read the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'allows push and pull access' do
@@ -254,7 +246,7 @@ describe Gitlab::GitAccess do
shared_examples '#check with a key that is not valid' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'key is too small' do
@@ -307,7 +299,7 @@ describe Gitlab::GitAccess do
describe '#add_project_moved_message!', :clean_gitlab_redis_shared_state do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when a redirect was not followed to find the project' do
@@ -335,7 +327,7 @@ describe Gitlab::GitAccess do
describe '#check_authentication_abilities!' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when download' do
@@ -381,7 +373,7 @@ describe Gitlab::GitAccess do
describe '#check_command_disabled!' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'over http' do
@@ -529,8 +521,8 @@ describe Gitlab::GitAccess do
end
describe '#check_download_access!' do
- it 'allows masters to pull' do
- project.add_master(user)
+ it 'allows maintainers to pull' do
+ project.add_maintainer(user)
expect { pull_access_check }.not_to raise_error
end
@@ -542,7 +534,7 @@ describe Gitlab::GitAccess do
end
it 'disallows blocked users to pull' do
- project.add_master(user)
+ project.add_maintainer(user)
user.block
expect { pull_access_check }.to raise_unauthorized('Your account has been blocked.')
@@ -724,10 +716,11 @@ describe Gitlab::GitAccess do
end
describe '#check_push_access!' do
+ let(:unprotected_branch) { 'unprotected_branch' }
+
before do
merge_into_protected_branch
end
- let(:unprotected_branch) { 'unprotected_branch' }
let(:changes) do
{ push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow",
@@ -741,26 +734,18 @@ describe Gitlab::GitAccess do
merge_into_protected_branch: "0b4bc9a #{merge_into_protected_branch} refs/heads/feature" }
end
- def stub_git_hooks
- # Running the `pre-receive` hook is expensive, and not necessary for this test.
- allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) do |service, &block|
- block.call(service)
- end
- end
-
def merge_into_protected_branch
@protected_branch_merge_commit ||= begin
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- stub_git_hooks
project.repository.add_branch(user, unprotected_branch, 'feature')
- target_branch = project.repository.lookup('feature')
+ rugged = project.repository.rugged
+ target_branch = rugged.rev_parse('feature')
source_branch = project.repository.create_file(
user,
'filename',
'This is the file content',
message: 'This is a good commit message',
branch_name: unprotected_branch)
- rugged = project.repository.rugged
author = { email: "email@example.com", time: Time.now, name: "Example Git User" }
merge_index = rugged.merge_commits(target_branch, source_branch)
@@ -785,7 +770,7 @@ describe Gitlab::GitAccess do
aggregate_failures do
matrix.each do |action, allowed|
- check = -> { access.send(:check_push_access!, changes[action]) }
+ check = -> { push_changes(changes[action]) }
if allowed
expect(&check).not_to raise_error,
@@ -812,7 +797,7 @@ describe Gitlab::GitAccess do
merge_into_protected_branch: true
},
- master: {
+ maintainer: {
push_new_branch: true,
push_master: true,
push_protected_branch: true,
@@ -917,7 +902,7 @@ describe Gitlab::GitAccess do
end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
- master: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
+ maintainer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
admin: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }))
end
end
@@ -989,7 +974,7 @@ describe Gitlab::GitAccess do
let(:project) { create(:project, :repository, :read_only) }
it 'denies push access' do
- project.add_master(user)
+ project.add_maintainer(user)
expect { push_access_check }.to raise_unauthorized('The repository is temporarily read-only. Please try again later.')
end
@@ -1119,9 +1104,9 @@ describe Gitlab::GitAccess do
it_behaves_like 'access after accepting terms'
end
- describe 'as a master of the project' do
+ describe 'as a maintainer of the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'access after accepting terms'
@@ -1152,6 +1137,17 @@ describe Gitlab::GitAccess do
private
+ def access
+ described_class.new(actor, project, protocol,
+ authentication_abilities: authentication_abilities,
+ namespace_path: namespace_path, project_path: project_path,
+ redirected_path: redirected_path, auth_result_type: auth_result_type)
+ end
+
+ def push_changes(changes)
+ access.check('git-receive-pack', changes)
+ end
+
def raise_unauthorized(message)
raise_error(Gitlab::GitAccess::UnauthorizedError, message)
end
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 7951cbe7b1d..54f2ea33f90 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -17,7 +17,7 @@ describe Gitlab::GitalyClient::CommitService do
repository: repository_message,
left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
right_commit_id: commit.id,
- collapse_diffs: true,
+ collapse_diffs: false,
enforce_limits: true,
**Gitlab::Git::DiffCollection.collection_limits.to_h
)
@@ -35,7 +35,7 @@ describe Gitlab::GitalyClient::CommitService do
repository: repository_message,
left_commit_id: Gitlab::Git::EMPTY_TREE_ID,
right_commit_id: initial_commit.id,
- collapse_diffs: true,
+ collapse_diffs: false,
enforce_limits: true,
**Gitlab::Git::DiffCollection.collection_limits.to_h
)
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 9709f1f5646..eaf64e3c9b4 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -1,10 +1,10 @@
require 'spec_helper'
describe Gitlab::GitalyClient::OperationService do
- let(:project) { create(:project) }
+ set(:project) { create(:project, :repository) }
let(:repository) { project.repository.raw }
let(:client) { described_class.new(repository) }
- let(:user) { create(:user) }
+ set(:user) { create(:user) }
let(:gitaly_user) { Gitlab::Git::User.from_gitlab(user).to_gitaly }
describe '#user_create_branch' do
@@ -53,6 +53,47 @@ describe Gitlab::GitalyClient::OperationService do
end
end
+ describe '#user_update_branch' do
+ let(:branch_name) { 'my-branch' }
+ let(:newrev) { '01e' }
+ let(:oldrev) { '01d' }
+ let(:request) do
+ Gitaly::UserUpdateBranchRequest.new(
+ repository: repository.gitaly_repository,
+ branch_name: branch_name,
+ newrev: newrev,
+ oldrev: oldrev,
+ user: gitaly_user
+ )
+ end
+ let(:response) { Gitaly::UserUpdateBranchResponse.new }
+
+ subject { client.user_update_branch(branch_name, user, newrev, oldrev) }
+
+ it 'sends a user_update_branch message' do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_update_branch).with(request, kind_of(Hash))
+ .and_return(response)
+
+ subject
+ end
+
+ context "when pre_receive_error is present" do
+ let(:response) do
+ Gitaly::UserUpdateBranchResponse.new(pre_receive_error: "something failed")
+ end
+
+ it "throws a PreReceive exception" do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_update_branch).with(request, kind_of(Hash))
+ .and_return(response)
+
+ expect { subject }.to raise_error(
+ Gitlab::Git::PreReceiveError, "something failed")
+ end
+ end
+ end
+
describe '#user_delete_branch' do
let(:branch_name) { 'my-branch' }
let(:request) do
@@ -110,18 +151,104 @@ describe Gitlab::GitalyClient::OperationService do
end
let(:response) { Gitaly::UserFFBranchResponse.new(branch_update: branch_update) }
- subject { client.user_ff_branch(user, source_sha, target_branch) }
-
- it 'sends a user_ff_branch message and returns a BranchUpdate object' do
+ before do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_ff_branch).with(request, kind_of(Hash))
.and_return(response)
+ end
+ subject { client.user_ff_branch(user, source_sha, target_branch) }
+
+ it 'sends a user_ff_branch message and returns a BranchUpdate object' do
expect(subject).to be_a(Gitlab::Git::OperationService::BranchUpdate)
expect(subject.newrev).to eq(source_sha)
expect(subject.repo_created).to be(false)
expect(subject.branch_created).to be(false)
end
+
+ context 'when the response has no branch_update' do
+ let(:response) { Gitaly::UserFFBranchResponse.new }
+
+ it { expect(subject).to be_nil }
+ end
+ end
+
+ shared_examples 'cherry pick and revert errors' do
+ context 'when a pre_receive_error is present' do
+ let(:response) { response_class.new(pre_receive_error: "something failed") }
+
+ it 'raises a PreReceiveError' do
+ expect { subject }.to raise_error(Gitlab::Git::PreReceiveError, "something failed")
+ end
+ end
+
+ context 'when a commit_error is present' do
+ let(:response) { response_class.new(commit_error: "something failed") }
+
+ it 'raises a CommitError' do
+ expect { subject }.to raise_error(Gitlab::Git::CommitError, "something failed")
+ end
+ end
+
+ context 'when a create_tree_error is present' do
+ let(:response) { response_class.new(create_tree_error: "something failed") }
+
+ it 'raises a CreateTreeError' do
+ expect { subject }.to raise_error(Gitlab::Git::Repository::CreateTreeError, "something failed")
+ end
+ end
+
+ context 'when branch_update is nil' do
+ let(:response) { response_class.new }
+
+ it { expect(subject).to be_nil }
+ end
+ end
+
+ describe '#user_cherry_pick' do
+ let(:response_class) { Gitaly::UserCherryPickResponse }
+
+ subject do
+ client.user_cherry_pick(
+ user: user,
+ commit: repository.commit,
+ branch_name: 'master',
+ message: 'Cherry-pick message',
+ start_branch_name: 'master',
+ start_repository: repository
+ )
+ end
+
+ before do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_cherry_pick).with(kind_of(Gitaly::UserCherryPickRequest), kind_of(Hash))
+ .and_return(response)
+ end
+
+ it_behaves_like 'cherry pick and revert errors'
+ end
+
+ describe '#user_revert' do
+ let(:response_class) { Gitaly::UserRevertResponse }
+
+ subject do
+ client.user_revert(
+ user: user,
+ commit: repository.commit,
+ branch_name: 'master',
+ message: 'Revert message',
+ start_branch_name: 'master',
+ start_repository: repository
+ )
+ end
+
+ before do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_revert).with(kind_of(Gitaly::UserRevertRequest), kind_of(Hash))
+ .and_return(response)
+ end
+
+ it_behaves_like 'cherry pick and revert errors'
end
describe '#user_squash' do
@@ -162,7 +289,7 @@ describe Gitlab::GitalyClient::OperationService do
Gitaly::UserSquashResponse.new(git_error: "something failed")
end
- it "throws a PreReceive exception" do
+ it "raises a GitError exception" do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_squash).with(request, kind_of(Hash))
.and_return(response)
@@ -171,5 +298,41 @@ describe Gitlab::GitalyClient::OperationService do
Gitlab::Git::Repository::GitError, "something failed")
end
end
+
+ describe '#user_commit_files' do
+ subject do
+ client.user_commit_files(
+ gitaly_user, 'my-branch', 'Commit files message', [], 'janedoe@example.com', 'Jane Doe',
+ 'master', repository)
+ end
+
+ before do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_commit_files).with(kind_of(Enumerator), kind_of(Hash))
+ .and_return(response)
+ end
+
+ context 'when a pre_receive_error is present' do
+ let(:response) { Gitaly::UserCommitFilesResponse.new(pre_receive_error: "something failed") }
+
+ it 'raises a PreReceiveError' do
+ expect { subject }.to raise_error(Gitlab::Git::PreReceiveError, "something failed")
+ end
+ end
+
+ context 'when an index_error is present' do
+ let(:response) { Gitaly::UserCommitFilesResponse.new(index_error: "something failed") }
+
+ it 'raises a PreReceiveError' do
+ expect { subject }.to raise_error(Gitlab::Git::Index::IndexError, "something failed")
+ end
+ end
+
+ context 'when branch_update is nil' do
+ let(:response) { Gitaly::UserCommitFilesResponse.new }
+
+ it { expect(subject).to be_nil }
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
index 257e4c50f2d..400d426c949 100644
--- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
@@ -18,6 +18,44 @@ describe Gitlab::GitalyClient::RefService do
end
end
+ describe '#remote_branches' do
+ let(:remote_name) { 'my_remote' }
+ subject { client.remote_branches(remote_name) }
+
+ it 'sends a find_all_remote_branches message' do
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_all_remote_branches)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return([])
+
+ subject
+ end
+
+ it 'concantes and returns the response branches as Gitlab::Git::Branch objects' do
+ target_commits = create_list(:gitaly_commit, 4)
+ response_branches = target_commits.each_with_index.map do |gitaly_commit, i|
+ Gitaly::Branch.new(name: "#{remote_name}/#{i}", target_commit: gitaly_commit)
+ end
+ response = [
+ Gitaly::FindAllRemoteBranchesResponse.new(branches: response_branches[0, 2]),
+ Gitaly::FindAllRemoteBranchesResponse.new(branches: response_branches[2, 2])
+ ]
+
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_all_remote_branches).and_return(response)
+
+ expect(subject.length).to be(response_branches.length)
+
+ response_branches.each_with_index do |gitaly_branch, i|
+ branch = subject[i]
+ commit = Gitlab::Git::Commit.new(repository, gitaly_branch.target_commit)
+
+ expect(branch.name).to eq(i.to_s) # It removes the `remote/` prefix
+ expect(branch.dereferenced_target).to eq(commit)
+ end
+ end
+ end
+
describe '#branch_names' do
it 'sends a find_all_branch_names message' do
expect_any_instance_of(Gitaly::RefService::Stub)
diff --git a/spec/lib/gitlab/gitaly_client/storage_service_spec.rb b/spec/lib/gitlab/gitaly_client/storage_service_spec.rb
new file mode 100644
index 00000000000..6c25e2d6ebd
--- /dev/null
+++ b/spec/lib/gitlab/gitaly_client/storage_service_spec.rb
@@ -0,0 +1,13 @@
+require 'spec_helper'
+
+describe Gitlab::GitalyClient::StorageService do
+ describe '#delete_all_repositories' do
+ let!(:project) { create(:project, :repository) }
+
+ it 'removes all repositories' do
+ described_class.new(project.repository_storage).delete_all_repositories
+
+ expect(project.repository.exists?).to be(false)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
index 6ad9f5ef766..5f67fe6b952 100644
--- a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
@@ -70,6 +70,15 @@ describe Gitlab::GitalyClient::WikiService do
subject
end
+ it 'sends a limit of 0 to wiki_get_all_pages' do
+ expect_any_instance_of(Gitaly::WikiService::Stub)
+ .to receive(:wiki_get_all_pages)
+ .with(gitaly_request_with_params(limit: 0), kind_of(Hash))
+ .and_return([].each)
+
+ subject
+ end
+
it 'concatenates the raw data and returns a pair of WikiPage and WikiPageVersion for each page' do
expect_any_instance_of(Gitaly::WikiService::Stub)
.to receive(:wiki_get_all_pages)
@@ -84,5 +93,18 @@ describe Gitlab::GitalyClient::WikiService do
expect(wiki_page_2.raw_data).to eq('cd')
expect(wiki_page_2_version.format).to eq('markdown')
end
+
+ context 'with limits' do
+ subject { client.get_all_pages(limit: 1) }
+
+ it 'sends a request with the limit' do
+ expect_any_instance_of(Gitaly::WikiService::Stub)
+ .to receive(:wiki_get_all_pages)
+ .with(gitaly_request_with_params(limit: 1), kind_of(Hash))
+ .and_return([].each)
+
+ subject
+ end
+ end
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 51fad6c6838..c51985f00a2 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
@@ -27,9 +27,9 @@ 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'),
- created_at: Time.zone.now,
- updated_at: Time.zone.now,
- merged_at: Time.zone.now
+ created_at: 1.second.ago,
+ updated_at: 1.second.ago,
+ merged_at: 1.second.ago
)
end
@@ -158,7 +158,6 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
expect(importer.repository_updates_counter)
.to receive(:increment)
- .with(project: project.path_with_namespace)
.and_call_original
Timecop.freeze do
diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb
index 50e8d7183ce..22ad88e28cb 100644
--- a/spec/lib/gitlab/gitlab_import/client_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/client_spec.rb
@@ -15,4 +15,88 @@ describe Gitlab::GitlabImport::Client do
expect(key).to be_kind_of(Symbol)
end
end
+
+ it 'uses membership and simple flags' do
+ stub_request('/api/v4/projects?membership=true&page=1&per_page=100&simple=true')
+
+ expect_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([])
+
+ expect(client.projects.to_a).to eq []
+ end
+
+ shared_examples 'pagination params' do
+ before do
+ allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([])
+ end
+
+ it 'allows page_limit param' do
+ allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return(element_list)
+
+ expect(client).to receive(:lazy_page_iterator).with(hash_including(page_limit: 2)).and_call_original
+
+ client.send(method, *args, page_limit: 2, per_page: 1).to_a
+ end
+
+ it 'allows per_page param' do
+ expect(client).to receive(:lazy_page_iterator).with(hash_including(per_page: 2)).and_call_original
+
+ client.send(method, *args, per_page: 2).to_a
+ end
+
+ it 'allows starting_page param' do
+ expect(client).to receive(:lazy_page_iterator).with(hash_including(starting_page: 3)).and_call_original
+
+ client.send(method, *args, starting_page: 3).to_a
+ end
+ end
+
+ describe '#projects' do
+ subject(:method) { :projects }
+ let(:args) { [] }
+ let(:element_list) { build_list(:project, 2) }
+
+ before do
+ stub_request('/api/v4/projects?membership=true&page=1&per_page=1&simple=true')
+ stub_request('/api/v4/projects?membership=true&page=2&per_page=1&simple=true')
+ stub_request('/api/v4/projects?membership=true&page=1&per_page=2&simple=true')
+ stub_request('/api/v4/projects?membership=true&page=3&per_page=100&simple=true')
+ end
+
+ it_behaves_like 'pagination params'
+ end
+
+ describe '#issues' do
+ subject(:method) { :issues }
+ let(:args) { [1] }
+ let(:element_list) { build_list(:issue, 2) }
+
+ before do
+ stub_request('/api/v4/projects/1/issues?page=1&per_page=1')
+ stub_request('/api/v4/projects/1/issues?page=2&per_page=1')
+ stub_request('/api/v4/projects/1/issues?page=1&per_page=2')
+ stub_request('/api/v4/projects/1/issues?page=3&per_page=100')
+ end
+
+ it_behaves_like 'pagination params'
+ end
+
+ describe '#issue_comments' do
+ subject(:method) { :issue_comments }
+ let(:args) { [1, 1] }
+ let(:element_list) { build_list(:note_on_issue, 2) }
+
+ before do
+ stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=1')
+ stub_request('/api/v4/projects/1/issues/1/notes?page=2&per_page=1')
+ stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=2')
+ stub_request('/api/v4/projects/1/issues/1/notes?page=3&per_page=100')
+ end
+
+ it_behaves_like 'pagination params'
+ end
+
+ def stub_request(path)
+ WebMock.stub_request(:get, "https://gitlab.com#{path}")
+ .to_return(status: 200)
+ end
end
diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb
index 017facd0f5e..031f57dbc65 100644
--- a/spec/lib/gitlab/google_code_import/importer_spec.rb
+++ b/spec/lib/gitlab/google_code_import/importer_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject { described_class.new(project) }
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
project.create_import_data(data: import_data)
end
diff --git a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
index 6fbffc38444..1a2c6ef25c4 100644
--- a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
+++ b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
@@ -141,7 +141,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
expect(invalid_gpg_signature.reload.verification_status).to eq 'unverified_key'
# InvalidGpgSignatureUpdater is called by the after_update hook
- user.update_attributes!(email: GpgHelpers::User1.emails.first)
+ user.update!(email: GpgHelpers::User1.emails.first)
expect(invalid_gpg_signature.reload).to have_attributes(
project: project,
@@ -166,7 +166,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
)
# InvalidGpgSignatureUpdater is called by the after_update hook
- user.update_attributes!(email: 'still.unrelated@example.com')
+ user.update!(email: 'still.unrelated@example.com')
expect(invalid_gpg_signature.reload).to have_attributes(
project: project,
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
new file mode 100644
index 00000000000..95bf7685ade
--- /dev/null
+++ b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
@@ -0,0 +1,103 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Authorize::AuthorizeResource do
+ let(:fake_class) do
+ Class.new do
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ attr_reader :user, :found_object
+
+ authorize :read_the_thing
+
+ def initialize(user, found_object)
+ @user, @found_object = user, found_object
+ end
+
+ def find_object
+ found_object
+ end
+
+ def current_user
+ user
+ end
+ end
+ end
+
+ let(:user) { build(:user) }
+ let(:project) { build(:project) }
+ subject(:loading_resource) { fake_class.new(user, project) }
+
+ context 'when the user is allowed to perform the action' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do
+ true
+ end
+ end
+
+ describe '#authorized_find' do
+ it 'returns the object' do
+ expect(loading_resource.authorized_find).to eq(project)
+ end
+ end
+
+ describe '#authorized_find!' do
+ it 'returns the object' do
+ expect(loading_resource.authorized_find!).to eq(project)
+ end
+ end
+
+ describe '#authorize!' do
+ it 'does not raise an error' do
+ expect { loading_resource.authorize!(project) }.not_to raise_error
+ end
+ end
+
+ describe '#authorized?' do
+ it 'is true' do
+ expect(loading_resource.authorized?(project)).to be(true)
+ end
+ end
+ end
+
+ context 'when the user is not allowed to perform the action' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do
+ false
+ end
+ end
+
+ describe '#authorized_find' do
+ it 'returns `nil`' do
+ expect(loading_resource.authorized_find).to be_nil
+ end
+ end
+
+ describe '#authorized_find!' do
+ it 'raises an error' do
+ expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ describe '#authorize!' do
+ it 'does not raise an error' do
+ expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ describe '#authorized?' do
+ it 'is false' do
+ expect(loading_resource.authorized?(project)).to be(false)
+ end
+ end
+ end
+
+ context 'when the class does not define #find_object' do
+ let(:fake_class) do
+ Class.new { include Gitlab::Graphql::Authorize::AuthorizeResource }
+ end
+
+ it 'raises a comprehensive error message' do
+ expect { fake_class.new.find_object }.to raise_error(/Implement #find_object in #{fake_class.name}/)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/authorize_spec.rb b/spec/lib/gitlab/graphql/authorize_spec.rb
new file mode 100644
index 00000000000..9c17a3b0e4b
--- /dev/null
+++ b/spec/lib/gitlab/graphql/authorize_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Authorize do
+ describe '#authorize' do
+ it 'adds permissions from subclasses to those of superclasses when used on classes' do
+ base_class = Class.new do
+ extend Gitlab::Graphql::Authorize
+
+ authorize :base_authorization
+ end
+ sub_class = Class.new(base_class) do
+ authorize :sub_authorization
+ end
+
+ expect(base_class.required_permissions).to contain_exactly(:base_authorization)
+ expect(sub_class.required_permissions)
+ .to contain_exactly(:base_authorization, :sub_authorization)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb b/spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb
new file mode 100644
index 00000000000..96615ae80de
--- /dev/null
+++ b/spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb
@@ -0,0 +1,112 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Connections::KeysetConnection do
+ let(:nodes) { Project.all.order(id: :asc) }
+ let(:arguments) { {} }
+ subject(:connection) do
+ described_class.new(nodes, arguments, max_page_size: 3)
+ end
+
+ def encoded_property(value)
+ Base64.strict_encode64(value.to_s)
+ end
+
+ describe '#cursor_from_nodes' do
+ let(:project) { create(:project) }
+
+ it 'returns an encoded ID' do
+ expect(connection.cursor_from_node(project))
+ .to eq(encoded_property(project.id))
+ end
+
+ context 'when an order was specified' do
+ let(:nodes) { Project.order(:updated_at) }
+
+ it 'returns the encoded value of the order' do
+ expect(connection.cursor_from_node(project))
+ .to eq(encoded_property(project.updated_at))
+ end
+ end
+ end
+
+ describe '#sliced_nodes' do
+ let(:projects) { create_list(:project, 4) }
+
+ context 'when before is passed' do
+ let(:arguments) { { before: encoded_property(projects[1].id) } }
+
+ it 'only returns the project before the selected one' do
+ expect(subject.sliced_nodes).to contain_exactly(projects.first)
+ end
+
+ context 'when the sort order is descending' do
+ let(:nodes) { Project.all.order(id: :desc) }
+
+ it 'returns the correct nodes' do
+ expect(subject.sliced_nodes).to contain_exactly(*projects[2..-1])
+ end
+ end
+ end
+
+ context 'when after is passed' do
+ let(:arguments) { { after: encoded_property(projects[1].id) } }
+
+ it 'only returns the project before the selected one' do
+ expect(subject.sliced_nodes).to contain_exactly(*projects[2..-1])
+ end
+
+ context 'when the sort order is descending' do
+ let(:nodes) { Project.all.order(id: :desc) }
+
+ it 'returns the correct nodes' do
+ expect(subject.sliced_nodes).to contain_exactly(projects.first)
+ end
+ end
+ end
+
+ context 'when both before and after are passed' do
+ let(:arguments) do
+ {
+ after: encoded_property(projects[1].id),
+ before: encoded_property(projects[3].id)
+ }
+ end
+
+ it 'returns the expected set' do
+ expect(subject.sliced_nodes).to contain_exactly(projects[2])
+ end
+ end
+ end
+
+ describe '#paged_nodes' do
+ let!(:projects) { create_list(:project, 5) }
+
+ it 'returns the collection limited to max page size' do
+ expect(subject.paged_nodes.size).to eq(3)
+ end
+
+ context 'when `first` is passed' do
+ let(:arguments) { { first: 2 } }
+
+ it 'returns only the first elements' do
+ expect(subject.paged_nodes).to contain_exactly(projects.first, projects.second)
+ end
+ end
+
+ context 'when `last` is passed' do
+ let(:arguments) { { last: 2 } }
+
+ it 'returns only the last elements' do
+ expect(subject.paged_nodes).to contain_exactly(projects[3], projects[4])
+ end
+ end
+
+ context 'when both are passed' do
+ let(:arguments) { { first: 2, last: 2 } }
+
+ it 'raises an error' do
+ expect { subject.paged_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphs/commits_spec.rb b/spec/lib/gitlab/graphs/commits_spec.rb
index b2084f56640..530d4a981bf 100644
--- a/spec/lib/gitlab/graphs/commits_spec.rb
+++ b/spec/lib/gitlab/graphs/commits_spec.rb
@@ -29,7 +29,7 @@ describe Gitlab::Graphs::Commits do
context 'with commits from yesterday and today' do
subject { described_class.new([commit2, commit1_yesterday]) }
describe '#commit_per_day' do
- it { expect(subject.commit_per_day).to eq 1 }
+ it { expect(subject.commit_per_day).to eq 1.0 }
end
describe '#duration' do
diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
index 813ae43b4d3..7eac2cacb90 100644
--- a/spec/lib/gitlab/hashed_storage/migrator_spec.rb
+++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
@@ -65,7 +65,7 @@ describe Gitlab::HashedStorage::Migrator do
end
it 'migrate project' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
subject.migrate(project)
end
diff --git a/spec/lib/gitlab/hook_data/base_builder_spec.rb b/spec/lib/gitlab/hook_data/base_builder_spec.rb
new file mode 100644
index 00000000000..a921dd766c3
--- /dev/null
+++ b/spec/lib/gitlab/hook_data/base_builder_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe Gitlab::HookData::BaseBuilder do
+ describe '#absolute_image_urls' do
+ let(:subclass) do
+ Class.new(described_class) do
+ public :absolute_image_urls
+ end
+ end
+
+ subject { subclass.new(nil) }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where do
+ {
+ 'relative image URL' => {
+ input: '![an image](foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/foo.png)"
+ },
+ 'HTTP URL' => {
+ input: '![an image](http://example.com/foo.png)',
+ output: '![an image](http://example.com/foo.png)'
+ },
+ 'HTTPS URL' => {
+ input: '![an image](https://example.com/foo.png)',
+ output: '![an image](https://example.com/foo.png)'
+ },
+ 'protocol-relative URL' => {
+ input: '![an image](//example.com/foo.png)',
+ output: '![an image](//example.com/foo.png)'
+ },
+ 'URL reference by title' => {
+ input: "![foo]\n\n[foo]: foo.png",
+ output: "![foo]\n\n[foo]: foo.png"
+ },
+ 'URL reference by label' => {
+ input: "![][foo]\n\n[foo]: foo.png",
+ output: "![][foo]\n\n[foo]: foo.png"
+ },
+ 'in Markdown inline code block' => {
+ input: '`![an image](foo.png)`',
+ output: "`![an image](#{Gitlab.config.gitlab.url}/foo.png)`"
+ },
+ 'in HTML tag on the same line' => {
+ input: '<p>![an image](foo.png)</p>',
+ output: "<p>![an image](#{Gitlab.config.gitlab.url}/foo.png)</p>"
+ },
+ 'in Markdown multi-line code block' => {
+ input: "```\n![an image](foo.png)\n```",
+ output: "```\n![an image](foo.png)\n```"
+ },
+ 'in HTML tag on different lines' => {
+ input: "<p>\n![an image](foo.png)\n</p>",
+ output: "<p>\n![an image](foo.png)\n</p>"
+ }
+ }
+ end
+
+ with_them do
+ it { expect(subject.absolute_image_urls(input)).to eq(output) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
index 506b2c0be20..60093474f8a 100644
--- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
@@ -40,5 +40,14 @@ describe Gitlab::HookData::IssueBuilder do
expect(data).to include(:human_total_time_spent)
expect(data).to include(:assignee_ids)
end
+
+ context 'when the issue has an image in the description' do
+ let(:issue_with_description) { create(:issue, description: 'test![Issue_Image](/uploads/abc/Issue_Image.png)') }
+ let(:builder) { described_class.new(issue_with_description) }
+
+ it 'sets the image to use an absolute URL' do
+ expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
index b61614e4790..dd586af6118 100644
--- a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
@@ -56,5 +56,14 @@ describe Gitlab::HookData::MergeRequestBuilder do
expect(data).to include(:human_time_estimate)
expect(data).to include(:human_total_time_spent)
end
+
+ context 'when the MR has an image in the description' do
+ let(:mr_with_description) { create(:merge_request, description: 'test![Issue_Image](/uploads/abc/Issue_Image.png)') }
+ let(:builder) { described_class.new(mr_with_description) }
+
+ it 'sets the image to use an absolute URL' do
+ expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/http_io_spec.rb b/spec/lib/gitlab/http_io_spec.rb
new file mode 100644
index 00000000000..788bddb8f59
--- /dev/null
+++ b/spec/lib/gitlab/http_io_spec.rb
@@ -0,0 +1,318 @@
+require 'spec_helper'
+
+describe Gitlab::HttpIO do
+ include HttpIOHelpers
+
+ let(:http_io) { described_class.new(url, size) }
+
+ let(:url) { 'http://object-storage/trace' }
+ let(:file_path) { expand_fixture_path('trace/sample_trace') }
+ let(:file_body) { File.read(file_path).force_encoding(Encoding::BINARY) }
+ let(:size) { File.size(file_path) }
+
+ describe '#close' do
+ subject { http_io.close }
+
+ it { is_expected.to be_nil }
+ end
+
+ describe '#binmode' do
+ subject { http_io.binmode }
+
+ it { is_expected.to be_nil }
+ end
+
+ describe '#binmode?' do
+ subject { http_io.binmode? }
+
+ it { is_expected.to be_truthy }
+ end
+
+ describe '#path' do
+ subject { http_io.path }
+
+ it { is_expected.to be_nil }
+ end
+
+ describe '#url' do
+ subject { http_io.url }
+
+ it { is_expected.to eq(url) }
+ end
+
+ describe '#seek' do
+ subject { http_io.seek(pos, where) }
+
+ context 'when moves pos to end of the file' do
+ let(:pos) { 0 }
+ let(:where) { IO::SEEK_END }
+
+ it { is_expected.to eq(size) }
+ end
+
+ context 'when moves pos to middle of the file' do
+ let(:pos) { size / 2 }
+ let(:where) { IO::SEEK_SET }
+
+ it { is_expected.to eq(size / 2) }
+ end
+
+ context 'when moves pos around' do
+ it 'matches the result' do
+ expect(http_io.seek(0)).to eq(0)
+ expect(http_io.seek(100, IO::SEEK_CUR)).to eq(100)
+ expect { http_io.seek(size + 1, IO::SEEK_CUR) }.to raise_error('new position is outside of file')
+ end
+ end
+ end
+
+ describe '#eof?' do
+ subject { http_io.eof? }
+
+ context 'when current pos is at end of the file' do
+ before do
+ http_io.seek(size, IO::SEEK_SET)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when current pos is not at end of the file' do
+ before do
+ http_io.seek(0, IO::SEEK_SET)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#each_line' do
+ subject { http_io.each_line }
+
+ let(:string_io) { StringIO.new(file_body) }
+
+ before do
+ stub_remote_url_206(url, file_path)
+ end
+
+ it 'yields lines' do
+ expect { |b| http_io.each_line(&b) }.to yield_successive_args(*string_io.each_line.to_a)
+ end
+
+ context 'when buckets on GCS' do
+ context 'when BUFFER_SIZE is larger than file size' do
+ before do
+ stub_remote_url_200(url, file_path)
+ set_larger_buffer_size_than(size)
+ end
+
+ it 'calls get_chunk only once' do
+ expect_any_instance_of(Net::HTTP).to receive(:request).once.and_call_original
+
+ http_io.each_line { |line| }
+ end
+ end
+ end
+ end
+
+ describe '#read' do
+ subject { http_io.read(length) }
+
+ context 'when there are no network issue' do
+ before do
+ stub_remote_url_206(url, file_path)
+ end
+
+ context 'when read whole size' do
+ let(:length) { nil }
+
+ context 'when BUFFER_SIZE is smaller than file size' do
+ before do
+ set_smaller_buffer_size_than(size)
+ end
+
+ it 'reads a trace' do
+ is_expected.to eq(file_body)
+ end
+ end
+
+ context 'when BUFFER_SIZE is larger than file size' do
+ before do
+ set_larger_buffer_size_than(size)
+ end
+
+ it 'reads a trace' do
+ is_expected.to eq(file_body)
+ end
+ end
+ end
+
+ context 'when read only first 100 bytes' do
+ let(:length) { 100 }
+
+ context 'when BUFFER_SIZE is smaller than file size' do
+ before do
+ set_smaller_buffer_size_than(size)
+ end
+
+ it 'reads a trace' do
+ is_expected.to eq(file_body[0, length])
+ end
+ end
+
+ context 'when BUFFER_SIZE is larger than file size' do
+ before do
+ set_larger_buffer_size_than(size)
+ end
+
+ it 'reads a trace' do
+ is_expected.to eq(file_body[0, length])
+ end
+ end
+ end
+
+ context 'when tries to read oversize' do
+ let(:length) { size + 1000 }
+
+ context 'when BUFFER_SIZE is smaller than file size' do
+ before do
+ set_smaller_buffer_size_than(size)
+ end
+
+ it 'reads a trace' do
+ is_expected.to eq(file_body)
+ end
+ end
+
+ context 'when BUFFER_SIZE is larger than file size' do
+ before do
+ set_larger_buffer_size_than(size)
+ end
+
+ it 'reads a trace' do
+ is_expected.to eq(file_body)
+ end
+ end
+ end
+
+ context 'when tries to read 0 bytes' do
+ let(:length) { 0 }
+
+ context 'when BUFFER_SIZE is smaller than file size' do
+ before do
+ set_smaller_buffer_size_than(size)
+ end
+
+ it 'reads a trace' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when BUFFER_SIZE is larger than file size' do
+ before do
+ set_larger_buffer_size_than(size)
+ end
+
+ it 'reads a trace' do
+ is_expected.to be_empty
+ end
+ end
+ end
+ end
+
+ context 'when there is anetwork issue' do
+ let(:length) { nil }
+
+ before do
+ stub_remote_url_500(url)
+ end
+
+ it 'reads a trace' do
+ expect { subject }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
+ end
+ end
+ end
+
+ describe '#readline' do
+ subject { http_io.readline }
+
+ let(:string_io) { StringIO.new(file_body) }
+
+ before do
+ stub_remote_url_206(url, file_path)
+ end
+
+ shared_examples 'all line matching' do
+ it 'reads a line' do
+ (0...file_body.lines.count).each do
+ expect(http_io.readline).to eq(string_io.readline)
+ end
+ end
+ end
+
+ context 'when there is anetwork issue' do
+ let(:length) { nil }
+
+ before do
+ stub_remote_url_500(url)
+ end
+
+ it 'reads a trace' do
+ expect { subject }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
+ end
+ end
+
+ context 'when BUFFER_SIZE is smaller than file size' do
+ before do
+ set_smaller_buffer_size_than(size)
+ end
+
+ it_behaves_like 'all line matching'
+ end
+
+ context 'when BUFFER_SIZE is larger than file size' do
+ before do
+ set_larger_buffer_size_than(size)
+ end
+
+ it_behaves_like 'all line matching'
+ end
+
+ context 'when pos is at middle of the file' do
+ before do
+ set_smaller_buffer_size_than(size)
+
+ http_io.seek(size / 2)
+ string_io.seek(size / 2)
+ end
+
+ it 'reads from pos' do
+ expect(http_io.readline).to eq(string_io.readline)
+ end
+ end
+ end
+
+ describe '#write' do
+ subject { http_io.write(nil) }
+
+ it { expect { subject }.to raise_error(NotImplementedError) }
+ end
+
+ describe '#truncate' do
+ subject { http_io.truncate(nil) }
+
+ it { expect { subject }.to raise_error(NotImplementedError) }
+ end
+
+ describe '#flush' do
+ subject { http_io.flush }
+
+ it { expect { subject }.to raise_error(NotImplementedError) }
+ end
+
+ describe '#present?' do
+ subject { http_io.present? }
+
+ it { is_expected.to be_truthy }
+ end
+end
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb
new file mode 100644
index 00000000000..5059d68e54b
--- /dev/null
+++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
+ let!(:service) { described_class.new }
+ let!(:project) { create(:project, :with_object_export) }
+ let(:shared) { project.import_export_shared }
+ let!(:user) { create(:user) }
+
+ describe '#execute' do
+ before do
+ allow(service).to receive(:strategy_execute)
+ stub_feature_flags(import_export_object_storage: true)
+ end
+
+ it 'returns if project exported file is not found' do
+ allow(project).to receive(:export_project_object_exists?).and_return(false)
+
+ expect(service).not_to receive(:strategy_execute)
+
+ service.execute(user, project)
+ end
+
+ it 'creates a lock file in the export dir' do
+ allow(service).to receive(:delete_after_export_lock)
+
+ service.execute(user, project)
+
+ expect(lock_path_exist?).to be_truthy
+ end
+
+ context 'when the method succeeds' do
+ it 'removes the lock file' do
+ service.execute(user, project)
+
+ expect(lock_path_exist?).to be_falsey
+ end
+ end
+
+ context 'when the method fails' do
+ before do
+ allow(service).to receive(:strategy_execute).and_call_original
+ end
+
+ context 'when validation fails' do
+ before do
+ allow(service).to receive(:invalid?).and_return(true)
+ end
+
+ it 'does not create the lock file' do
+ expect(service).not_to receive(:create_or_update_after_export_lock)
+
+ service.execute(user, project)
+ end
+
+ it 'does not execute main logic' do
+ expect(service).not_to receive(:strategy_execute)
+
+ service.execute(user, project)
+ end
+
+ it 'logs validation errors in shared context' do
+ expect(service).to receive(:log_validation_errors)
+
+ service.execute(user, project)
+ end
+ end
+
+ context 'when an exception is raised' do
+ it 'removes the lock' do
+ expect { service.execute(user, project) }.to raise_error(NotImplementedError)
+
+ expect(lock_path_exist?).to be_falsey
+ end
+ end
+ end
+ end
+
+ describe '#log_validation_errors' do
+ it 'add the message to the shared context' do
+ errors = %w(test_message test_message2)
+
+ allow(service).to receive(:invalid?).and_return(true)
+ allow(service.errors).to receive(:full_messages).and_return(errors)
+
+ expect(shared).to receive(:add_error_message).twice.and_call_original
+
+ service.execute(user, project)
+
+ expect(shared.errors).to eq errors
+ end
+ end
+
+ describe '#to_json' do
+ it 'adds the current strategy class to the serialized attributes' do
+ params = { param1: 1 }
+ result = params.merge(klass: described_class.to_s).to_json
+
+ expect(described_class.new(params).to_json).to eq result
+ end
+ end
+
+ def lock_path_exist?
+ File.exist?(described_class.lock_file_path(project))
+ end
+end
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
index ed54d87de4a..566b7f46c87 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
@@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
describe '#execute' do
before do
allow(service).to receive(:strategy_execute)
+ stub_feature_flags(import_export_object_storage: false)
end
it 'returns if project exported file is not found' do
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
index 5fe57d9987b..7f2e0a4ee2c 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
@@ -24,13 +24,34 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
end
describe '#execute' do
- it 'removes the exported project file after the upload' do
- allow(strategy).to receive(:send_file)
- allow(strategy).to receive(:handle_response_error)
+ context 'without object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: false)
+ end
+
+ it 'removes the exported project file after the upload' do
+ allow(strategy).to receive(:send_file)
+ allow(strategy).to receive(:handle_response_error)
+
+ expect(project).to receive(:remove_exported_project_file)
+
+ strategy.execute(user, project)
+ end
+ end
+
+ context 'with object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ end
- expect(project).to receive(:remove_exported_project_file)
+ it 'removes the exported project file after the upload' do
+ allow(strategy).to receive(:send_file)
+ allow(strategy).to receive(:handle_response_error)
- strategy.execute(user, project)
+ expect(project).to receive(:remove_exported_project_file)
+
+ strategy.execute(user, project)
+ 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 2ea66479c1b..e9a1932407d 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -7,6 +7,7 @@ issues:
- updated_by
- milestone
- notes
+- resource_label_events
- label_links
- labels
- last_edited_by
@@ -76,6 +77,7 @@ merge_requests:
- updated_by
- milestone
- notes
+- resource_label_events
- label_links
- labels
- last_edited_by
@@ -87,6 +89,7 @@ merge_requests:
- merge_request_diff
- events
- merge_requests_closing_issues
+- cached_closes_issues
- metrics
- timelogs
- head_pipeline
@@ -211,6 +214,7 @@ project:
- slack_service
- microsoft_teams_service
- mattermost_service
+- hangouts_chat_service
- buildkite_service
- bamboo_service
- teamcity_service
@@ -293,6 +297,8 @@ project:
- deploy_tokens
- settings
- ci_cd_settings
+- import_export_upload
+- repository_languages
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
index 2223f163177..90e6d653d34 100644
--- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AvatarSaver do
before do
FileUtils.mkdir_p("#{shared.export_path}/avatar/")
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_feature_flags(import_export_object_storage: false)
end
after do
diff --git a/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb b/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb
new file mode 100644
index 00000000000..287745eb40e
--- /dev/null
+++ b/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::FileImporter do
+ let(:shared) { Gitlab::ImportExport::Shared.new(nil) }
+ let(:storage_path) { "#{Dir.tmpdir}/file_importer_spec" }
+ let(:valid_file) { "#{shared.export_path}/valid.json" }
+ let(:symlink_file) { "#{shared.export_path}/invalid.json" }
+ let(:hidden_symlink_file) { "#{shared.export_path}/.hidden" }
+ let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" }
+ let(:evil_symlink_file) { "#{shared.export_path}/.\nevil" }
+
+ before do
+ stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(storage_path)
+ allow_any_instance_of(Gitlab::ImportExport::CommandLineUtil).to receive(:untar_zxf).and_return(true)
+ allow_any_instance_of(Gitlab::ImportExport::Shared).to receive(:relative_archive_path).and_return('test')
+ allow(SecureRandom).to receive(:hex).and_return('abcd')
+ setup_files
+ end
+
+ after do
+ FileUtils.rm_rf(storage_path)
+ end
+
+ context 'normal run' do
+ before do
+ described_class.import(project: build(:project), archive_file: '', shared: shared)
+ end
+
+ it 'removes symlinks in root folder' do
+ expect(File.exist?(symlink_file)).to be false
+ end
+
+ it 'removes hidden symlinks in root folder' do
+ expect(File.exist?(hidden_symlink_file)).to be false
+ end
+
+ it 'removes evil symlinks in root folder' do
+ expect(File.exist?(evil_symlink_file)).to be false
+ end
+
+ it 'removes symlinks in subfolders' do
+ expect(File.exist?(subfolder_symlink_file)).to be false
+ end
+
+ it 'does not remove a valid file' do
+ expect(File.exist?(valid_file)).to be true
+ end
+
+ it 'creates the file in the right subfolder' do
+ expect(shared.export_path).to include('test/abcd')
+ end
+ end
+
+ context 'error' do
+ before do
+ allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError)
+ described_class.import(project: build(:project), archive_file: '', shared: shared)
+ end
+
+ it 'removes symlinks in root folder' do
+ expect(File.exist?(symlink_file)).to be false
+ end
+
+ it 'removes hidden symlinks in root folder' do
+ expect(File.exist?(hidden_symlink_file)).to be false
+ end
+
+ it 'removes symlinks in subfolders' do
+ expect(File.exist?(subfolder_symlink_file)).to be false
+ end
+
+ it 'does not remove a valid file' do
+ expect(File.exist?(valid_file)).to be true
+ end
+ end
+
+ def setup_files
+ FileUtils.mkdir_p("#{shared.export_path}/subfolder/")
+ FileUtils.touch(valid_file)
+ FileUtils.ln_s(valid_file, symlink_file)
+ FileUtils.ln_s(valid_file, subfolder_symlink_file)
+ FileUtils.ln_s(valid_file, hidden_symlink_file)
+ FileUtils.ln_s(valid_file, evil_symlink_file)
+ end
+end
diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb
index 58b9fb06cc5..78fccdf1dfc 100644
--- a/spec/lib/gitlab/import_export/file_importer_spec.rb
+++ b/spec/lib/gitlab/import_export/file_importer_spec.rb
@@ -7,6 +7,7 @@ describe Gitlab::ImportExport::FileImporter do
let(:symlink_file) { "#{shared.export_path}/invalid.json" }
let(:hidden_symlink_file) { "#{shared.export_path}/.hidden" }
let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" }
+ let(:evil_symlink_file) { "#{shared.export_path}/.\nevil" }
before do
stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
@@ -23,7 +24,7 @@ describe Gitlab::ImportExport::FileImporter do
context 'normal run' do
before do
- described_class.import(archive_file: '', shared: shared)
+ described_class.import(project: nil, archive_file: '', shared: shared)
end
it 'removes symlinks in root folder' do
@@ -34,6 +35,10 @@ describe Gitlab::ImportExport::FileImporter do
expect(File.exist?(hidden_symlink_file)).to be false
end
+ it 'removes evil symlinks in root folder' do
+ expect(File.exist?(evil_symlink_file)).to be false
+ end
+
it 'removes symlinks in subfolders' do
expect(File.exist?(subfolder_symlink_file)).to be false
end
@@ -50,7 +55,7 @@ describe Gitlab::ImportExport::FileImporter do
context 'error' do
before do
allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError)
- described_class.import(archive_file: '', shared: shared)
+ described_class.import(project: nil, archive_file: '', shared: shared)
end
it 'removes symlinks in root folder' do
@@ -75,5 +80,7 @@ describe Gitlab::ImportExport::FileImporter do
FileUtils.touch(valid_file)
FileUtils.ln_s(valid_file, symlink_file)
FileUtils.ln_s(valid_file, subfolder_symlink_file)
+ FileUtils.ln_s(valid_file, hidden_symlink_file)
+ FileUtils.ln_s(valid_file, evil_symlink_file)
end
end
diff --git a/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb
new file mode 100644
index 00000000000..6a803c48b34
--- /dev/null
+++ b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::GroupProjectObjectBuilder do
+ let(:project) do
+ create(:project,
+ :builds_disabled,
+ :issues_disabled,
+ name: 'project',
+ path: 'project',
+ group: create(:group))
+ end
+
+ context 'labels' do
+ it 'finds the right group label' do
+ group_label = create(:group_label, 'name': 'group label', 'group': project.group)
+
+ expect(described_class.build(Label,
+ 'title' => 'group label',
+ 'project' => project,
+ 'group' => project.group)).to eq(group_label)
+ end
+
+ it 'creates a new label' do
+ label = described_class.build(Label,
+ 'title' => 'group label',
+ 'project' => project,
+ 'group' => project.group)
+
+ expect(label.persisted?).to be true
+ end
+ end
+
+ context 'milestones' do
+ it 'finds the right group milestone' do
+ milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group)
+
+ expect(described_class.build(Milestone,
+ 'title' => 'group milestone',
+ 'project' => project,
+ 'group' => project.group)).to eq(milestone)
+ end
+
+ it 'creates a new milestone' do
+ milestone = described_class.build(Milestone,
+ 'title' => 'group milestone',
+ 'project' => project,
+ 'group' => project.group)
+
+ expect(milestone.persisted?).to be true
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/importer_object_storage_spec.rb b/spec/lib/gitlab/import_export/importer_object_storage_spec.rb
new file mode 100644
index 00000000000..24a994b3611
--- /dev/null
+++ b/spec/lib/gitlab/import_export/importer_object_storage_spec.rb
@@ -0,0 +1,115 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::Importer do
+ let(:user) { create(:user) }
+ let(:test_path) { "#{Dir.tmpdir}/importer_spec" }
+ let(:shared) { project.import_export_shared }
+ let(:project) { create(:project) }
+ let(:import_file) { fixture_file_upload('spec/features/projects/import_export/test_project_export.tar.gz') }
+
+ subject(:importer) { described_class.new(project) }
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path)
+ allow_any_instance_of(Gitlab::ImportExport::FileImporter).to receive(:remove_import_file)
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(shared.export_path)
+ ImportExportUpload.create(project: project, import_file: import_file)
+ end
+
+ after do
+ FileUtils.rm_rf(test_path)
+ end
+
+ describe '#execute' do
+ it 'succeeds' do
+ importer.execute
+
+ expect(shared.errors).to be_empty
+ end
+
+ it 'extracts the archive' do
+ expect(Gitlab::ImportExport::FileImporter).to receive(:import).and_call_original
+
+ importer.execute
+ end
+
+ it 'checks the version' do
+ expect(Gitlab::ImportExport::VersionChecker).to receive(:check!).and_call_original
+
+ importer.execute
+ end
+
+ context 'all restores are executed' do
+ [
+ Gitlab::ImportExport::AvatarRestorer,
+ Gitlab::ImportExport::RepoRestorer,
+ Gitlab::ImportExport::WikiRestorer,
+ Gitlab::ImportExport::UploadsRestorer,
+ Gitlab::ImportExport::LfsRestorer,
+ Gitlab::ImportExport::StatisticsRestorer
+ ].each do |restorer|
+ it "calls the #{restorer}" do
+ fake_restorer = double(restorer.to_s)
+
+ expect(fake_restorer).to receive(:restore).and_return(true).at_least(1)
+ expect(restorer).to receive(:new).and_return(fake_restorer).at_least(1)
+
+ importer.execute
+ end
+ end
+
+ it 'restores the ProjectTree' do
+ expect(Gitlab::ImportExport::ProjectTreeRestorer).to receive(:new).and_call_original
+
+ importer.execute
+ end
+
+ it 'removes the import file' do
+ expect(importer).to receive(:remove_import_file).and_call_original
+
+ importer.execute
+
+ expect(project.import_export_upload.import_file&.file).to be_nil
+ end
+ end
+
+ context 'when project successfully restored' do
+ let!(:existing_project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:project, namespace: user.namespace, name: 'whatever', path: 'whatever') }
+
+ before do
+ restorers = double(:restorers, all?: true)
+
+ allow(subject).to receive(:import_file).and_return(true)
+ allow(subject).to receive(:check_version!).and_return(true)
+ allow(subject).to receive(:restorers).and_return(restorers)
+ allow(project).to receive(:import_data).and_return(double(data: { 'original_path' => existing_project.path }))
+ end
+
+ context 'when import_data' do
+ context 'has original_path' do
+ it 'overwrites existing project' do
+ expect_any_instance_of(::Projects::OverwriteProjectService).to receive(:execute).with(existing_project)
+
+ subject.execute
+ end
+ end
+
+ context 'has not original_path' do
+ before do
+ allow(project).to receive(:import_data).and_return(double(data: {}))
+ end
+
+ it 'does not call the overwrite service' do
+ expect_any_instance_of(::Projects::OverwriteProjectService).not_to receive(:execute).with(existing_project)
+
+ subject.execute
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb
index c074e61da26..f07946824c4 100644
--- a/spec/lib/gitlab/import_export/importer_spec.rb
+++ b/spec/lib/gitlab/import_export/importer_spec.rb
@@ -10,9 +10,10 @@ describe Gitlab::ImportExport::Importer do
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path)
+ allow_any_instance_of(Gitlab::ImportExport::FileImporter).to receive(:remove_import_file)
+
FileUtils.mkdir_p(shared.export_path)
FileUtils.cp(Rails.root.join('spec/features/projects/import_export/test_project_export.tar.gz'), test_path)
- allow(subject).to receive(:remove_import_file)
end
after do
@@ -69,7 +70,7 @@ describe Gitlab::ImportExport::Importer do
let(:project) { create(:project, namespace: user.namespace, name: 'whatever', path: 'whatever') }
before do
- restorers = double
+ restorers = double(:restorers, all?: true)
allow(subject).to receive(:import_file).and_return(true)
allow(subject).to receive(:check_version!).and_return(true)
diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb
index 246f009ad27..67e4c289906 100644
--- a/spec/lib/gitlab/import_export/members_mapper_spec.rb
+++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb
@@ -111,7 +111,7 @@ describe Gitlab::ImportExport::MembersMapper do
end
it 'maps the project member if it already exists' do
- project.add_master(user2)
+ project.add_maintainer(user2)
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json
index c13cf4a0507..ba2248073f5 100644
--- a/spec/lib/gitlab/import_export/project.light.json
+++ b/spec/lib/gitlab/import_export/project.light.json
@@ -7,7 +7,7 @@
"milestones": [
{
"id": 1,
- "title": "Project milestone",
+ "title": "A milestone",
"project_id": 8,
"description": "Project-level milestone",
"due_date": null,
@@ -66,8 +66,8 @@
"group_milestone_id": null,
"milestone": {
"id": 1,
- "title": "Project milestone",
- "project_id": 8,
+ "title": "A milestone",
+ "group_id": 8,
"description": "Project-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
@@ -86,7 +86,7 @@
"updated_at": "2017-08-15T18:37:40.795Z",
"label": {
"id": 6,
- "title": "Another project label",
+ "title": "Another label",
"color": "#A8D695",
"project_id": null,
"created_at": "2017-08-15T18:37:19.698Z",
diff --git a/spec/lib/gitlab/import_export/project.milestone-iid.json b/spec/lib/gitlab/import_export/project.milestone-iid.json
new file mode 100644
index 00000000000..b028147b5eb
--- /dev/null
+++ b/spec/lib/gitlab/import_export/project.milestone-iid.json
@@ -0,0 +1,80 @@
+{
+ "description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
+ "import_type": "gitlab_project",
+ "creator_id": 123,
+ "visibility_level": 10,
+ "archived": false,
+ "issues": [
+ {
+ "id": 1,
+ "title": "Fugiat est minima quae maxime non similique.",
+ "assignee_id": null,
+ "project_id": 8,
+ "author_id": 1,
+ "created_at": "2017-07-07T18:13:01.138Z",
+ "updated_at": "2017-08-15T18:37:40.807Z",
+ "branch_name": null,
+ "description": "Quam totam fuga numquam in eveniet.",
+ "state": "opened",
+ "iid": 20,
+ "updated_by_id": 1,
+ "confidential": false,
+ "due_date": null,
+ "moved_to_id": null,
+ "lock_version": null,
+ "time_estimate": 0,
+ "closed_at": null,
+ "last_edited_at": null,
+ "last_edited_by_id": null,
+ "group_milestone_id": null,
+ "milestone": {
+ "id": 1,
+ "title": "Group-level milestone",
+ "description": "Group-level milestone",
+ "due_date": null,
+ "created_at": "2016-06-14T15:02:04.415Z",
+ "updated_at": "2016-06-14T15:02:04.415Z",
+ "state": "active",
+ "iid": 1,
+ "group_id": 8
+ }
+ },
+ {
+ "id": 2,
+ "title": "est minima quae maxime non similique.",
+ "assignee_id": null,
+ "project_id": 8,
+ "author_id": 1,
+ "created_at": "2017-07-07T18:13:01.138Z",
+ "updated_at": "2017-08-15T18:37:40.807Z",
+ "branch_name": null,
+ "description": "Quam totam fuga numquam in eveniet.",
+ "state": "opened",
+ "iid": 21,
+ "updated_by_id": 1,
+ "confidential": false,
+ "due_date": null,
+ "moved_to_id": null,
+ "lock_version": null,
+ "time_estimate": 0,
+ "closed_at": null,
+ "last_edited_at": null,
+ "last_edited_by_id": null,
+ "group_milestone_id": null,
+ "milestone": {
+ "id": 2,
+ "title": "Another milestone",
+ "project_id": 8,
+ "description": "milestone",
+ "due_date": null,
+ "created_at": "2016-06-14T15:02:04.415Z",
+ "updated_at": "2016-06-14T15:02:04.415Z",
+ "state": "active",
+ "iid": 1,
+ "group_id": null
+ }
+ }
+ ],
+ "snippets": [],
+ "hooks": []
+}
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 68ddc947e02..a88ac0a091e 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
@shared = @project.import_export_shared
allow(@shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/')
- allow_any_instance_of(Repository).to receive(:fetch_ref).and_return(true)
+ allow_any_instance_of(Repository).to receive(:fetch_source_branch!).and_return(true)
allow_any_instance_of(Gitlab::Git::Repository).to receive(:branch_exists?).and_return(false)
expect_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch).with('feature', 'DCBA')
@@ -189,8 +189,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
@project.pipelines.zip([2, 2, 2, 2, 2])
.each do |(pipeline, expected_status_size)|
- expect(pipeline.statuses.size).to eq(expected_status_size)
- end
+ expect(pipeline.statuses.size).to eq(expected_status_size)
+ end
end
end
@@ -246,13 +246,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(project.issues.size).to eq(results.fetch(:issues, 0))
end
- it 'has issue with group label and project label' do
- labels = project.issues.first.labels
-
- expect(labels.where(type: "ProjectLabel").count).to eq(results.fetch(:first_issue_labels, 0))
- expect(labels.where(type: "ProjectLabel").where.not(group_id: nil).count).to eq(0)
- end
-
it 'does not set params that are excluded from import_export settings' do
expect(project.import_type).to be_nil
expect(project.creator_id).not_to eq 123
@@ -268,12 +261,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
it 'has group milestone' do
expect(project.group.milestones.size).to eq(results.fetch(:milestones, 0))
end
-
- it 'has issue with group label' do
- labels = project.issues.first.labels
-
- expect(labels.where(type: "GroupLabel").count).to eq(results.fetch(:first_issue_labels, 0))
- end
end
context 'Light JSON' do
@@ -360,13 +347,72 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
it_behaves_like 'restores project correctly',
issues: 2,
labels: 1,
- milestones: 1,
+ milestones: 2,
first_issue_labels: 1
it_behaves_like 'restores group correctly',
- labels: 1,
- milestones: 1,
+ labels: 0,
+ milestones: 0,
first_issue_labels: 1
end
+
+ context 'with existing group models' do
+ let!(:project) do
+ create(:project,
+ :builds_disabled,
+ :issues_disabled,
+ name: 'project',
+ path: 'project',
+ group: create(:group))
+ end
+
+ before do
+ project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json")
+ end
+
+ it 'imports labels' do
+ create(:group_label, name: 'Another label', group: project.group)
+
+ expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error)
+
+ restored_project_json
+
+ expect(project.labels.count).to eq(1)
+ end
+
+ it 'imports milestones' do
+ create(:milestone, name: 'A milestone', group: project.group)
+
+ expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error)
+
+ restored_project_json
+
+ expect(project.group.milestones.count).to eq(1)
+ expect(project.milestones.count).to eq(0)
+ end
+ end
+
+ context 'with clashing milestones on IID' do
+ let!(:project) do
+ create(:project,
+ :builds_disabled,
+ :issues_disabled,
+ name: 'project',
+ path: 'project',
+ group: create(:group))
+ end
+
+ it 'preserves the project milestone IID' do
+ project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.milestone-iid.json")
+
+ expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error)
+
+ restored_project_json
+
+ expect(project.milestones.count).to eq(2)
+ expect(Milestone.find_by_title('Another milestone').iid).to eq(1)
+ expect(Milestone.find_by_title('Group-level milestone').iid).to eq(2)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 2b8a11ce8f9..fec8a2af9ab 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
let!(:project) { setup_project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
allow_any_instance_of(MergeRequest).to receive(:source_branch_sha).and_return('ABCD')
allow_any_instance_of(MergeRequest).to receive(:target_branch_sha).and_return('DCBA')
@@ -217,8 +217,8 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(member_emails).not_to include('group@member.com')
end
- it 'does not export group members as master' do
- Group.first.add_master(user)
+ it 'does not export group members as maintainer' do
+ Group.first.add_maintainer(user)
expect(member_emails).not_to include('group@member.com')
end
diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb
index 187ec8fcfa2..5a646b4aac8 100644
--- a/spec/lib/gitlab/import_export/repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ImportExport::RepoSaver do
let(:bundler) { described_class.new(project: project, shared: shared) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb
new file mode 100644
index 00000000000..02f1a4b81aa
--- /dev/null
+++ b/spec/lib/gitlab/import_export/saver_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+require 'fileutils'
+
+describe Gitlab::ImportExport::Saver do
+ let!(:project) { create(:project, :public, name: 'project') }
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:shared) { project.import_export_shared }
+ subject { described_class.new(project: project, shared: shared) }
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+
+ FileUtils.mkdir_p(shared.export_path)
+ FileUtils.touch("#{shared.export_path}/tmp.bundle")
+ end
+
+ after do
+ FileUtils.rm_rf(export_path)
+ end
+
+ context 'local archive' do
+ it 'saves the repo to disk' do
+ stub_feature_flags(import_export_object_storage: false)
+
+ subject.save
+
+ expect(shared.errors).to be_empty
+ expect(Dir.empty?(shared.archive_path)).to be false
+ end
+ end
+
+ context 'object storage' do
+ it 'saves the repo using object storage' do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(ImportExportUploader)
+
+ subject.save
+
+ expect(ImportExportUpload.find_by(project: project).export_file.url)
+ .to match(%r[\/uploads\/-\/system\/import_export_upload\/export_file.*])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
new file mode 100644
index 00000000000..9c3870a0af8
--- /dev/null
+++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::UploadsManager do
+ let(:shared) { project.import_export_shared }
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:project) { create(:project) }
+ let(:exported_file_path) { "#{shared.export_path}/uploads/#{upload.secret}/#{File.basename(upload.path)}" }
+
+ subject(:manager) { described_class.new(project: project, shared: shared) }
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ FileUtils.mkdir_p(shared.export_path)
+ end
+
+ after do
+ FileUtils.rm_rf(shared.export_path)
+ end
+
+ describe '#save' do
+ context 'when the project has uploads locally stored' do
+ let(:upload) { create(:upload, :issuable_upload, :with_file, model: project) }
+
+ before do
+ project.uploads << upload
+ end
+
+ it 'does not cause errors' do
+ manager.save
+
+ expect(shared.errors).to be_empty
+ end
+
+ it 'copies the file in the correct location when there is an upload' do
+ manager.save
+
+ expect(File).to exist(exported_file_path)
+ end
+ end
+
+ context 'using object storage' do
+ let!(:upload) { create(:upload, :issuable_upload, :object_storage, model: project) }
+
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+ end
+
+ it 'saves the file' do
+ fake_uri = double
+
+ expect(fake_uri).to receive(:open).and_return(StringIO.new('File content'))
+ expect(URI).to receive(:parse).and_return(fake_uri)
+
+ manager.save
+
+ expect(File.read(exported_file_path)).to eq('File content')
+ end
+ end
+
+ describe '#restore' do
+ context 'using object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038'))
+ FileUtils.touch(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038', "dummy.txt"))
+ end
+
+ it 'restores the file' do
+ manager.restore
+
+ expect(project.uploads.size).to eq(1)
+ expect(project.uploads.first.build_uploader.filename).to eq('dummy.txt')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
index 095687fa89d..c716edd9397 100644
--- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
@@ -7,6 +7,7 @@ describe Gitlab::ImportExport::UploadsSaver do
let(:shared) { project.import_export_shared }
before do
+ stub_feature_flags(import_export_object_storage: false)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
@@ -30,7 +31,7 @@ describe Gitlab::ImportExport::UploadsSaver do
it 'copies the uploads to the export path' do
saver.save
- uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) }
+ uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) }
expect(uploads).to include('banana_sample.gif')
end
@@ -52,7 +53,7 @@ describe Gitlab::ImportExport::UploadsSaver do
it 'copies the uploads to the export path' do
saver.save
- uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) }
+ uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) }
expect(uploads).to include('banana_sample.gif')
end
diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
index 24bc231d5a0..441aa1defe6 100644
--- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
@@ -10,7 +10,7 @@ describe Gitlab::ImportExport::WikiRepoSaver do
let!(:project_wiki) { ProjectWiki.new(project, user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
project_wiki.wiki
project_wiki.create_page("index", "test content")
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index 10341486512..94abf9679c4 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -5,14 +5,16 @@ describe Gitlab::ImportSources do
it 'returns a hash' do
expected =
{
- 'GitHub' => 'github',
- 'Bitbucket' => 'bitbucket',
- 'GitLab.com' => 'gitlab',
- 'Google Code' => 'google_code',
- 'FogBugz' => 'fogbugz',
- 'Repo by URL' => 'git',
- 'GitLab export' => 'gitlab_project',
- 'Gitea' => 'gitea'
+ 'GitHub' => 'github',
+ 'Bitbucket Cloud' => 'bitbucket',
+ 'Bitbucket Server' => 'bitbucket_server',
+ 'GitLab.com' => 'gitlab',
+ 'Google Code' => 'google_code',
+ 'FogBugz' => 'fogbugz',
+ 'Repo by URL' => 'git',
+ 'GitLab export' => 'gitlab_project',
+ 'Gitea' => 'gitea',
+ 'Manifest file' => 'manifest'
}
expect(described_class.options).to eq(expected)
@@ -25,12 +27,14 @@ describe Gitlab::ImportSources do
%w(
github
bitbucket
+ bitbucket_server
gitlab
google_code
fogbugz
git
gitlab_project
gitea
+ manifest
)
expect(described_class.values).to eq(expected)
@@ -43,6 +47,7 @@ describe Gitlab::ImportSources do
%w(
github
bitbucket
+ bitbucket_server
gitlab
google_code
fogbugz
@@ -58,12 +63,14 @@ describe Gitlab::ImportSources do
import_sources = {
'github' => Gitlab::GithubImport::ParallelImporter,
'bitbucket' => Gitlab::BitbucketImport::Importer,
+ 'bitbucket_server' => Gitlab::BitbucketServerImport::Importer,
'gitlab' => Gitlab::GitlabImport::Importer,
'google_code' => Gitlab::GoogleCodeImport::Importer,
'fogbugz' => Gitlab::FogbugzImport::Importer,
'git' => nil,
'gitlab_project' => Gitlab::ImportExport::Importer,
- 'gitea' => Gitlab::LegacyGithubImport::Importer
+ 'gitea' => Gitlab::LegacyGithubImport::Importer,
+ 'manifest' => nil
}
import_sources.each do |name, klass|
@@ -76,13 +83,15 @@ describe Gitlab::ImportSources do
describe '.title' do
import_sources = {
'github' => 'GitHub',
- 'bitbucket' => 'Bitbucket',
+ 'bitbucket' => 'Bitbucket Cloud',
+ 'bitbucket_server' => 'Bitbucket Server',
'gitlab' => 'GitLab.com',
'google_code' => 'Google Code',
'fogbugz' => 'FogBugz',
'git' => 'Repo by URL',
'gitlab_project' => 'GitLab export',
- 'gitea' => 'Gitea'
+ 'gitea' => 'Gitea',
+ 'manifest' => 'Manifest file'
}
import_sources.each do |name, title|
@@ -93,7 +102,7 @@ describe Gitlab::ImportSources do
end
describe 'imports_repository? checker' do
- let(:allowed_importers) { %w[github gitlab_project] }
+ let(:allowed_importers) { %w[github gitlab_project bitbucket_server] }
it 'fails if any importer other than the allowed ones implements this method' do
current_importers = described_class.values.select { |kind| described_class.importer(kind).try(:imports_repository?) }
diff --git a/spec/lib/gitlab/json_logger_spec.rb b/spec/lib/gitlab/json_logger_spec.rb
new file mode 100644
index 00000000000..0a62785f880
--- /dev/null
+++ b/spec/lib/gitlab/json_logger_spec.rb
@@ -0,0 +1,29 @@
+# coding: utf-8
+require 'spec_helper'
+
+describe Gitlab::JsonLogger do
+ subject { described_class.new('/dev/null') }
+
+ let(:now) { Time.now }
+
+ describe '#format_message' do
+ it 'formats strings' do
+ output = subject.format_message('INFO', now, 'test', 'Hello world')
+ data = JSON.parse(output)
+
+ expect(data['severity']).to eq('INFO')
+ expect(data['time']).to eq(now.utc.iso8601(3))
+ expect(data['message']).to eq('Hello world')
+ end
+
+ it 'formats hashes' do
+ output = subject.format_message('INFO', now, 'test', { hello: 1 })
+ data = JSON.parse(output)
+
+ expect(data['severity']).to eq('INFO')
+ expect(data['time']).to eq(now.utc.iso8601(3))
+ expect(data['hello']).to eq(1)
+ expect(data['message']).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/config_map_spec.rb b/spec/lib/gitlab/kubernetes/config_map_spec.rb
index 33dfa461202..fe65d03875f 100644
--- a/spec/lib/gitlab/kubernetes/config_map_spec.rb
+++ b/spec/lib/gitlab/kubernetes/config_map_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Kubernetes::ConfigMap do
let(:kubeclient) { double('kubernetes client') }
let(:application) { create(:clusters_applications_prometheus) }
- let(:config_map) { described_class.new(application.name, application.values) }
+ let(:config_map) { described_class.new(application.name, application.files) }
let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
let(:metadata) do
@@ -15,11 +15,17 @@ describe Gitlab::Kubernetes::ConfigMap do
end
describe '#generate' do
- let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: { values: application.values }) }
+ let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: application.files) }
subject { config_map.generate }
it 'should build a Kubeclient Resource' do
is_expected.to eq(resource)
end
end
+
+ describe '#config_map_name' do
+ it 'returns the config_map name' do
+ expect(config_map.config_map_name).to eq("values-content-configuration-#{application.name}")
+ end
+ end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
index aa7e43dfb16..341f71a3e49 100644
--- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
@@ -39,7 +39,7 @@ describe Gitlab::Kubernetes::Helm::Api do
end
context 'with a ConfigMap' do
- let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application.name, application.values).generate }
+ let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application.name, application.files).generate }
it 'creates a ConfigMap on kubeclient' do
expect(client).to receive(:create_config_map).with(resource).once
@@ -49,33 +49,33 @@ describe Gitlab::Kubernetes::Helm::Api do
end
end
- describe '#installation_status' do
+ describe '#status' do
let(:phase) { Gitlab::Kubernetes::Pod::RUNNING }
let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation
it 'fetches POD phase from kubernetes cluster' do
expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod)
- expect(subject.installation_status(command.pod_name)).to eq(phase)
+ expect(subject.status(command.pod_name)).to eq(phase)
end
end
- describe '#installation_log' do
+ describe '#log' do
let(:log) { 'some output' }
let(:response) { RestClient::Response.new(log) }
it 'fetches POD phase from kubernetes cluster' do
expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response)
- expect(subject.installation_log(command.pod_name)).to eq(log)
+ expect(subject.log(command.pod_name)).to eq(log)
end
end
- describe '#delete_installation_pod!' do
+ describe '#delete_pod!' do
it 'deletes the POD from kubernetes cluster' do
expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_namespace).once
- subject.delete_installation_pod!(command.pod_name)
+ subject.delete_pod!(command.pod_name)
end
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
index 7be8be54d5e..d50616e95e8 100644
--- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
@@ -2,7 +2,25 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Helm::BaseCommand do
let(:application) { create(:clusters_applications_helm) }
- let(:base_command) { described_class.new(application.name) }
+ let(:test_class) do
+ Class.new do
+ include Gitlab::Kubernetes::Helm::BaseCommand
+
+ def name
+ "test-class-name"
+ end
+
+ def files
+ {
+ some: 'value'
+ }
+ end
+ end
+ end
+
+ let(:base_command) do
+ test_class.new
+ end
subject { base_command }
@@ -18,15 +36,9 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
end
end
- describe '#config_map?' do
- subject { base_command.config_map? }
-
- it { is_expected.to be_falsy }
- end
-
describe '#pod_name' do
subject { base_command.pod_name }
- it { is_expected.to eq('install-helm') }
+ it { is_expected.to eq('install-test-class-name') }
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/certificate_spec.rb b/spec/lib/gitlab/kubernetes/helm/certificate_spec.rb
new file mode 100644
index 00000000000..167bee22fc3
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/helm/certificate_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::Helm::Certificate do
+ describe '.generate_root' do
+ subject { described_class.generate_root }
+
+ it 'should generate a root CA that expires a long way in the future' do
+ expect(subject.cert.not_after).to be > 999.years.from_now
+ end
+ end
+
+ describe '#issue' do
+ subject { described_class.generate_root.issue }
+
+ it 'should generate a cert that expires soon' do
+ expect(subject.cert.not_after).to be < 60.minutes.from_now
+ end
+
+ context 'passing in INFINITE_EXPIRY' do
+ subject { described_class.generate_root.issue(expires_in: described_class::INFINITE_EXPIRY) }
+
+ it 'should generate a cert that expires a long way in the future' do
+ expect(subject.cert.not_after).to be > 999.years.from_now
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
index 89e36a298f8..dcbc046cf00 100644
--- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
@@ -2,9 +2,9 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InitCommand do
let(:application) { create(:clusters_applications_helm) }
- let(:commands) { 'helm init >/dev/null' }
+ let(:commands) { 'helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem >/dev/null' }
- subject { described_class.new(application.name) }
+ subject { described_class.new(name: application.name, files: {}) }
it_behaves_like 'helm commands'
end
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index 25c6fa3b9a3..982e2f41043 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -1,83 +1,82 @@
require 'rails_helper'
describe Gitlab::Kubernetes::Helm::InstallCommand do
- let(:application) { create(:clusters_applications_prometheus) }
- let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
- let(:install_command) { application.install_command }
+ let(:files) { { 'ca.pem': 'some file content' } }
+ let(:repository) { 'https://repository.example.com' }
+ let(:version) { '1.2.3' }
+
+ let(:install_command) do
+ described_class.new(
+ name: 'app-name',
+ chart: 'chart-name',
+ files: files,
+ version: version, repository: repository
+ )
+ end
subject { install_command }
- context 'for ingress' do
- let(:application) { create(:clusters_applications_ingress) }
-
- it_behaves_like 'helm commands' do
- let(:commands) do
- <<~EOS
- helm init --client-only >/dev/null
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
- EOS
- end
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm repo add app-name https://repository.example.com
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
+ EOS
end
end
- context 'for prometheus' do
- let(:application) { create(:clusters_applications_prometheus) }
+ context 'when there is no repository' do
+ let(:repository) { nil }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --client-only >/dev/null
- helm install #{application.chart} --name #{application.name} --version #{application.version} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
EOS
end
end
end
- context 'for runner' do
- let(:ci_runner) { create(:ci_runner) }
- let(:application) { create(:clusters_applications_runner, runner: ci_runner) }
+ context 'when there is no ca.pem file' do
+ let(:files) { { 'file.txt': 'some content' } }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --client-only >/dev/null
- helm repo add #{application.name} #{application.repository}
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ helm repo add app-name https://repository.example.com
+ helm install chart-name --name app-name --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
EOS
end
end
end
- context 'for jupyter' do
- let(:application) { create(:clusters_applications_jupyter) }
+ context 'when there is no version' do
+ let(:version) { nil }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --client-only >/dev/null
- helm repo add #{application.name} #{application.repository}
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ helm repo add app-name https://repository.example.com
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
EOS
end
end
end
- describe '#config_map?' do
- subject { install_command.config_map? }
-
- it { is_expected.to be_truthy }
- end
-
describe '#config_map_resource' do
let(:metadata) do
{
- name: "values-content-configuration-#{application.name}",
- namespace: namespace,
- labels: { name: "values-content-configuration-#{application.name}" }
+ name: "values-content-configuration-app-name",
+ namespace: 'gitlab-managed-apps',
+ labels: { name: "values-content-configuration-app-name" }
}
end
- let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: { values: application.values }) }
+ let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
subject { install_command.config_map_resource }
diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
index 43adc80d576..ec64193c0b2 100644
--- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
@@ -2,14 +2,13 @@ require 'rails_helper'
describe Gitlab::Kubernetes::Helm::Pod do
describe '#generate' do
- let(:cluster) { create(:cluster) }
- let(:app) { create(:clusters_applications_prometheus, cluster: cluster) }
+ let(:app) { create(:clusters_applications_prometheus) }
let(:command) { app.install_command }
let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
subject { described_class.new(command, namespace) }
- shared_examples 'helm pod' do
+ context 'with a command' do
it 'should generate a Kubeclient::Resource' do
expect(subject.generate).to be_a_kind_of(Kubeclient::Resource)
end
@@ -41,10 +40,6 @@ describe Gitlab::Kubernetes::Helm::Pod do
spec = subject.generate.spec
expect(spec.restartPolicy).to eq('Never')
end
- end
-
- context 'with a install command' do
- it_behaves_like 'helm pod'
it 'should include volumes for the container' do
container = subject.generate.spec.containers.first
@@ -60,24 +55,8 @@ describe Gitlab::Kubernetes::Helm::Pod do
it 'should mount configMap specification in the volume' do
volume = subject.generate.spec.volumes.first
expect(volume.configMap['name']).to eq("values-content-configuration-#{app.name}")
- expect(volume.configMap['items'].first['key']).to eq('values')
- expect(volume.configMap['items'].first['path']).to eq('values.yaml')
- end
- end
-
- context 'with a init command' do
- let(:app) { create(:clusters_applications_helm, cluster: cluster) }
-
- it_behaves_like 'helm pod'
-
- it 'should not include volumeMounts inside the container' do
- container = subject.generate.spec.containers.first
- expect(container.volumeMounts).to be_nil
- end
-
- it 'should not a volume inside the specification' do
- spec = subject.generate.spec
- expect(spec.volumes).to be_nil
+ expect(volume.configMap['items'].first['key']).to eq(:'values.yaml')
+ expect(volume.configMap['items'].first['path']).to eq(:'values.yaml')
end
end
end
diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb
index 34b33772578..5c03a2ce7d3 100644
--- a/spec/lib/gitlab/kubernetes_spec.rb
+++ b/spec/lib/gitlab/kubernetes_spec.rb
@@ -70,4 +70,19 @@ describe Gitlab::Kubernetes do
it { is_expected.to eq(YAML.load_file(path)) }
end
end
+
+ describe '#add_terminal_auth' do
+ it 'adds authentication parameters to a hash' do
+ terminal = { original: 'value' }
+
+ add_terminal_auth(terminal, token: 'foo', max_session_time: 0, ca_pem: 'bar')
+
+ expect(terminal).to eq(
+ original: 'value',
+ headers: { 'Authorization' => ['Bearer foo'] },
+ max_session_time: 0,
+ ca_pem: 'bar'
+ )
+ end
+ end
end
diff --git a/spec/lib/gitlab/language_detection_spec.rb b/spec/lib/gitlab/language_detection_spec.rb
new file mode 100644
index 00000000000..9636fbd401b
--- /dev/null
+++ b/spec/lib/gitlab/language_detection_spec.rb
@@ -0,0 +1,85 @@
+require 'spec_helper'
+
+describe Gitlab::LanguageDetection do
+ set(:project) { create(:project, :repository) }
+ set(:ruby) { create(:programming_language, name: 'Ruby') }
+ set(:haskell) { create(:programming_language, name: 'Haskell') }
+ let(:repository) { project.repository }
+ let(:detection) do
+ [{ value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" },
+ { value: 12.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" },
+ { value: 7.9, label: "Elixir", color: "#e34c26", highlight: "#e34c26" },
+ { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" },
+ { value: 1.51, label: "Go", color: "#2a4776", highlight: "#244776" },
+ { value: 1.1, label: "MepmepLang", color: "#2a4776", highlight: "#244776" }]
+ end
+ let(:repository_languages) do
+ [RepositoryLanguage.new(share: 10, programming_language: ruby)]
+ end
+
+ subject { described_class.new(repository, repository_languages) }
+
+ before do
+ allow(repository).to receive(:languages).and_return(detection)
+ end
+
+ describe '#languages' do
+ it 'returns the language names' do
+ expect(subject.languages).to eq(%w[Ruby JavaScript Elixir CoffeeScript Go])
+ end
+ end
+
+ describe '#insertions' do
+ let(:programming_languages) { [ruby, haskell] }
+ let(:detection) do
+ [{ value: 10, label: haskell.name, color: haskell.color }]
+ end
+
+ it 'only includes new languages' do
+ insertions = subject.insertions(programming_languages)
+
+ expect(insertions).not_to be_empty
+ expect(insertions.first[:project_id]).to be(project.id)
+ expect(insertions.first[:programming_language_id]).to be(haskell.id)
+ expect(insertions.first[:share]).to be(10)
+ end
+ end
+
+ describe '#updates' do
+ it 'updates the share of languages' do
+ first_update = subject.updates.first
+
+ expect(first_update).not_to be_nil
+ expect(first_update[:programming_language_id]).to eq(ruby.id)
+ expect(first_update[:share]).to eq(66.63)
+ end
+
+ it 'does not include languages to be removed' do
+ ids = subject.updates.map { |h| h[:programming_language_id] }
+
+ expect(ids).not_to include(haskell.id)
+ end
+
+ context 'when silent writes occur' do
+ let(:repository_languages) do
+ [RepositoryLanguage.new(share: 66.63, programming_language: ruby)]
+ end
+
+ it "doesn't include them in the result" do
+ expect(subject.updates).to be_empty
+ end
+ end
+ end
+
+ describe '#deletions' do
+ let(:repository_languages) do
+ [RepositoryLanguage.new(share: 10, programming_language: ruby),
+ RepositoryLanguage.new(share: 5, programming_language: haskell)]
+ end
+
+ it 'lists undetected languages' do
+ expect(subject.deletions).not_to be_empty
+ expect(subject.deletions).to include(haskell.id)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/manifest_import/manifest_spec.rb b/spec/lib/gitlab/manifest_import/manifest_spec.rb
new file mode 100644
index 00000000000..ab305fb2316
--- /dev/null
+++ b/spec/lib/gitlab/manifest_import/manifest_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Gitlab::ManifestImport::Manifest, :postgresql do
+ let(:file) { File.open(Rails.root.join('spec/fixtures/aosp_manifest.xml')) }
+ let(:manifest) { described_class.new(file) }
+
+ describe '#valid?' do
+ context 'valid file' do
+ it { expect(manifest.valid?).to be true }
+ end
+
+ context 'missing or invalid attributes' do
+ let(:file) { Tempfile.new('foo') }
+
+ before do
+ content = <<~EOS
+ <manifest>
+ <remote review="invalid-url" />
+ <project name="platform/build"/>
+ </manifest>
+ EOS
+
+ file.write(content)
+ file.rewind
+ end
+
+ it { expect(manifest.valid?).to be false }
+
+ describe 'errors' do
+ before do
+ manifest.valid?
+ end
+
+ it { expect(manifest.errors).to include('Make sure a <remote> tag is present and is valid.') }
+ it { expect(manifest.errors).to include('Make sure every <project> tag has name and path attributes.') }
+ end
+ end
+ end
+
+ describe '#projects' do
+ it { expect(manifest.projects.size).to eq(660) }
+ it { expect(manifest.projects[0][:name]).to eq('platform/build') }
+ it { expect(manifest.projects[0][:path]).to eq('build/make') }
+ it { expect(manifest.projects[0][:url]).to eq('https://android-review.googlesource.com/platform/build') }
+ end
+end
diff --git a/spec/lib/gitlab/manifest_import/project_creator_spec.rb b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
new file mode 100644
index 00000000000..1d01d437535
--- /dev/null
+++ b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Gitlab::ManifestImport::ProjectCreator, :postgresql do
+ let(:group) { create(:group) }
+ let(:user) { create(:user) }
+ let(:repository) do
+ {
+ path: 'device/common',
+ url: 'https://android-review.googlesource.com/device/common'
+ }
+ end
+
+ before do
+ group.add_owner(user)
+ end
+
+ subject { described_class.new(repository, group, user) }
+
+ describe '#execute' do
+ it { expect(subject.execute).to be_a(Project) }
+ it { expect { subject.execute }.to change { Project.count }.by(1) }
+ it { expect { subject.execute }.to change { Group.count }.by(1) }
+
+ it 'creates project with valid full path and import url' do
+ subject.execute
+
+ project = Project.last
+
+ expect(project.full_path).to eq(File.join(group.path, 'device/common'))
+ expect(project.import_url).to eq('https://android-review.googlesource.com/device/common')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/middleware/basic_health_check_spec.rb b/spec/lib/gitlab/middleware/basic_health_check_spec.rb
new file mode 100644
index 00000000000..187d903a5e1
--- /dev/null
+++ b/spec/lib/gitlab/middleware/basic_health_check_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+
+describe Gitlab::Middleware::BasicHealthCheck do
+ let(:app) { double(:app) }
+ let(:middleware) { described_class.new(app) }
+ let(:env) { {} }
+
+ describe '#call' do
+ context 'outside IP' do
+ before do
+ env['REMOTE_ADDR'] = '8.8.8.8'
+ end
+
+ it 'returns a 404' do
+ env['PATH_INFO'] = described_class::HEALTH_PATH
+
+ response = middleware.call(env)
+
+ expect(response[0]).to eq(404)
+ end
+
+ it 'forwards the call for other paths' do
+ env['PATH_INFO'] = '/'
+
+ expect(app).to receive(:call)
+
+ middleware.call(env)
+ end
+ end
+
+ context 'whitelisted IP' do
+ before do
+ env['REMOTE_ADDR'] = '127.0.0.1'
+ end
+
+ it 'returns 200 response when endpoint is hit' do
+ env['PATH_INFO'] = described_class::HEALTH_PATH
+
+ expect(app).not_to receive(:call)
+
+ response = middleware.call(env)
+
+ expect(response[0]).to eq(200)
+ expect(response[1]).to eq({ 'Content-Type' => 'text/plain' })
+ expect(response[2]).to eq(['GitLab OK'])
+ end
+
+ it 'forwards the call for other paths' do
+ env['PATH_INFO'] = '/-/readiness'
+
+ expect(app).to receive(:call)
+
+ middleware.call(env)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index b24c9882c0c..7a3a9ab875b 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -79,7 +79,7 @@ describe Gitlab::Middleware::Go do
let(:current_user) { project.creator }
before do
- project.team.add_master(current_user)
+ project.team.add_maintainer(current_user)
end
shared_examples 'authenticated' do
diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb
index a2ba91dae80..f788f8ee276 100644
--- a/spec/lib/gitlab/middleware/multipart_spec.rb
+++ b/spec/lib/gitlab/middleware/multipart_spec.rb
@@ -7,18 +7,47 @@ describe Gitlab::Middleware::Multipart do
let(:middleware) { described_class.new(app) }
let(:original_filename) { 'filename' }
- it 'opens top-level files' do
- Tempfile.open('top-level') do |tempfile|
- env = post_env({ 'file' => tempfile.path }, { 'file.name' => original_filename }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+ shared_examples_for 'multipart upload files' do
+ it 'opens top-level files' do
+ Tempfile.open('top-level') do |tempfile|
+ env = post_env({ 'file' => tempfile.path }, { 'file.name' => original_filename, 'file.path' => tempfile.path, 'file.remote_id' => remote_id }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+ expect_uploaded_file(tempfile, %w(file))
+
+ middleware.call(env)
+ end
+ end
+
+ it 'opens files one level deep' do
+ Tempfile.open('one-level') do |tempfile|
+ in_params = { 'user' => { 'avatar' => { '.name' => original_filename, '.path' => tempfile.path, '.remote_id' => remote_id } } }
+ env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect_uploaded_file(tempfile, %w(user avatar))
+
+ middleware.call(env)
+ end
+ end
+
+ it 'opens files two levels deep' do
+ Tempfile.open('two-levels') do |tempfile|
+ in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => original_filename, '.path' => tempfile.path, '.remote_id' => remote_id } } } }
+ env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect_uploaded_file(tempfile, %w(project milestone themesong))
+
+ middleware.call(env)
+ end
+ end
+
+ def expect_uploaded_file(tempfile, path, remote: false)
expect(app).to receive(:call) do |env|
- file = Rack::Request.new(env).params['file']
+ file = Rack::Request.new(env).params.dig(*path)
expect(file).to be_a(::UploadedFile)
expect(file.path).to eq(tempfile.path)
expect(file.original_filename).to eq(original_filename)
+ expect(file.remote_id).to eq(remote_id)
end
-
- middleware.call(env)
end
end
@@ -34,32 +63,39 @@ describe Gitlab::Middleware::Multipart do
expect { middleware.call(env) }.to raise_error(JWT::InvalidIssuerError)
end
- it 'opens files one level deep' do
- Tempfile.open('one-level') do |tempfile|
- in_params = { 'user' => { 'avatar' => { '.name' => original_filename } } }
- env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+ context 'with remote file' do
+ let(:remote_id) { 'someid' }
- expect(app).to receive(:call) do |env|
- file = Rack::Request.new(env).params['user']['avatar']
- expect(file).to be_a(::UploadedFile)
- expect(file.path).to eq(tempfile.path)
- expect(file.original_filename).to eq(original_filename)
- end
+ it_behaves_like 'multipart upload files'
+ end
- middleware.call(env)
- end
+ context 'with local file' do
+ let(:remote_id) { nil }
+
+ it_behaves_like 'multipart upload files'
end
- it 'opens files two levels deep' do
+ it 'allows symlinks for uploads dir' do
Tempfile.open('two-levels') do |tempfile|
- in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => original_filename } } } }
- env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+ symlinked_dir = '/some/dir/uploads'
+ symlinked_path = File.join(symlinked_dir, File.basename(tempfile.path))
+ env = post_env({ 'file' => symlinked_path }, { 'file.name' => original_filename, 'file.path' => symlinked_path }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ allow(FileUploader).to receive(:root).and_return(symlinked_dir)
+ allow(UploadedFile).to receive(:allowed_paths).and_return([symlinked_dir, Gitlab.config.uploads.storage_path])
+ allow(File).to receive(:realpath).and_call_original
+ allow(File).to receive(:realpath).with(symlinked_dir).and_return(Dir.tmpdir)
+ allow(File).to receive(:realpath).with(symlinked_path).and_return(tempfile.path)
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with(symlinked_dir).and_return(true)
+
+ # override Dir.tmpdir because this dir is in the list of allowed paths
+ # and it would match FileUploader.root path (which in this test is linked
+ # to /tmp too)
+ allow(Dir).to receive(:tmpdir).and_return(File.join(Dir.tmpdir, 'tmpsubdir'))
expect(app).to receive(:call) do |env|
- file = Rack::Request.new(env).params['project']['milestone']['themesong']
- expect(file).to be_a(::UploadedFile)
- expect(file.path).to eq(tempfile.path)
- expect(file.original_filename).to eq(original_filename)
+ expect(Rack::Request.new(env).params['file']).to be_a(::UploadedFile)
end
middleware.call(env)
diff --git a/spec/lib/gitlab/middleware/read_only_spec.rb b/spec/lib/gitlab/middleware/read_only_spec.rb
index 39ec2f37a83..8fbeaa065fa 100644
--- a/spec/lib/gitlab/middleware/read_only_spec.rb
+++ b/spec/lib/gitlab/middleware/read_only_spec.rb
@@ -2,28 +2,7 @@ require 'spec_helper'
describe Gitlab::Middleware::ReadOnly do
include Rack::Test::Methods
-
- RSpec::Matchers.define :be_a_redirect do
- match do |response|
- response.status == 301
- end
- end
-
- RSpec::Matchers.define :disallow_request do
- match do |middleware|
- alert = middleware.env['rack.session'].to_hash
- .dig('flash', 'flashes', 'alert')
-
- alert&.include?('You cannot perform write operations')
- end
- end
-
- RSpec::Matchers.define :disallow_request_in_json do
- match do |response|
- json_response = JSON.parse(response.body)
- response.body.include?('You cannot perform write operations') && json_response.key?('message')
- end
- end
+ using RSpec::Parameterized::TableSyntax
let(:rack_stack) do
rack = Rack::Builder.new do
@@ -65,38 +44,38 @@ describe Gitlab::Middleware::ReadOnly do
it 'expects PATCH requests to be disallowed' do
response = request.patch('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
it 'expects PUT requests to be disallowed' do
response = request.put('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
it 'expects POST requests to be disallowed' do
response = request.post('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
it 'expects a internal POST request to be allowed after a disallowed request' do
response = request.post('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
response = request.post("/api/#{API::API.version}/internal")
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
end
it 'expects DELETE requests to be disallowed' do
response = request.delete('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
@@ -104,7 +83,7 @@ describe Gitlab::Middleware::ReadOnly do
expect(Rails.application.routes).to receive(:recognize_path).and_call_original
response = request.post('/root/gitlab-ce/new/master/app/info/lfs/objects/batch')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
@@ -117,39 +96,41 @@ describe Gitlab::Middleware::ReadOnly do
context 'whitelisted requests' do
it 'expects a POST internal request to be allowed' do
expect(Rails.application.routes).not_to receive(:recognize_path)
-
response = request.post("/api/#{API::API.version}/internal")
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
end
- it 'expects a POST LFS request to batch URL to be allowed' do
- expect(Rails.application.routes).to receive(:recognize_path).and_call_original
- response = request.post('/root/rouge.git/info/lfs/objects/batch')
+ it 'expects requests to sidekiq admin to be allowed' do
+ response = request.post('/admin/sidekiq')
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
- end
- it 'expects a POST request to git-upload-pack URL to be allowed' do
- expect(Rails.application.routes).to receive(:recognize_path).and_call_original
- response = request.post('/root/rouge.git/git-upload-pack')
+ response = request.get('/admin/sidekiq')
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
end
- it 'expects requests to sidekiq admin to be allowed' do
- response = request.post('/admin/sidekiq')
+ where(:description, :path) do
+ 'LFS request to batch' | '/root/rouge.git/info/lfs/objects/batch'
+ 'LFS request to locks verify' | '/root/rouge.git/info/lfs/locks/verify'
+ 'LFS request to locks create' | '/root/rouge.git/info/lfs/locks'
+ 'LFS request to locks unlock' | '/root/rouge.git/info/lfs/locks/1/unlock'
+ 'request to git-upload-pack' | '/root/rouge.git/git-upload-pack'
+ 'request to git-receive-pack' | '/root/rouge.git/git-receive-pack'
+ end
- expect(response).not_to be_a_redirect
- expect(subject).not_to disallow_request
+ with_them do
+ it "expects a POST #{description} URL to be allowed" do
+ expect(Rails.application.routes).to receive(:recognize_path).and_call_original
+ response = request.post(path)
- response = request.get('/admin/sidekiq')
-
- expect(response).not_to be_a_redirect
- expect(subject).not_to disallow_request
+ expect(response).not_to be_redirect
+ expect(subject).not_to disallow_request
+ end
end
end
end
diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb
index 1dbead16d5b..c1b84e9f077 100644
--- a/spec/lib/gitlab/popen_spec.rb
+++ b/spec/lib/gitlab/popen_spec.rb
@@ -55,6 +55,19 @@ describe Gitlab::Popen do
end
end
+ context 'with a process that writes a lot of data to stderr' do
+ let(:test_string) { 'The quick brown fox jumped over the lazy dog' }
+ # The pipe buffer is typically 64K. This string is about 440K.
+ let(:spew_command) { ['bash', '-c', "for i in {1..10000}; do echo '#{test_string}' 1>&2; done"] }
+
+ it 'returns zero' do
+ output, status = @klass.new.popen(spew_command, path)
+
+ expect(output).to include(test_string)
+ expect(status).to eq(0)
+ end
+ end
+
context 'without a directory argument' do
before do
@output, @status = @klass.new.popen(%w(ls))
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index f3cd6961e94..00c62c7bf96 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -41,7 +41,7 @@ describe Gitlab::ProjectAuthorizations do
it 'includes the correct access levels' do
mapping = map_access_levels(authorizations)
- expect(mapping[owned_project.id]).to eq(Gitlab::Access::MASTER)
+ expect(mapping[owned_project.id]).to eq(Gitlab::Access::MAINTAINER)
expect(mapping[other_project.id]).to eq(Gitlab::Access::REPORTER)
expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER)
end
@@ -62,11 +62,11 @@ describe Gitlab::ProjectAuthorizations do
end
it 'uses the greatest access level when a user is a member of a nested group' do
- nested_group.add_master(user)
+ nested_group.add_maintainer(user)
mapping = map_access_levels(authorizations)
- expect(mapping[nested_project.id]).to eq(Gitlab::Access::MASTER)
+ expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 50224bde722..4a0dc3686ec 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -64,6 +64,49 @@ describe Gitlab::ProjectSearchResults do
end
end
+ shared_examples 'blob search repository ref' do |entity_type|
+ let(:query) { 'files' }
+ let(:file_finder) { double }
+ let(:project_branch) { 'project_branch' }
+
+ subject(:results) { described_class.new(user, project, query, repository_ref).objects(blob_type) }
+
+ before do
+ allow(entity).to receive(:default_branch).and_return(project_branch)
+ allow(file_finder).to receive(:find).and_return([])
+ end
+
+ context 'when repository_ref exists' do
+ let(:repository_ref) { 'ref_branch' }
+
+ it 'uses it' do
+ expect(Gitlab::FileFinder).to receive(:new).with(project, repository_ref).and_return(file_finder)
+
+ results
+ end
+ end
+
+ context 'when repository_ref is not present' do
+ let(:repository_ref) { nil }
+
+ it "uses #{entity_type} repository default reference" do
+ expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder)
+
+ results
+ end
+ end
+
+ context 'when repository_ref is blank' do
+ let(:repository_ref) { '' }
+
+ it "uses #{entity_type} repository default reference" do
+ expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder)
+
+ results
+ end
+ end
+ end
+
describe 'blob search' do
let(:project) { create(:project, :public, :repository) }
@@ -75,6 +118,11 @@ describe Gitlab::ProjectSearchResults do
let(:expected_file_by_content) { 'CHANGELOG' }
end
+ it_behaves_like 'blob search repository ref', 'project' do
+ let(:blob_type) { 'blobs' }
+ let(:entity) { project }
+ end
+
describe 'parsing results' do
let(:results) { project.repository.search_files_by_content('feature', 'master') }
let(:search_result) { results.first }
@@ -212,6 +260,11 @@ describe Gitlab::ProjectSearchResults do
let(:expected_file_by_name) { 'Files/Title.md' }
let(:expected_file_by_content) { 'CHANGELOG.md' }
end
+
+ it_behaves_like 'blob search repository ref', 'wiki' do
+ let(:blob_type) { 'wiki_blobs' }
+ let(:entity) { project.wiki }
+ end
end
it 'does not list issues on private projects' do
@@ -385,7 +438,7 @@ describe Gitlab::ProjectSearchResults do
let!(:private_project) { create(:project, :private, :repository, creator: creator, namespace: creator.namespace) }
let(:team_master) do
user = create(:user, username: 'private-project-master')
- private_project.add_master(user)
+ private_project.add_maintainer(user)
user
end
let(:team_reporter) do
diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb
index 85971f2a7ef..5bd4d6c6a48 100644
--- a/spec/lib/gitlab/repository_cache_adapter_spec.rb
+++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb
@@ -67,10 +67,18 @@ describe Gitlab::RepositoryCacheAdapter do
describe '#expire_method_caches' do
it 'expires the caches of the given methods' do
- expect(cache).to receive(:expire).with(:readme)
+ expect(cache).to receive(:expire).with(:rendered_readme)
expect(cache).to receive(:expire).with(:gitignore)
- repository.expire_method_caches(%i(readme gitignore))
+ repository.expire_method_caches(%i(rendered_readme gitignore))
+ end
+
+ it 'does not expire caches for non-existent methods' do
+ expect(cache).not_to receive(:expire).with(:nonexistent)
+ expect(Rails.logger).to(
+ receive(:error).with("Requested to expire non-existent method 'nonexistent' for Repository"))
+
+ repository.expire_method_caches(%i(nonexistent))
end
end
end
diff --git a/spec/lib/gitlab/sanitizers/svg_spec.rb b/spec/lib/gitlab/sanitizers/svg_spec.rb
index 030c2063ab2..df46a874528 100644
--- a/spec/lib/gitlab/sanitizers/svg_spec.rb
+++ b/spec/lib/gitlab/sanitizers/svg_spec.rb
@@ -7,9 +7,9 @@ describe Gitlab::Sanitizers::SVG do
describe '.clean' do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
- let(:data) { open(input_svg_path).read }
+ let(:data) { File.read(input_svg_path) }
let(:sanitized_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') }
- let(:sanitized) { open(sanitized_svg_path).read }
+ let(:sanitized) { File.read(sanitized_svg_path) }
it 'delegates sanitization to scrubber' do
expect_any_instance_of(Gitlab::Sanitizers::SVG::Scrubber).to receive(:scrub).at_least(:once)
diff --git a/spec/lib/gitlab/serializer/ci/variables_spec.rb b/spec/lib/gitlab/serializer/ci/variables_spec.rb
index c4b7fda5dbb..1d1fd5b0763 100644
--- a/spec/lib/gitlab/serializer/ci/variables_spec.rb
+++ b/spec/lib/gitlab/serializer/ci/variables_spec.rb
@@ -1,4 +1,4 @@
-require 'spec_helper'
+require 'fast_spec_helper'
describe Gitlab::Serializer::Ci::Variables do
subject do
@@ -6,11 +6,11 @@ describe Gitlab::Serializer::Ci::Variables do
end
let(:object) do
- [{ key: :key, value: 'value', public: true },
+ [{ 'key' => :key, 'value' => 'value', 'public' => true },
{ key: 'wee', value: 1, public: false }]
end
- it 'converts keys into strings' do
+ it 'converts keys into strings and symbolizes hash' do
is_expected.to eq([
{ key: 'key', value: 'value', public: true },
{ key: 'wee', value: 1, public: false }
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index c435f988cdd..f8bf896950e 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -403,46 +403,36 @@ describe Gitlab::Shell do
end
describe '#create_repository' do
- shared_examples '#create_repository' do
- let(:repository_storage) { 'default' }
- let(:repository_storage_path) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
- end
- end
- let(:repo_name) { 'project/path' }
- let(:created_path) { File.join(repository_storage_path, repo_name + '.git') }
-
- after do
- FileUtils.rm_rf(created_path)
+ let(:repository_storage) { 'default' }
+ let(:repository_storage_path) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
end
+ end
+ let(:repo_name) { 'project/path' }
+ let(:created_path) { File.join(repository_storage_path, repo_name + '.git') }
- it 'creates a repository' do
- expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_truthy
-
- expect(File.stat(created_path).mode & 0o777).to eq(0o770)
+ after do
+ FileUtils.rm_rf(created_path)
+ end
- hooks_path = File.join(created_path, 'hooks')
- expect(File.lstat(hooks_path)).to be_symlink
- expect(File.realpath(hooks_path)).to eq(gitlab_shell_hooks_path)
- end
+ it 'creates a repository' do
+ expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_truthy
- it 'returns false when the command fails' do
- FileUtils.mkdir_p(File.dirname(created_path))
- # This file will block the creation of the repo's .git directory. That
- # should cause #create_repository to fail.
- FileUtils.touch(created_path)
+ expect(File.stat(created_path).mode & 0o777).to eq(0o770)
- expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_falsy
- end
+ hooks_path = File.join(created_path, 'hooks')
+ expect(File.lstat(hooks_path)).to be_symlink
+ expect(File.realpath(hooks_path)).to eq(gitlab_shell_hooks_path)
end
- context 'with gitaly' do
- it_behaves_like '#create_repository'
- end
+ it 'returns false when the command fails' do
+ FileUtils.mkdir_p(File.dirname(created_path))
+ # This file will block the creation of the repo's .git directory. That
+ # should cause #create_repository to fail.
+ FileUtils.touch(created_path)
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#create_repository'
+ expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_falsy
end
end
@@ -513,22 +503,12 @@ describe Gitlab::Shell do
end
end
- shared_examples 'fetch_remote' do |gitaly_on|
+ describe '#fetch_remote' do
def fetch_remote(ssh_auth = nil, prune = true)
gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', ssh_auth: ssh_auth, prune: prune)
end
- def expect_gitlab_projects(fail = false, options = {})
- expect(gitlab_projects).to receive(:fetch_remote).with(
- 'remote-name',
- timeout,
- options
- ).and_return(!fail)
-
- allow(gitlab_projects).to receive(:output).and_return('error') if fail
- end
-
- def expect_gitaly_call(fail, options = {})
+ def expect_call(fail, options = {})
receive_fetch_remote =
if fail
receive(:fetch_remote).and_raise(GRPC::NotFound)
@@ -539,16 +519,6 @@ describe Gitlab::Shell do
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive_fetch_remote
end
- if gitaly_on
- def expect_call(fail, options = {})
- expect_gitaly_call(fail, options)
- end
- else
- def expect_call(fail, options = {})
- expect_gitlab_projects(fail, options)
- end
- end
-
def build_ssh_auth(opts = {})
defaults = {
ssh_import?: true,
@@ -634,14 +604,6 @@ describe Gitlab::Shell do
expect(fetch_remote(ssh_auth)).to be_truthy
end
end
- end
-
- describe '#fetch_remote local', :skip_gitaly_mock do
- it_should_behave_like 'fetch_remote', false
- end
-
- describe '#fetch_remote gitaly' do
- it_should_behave_like 'fetch_remote', true
context 'gitaly call' do
let(:remote_name) { 'remote-name' }
@@ -683,25 +645,6 @@ describe Gitlab::Shell do
end.to raise_error(Gitlab::Shell::Error, "error")
end
end
-
- context 'without gitaly', :disable_gitaly do
- it 'returns true when the command succeeds' do
- expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true }
-
- result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
-
- expect(result).to be_truthy
- end
-
- it 'raises an exception when the command fails' do
- allow(gitlab_projects).to receive(:output) { 'error' }
- expect(gitlab_projects).to receive(:import_project) { false }
-
- expect do
- gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
- end.to raise_error(Gitlab::Shell::Error, "error")
- end
- end
end
end
diff --git a/spec/lib/gitlab/slash_commands/issue_move_spec.rb b/spec/lib/gitlab/slash_commands/issue_move_spec.rb
index d41441c9472..9a990e1fad7 100644
--- a/spec/lib/gitlab/slash_commands/issue_move_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_move_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::SlashCommands::IssueMove, service: true do
set(:other_project) { create(:project, namespace: project.namespace) }
before do
- [project, other_project].each { |prj| prj.add_master(user) }
+ [project, other_project].each { |prj| prj.add_maintainer(user) }
end
subject { described_class.new(project, chat_name) }
diff --git a/spec/lib/gitlab/slash_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
index 8e7df946529..724c76ade6e 100644
--- a/spec/lib/gitlab/slash_commands/issue_new_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::SlashCommands::IssueNew do
let(:regex_match) { described_class.match("issue create bird is the word") }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
subject do
diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
index 189e9592f1b..47787307990 100644
--- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
@@ -22,7 +22,7 @@ describe Gitlab::SlashCommands::IssueSearch do
context 'the user has access' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'returns all results' do
diff --git a/spec/lib/gitlab/slash_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
index b1db1638237..5c4ba2736ba 100644
--- a/spec/lib/gitlab/slash_commands/issue_show_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::SlashCommands::IssueShow do
let(:regex_match) { described_class.match("issue show #{issue.iid}") }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
subject do
diff --git a/spec/lib/gitlab/url_sanitizer_spec.rb b/spec/lib/gitlab/url_sanitizer_spec.rb
index fc8991fd31f..b41a81a8167 100644
--- a/spec/lib/gitlab/url_sanitizer_spec.rb
+++ b/spec/lib/gitlab/url_sanitizer_spec.rb
@@ -92,6 +92,7 @@ describe Gitlab::UrlSanitizer do
context 'credentials in URL' do
where(:url, :credentials) do
'http://foo:bar@example.com' | { user: 'foo', password: 'bar' }
+ 'http://foo:bar:baz@example.com' | { user: 'foo', password: 'bar:baz' }
'http://:bar@example.com' | { user: nil, password: 'bar' }
'http://foo:@example.com' | { user: 'foo', password: nil }
'http://foo@example.com' | { user: 'foo', password: nil }
@@ -144,6 +145,10 @@ describe Gitlab::UrlSanitizer do
'http://foo:@example.com' | 'http://foo@example.com'
'http://:bar@example.com' | :same
'http://foo:bar@example.com' | :same
+ 'http://foo:g p@example.com' | 'http://foo:g%20p@example.com'
+ 'http://foo:s/h@example.com' | 'http://foo:s%2Fh@example.com'
+ 'http://t u:a#b@example.com' | 'http://t%20u:a%23b@example.com'
+ 'http://t+u:a#b@example.com' | 'http://t%2Bu:a%23b@example.com'
end
with_them do
@@ -159,7 +164,7 @@ describe Gitlab::UrlSanitizer do
url_sanitizer = described_class.new("https://foo:b?r@github.com/me/project.git")
expect(url_sanitizer.sanitized_url).to eq("https://github.com/me/project.git")
- expect(url_sanitizer.full_url).to eq("https://foo:b?r@github.com/me/project.git")
+ expect(url_sanitizer.full_url).to eq("https://foo:b%3Fr@github.com/me/project.git")
end
end
end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 20def4fefe2..a19b3c0ba66 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -133,7 +133,7 @@ describe Gitlab::UsageData do
expect(subject[:signup_enabled]).to eq(Gitlab::CurrentSettings.allow_signup?)
expect(subject[:ldap_enabled]).to eq(Gitlab.config.ldap.enabled)
expect(subject[:gravatar_enabled]).to eq(Gitlab::CurrentSettings.gravatar_enabled?)
- expect(subject[:omniauth_enabled]).to eq(Gitlab.config.omniauth.enabled)
+ expect(subject[:omniauth_enabled]).to eq(Gitlab::Auth.omniauth_enabled?)
expect(subject[:reply_by_email_enabled]).to eq(Gitlab::IncomingEmail.enabled?)
expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled)
expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled)
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
index 0469d984a40..9da06bb40f4 100644
--- a/spec/lib/gitlab/user_access_spec.rb
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -9,8 +9,8 @@ describe Gitlab::UserAccess do
describe '#can_push_to_branch?' do
describe 'push to none protected branch' do
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_push_to_branch?('random_branch')).to be_truthy
end
@@ -38,8 +38,8 @@ describe Gitlab::UserAccess do
expect(access.can_push_to_branch?('master')).to be_truthy
end
- it 'returns true if user is master' do
- empty_project.add_master(user)
+ it 'returns true if user is maintainer' do
+ empty_project.add_maintainer(user)
expect(project_access.can_push_to_branch?('master')).to be_truthy
end
@@ -83,8 +83,8 @@ describe Gitlab::UserAccess do
expect(access.can_push_to_branch?(branch.name)).to be_truthy
end
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_push_to_branch?(branch.name)).to be_truthy
end
@@ -113,8 +113,8 @@ describe Gitlab::UserAccess do
@branch = create :protected_branch, :developers_can_push, project: project
end
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_push_to_branch?(@branch.name)).to be_truthy
end
@@ -170,8 +170,8 @@ describe Gitlab::UserAccess do
@branch = create :protected_branch, :developers_can_merge, project: project
end
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
end
@@ -192,8 +192,8 @@ describe Gitlab::UserAccess do
describe '#can_create_tag?' do
describe 'push to none protected tag' do
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_create_tag?('random_tag')).to be_truthy
end
@@ -215,8 +215,8 @@ describe Gitlab::UserAccess do
let(:tag) { create(:protected_tag, project: project, name: "test") }
let(:not_existing_tag) { create :protected_tag, project: project }
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_create_tag?(tag.name)).to be_truthy
end
@@ -239,8 +239,8 @@ describe Gitlab::UserAccess do
@tag = create(:protected_tag, :developers_can_create, project: project)
end
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_create_tag?(@tag.name)).to be_truthy
end
@@ -261,8 +261,8 @@ describe Gitlab::UserAccess do
describe '#can_delete_branch?' do
describe 'delete unprotected branch' do
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_delete_branch?('random_branch')).to be_truthy
end
@@ -283,8 +283,8 @@ describe Gitlab::UserAccess do
describe 'delete protected branch' do
let(:branch) { create(:protected_branch, project: project, name: "test") }
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_delete_branch?(branch.name)).to be_truthy
end
diff --git a/spec/lib/gitlab/user_activities_spec.rb b/spec/lib/gitlab/user_activities_spec.rb
deleted file mode 100644
index 6bce2ee13cf..00000000000
--- a/spec/lib/gitlab/user_activities_spec.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::UserActivities, :clean_gitlab_redis_shared_state do
- let(:now) { Time.now }
-
- describe '.record' do
- context 'with no time given' do
- it 'uses Time.now and records an activity in SharedState' do
- Timecop.freeze do
- now # eager-load now
- described_class.record(42)
- end
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]])
- end
- end
- end
-
- context 'with a time given' do
- it 'uses the given time and records an activity in SharedState' do
- described_class.record(42, now)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]])
- end
- end
- end
- end
-
- describe '.delete' do
- context 'with a single key' do
- context 'and key exists' do
- it 'removes the pair from SharedState' do
- described_class.record(42, now)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]])
- end
-
- subject.delete(42)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
- end
- end
-
- context 'and key does not exist' do
- it 'removes the pair from SharedState' do
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
-
- subject.delete(42)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
- end
- end
- end
-
- context 'with multiple keys' do
- context 'and all keys exist' do
- it 'removes the pair from SharedState' do
- described_class.record(41, now)
- described_class.record(42, now)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['41', now.to_i.to_s], ['42', now.to_i.to_s]]])
- end
-
- subject.delete(41, 42)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
- end
- end
-
- context 'and some keys does not exist' do
- it 'removes the existing pair from SharedState' do
- described_class.record(42, now)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]])
- end
-
- subject.delete(41, 42)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
- end
- end
- end
- end
-
- describe 'Enumerable' do
- before do
- described_class.record(40, now)
- described_class.record(41, now)
- described_class.record(42, now)
- end
-
- it 'allows to read the activities sequentially' do
- expected = { '40' => now.to_i.to_s, '41' => now.to_i.to_s, '42' => now.to_i.to_s }
-
- actual = described_class.new.each_with_object({}) do |(key, time), actual|
- actual[key] = time
- end
-
- expect(actual).to eq(expected)
- end
-
- context 'with many records' do
- before do
- 1_000.times { |i| described_class.record(i, now) }
- end
-
- it 'is possible to loop through all the records' do
- expect(described_class.new.count).to eq(1_000)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 660671cefaf..23869f3d2da 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -36,22 +36,20 @@ describe Gitlab::Workhorse do
allow(described_class).to receive(:git_archive_cache_disabled?).and_return(cache_disabled)
end
- context 'when Gitaly workhorse_archive feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq('Gitlab-Workhorse-Send-Data')
- expect(command).to eq('git-archive')
- expect(params).to include(gitaly_params)
- end
+ expect(key).to eq('Gitlab-Workhorse-Send-Data')
+ expect(command).to eq('git-archive')
+ expect(params).to include(gitaly_params)
+ end
- context 'when archive caching is disabled' do
- let(:cache_disabled) { true }
+ context 'when archive caching is disabled' do
+ let(:cache_disabled) { true }
- it 'tells workhorse not to use the cache' do
- _, _, params = decode_workhorse_header(subject)
- expect(params).to include({ 'DisableCache' => true })
- end
+ it 'tells workhorse not to use the cache' do
+ _, _, params = decode_workhorse_header(subject)
+ expect(params).to include({ 'DisableCache' => true })
end
end
@@ -70,34 +68,22 @@ describe Gitlab::Workhorse do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
subject { described_class.send_git_patch(repository, diff_refs) }
- context 'when Gitaly workhorse_send_git_patch feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
-
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-format-patch")
- expect(params).to eq({
- 'GitalyServer' => {
- address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
- },
- 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
- repository: repository.gitaly_repository,
- left_commit_id: 'base',
- right_commit_id: 'head'
- ).to_json
- }.deep_stringify_keys)
- end
- end
-
- context 'when Gitaly workhorse_send_git_patch feature is disabled', :disable_gitaly do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-format-patch")
- expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head")
- end
+ expect(key).to eq("Gitlab-Workhorse-Send-Data")
+ expect(command).to eq("git-format-patch")
+ expect(params).to eq({
+ 'GitalyServer' => {
+ address: Gitlab::GitalyClient.address(project.repository_storage),
+ token: Gitlab::GitalyClient.token(project.repository_storage)
+ },
+ 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
+ repository: repository.gitaly_repository,
+ left_commit_id: 'base',
+ right_commit_id: 'head'
+ ).to_json
+ }.deep_stringify_keys)
end
end
@@ -143,34 +129,22 @@ describe Gitlab::Workhorse do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
subject { described_class.send_git_diff(repository, diff_refs) }
- context 'when Gitaly workhorse_send_git_diff feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
-
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-diff")
- expect(params).to eq({
- 'GitalyServer' => {
- address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
- },
- 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
- repository: repository.gitaly_repository,
- left_commit_id: 'base',
- right_commit_id: 'head'
- ).to_json
- }.deep_stringify_keys)
- end
- end
-
- context 'when Gitaly workhorse_send_git_diff feature is disabled', :disable_gitaly do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-diff")
- expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head")
- end
+ expect(key).to eq("Gitlab-Workhorse-Send-Data")
+ expect(command).to eq("git-diff")
+ expect(params).to eq({
+ 'GitalyServer' => {
+ address: Gitlab::GitalyClient.address(project.repository_storage),
+ token: Gitlab::GitalyClient.token(project.repository_storage)
+ },
+ 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
+ repository: repository.gitaly_repository,
+ left_commit_id: 'base',
+ right_commit_id: 'head'
+ ).to_json
+ }.deep_stringify_keys)
end
end
@@ -189,7 +163,7 @@ describe Gitlab::Workhorse do
end
it 'accepts a trailing newline' do
- open(described_class.secret_path, 'a') { |f| f.write "\n" }
+ File.open(described_class.secret_path, 'a') { |f| f.write "\n" }
expect(subject.length).to eq(32)
end
@@ -425,34 +399,22 @@ describe Gitlab::Workhorse do
subject { described_class.send_git_blob(repository, blob) }
- context 'when Gitaly workhorse_raw_show feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
-
- expect(key).to eq('Gitlab-Workhorse-Send-Data')
- expect(command).to eq('git-blob')
- expect(params).to eq({
- 'GitalyServer' => {
- address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
- },
- 'GetBlobRequest' => {
- repository: repository.gitaly_repository.to_h,
- oid: blob.id,
- limit: -1
- }
- }.deep_stringify_keys)
- end
- end
-
- context 'when Gitaly workhorse_raw_show feature is disabled', :disable_gitaly do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq('Gitlab-Workhorse-Send-Data')
- expect(command).to eq('git-blob')
- expect(params).to eq('RepoPath' => repository.path_to_repo, 'BlobId' => blob.id)
- end
+ expect(key).to eq('Gitlab-Workhorse-Send-Data')
+ expect(command).to eq('git-blob')
+ expect(params).to eq({
+ 'GitalyServer' => {
+ address: Gitlab::GitalyClient.address(project.repository_storage),
+ token: Gitlab::GitalyClient.token(project.repository_storage)
+ },
+ 'GetBlobRequest' => {
+ repository: repository.gitaly_repository.to_h,
+ oid: blob.id,
+ limit: -1
+ }
+ }.deep_stringify_keys)
end
end
diff --git a/spec/lib/uploaded_file_spec.rb b/spec/lib/uploaded_file_spec.rb
index cc99e7e8911..a2f5c2e7121 100644
--- a/spec/lib/uploaded_file_spec.rb
+++ b/spec/lib/uploaded_file_spec.rb
@@ -1,24 +1,28 @@
require 'spec_helper'
describe UploadedFile do
- describe ".from_params" do
- let(:temp_dir) { Dir.tmpdir }
- let(:temp_file) { Tempfile.new("test", temp_dir) }
- let(:upload_path) { nil }
+ let(:temp_dir) { Dir.tmpdir }
+ let(:temp_file) { Tempfile.new("test", temp_dir) }
- subject do
- described_class.from_params(params, :file, upload_path)
- end
+ before do
+ FileUtils.touch(temp_file)
+ end
- before do
- FileUtils.touch(temp_file)
- end
+ after do
+ FileUtils.rm_f(temp_file)
+ end
+
+ describe ".from_params" do
+ let(:upload_path) { nil }
after do
- FileUtils.rm_f(temp_file)
FileUtils.rm_r(upload_path) if upload_path
end
+ subject do
+ described_class.from_params(params, :file, upload_path)
+ end
+
context 'when valid file is specified' do
context 'only local path is specified' do
let(:params) do
@@ -37,7 +41,7 @@ describe UploadedFile do
context 'all parameters are specified' do
let(:params) do
{ 'file.path' => temp_file.path,
- 'file.name' => 'my_file.txt',
+ 'file.name' => 'dir/my file&.txt',
'file.type' => 'my/type',
'file.sha256' => 'sha256',
'file.remote_id' => 'remote_id' }
@@ -48,7 +52,7 @@ describe UploadedFile do
end
it "generates filename from path" do
- expect(subject.original_filename).to eq('my_file.txt')
+ expect(subject.original_filename).to eq('my_file_.txt')
expect(subject.content_type).to eq('my/type')
expect(subject.sha256).to eq('sha256')
expect(subject.remote_id).to eq('remote_id')
@@ -113,4 +117,11 @@ describe UploadedFile do
end
end
end
+
+ describe '#sanitize_filename' do
+ it { expect(described_class.new(temp_file.path).sanitize_filename('spaced name')).to eq('spaced_name') }
+ it { expect(described_class.new(temp_file.path).sanitize_filename('#$%^&')).to eq('_____') }
+ it { expect(described_class.new(temp_file.path).sanitize_filename('..')).to eq('_..') }
+ it { expect(described_class.new(temp_file.path).sanitize_filename('')).to eq('unnamed') }
+ end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index a9a45367b4a..ff1a5aa2536 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -314,6 +314,17 @@ describe Notify do
end
end
+ describe 'that are new with a description' do
+ subject { described_class.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
+
+ it_behaves_like 'it should show Gmail Actions View Merge request link'
+ it_behaves_like "an unsubscribeable thread"
+
+ it 'contains the description' do
+ is_expected.to have_body_text(merge_request.description)
+ end
+ end
+
describe 'that have been relabeled' do
subject { described_class.relabeled_merge_request_email(recipient.id, merge_request.id, %w[foo bar baz], current_user.id) }
@@ -541,7 +552,7 @@ describe Notify do
describe 'project access requested' do
let(:project) do
create(:project, :public, :access_requestable) do |project|
- project.add_master(project.owner)
+ project.add_maintainer(project.owner)
end
end
@@ -616,8 +627,8 @@ describe Notify do
end
describe 'project invitation' do
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
- let(:project_member) { invite_to_project(project, inviter: master) }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
+ let(:project_member) { invite_to_project(project, inviter: maintainer) }
subject { described_class.member_invited_email('project', project_member.id, project_member.invite_token) }
@@ -636,9 +647,9 @@ describe Notify do
describe 'project invitation accepted' do
let(:invited_user) { create(:user, name: 'invited user') }
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:project_member) do
- invitee = invite_to_project(project, inviter: master)
+ invitee = invite_to_project(project, inviter: maintainer)
invitee.accept_invite!(invited_user)
invitee
end
@@ -659,14 +670,14 @@ describe Notify do
end
describe 'project invitation declined' do
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:project_member) do
- invitee = invite_to_project(project, inviter: master)
+ invitee = invite_to_project(project, inviter: maintainer)
invitee.decline_invite!
invitee
end
- subject { described_class.member_invite_declined_email('project', project.id, project_member.invite_email, master.id) }
+ subject { described_class.member_invite_declined_email('project', project.id, project_member.invite_email, maintainer.id) }
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
@@ -1355,7 +1366,8 @@ describe Notify do
it 'only sends the text template' do
stub_application_setting(html_emails_enabled: false)
- EmailTemplateInterceptor.delivering_email(multipart_mail)
+ Gitlab::Email::Hook::EmailTemplateInterceptor
+ .delivering_email(multipart_mail)
expect(multipart_mail).to have_part_with('text/plain')
expect(multipart_mail).not_to have_part_with('text/html')
@@ -1366,7 +1378,8 @@ describe Notify do
it 'sends a multipart message' do
stub_application_setting(html_emails_enabled: true)
- EmailTemplateInterceptor.delivering_email(multipart_mail)
+ Gitlab::Email::Hook::EmailTemplateInterceptor
+ .delivering_email(multipart_mail)
expect(multipart_mail).to have_part_with('text/plain')
expect(multipart_mail).to have_part_with('text/html')
diff --git a/spec/mailers/previews/notify_preview.rb b/spec/mailers/previews/notify_preview.rb
deleted file mode 100644
index e32fd0bd120..00000000000
--- a/spec/mailers/previews/notify_preview.rb
+++ /dev/null
@@ -1,170 +0,0 @@
-class NotifyPreview < ActionMailer::Preview
- def note_merge_request_email_for_individual_note
- note_email(:note_merge_request_email) do
- note = <<-MD.strip_heredoc
- This is an individual note on a merge request :smiley:
-
- In this notification email, we expect to see:
-
- - The note contents (that's what you're looking at)
- - A link to view this note on Gitlab
- - An explanation for why the user is receiving this notification
- MD
-
- create_note(noteable_type: 'merge_request', noteable_id: merge_request.id, note: note)
- end
- end
-
- def note_merge_request_email_for_discussion
- note_email(:note_merge_request_email) do
- note = <<-MD.strip_heredoc
- This is a new discussion on a merge request :smiley:
-
- In this notification email, we expect to see:
-
- - A line saying who started this discussion
- - The note contents (that's what you're looking at)
- - A link to view this discussion on Gitlab
- - An explanation for why the user is receiving this notification
- MD
-
- create_note(noteable_type: 'merge_request', noteable_id: merge_request.id, type: 'DiscussionNote', note: note)
- end
- end
-
- def note_merge_request_email_for_diff_discussion
- note_email(:note_merge_request_email) do
- note = <<-MD.strip_heredoc
- This is a new discussion on a merge request :smiley:
-
- In this notification email, we expect to see:
-
- - A line saying who started this discussion and on what file
- - The diff
- - The note contents (that's what you're looking at)
- - A link to view this discussion on Gitlab
- - An explanation for why the user is receiving this notification
- MD
-
- position = Gitlab::Diff::Position.new(
- old_path: "files/ruby/popen.rb",
- new_path: "files/ruby/popen.rb",
- old_line: nil,
- new_line: 14,
- diff_refs: merge_request.diff_refs
- )
-
- create_note(noteable_type: 'merge_request', noteable_id: merge_request.id, type: 'DiffNote', position: position, note: note)
- end
- end
-
- def closed_issue_email
- Notify.closed_issue_email(user.id, issue.id, user.id).message
- end
-
- def issue_status_changed_email
- Notify.issue_status_changed_email(user.id, issue.id, 'closed', user.id).message
- end
-
- def closed_merge_request_email
- Notify.closed_merge_request_email(user.id, issue.id, user.id).message
- end
-
- def merge_request_status_email
- Notify.merge_request_status_email(user.id, merge_request.id, 'closed', user.id).message
- end
-
- def merged_merge_request_email
- Notify.merged_merge_request_email(user.id, merge_request.id, user.id).message
- end
-
- def member_access_denied_email
- Notify.member_access_denied_email('project', project.id, user.id).message
- end
-
- def member_access_granted_email
- Notify.member_access_granted_email('project', user.id).message
- end
-
- def member_access_requested_email
- Notify.member_access_requested_email('group', user.id, 'some@example.com').message
- end
-
- def member_invite_accepted_email
- Notify.member_invite_accepted_email('project', user.id).message
- end
-
- def member_invite_declined_email
- Notify.member_invite_declined_email(
- 'project',
- project.id,
- 'invite@example.com',
- user.id
- ).message
- end
-
- def member_invited_email
- Notify.member_invited_email('project', user.id, '1234').message
- end
-
- def pages_domain_enabled_email
- cleanup do
- pages_domain = PagesDomain.new(domain: 'my.example.com', project: project, verified_at: Time.now, enabled_until: 1.week.from_now)
-
- Notify.pages_domain_enabled_email(pages_domain, user).message
- end
- end
-
- def pipeline_success_email
- Notify.pipeline_success_email(pipeline, pipeline.user.try(:email))
- end
-
- def pipeline_failed_email
- Notify.pipeline_failed_email(pipeline, pipeline.user.try(:email))
- end
-
- private
-
- def project
- @project ||= Project.find_by_full_path('gitlab-org/gitlab-test')
- end
-
- def issue
- @merge_request ||= project.issues.first
- end
-
- def merge_request
- @merge_request ||= project.merge_requests.first
- end
-
- def pipeline
- @pipeline = Ci::Pipeline.last
- end
-
- def user
- @user ||= User.last
- end
-
- def create_note(params)
- Notes::CreateService.new(project, user, params).execute
- end
-
- def note_email(method)
- cleanup do
- note = yield
-
- Notify.public_send(method, user.id, note)
- end
- end
-
- def cleanup
- email = nil
-
- ActiveRecord::Base.transaction do
- email = yield
- raise ActiveRecord::Rollback
- end
-
- email
- end
-end
diff --git a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
index 4395e2f8264..5c6f213e15b 100644
--- a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
+++ b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
@@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnServices, :migration, :sidekiq do
end
it 'correctly processes services' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(services_table.where(confidential_note_events: nil).count).to eq 4
expect(services_table.where(confidential_note_events: true).count).to eq 1
diff --git a/spec/migrations/add_foreign_key_from_notification_settings_to_users_spec.rb b/spec/migrations/add_foreign_key_from_notification_settings_to_users_spec.rb
new file mode 100644
index 00000000000..656d4f75e3b
--- /dev/null
+++ b/spec/migrations/add_foreign_key_from_notification_settings_to_users_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20180710162338_add_foreign_key_from_notification_settings_to_users.rb')
+
+describe AddForeignKeyFromNotificationSettingsToUsers, :migration do
+ let(:notification_settings) { table(:notification_settings) }
+ let(:users) { table(:users) }
+ let(:projects) { table(:projects) }
+
+ before do
+ users.create!(email: 'email@email.com', name: 'foo', username: 'foo', projects_limit: 0)
+ projects.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce', namespace_id: 1)
+ end
+
+ describe 'removal of orphans without user' do
+ let!(:notification_setting_without_user) { create_notification_settings!(user_id: 123) }
+ let!(:notification_setting_with_user) { create_notification_settings!(user_id: users.last.id) }
+
+ it 'removes orphaned notification_settings without user' do
+ expect { migrate! }.to change { notification_settings.count }.by(-1)
+ end
+
+ it "doesn't remove notification_settings with valid user" do
+ expect { migrate! }.not_to change { notification_setting_with_user.reload }
+ end
+ end
+
+ def create_notification_settings!(**opts)
+ notification_settings.create!(
+ source_id: projects.last.id,
+ source_type: 'Project',
+ user_id: users.last.id,
+ **opts)
+ end
+end
diff --git a/spec/migrations/enqueue_delete_diff_files_workers_spec.rb b/spec/migrations/enqueue_delete_diff_files_workers_spec.rb
index 686027822b8..6bae870920c 100644
--- a/spec/migrations/enqueue_delete_diff_files_workers_spec.rb
+++ b/spec/migrations/enqueue_delete_diff_files_workers_spec.rb
@@ -2,47 +2,16 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20180619121030_enqueue_delete_diff_files_workers.rb')
describe EnqueueDeleteDiffFilesWorkers, :migration, :sidekiq do
- let(:merge_request_diffs) { table(:merge_request_diffs) }
- let(:merge_requests) { table(:merge_requests) }
- let(:namespaces) { table(:namespaces) }
- let(:projects) { table(:projects) }
-
- before do
- stub_const("#{described_class.name}::BATCH_SIZE", 2)
-
- namespaces.create!(id: 1, name: 'gitlab', path: 'gitlab')
- projects.create!(id: 1, namespace_id: 1, name: 'gitlab', path: 'gitlab')
-
- merge_requests.create!(id: 1, target_project_id: 1, source_project_id: 1, target_branch: 'feature', source_branch: 'master', state: 'merged')
-
- merge_request_diffs.create!(id: 1, merge_request_id: 1, state: 'collected')
- merge_request_diffs.create!(id: 2, merge_request_id: 1, state: 'without_files')
- merge_request_diffs.create!(id: 3, merge_request_id: 1, state: 'collected')
- merge_request_diffs.create!(id: 4, merge_request_id: 1, state: 'collected')
- merge_request_diffs.create!(id: 5, merge_request_id: 1, state: 'empty')
- merge_request_diffs.create!(id: 6, merge_request_id: 1, state: 'collected')
-
- merge_requests.update(1, latest_merge_request_diff_id: 6)
- end
-
- it 'correctly schedules diff file deletion workers' do
+ it 'correctly schedules diff files deletion schedulers' do
Sidekiq::Testing.fake! do
- Timecop.freeze do
- migrate!
+ expect(BackgroundMigrationWorker)
+ .to receive(:perform_async)
+ .with(described_class::SCHEDULER)
+ .and_call_original
- # 1st batch
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(8.minutes, 1)
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(9.minutes, 3)
- # 2nd batch
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(16.minutes, 4)
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(17.minutes, 6)
- expect(BackgroundMigrationWorker.jobs.size).to eq(4)
- end
- end
- end
+ migrate!
- it 'migrates the data' do
- expect { migrate! }.to change { merge_request_diffs.where(state: 'without_files').count }
- .from(1).to(4)
+ expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ end
end
end
diff --git a/spec/migrations/generate_missing_routes_spec.rb b/spec/migrations/generate_missing_routes_spec.rb
new file mode 100644
index 00000000000..32515d353b0
--- /dev/null
+++ b/spec/migrations/generate_missing_routes_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20180702134423_generate_missing_routes.rb')
+
+describe GenerateMissingRoutes, :migration do
+ describe '#up' do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:routes) { table(:routes) }
+
+ it 'creates routes for projects without a route' do
+ namespace = namespaces.create!(name: 'GitLab', path: 'gitlab')
+
+ routes.create!(
+ path: 'gitlab',
+ source_type: 'Namespace',
+ source_id: namespace.id
+ )
+
+ project = projects.create!(
+ name: 'GitLab CE',
+ path: 'gitlab-ce',
+ namespace_id: namespace.id
+ )
+
+ described_class.new.up
+
+ route = routes.where(source_type: 'Project').take
+
+ expect(route.source_id).to eq(project.id)
+ expect(route.path).to eq("gitlab/gitlab-ce-#{project.id}")
+ end
+
+ it 'creates routes for namespaces without a route' do
+ namespace = namespaces.create!(name: 'GitLab', path: 'gitlab')
+
+ described_class.new.up
+
+ route = routes.where(source_type: 'Namespace').take
+
+ expect(route.source_id).to eq(namespace.id)
+ expect(route.path).to eq("gitlab-#{namespace.id}")
+ end
+
+ it 'does not create routes for namespaces that already have a route' do
+ namespace = namespaces.create!(name: 'GitLab', path: 'gitlab')
+
+ routes.create!(
+ path: 'gitlab',
+ source_type: 'Namespace',
+ source_id: namespace.id
+ )
+
+ described_class.new.up
+
+ expect(routes.count).to eq(1)
+ end
+
+ it 'does not create routes for projects that already have a route' do
+ namespace = namespaces.create!(name: 'GitLab', path: 'gitlab')
+
+ routes.create!(
+ path: 'gitlab',
+ source_type: 'Namespace',
+ source_id: namespace.id
+ )
+
+ project = projects.create!(
+ name: 'GitLab CE',
+ path: 'gitlab-ce',
+ namespace_id: namespace.id
+ )
+
+ routes.create!(
+ path: 'gitlab/gitlab-ce',
+ source_type: 'Project',
+ source_id: project.id
+ )
+
+ described_class.new.up
+
+ expect(routes.count).to eq(2)
+ end
+ end
+end
diff --git a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
index dd2b08099f2..495e86ee888 100644
--- a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
+++ b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
@@ -14,7 +14,7 @@ describe IssuesMovedToIdForeignKey, :migration, schema: 20171114150259 do
it 'removes the orphaned moved_to_id' do
subject.down
- issue_third.update_attributes(moved_to_id: 100000)
+ issue_third.update(moved_to_id: 100000)
subject.up
diff --git a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
index df009cec25c..ba4c66057d4 100644
--- a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
+++ b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
@@ -12,7 +12,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
class KubernetesService < ActiveRecord::Base
self.table_name = 'services'
- serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize
+ serialize :properties, JSON
default_value_for :active, true
default_value_for :type, 'KubernetesService'
@@ -175,7 +175,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
end
end
- def tr(s)
- s.delete("'")
+ def tr(str)
+ str.delete("'")
end
end
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
index ac34efa4f9d..6219a67c900 100644
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -4,12 +4,10 @@ require 'spec_helper'
require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_worker_jobs.rb')
describe MigrateProcessCommitWorkerJobs do
- let(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ set(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ set(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
let(:commit) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.commit.raw.rugged_commit
- end
+ Gitlab::Git::Commit.last(project.repository.raw)
end
describe 'Project' do
@@ -28,32 +26,13 @@ describe MigrateProcessCommitWorkerJobs do
end
end
- describe '#repository_storage_path' do
- it 'returns the storage path for the repository' do
- migration_project = described_class::Project
- .find_including_path(project.id)
-
- expect(File.directory?(migration_project.repository_storage_path))
- .to eq(true)
- end
- end
-
- describe '#repository_path' do
- it 'returns the path to the repository' do
- migration_project = described_class::Project
- .find_including_path(project.id)
-
- expect(File.directory?(migration_project.repository_path)).to eq(true)
- end
- end
-
describe '#repository' do
- it 'returns a Rugged::Repository' do
+ it 'returns a mock implemention of ::Repository' do
migration_project = described_class::Project
.find_including_path(project.id)
- expect(migration_project.repository)
- .to be_an_instance_of(Rugged::Repository)
+ expect(migration_project.repository).to respond_to(:storage)
+ expect(migration_project.repository).to respond_to(:gitaly_repository)
end
end
end
@@ -71,7 +50,7 @@ describe MigrateProcessCommitWorkerJobs do
before do
Sidekiq.redis do |redis|
- job = JSON.dump(args: [project.id, user.id, commit.oid])
+ job = JSON.dump(args: [project.id, user.id, commit.id])
redis.lpush('queue:process_commit', job)
end
end
@@ -87,9 +66,10 @@ describe MigrateProcessCommitWorkerJobs do
end
it 'skips jobs using commits that no longer exist' do
- allow_any_instance_of(Rugged::Repository).to receive(:lookup)
- .with(commit.oid)
- .and_raise(Rugged::OdbError)
+ allow_any_instance_of(Gitlab::GitalyClient::CommitService)
+ .to receive(:find_commit)
+ .with(commit.id)
+ .and_return(nil)
migration.up
@@ -103,11 +83,7 @@ describe MigrateProcessCommitWorkerJobs do
end
it 'encodes data to UTF-8' do
- allow_any_instance_of(Rugged::Repository).to receive(:lookup)
- .with(commit.oid)
- .and_return(commit)
-
- allow(commit).to receive(:message)
+ allow(commit).to receive(:body)
.and_return('김치'.force_encoding('BINARY'))
migration.up
@@ -139,7 +115,7 @@ describe MigrateProcessCommitWorkerJobs do
end
it 'includes the commit ID' do
- expect(commit_hash['id']).to eq(commit.oid)
+ expect(commit_hash['id']).to eq(commit.id)
end
it 'includes the commit message' do
@@ -151,27 +127,27 @@ describe MigrateProcessCommitWorkerJobs do
end
it 'includes the author date' do
- expect(commit_hash['authored_date']).to eq(commit.author[:time].to_s)
+ expect(commit_hash['authored_date']).to eq(commit.authored_date.to_s)
end
it 'includes the author name' do
- expect(commit_hash['author_name']).to eq(commit.author[:name])
+ expect(commit_hash['author_name']).to eq(commit.author_name)
end
it 'includes the author Email' do
- expect(commit_hash['author_email']).to eq(commit.author[:email])
+ expect(commit_hash['author_email']).to eq(commit.author_email)
end
it 'includes the commit date' do
- expect(commit_hash['committed_date']).to eq(commit.committer[:time].to_s)
+ expect(commit_hash['committed_date']).to eq(commit.committed_date.to_s)
end
it 'includes the committer name' do
- expect(commit_hash['committer_name']).to eq(commit.committer[:name])
+ expect(commit_hash['committer_name']).to eq(commit.committer_name)
end
it 'includes the committer Email' do
- expect(commit_hash['committer_email']).to eq(commit.committer[:email])
+ expect(commit_hash['committer_email']).to eq(commit.committer_email)
end
end
end
@@ -185,7 +161,7 @@ describe MigrateProcessCommitWorkerJobs do
before do
Sidekiq.redis do |redis|
- job = JSON.dump(args: [project.id, user.id, commit.oid])
+ job = JSON.dump(args: [project.id, user.id, commit.id])
redis.lpush('queue:process_commit', job)
migration.up
@@ -214,7 +190,7 @@ describe MigrateProcessCommitWorkerJobs do
end
it 'includes the commit SHA' do
- expect(job['args'][2]).to eq(commit.oid)
+ expect(job['args'][2]).to eq(commit.id)
end
end
end
diff --git a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
index a837498e1b1..dd6f5325750 100644
--- a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
+++ b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
@@ -44,7 +44,7 @@ describe MigrateStageIdReferenceInBackground, :migration, :sidekiq do
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(jobs.where(stage_id: nil).count).to eq 5
migrate!
diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb
index ce35276cbf5..5483e24fce7 100644
--- a/spec/migrations/migrate_stages_statuses_spec.rb
+++ b/spec/migrations/migrate_stages_statuses_spec.rb
@@ -34,7 +34,7 @@ describe MigrateStagesStatuses, :sidekiq, :migration do
end
it 'correctly migrates stages statuses' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(stages.where(status: nil).count).to eq 3
migrate!
diff --git a/spec/migrations/normalize_ldap_extern_uids_spec.rb b/spec/migrations/normalize_ldap_extern_uids_spec.rb
index 56a78f52802..a23a5d54e0a 100644
--- a/spec/migrations/normalize_ldap_extern_uids_spec.rb
+++ b/spec/migrations/normalize_ldap_extern_uids_spec.rb
@@ -27,18 +27,18 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do
migrate!
expect(BackgroundMigrationWorker.jobs[0]['args']).to eq([described_class::MIGRATION, [1, 2]])
- expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(5.minutes.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(2.minutes.from_now.to_f)
expect(BackgroundMigrationWorker.jobs[1]['args']).to eq([described_class::MIGRATION, [3, 4]])
- expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(10.minutes.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(4.minutes.from_now.to_f)
expect(BackgroundMigrationWorker.jobs[2]['args']).to eq([described_class::MIGRATION, [5, 5]])
- expect(BackgroundMigrationWorker.jobs[2]['at']).to eq(15.minutes.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[2]['at']).to eq(6.minutes.from_now.to_f)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
end
it 'migrates the LDAP identities' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
migrate!
identities.where(id: 1..4).each do |identity|
expect(identity.extern_uid).to eq("uid=foo #{identity.id},ou=people,dc=example,dc=com")
@@ -47,7 +47,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do
end
it 'does not modify non-LDAP identities' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
migrate!
identity = identities.last
expect(identity.extern_uid).to eq(" uid = foo 5, ou = People, dc = example, dc = com ")
diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
index ed306fb3d62..96bef107599 100644
--- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
+++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
@@ -20,7 +20,7 @@ describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(GpgKeySubkey.count).to eq(0)
migrate!
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
index d230f064444..9f7e47bae0d 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
@@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrations, :migration, :sidekiq do
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL'
expect(merge_request_diffs.where(non_empty).count).to eq 3
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
index 1aab4ae1650..5bcb923af7b 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
@@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrationsTakeTwo, :migration, :sidekiq do
end
it 'migrates the data' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL'
expect(merge_request_diffs.where(non_empty).count).to eq 3
diff --git a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
index c9fdbe95d13..76fe16581ac 100644
--- a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
@@ -53,7 +53,7 @@ describe ScheduleMergeRequestLatestMergeRequestDiffIdMigrations, :migration, :si
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(merge_requests_table.where(latest_merge_request_diff_id: nil).count).to eq 3
migrate!
diff --git a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
index 027f4a91c90..fa4ddd5fbc7 100644
--- a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
+++ b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
@@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnWebhooks, :migration, :sidekiq do
end
it 'correctly processes web hooks' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(web_hooks_table.where(confidential_note_events: nil).count).to eq 4
expect(web_hooks_table.where(confidential_note_events: true).count).to eq 1
diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb
index 7e75d5a5411..6dba132184c 100644
--- a/spec/models/ci/build_metadata_spec.rb
+++ b/spec/models/ci/build_metadata_spec.rb
@@ -30,7 +30,7 @@ describe Ci::BuildMetadata do
context 'when runner is assigned to the job' do
before do
- build.update_attributes(runner: runner)
+ build.update(runner: runner)
end
context 'when runner timeout is lower than project timeout' do
diff --git a/spec/models/ci/build_runner_session_spec.rb b/spec/models/ci/build_runner_session_spec.rb
new file mode 100644
index 00000000000..35622366829
--- /dev/null
+++ b/spec/models/ci/build_runner_session_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe Ci::BuildRunnerSession, model: true do
+ let!(:build) { create(:ci_build, :with_runner_session) }
+
+ subject { build.runner_session }
+
+ it { is_expected.to belong_to(:build) }
+
+ it { is_expected.to validate_presence_of(:build) }
+ it { is_expected.to validate_presence_of(:url).with_message('must be a valid URL') }
+
+ describe '#terminal_specification' do
+ let(:terminal_specification) { subject.terminal_specification }
+
+ it 'returns empty hash if no url' do
+ subject.url = ''
+
+ expect(terminal_specification).to be_empty
+ end
+
+ context 'when url is present' do
+ it 'returns ca_pem nil if empty certificate' do
+ subject.certificate = ''
+
+ expect(terminal_specification[:ca_pem]).to be_nil
+ end
+
+ it 'adds Authorization header if authorization is present' do
+ subject.authorization = 'whatever'
+
+ expect(terminal_specification[:headers]).to include(Authorization: ['whatever'])
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 6758adc59eb..32b8755ee9a 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -19,6 +19,7 @@ describe Ci::Build do
it { is_expected.to belong_to(:erased_by) }
it { is_expected.to have_many(:deployments) }
it { is_expected.to have_many(:trace_sections)}
+ it { is_expected.to have_one(:runner_session)}
it { is_expected.to validate_presence_of(:ref) }
it { is_expected.to respond_to(:has_trace?) }
it { is_expected.to respond_to(:trace) }
@@ -42,6 +43,20 @@ describe Ci::Build do
end
end
+ describe 'status' do
+ context 'when transitioning to any state from running' do
+ it 'removes runner_session' do
+ %w(success drop cancel).each do |event|
+ build = FactoryBot.create(:ci_build, :running, :with_runner_session, pipeline: pipeline)
+
+ build.fire_events!(event)
+
+ expect(build.reload.runner_session).to be_nil
+ end
+ end
+ end
+ end
+
describe '.manual_actions' do
let!(:manual_but_created) { create(:ci_build, :manual, status: :created, pipeline: pipeline) }
let!(:manual_but_succeeded) { create(:ci_build, :manual, status: :success, pipeline: pipeline) }
@@ -136,6 +151,42 @@ describe Ci::Build do
end
end
+ describe '.with_test_reports' do
+ subject { described_class.with_test_reports }
+
+ context 'when build has a test report' do
+ let!(:build) { create(:ci_build, :success, :test_reports) }
+
+ it 'selects the build' do
+ is_expected.to eq([build])
+ end
+ end
+
+ context 'when build does not have test reports' do
+ let!(:build) { create(:ci_build, :success, :trace_artifact) }
+
+ it 'does not select the build' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when there are multiple builds with test reports' do
+ let!(:builds) { create_list(:ci_build, 5, :success, :test_reports) }
+
+ it 'does not execute a query for selecting job artifact one by one' do
+ recorded = ActiveRecord::QueryRecorder.new do
+ subject.each do |build|
+ Ci::JobArtifact::TEST_REPORT_FILE_TYPES.each do |file_type|
+ build.public_send("job_artifacts_#{file_type}").file.exists?
+ end
+ end
+ end
+
+ expect(recorded.count).to eq(2)
+ end
+ end
+ end
+
describe '#actionize' do
context 'when build is a created' do
before do
@@ -171,18 +222,18 @@ describe Ci::Build do
let(:runner) { create(:ci_runner, :project, projects: [build.project]) }
before do
- runner.update_attributes(contacted_at: 1.second.ago)
+ runner.update(contacted_at: 1.second.ago)
end
it { is_expected.to be_truthy }
it 'that is inactive' do
- runner.update_attributes(active: false)
+ runner.update(active: false)
is_expected.to be_falsey
end
it 'that is not online' do
- runner.update_attributes(contacted_at: nil)
+ runner.update(contacted_at: nil)
is_expected.to be_falsey
end
@@ -246,7 +297,7 @@ describe Ci::Build do
context 'artifacts metadata does not exist' do
before do
- build.update_attributes(legacy_artifacts_metadata: nil)
+ build.update(legacy_artifacts_metadata: nil)
end
it { is_expected.to be_falsy }
@@ -499,6 +550,60 @@ describe Ci::Build do
end
end
+ describe '#has_test_reports?' do
+ subject { build.has_test_reports? }
+
+ context 'when build has a test report' do
+ let(:build) { create(:ci_build, :test_reports) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when build does not have test reports' do
+ let(:build) { create(:ci_build, :artifacts) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '#erase_test_reports!' do
+ subject { build.erase_test_reports! }
+
+ context 'when build has a test report' do
+ let!(:build) { create(:ci_build, :test_reports) }
+
+ it 'removes a test report' do
+ subject
+
+ expect(build.has_test_reports?).to be_falsy
+ end
+ end
+
+ context 'when build does not have test reports' do
+ let!(:build) { create(:ci_build, :artifacts) }
+
+ it 'does not erase anything' do
+ expect { subject }.not_to change { Ci::JobArtifact.count }
+ end
+ end
+ end
+
+ describe '#has_old_trace?' do
+ subject { build.has_old_trace? }
+
+ context 'when old trace exists' do
+ before do
+ build.update_column(:trace, 'old trace')
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when old trace does not exist' do
+ it { is_expected.to be_falsy }
+ end
+ end
+
describe '#trace=' do
it "expect to fail trace=" do
expect { build.trace = "new" }.to raise_error(NotImplementedError)
@@ -518,16 +623,32 @@ describe Ci::Build do
end
describe '#erase_old_trace!' do
- subject { build.send(:read_attribute, :trace) }
+ subject { build.erase_old_trace! }
- before do
- build.send(:write_attribute, :trace, 'old trace')
+ context 'when old trace exists' do
+ before do
+ build.update_column(:trace, 'old trace')
+ end
+
+ it "erases old trace" do
+ subject
+
+ expect(build.old_trace).to be_nil
+ end
+
+ it "executes UPDATE query" do
+ recorded = ActiveRecord::QueryRecorder.new { subject }
+
+ expect(recorded.log.select { |l| l.match?(/UPDATE.*ci_builds/) }.count).to eq(1)
+ end
end
- it "expect to receive data from database" do
- build.erase_old_trace!
+ context 'when old trace does not exist' do
+ it 'does not execute UPDATE query' do
+ recorded = ActiveRecord::QueryRecorder.new { subject }
- is_expected.to be_nil
+ expect(recorded.log.select { |l| l.match?(/UPDATE.*ci_builds/) }.count).to eq(0)
+ end
end
end
@@ -729,6 +850,10 @@ describe Ci::Build do
expect(build.artifacts_metadata.exists?).to be_falsy
end
+ it 'removes test reports' do
+ expect(build.job_artifacts.test_reports.count).to eq(0)
+ end
+
it 'erases build trace in trace file' do
expect(build).not_to have_trace
end
@@ -760,7 +885,7 @@ describe Ci::Build do
context 'build is erasable' do
context 'new artifacts' do
- let!(:build) { create(:ci_build, :trace_artifact, :success, :artifacts) }
+ let!(:build) { create(:ci_build, :test_reports, :trace_artifact, :success, :artifacts) }
describe '#erase' do
before do
@@ -1488,7 +1613,7 @@ describe Ci::Build do
expect(ProjectStatistics)
.not_to receive(:increment_statistic)
- build.project.update_attributes(pending_delete: true)
+ build.project.update(pending_delete: true)
build.project.destroy!
end
end
@@ -1566,6 +1691,7 @@ describe Ci::Build do
{ key: 'CI_JOB_NAME', value: 'test', public: true },
{ key: 'CI_JOB_STAGE', value: 'test', public: true },
{ key: 'CI_COMMIT_SHA', value: build.sha, public: true },
+ { key: 'CI_COMMIT_BEFORE_SHA', value: build.before_sha, public: true },
{ key: 'CI_COMMIT_REF_NAME', value: build.ref, public: true },
{ key: 'CI_COMMIT_REF_SLUG', value: build.ref_slug, public: true },
{ key: 'CI_BUILD_REF', value: build.sha, public: true },
@@ -1615,7 +1741,7 @@ describe Ci::Build do
end
before do
- build.update_attributes(user: user)
+ build.update(user: user)
end
it { user_variables.each { |v| is_expected.to include(v) } }
@@ -1693,7 +1819,7 @@ describe Ci::Build do
context 'when build started manually' do
before do
- build.update_attributes(when: :manual)
+ build.update(when: :manual)
end
let(:manual_variable) do
@@ -1709,7 +1835,7 @@ describe Ci::Build do
end
before do
- build.update_attributes(tag: true)
+ build.update(tag: true)
end
it { is_expected.to include(tag_variable) }
@@ -2221,6 +2347,34 @@ describe Ci::Build do
end
end
+ describe '#yaml_variables' do
+ before do
+ build.update_attribute(:yaml_variables, variables)
+ end
+
+ context 'when serialized valu is a symbolized hash' do
+ let(:variables) do
+ [{ key: :VARIABLE, value: 'my value 1' }]
+ end
+
+ it 'keeps symbolizes keys and stringifies variables names' do
+ expect(build.yaml_variables)
+ .to eq [{ key: 'VARIABLE', value: 'my value 1' }]
+ end
+ end
+
+ context 'when serialized value is a hash with string keys' do
+ let(:variables) do
+ [{ 'key' => :VARIABLE, 'value' => 'my value 2' }]
+ end
+
+ it 'symblizes variables hash' do
+ expect(build.yaml_variables)
+ .to eq [{ key: 'VARIABLE', value: 'my value 2' }]
+ end
+ end
+ end
+
describe 'state transition: any => [:pending]' do
let(:build) { create(:ci_build, :created) }
@@ -2291,18 +2445,18 @@ describe Ci::Build do
end
end
- describe 'state transition: any => [:running]' do
+ describe '#has_valid_build_dependencies?' do
shared_examples 'validation is active' do
context 'when depended job has not been completed yet' do
let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
- it { expect { job.run! }.not_to raise_error }
+ it { expect(job).to have_valid_build_dependencies }
end
context 'when artifacts of depended job has been expired' do
let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
- it { expect { job.run! }.to raise_error(Ci::Build::MissingDependenciesError) }
+ it { expect(job).not_to have_valid_build_dependencies }
end
context 'when artifacts of depended job has been erased' do
@@ -2312,7 +2466,7 @@ describe Ci::Build do
pre_stage_job.erase
end
- it { expect { job.run! }.to raise_error(Ci::Build::MissingDependenciesError) }
+ it { expect(job).not_to have_valid_build_dependencies }
end
end
@@ -2320,12 +2474,13 @@ describe Ci::Build do
context 'when depended job has not been completed yet' do
let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
- it { expect { job.run! }.not_to raise_error }
+ it { expect(job).to have_valid_build_dependencies }
end
+
context 'when artifacts of depended job has been expired' do
let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
- it { expect { job.run! }.not_to raise_error }
+ it { expect(job).to have_valid_build_dependencies }
end
context 'when artifacts of depended job has been erased' do
@@ -2335,7 +2490,7 @@ describe Ci::Build do
pre_stage_job.erase
end
- it { expect { job.run! }.not_to raise_error }
+ it { expect(job).to have_valid_build_dependencies }
end
end
@@ -2351,13 +2506,13 @@ describe Ci::Build do
context 'when "dependencies" keyword is not defined' do
let(:options) { {} }
- it { expect { job.run! }.not_to raise_error }
+ it { expect(job).to have_valid_build_dependencies }
end
context 'when "dependencies" keyword is empty' do
let(:options) { { dependencies: [] } }
- it { expect { job.run! }.not_to raise_error }
+ it { expect(job).to have_valid_build_dependencies }
end
context 'when "dependencies" keyword is specified' do
@@ -2605,4 +2760,219 @@ describe Ci::Build do
end
end
end
+
+ describe '#has_terminal?' do
+ let(:states) { described_class.state_machines[:status].states.keys - [:running] }
+
+ subject { build.has_terminal? }
+
+ it 'returns true if the build is running and it has a runner_session_url' do
+ build.build_runner_session(url: 'whatever')
+ build.status = :running
+
+ expect(subject).to be_truthy
+ end
+
+ context 'returns false' do
+ it 'when runner_session_url is empty' do
+ build.status = :running
+
+ expect(subject).to be_falsey
+ end
+
+ context 'unless the build is running' do
+ before do
+ build.build_runner_session(url: 'whatever')
+ end
+
+ it do
+ states.each do |state|
+ build.status = state
+
+ is_expected.to be_falsey
+ end
+ end
+ end
+ end
+ end
+
+ describe '#collect_test_reports!' do
+ subject { build.collect_test_reports!(test_reports) }
+
+ let(:test_reports) { Gitlab::Ci::Reports::TestReports.new }
+
+ it { expect(test_reports.get_suite(build.name).total_count).to eq(0) }
+
+ context 'when build has a test report' do
+ context 'when there is a JUnit test report from rspec test suite' do
+ before do
+ create(:ci_job_artifact, :junit, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the test suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_reports.get_suite(build.name).total_count).to eq(4)
+ expect(test_reports.get_suite(build.name).success_count).to be(2)
+ expect(test_reports.get_suite(build.name).failed_count).to be(2)
+ end
+ end
+
+ context 'when there is a JUnit test report from java ant test suite' do
+ before do
+ create(:ci_job_artifact, :junit_with_ant, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the test suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_reports.get_suite(build.name).total_count).to eq(3)
+ expect(test_reports.get_suite(build.name).success_count).to be(3)
+ expect(test_reports.get_suite(build.name).failed_count).to be(0)
+ end
+ end
+
+ context 'when there is a corrupted JUnit test report' do
+ before do
+ create(:ci_job_artifact, :junit_with_corrupted_data, job: build, project: build.project)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Ci::Parsers::Junit::JunitParserError)
+ end
+ end
+ end
+
+ context 'when build does not have test reports' do
+ it 'raises an error' do
+ expect { subject }.to raise_error(NoMethodError)
+ end
+ end
+ end
+
+ describe '#artifacts_metadata_entry' do
+ set(:build) { create(:ci_build, project: project) }
+ let(:path) { 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif' }
+
+ before do
+ stub_artifacts_object_storage
+ end
+
+ subject { build.artifacts_metadata_entry(path) }
+
+ context 'when using local storage' do
+ let!(:metadata) { create(:ci_job_artifact, :metadata, job: build) }
+
+ context 'for existing file' do
+ it 'does exist' do
+ is_expected.to be_exists
+ end
+ end
+
+ context 'for non-existing file' do
+ let(:path) { 'invalid-file' }
+
+ it 'does not exist' do
+ is_expected.not_to be_exists
+ end
+ end
+ end
+
+ context 'when using remote storage' do
+ include HttpIOHelpers
+
+ let!(:metadata) { create(:ci_job_artifact, :remote_store, :metadata, job: build) }
+ let(:file_path) { expand_fixture_path('ci_build_artifacts_metadata.gz') }
+
+ before do
+ stub_remote_url_206(metadata.file.url, file_path)
+ end
+
+ context 'for existing file' do
+ it 'does exist' do
+ is_expected.to be_exists
+ end
+ end
+
+ context 'for non-existing file' do
+ let(:path) { 'invalid-file' }
+
+ it 'does not exist' do
+ is_expected.not_to be_exists
+ end
+ end
+ end
+ end
+
+ describe '#publishes_artifacts_reports?' do
+ let(:build) { create(:ci_build, options: options) }
+
+ subject { build.publishes_artifacts_reports? }
+
+ context 'when artifacts reports are defined' do
+ let(:options) do
+ { artifacts: { reports: { junit: "junit.xml" } } }
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when artifacts reports missing defined' do
+ let(:options) do
+ { artifacts: { paths: ["file.txt"] } }
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when options are missing' do
+ let(:options) { nil }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#runner_required_feature_names' do
+ let(:build) { create(:ci_build, options: options) }
+
+ subject { build.runner_required_feature_names }
+
+ context 'when artifacts reports are defined' do
+ let(:options) do
+ { artifacts: { reports: { junit: "junit.xml" } } }
+ end
+
+ it { is_expected.to include(:upload_multiple_artifacts) }
+ end
+ end
+
+ describe '#supported_runner?' do
+ set(:build) { create(:ci_build) }
+
+ subject { build.supported_runner?(runner_features) }
+
+ context 'when feature is required by build' do
+ before do
+ expect(build).to receive(:runner_required_feature_names) do
+ [:upload_multiple_artifacts]
+ end
+ end
+
+ context 'when runner provides given feature' do
+ let(:runner_features) do
+ { upload_multiple_artifacts: true }
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when runner does not provide given feature' do
+ let(:runner_features) do
+ {}
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 464897de306..915bf134d57 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -14,6 +14,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
before do
stub_feature_flags(ci_enable_live_trace: true)
+ stub_artifacts_object_storage
end
context 'FastDestroyAll' do
@@ -37,6 +38,22 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
end
+ describe '.all_stores' do
+ subject { described_class.all_stores }
+
+ it 'returns a correctly ordered array' do
+ is_expected.to eq(%w[redis database fog])
+ end
+
+ it 'returns redis store as the the lowest precedence' do
+ expect(subject.first).to eq('redis')
+ end
+
+ it 'returns fog store as the the highest precedence' do
+ expect(subject.last).to eq('fog')
+ end
+ end
+
describe '#data' do
subject { build_trace_chunk.data }
@@ -44,181 +61,269 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :redis }
before do
- build_trace_chunk.send(:redis_set_data, 'Sample data in redis')
+ build_trace_chunk.send(:unsafe_set_data!, 'Sample data in redis')
end
it { is_expected.to eq('Sample data in redis') }
end
context 'when data_store is database' do
- let(:data_store) { :db }
- let(:raw_data) { 'Sample data in db' }
+ let(:data_store) { :database }
+ let(:raw_data) { 'Sample data in database' }
- it { is_expected.to eq('Sample data in db') }
+ it { is_expected.to eq('Sample data in database') }
end
- end
-
- describe '#set_data' do
- subject { build_trace_chunk.send(:set_data, value) }
- let(:value) { 'Sample data' }
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
- context 'when value bytesize is bigger than CHUNK_SIZE' do
- let(:value) { 'a' * (described_class::CHUNK_SIZE + 1) }
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, 'Sample data in fog')
+ end
- it { expect { subject }.to raise_error('too much data') }
+ it { is_expected.to eq('Sample data in fog') }
end
+ end
- context 'when data_store is redis' do
- let(:data_store) { :redis }
+ describe '#append' do
+ subject { build_trace_chunk.append(new_data, offset) }
- it do
- expect(build_trace_chunk.send(:redis_data)).to be_nil
+ let(:new_data) { 'Sample new data' }
+ let(:offset) { 0 }
+ let(:merged_data) { data + new_data.to_s }
- subject
+ shared_examples_for 'Appending correctly' do
+ context 'when offset is negative' do
+ let(:offset) { -1 }
+
+ it { expect { subject }.to raise_error('Offset is out of range') }
+ end
- expect(build_trace_chunk.send(:redis_data)).to eq(value)
+ context 'when offset is bigger than data size' do
+ let(:offset) { data.bytesize + 1 }
+
+ it { expect { subject }.to raise_error('Offset is out of range') }
end
- context 'when fullfilled chunk size' do
- let(:value) { 'a' * described_class::CHUNK_SIZE }
+ context 'when new data overflows chunk size' do
+ let(:new_data) { 'a' * (described_class::CHUNK_SIZE + 1) }
- it 'schedules stashing data' do
- expect(Ci::BuildTraceChunkFlushWorker).to receive(:perform_async).once
+ it { expect { subject }.to raise_error('Chunk size overflow') }
+ end
+
+ context 'when offset is EOF' do
+ let(:offset) { data.bytesize }
+ it 'appends' do
subject
+
+ expect(build_trace_chunk.data).to eq(merged_data)
end
- end
- end
- context 'when data_store is database' do
- let(:data_store) { :db }
+ context 'when the other process is appending' do
+ let(:lease_key) { "trace_write:#{build_trace_chunk.build.id}:chunks:#{build_trace_chunk.chunk_index}" }
- it 'sets data' do
- expect(build_trace_chunk.raw_data).to be_nil
+ before do
+ stub_exclusive_lease_taken(lease_key)
+ end
- subject
+ it 'raise an error' do
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
- expect(build_trace_chunk.raw_data).to eq(value)
- expect(build_trace_chunk.persisted?).to be_truthy
- end
+ context 'when new_data is nil' do
+ let(:new_data) { nil }
- context 'when raw_data is not changed' do
- it 'does not execute UPDATE' do
- expect(build_trace_chunk.raw_data).to be_nil
- build_trace_chunk.save!
+ it 'raises an error' do
+ expect { subject }.to raise_error('New data is missing')
+ end
+ end
- # First set
- expect(ActiveRecord::QueryRecorder.new { subject }.count).to be > 0
- expect(build_trace_chunk.raw_data).to eq(value)
- expect(build_trace_chunk.persisted?).to be_truthy
+ context 'when new_data is empty' do
+ let(:new_data) { '' }
- # Second set
- build_trace_chunk.reload
- expect(ActiveRecord::QueryRecorder.new { subject }.count).to be(0)
+ it 'does not append' do
+ subject
+
+ expect(build_trace_chunk.data).to eq(data)
+ end
+
+ it 'does not execute UPDATE' do
+ ActiveRecord::QueryRecorder.new { subject }.log.map do |query|
+ expect(query).not_to include('UPDATE')
+ end
+ end
end
end
- context 'when fullfilled chunk size' do
- it 'does not schedule stashing data' do
- expect(Ci::BuildTraceChunkFlushWorker).not_to receive(:perform_async)
+ context 'when offset is middle of datasize' do
+ let(:offset) { data.bytesize / 2 }
+ it 'appends' do
subject
+
+ expect(build_trace_chunk.data).to eq(data.byteslice(0, offset) + new_data)
end
end
end
- end
- describe '#truncate' do
- subject { build_trace_chunk.truncate(offset) }
+ shared_examples_for 'Scheduling sidekiq worker to flush data to persist store' do
+ context 'when new data fullfilled chunk size' do
+ let(:new_data) { 'a' * described_class::CHUNK_SIZE }
- shared_examples_for 'truncates' do
- context 'when offset is negative' do
- let(:offset) { -1 }
+ it 'schedules trace chunk flush worker' do
+ expect(Ci::BuildTraceChunkFlushWorker).to receive(:perform_async).once
- it { expect { subject }.to raise_error('Offset is out of range') }
- end
+ subject
+ end
- context 'when offset is bigger than data size' do
- let(:offset) { data.bytesize + 1 }
+ it 'migrates data to object storage' do
+ perform_enqueued_jobs do
+ subject
- it { expect { subject }.to raise_error('Offset is out of range') }
+ build_trace_chunk.reload
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(build_trace_chunk.data).to eq(new_data)
+ end
+ end
end
+ end
- context 'when offset is 10' do
- let(:offset) { 10 }
+ shared_examples_for 'Scheduling no sidekiq worker' do
+ context 'when new data fullfilled chunk size' do
+ let(:new_data) { 'a' * described_class::CHUNK_SIZE }
+
+ it 'does not schedule trace chunk flush worker' do
+ expect(Ci::BuildTraceChunkFlushWorker).not_to receive(:perform_async)
- it 'truncates' do
subject
+ end
- expect(build_trace_chunk.data).to eq(data.byteslice(0, offset))
+ it 'does not migrate data to object storage' do
+ perform_enqueued_jobs do
+ data_store = build_trace_chunk.data_store
+
+ subject
+
+ build_trace_chunk.reload
+ expect(build_trace_chunk.data_store).to eq(data_store)
+ end
end
end
end
context 'when data_store is redis' do
let(:data_store) { :redis }
- let(:data) { 'Sample data in redis' }
- before do
- build_trace_chunk.send(:redis_set_data, data)
+ context 'when there are no data' do
+ let(:data) { '' }
+
+ it 'has no data' do
+ expect(build_trace_chunk.data).to be_empty
+ end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling sidekiq worker to flush data to persist store'
end
- it_behaves_like 'truncates'
- end
+ context 'when there are some data' do
+ let(:data) { 'Sample data in redis' }
- context 'when data_store is database' do
- let(:data_store) { :db }
- let(:raw_data) { 'Sample data in db' }
- let(:data) { raw_data }
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
- it_behaves_like 'truncates'
+ it 'has data' do
+ expect(build_trace_chunk.data).to eq(data)
+ end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling sidekiq worker to flush data to persist store'
+ end
end
- end
- describe '#append' do
- subject { build_trace_chunk.append(new_data, offset) }
+ context 'when data_store is database' do
+ let(:data_store) { :database }
- let(:new_data) { 'Sample new data' }
- let(:offset) { 0 }
- let(:total_data) { data + new_data }
+ context 'when there are no data' do
+ let(:data) { '' }
- shared_examples_for 'appends' do
- context 'when offset is negative' do
- let(:offset) { -1 }
+ it 'has no data' do
+ expect(build_trace_chunk.data).to be_empty
+ end
- it { expect { subject }.to raise_error('Offset is out of range') }
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling no sidekiq worker'
end
- context 'when offset is bigger than data size' do
- let(:offset) { data.bytesize + 1 }
+ context 'when there are some data' do
+ let(:raw_data) { 'Sample data in database' }
+ let(:data) { raw_data }
- it { expect { subject }.to raise_error('Offset is out of range') }
+ it 'has data' do
+ expect(build_trace_chunk.data).to eq(data)
+ end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling no sidekiq worker'
end
+ end
- context 'when offset is bigger than data size' do
- let(:new_data) { 'a' * (described_class::CHUNK_SIZE + 1) }
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
- it { expect { subject }.to raise_error('Chunk size overflow') }
+ context 'when there are no data' do
+ let(:data) { '' }
+
+ it 'has no data' do
+ expect(build_trace_chunk.data).to be_empty
+ end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling no sidekiq worker'
end
- context 'when offset is EOF' do
- let(:offset) { data.bytesize }
+ context 'when there are some data' do
+ let(:data) { 'Sample data in fog' }
- it 'appends' do
- subject
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
- expect(build_trace_chunk.data).to eq(total_data)
+ it 'has data' do
+ expect(build_trace_chunk.data).to eq(data)
end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling no sidekiq worker'
+ end
+ end
+ end
+
+ describe '#truncate' do
+ subject { build_trace_chunk.truncate(offset) }
+
+ shared_examples_for 'truncates' do
+ context 'when offset is negative' do
+ let(:offset) { -1 }
+
+ it { expect { subject }.to raise_error('Offset is out of range') }
+ end
+
+ context 'when offset is bigger than data size' do
+ let(:offset) { data.bytesize + 1 }
+
+ it { expect { subject }.to raise_error('Offset is out of range') }
end
context 'when offset is 10' do
let(:offset) { 10 }
- it 'appends' do
+ it 'truncates' do
subject
- expect(build_trace_chunk.data).to eq(data.byteslice(0, offset) + new_data)
+ expect(build_trace_chunk.data).to eq(data.byteslice(0, offset))
end
end
end
@@ -228,18 +333,29 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data) { 'Sample data in redis' }
before do
- build_trace_chunk.send(:redis_set_data, data)
+ build_trace_chunk.send(:unsafe_set_data!, data)
end
- it_behaves_like 'appends'
+ it_behaves_like 'truncates'
end
context 'when data_store is database' do
- let(:data_store) { :db }
- let(:raw_data) { 'Sample data in db' }
+ let(:data_store) { :database }
+ let(:raw_data) { 'Sample data in database' }
let(:data) { raw_data }
- it_behaves_like 'appends'
+ it_behaves_like 'truncates'
+ end
+
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
+ let(:data) { 'Sample data in fog' }
+
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
+
+ it_behaves_like 'truncates'
end
end
@@ -253,7 +369,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data) { 'Sample data in redis' }
before do
- build_trace_chunk.send(:redis_set_data, data)
+ build_trace_chunk.send(:unsafe_set_data!, data)
end
it { is_expected.to eq(data.bytesize) }
@@ -265,10 +381,10 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when data_store is database' do
- let(:data_store) { :db }
+ let(:data_store) { :database }
context 'when data exists' do
- let(:raw_data) { 'Sample data in db' }
+ let(:raw_data) { 'Sample data in database' }
let(:data) { raw_data }
it { is_expected.to eq(data.bytesize) }
@@ -278,10 +394,43 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it { is_expected.to eq(0) }
end
end
+
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
+
+ context 'when data exists' do
+ let(:data) { 'Sample data in fog' }
+ let(:key) { "tmp/builds/#{build.id}/chunks/#{chunk_index}.log" }
+
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
+
+ it { is_expected.to eq(data.bytesize) }
+ end
+
+ context 'when data does not exist' do
+ it { is_expected.to eq(0) }
+ end
+ end
end
- describe '#use_database!' do
- subject { build_trace_chunk.use_database! }
+ describe '#persist_data!' do
+ subject { build_trace_chunk.persist_data! }
+
+ shared_examples_for 'Atomic operation' do
+ context 'when the other process is persisting' do
+ let(:lease_key) { "trace_write:#{build_trace_chunk.build.id}:chunks:#{build_trace_chunk.chunk_index}" }
+
+ before do
+ stub_exclusive_lease_taken(lease_key)
+ end
+
+ it 'raise an error' do
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+ end
context 'when data_store is redis' do
let(:data_store) { :redis }
@@ -290,46 +439,93 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data) { 'Sample data in redis' }
before do
- build_trace_chunk.send(:redis_set_data, data)
+ build_trace_chunk.send(:unsafe_set_data!, data)
end
- it 'stashes the data' do
- expect(build_trace_chunk.data_store).to eq('redis')
- expect(build_trace_chunk.send(:redis_data)).to eq(data)
- expect(build_trace_chunk.raw_data).to be_nil
+ it 'persists the data' do
+ expect(build_trace_chunk.redis?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to eq(data)
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect { Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk) }.to raise_error(Excon::Error::NotFound)
subject
- expect(build_trace_chunk.data_store).to eq('db')
- expect(build_trace_chunk.send(:redis_data)).to be_nil
- expect(build_trace_chunk.raw_data).to eq(data)
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
end
+
+ it_behaves_like 'Atomic operation'
end
context 'when data does not exist' do
- it 'does not call UPDATE' do
- expect(ActiveRecord::QueryRecorder.new { subject }.count).to eq(0)
+ it 'does not persist' do
+ expect { subject }.to raise_error('Can not persist empty data')
end
end
end
context 'when data_store is database' do
- let(:data_store) { :db }
+ let(:data_store) { :database }
- it 'does not call UPDATE' do
- expect(ActiveRecord::QueryRecorder.new { subject }.count).to eq(0)
+ context 'when data exists' do
+ let(:data) { 'Sample data in database' }
+
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
+
+ it 'persists the data' do
+ expect(build_trace_chunk.database?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to eq(data)
+ expect { Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk) }.to raise_error(Excon::Error::NotFound)
+
+ subject
+
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
+ end
+
+ it_behaves_like 'Atomic operation'
end
- end
- end
- describe 'ExclusiveLock' do
- before do
- stub_exclusive_lease_taken
- stub_const('Ci::BuildTraceChunk::WRITE_LOCK_RETRY', 1)
+ context 'when data does not exist' do
+ it 'does not persist' do
+ expect { subject }.to raise_error('Can not persist empty data')
+ end
+ end
end
- it 'raise an error' do
- expect { build_trace_chunk.append('ABC', 0) }.to raise_error('Failed to obtain write lock')
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
+
+ context 'when data exists' do
+ let(:data) { 'Sample data in fog' }
+
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
+
+ it 'does not change data store' do
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
+
+ subject
+
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
+ end
+
+ it_behaves_like 'Atomic operation'
+ end
end
end
diff --git a/spec/models/ci/build_trace_chunks/database_spec.rb b/spec/models/ci/build_trace_chunks/database_spec.rb
new file mode 100644
index 00000000000..d8fc9d57e95
--- /dev/null
+++ b/spec/models/ci/build_trace_chunks/database_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe Ci::BuildTraceChunks::Database do
+ let(:data_store) { described_class.new }
+
+ describe '#available?' do
+ subject { data_store.available? }
+
+ it { is_expected.to be_truthy }
+ end
+
+ describe '#data' do
+ subject { data_store.data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :database_with_data, initial_data: 'sample data in database') }
+
+ it 'returns the data' do
+ is_expected.to eq('sample data in database')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :database_without_data) }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#set_data' do
+ subject { data_store.set_data(model, data) }
+
+ let(:data) { 'abc123' }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :database_with_data, initial_data: 'sample data in database') }
+
+ it 'overwrites data' do
+ expect(data_store.data(model)).to eq('sample data in database')
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :database_without_data) }
+
+ it 'sets new data' do
+ expect(data_store.data(model)).to be_nil
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+ end
+
+ describe '#delete_data' do
+ subject { data_store.delete_data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :database_with_data, initial_data: 'sample data in database') }
+
+ it 'deletes data' do
+ expect(data_store.data(model)).to eq('sample data in database')
+
+ subject
+
+ expect(data_store.data(model)).to be_nil
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :database_without_data) }
+
+ it 'does nothing' do
+ expect(data_store.data(model)).to be_nil
+
+ subject
+
+ expect(data_store.data(model)).to be_nil
+ end
+ end
+ end
+
+ describe '#keys' do
+ subject { data_store.keys(relation) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+
+ before do
+ create(:ci_build_trace_chunk, :database_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :database_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'returns empty array' do
+ is_expected.to eq([])
+ end
+ end
+end
diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb
new file mode 100644
index 00000000000..8f49190af13
--- /dev/null
+++ b/spec/models/ci/build_trace_chunks/fog_spec.rb
@@ -0,0 +1,146 @@
+require 'spec_helper'
+
+describe Ci::BuildTraceChunks::Fog do
+ let(:data_store) { described_class.new }
+
+ before do
+ stub_artifacts_object_storage
+ end
+
+ describe '#available?' do
+ subject { data_store.available? }
+
+ context 'when object storage is enabled' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when object storage is disabled' do
+ before do
+ stub_artifacts_object_storage(enabled: false)
+ end
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '#data' do
+ subject { data_store.data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') }
+
+ it 'returns the data' do
+ is_expected.to eq('sample data in fog')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) }
+
+ it 'returns nil' do
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+ end
+ end
+ end
+
+ describe '#set_data' do
+ subject { data_store.set_data(model, data) }
+
+ let(:data) { 'abc123' }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') }
+
+ it 'overwrites data' do
+ expect(data_store.data(model)).to eq('sample data in fog')
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) }
+
+ it 'sets new data' do
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+ end
+
+ describe '#delete_data' do
+ subject { data_store.delete_data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') }
+
+ it 'deletes data' do
+ expect(data_store.data(model)).to eq('sample data in fog')
+
+ subject
+
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) }
+
+ it 'does nothing' do
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+
+ subject
+
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+ end
+ end
+ end
+
+ describe '#keys' do
+ subject { data_store.keys(relation) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+
+ before do
+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'returns keys' do
+ is_expected.to eq([[build.id, 0], [build.id, 1]])
+ end
+ end
+
+ describe '#delete_keys' do
+ subject { data_store.delete_keys(keys) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+ let(:keys) { data_store.keys(relation) }
+
+ before do
+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'deletes multiple data' do
+ ::Fog::Storage.new(JobArtifactUploader.object_store_credentials).tap do |connection|
+ expect(connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/0.log")[:body]).to be_present
+ expect(connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/1.log")[:body]).to be_present
+ end
+
+ subject
+
+ ::Fog::Storage.new(JobArtifactUploader.object_store_credentials).tap do |connection|
+ expect { connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/0.log")[:body] }.to raise_error(Excon::Error::NotFound)
+ expect { connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/1.log")[:body] }.to raise_error(Excon::Error::NotFound)
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/build_trace_chunks/redis_spec.rb b/spec/models/ci/build_trace_chunks/redis_spec.rb
new file mode 100644
index 00000000000..9da1e6a95ee
--- /dev/null
+++ b/spec/models/ci/build_trace_chunks/redis_spec.rb
@@ -0,0 +1,132 @@
+require 'spec_helper'
+
+describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do
+ let(:data_store) { described_class.new }
+
+ describe '#available?' do
+ subject { data_store.available? }
+
+ it { is_expected.to be_truthy }
+ end
+
+ describe '#data' do
+ subject { data_store.data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'sample data in redis') }
+
+ it 'returns the data' do
+ is_expected.to eq('sample data in redis')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_without_data) }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#set_data' do
+ subject { data_store.set_data(model, data) }
+
+ let(:data) { 'abc123' }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'sample data in redis') }
+
+ it 'overwrites data' do
+ expect(data_store.data(model)).to eq('sample data in redis')
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_without_data) }
+
+ it 'sets new data' do
+ expect(data_store.data(model)).to be_nil
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+ end
+
+ describe '#delete_data' do
+ subject { data_store.delete_data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'sample data in redis') }
+
+ it 'deletes data' do
+ expect(data_store.data(model)).to eq('sample data in redis')
+
+ subject
+
+ expect(data_store.data(model)).to be_nil
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_without_data) }
+
+ it 'does nothing' do
+ expect(data_store.data(model)).to be_nil
+
+ subject
+
+ expect(data_store.data(model)).to be_nil
+ end
+ end
+ end
+
+ describe '#keys' do
+ subject { data_store.keys(relation) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+
+ before do
+ create(:ci_build_trace_chunk, :redis_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :redis_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'returns keys' do
+ is_expected.to eq([[build.id, 0], [build.id, 1]])
+ end
+ end
+
+ describe '#delete_keys' do
+ subject { data_store.delete_keys(keys) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+ let(:keys) { data_store.keys(relation) }
+
+ before do
+ create(:ci_build_trace_chunk, :redis_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :redis_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'deletes multiple data' do
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.exists("gitlab:ci:trace:#{build.id}:chunks:0")).to be_truthy
+ expect(redis.exists("gitlab:ci:trace:#{build.id}:chunks:1")).to be_truthy
+ end
+
+ subject
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.exists("gitlab:ci:trace:#{build.id}:chunks:0")).to be_falsy
+ expect(redis.exists("gitlab:ci:trace:#{build.id}:chunks:1")).to be_falsy
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index efddd4e7662..1bf338f4c70 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -15,6 +15,22 @@ describe Ci::JobArtifact do
it { is_expected.to delegate_method(:open).to(:file) }
it { is_expected.to delegate_method(:exists?).to(:file) }
+ describe '.test_reports' do
+ subject { described_class.test_reports }
+
+ context 'when there is a test report' do
+ let!(:artifact) { create(:ci_job_artifact, :junit) }
+
+ it { is_expected.to eq([artifact]) }
+ end
+
+ context 'when there are no test reports' do
+ let!(:artifact) { create(:ci_job_artifact, :archive) }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe 'callbacks' do
subject { create(:ci_job_artifact, :archive) }
@@ -25,7 +41,7 @@ describe Ci::JobArtifact do
end
it 'does not schedule the migration' do
- expect(ObjectStorageUploadWorker).not_to receive(:perform_async)
+ expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
subject
end
@@ -87,6 +103,40 @@ describe Ci::JobArtifact do
end
end
+ describe 'validates file format' do
+ subject { artifact }
+
+ context 'when archive type with zip format' do
+ let(:artifact) { build(:ci_job_artifact, :archive, file_format: :zip) }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when archive type with gzip format' do
+ let(:artifact) { build(:ci_job_artifact, :archive, file_format: :gzip) }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when archive type without format specification' do
+ let(:artifact) { build(:ci_job_artifact, :archive, file_format: nil) }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when junit type with zip format' do
+ let(:artifact) { build(:ci_job_artifact, :junit, file_format: :zip) }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when junit type with gzip format' do
+ let(:artifact) { build(:ci_job_artifact, :junit, file_format: :gzip) }
+
+ it { is_expected.to be_valid }
+ end
+ end
+
describe '#file' do
subject { artifact.file }
@@ -97,6 +147,34 @@ describe Ci::JobArtifact do
end
end
+ describe '#each_blob' do
+ context 'when file format is gzip' do
+ context 'when gzip file contains one file' do
+ let(:artifact) { build(:ci_job_artifact, :junit) }
+
+ it 'iterates blob once' do
+ expect { |b| artifact.each_blob(&b) }.to yield_control.once
+ end
+ end
+
+ context 'when gzip file contains three files' do
+ let(:artifact) { build(:ci_job_artifact, :junit_with_three_testsuites) }
+
+ it 'iterates blob three times' do
+ expect { |b| artifact.each_blob(&b) }.to yield_control.exactly(3).times
+ end
+ end
+ end
+
+ context 'when there are no adapters for the file format' do
+ let(:artifact) { build(:ci_job_artifact, :junit, file_format: :zip) }
+
+ it 'raises an error' do
+ expect { |b| artifact.each_blob(&b) }.to raise_error(described_class::NotSupportedAdapterError)
+ end
+ end
+ end
+
describe '#expire_in' do
subject { artifact.expire_in }
@@ -163,7 +241,7 @@ describe Ci::JobArtifact do
expect(ProjectStatistics)
.not_to receive(:increment_statistic)
- project.update_attributes(pending_delete: true)
+ project.update(pending_delete: true)
project.destroy!
end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index a41657b53b7..77b7332a761 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1851,6 +1851,85 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#has_test_reports?' do
+ subject { pipeline.has_test_reports? }
+
+ context 'when pipeline has builds with test reports' do
+ before do
+ create(:ci_build, :test_reports, pipeline: pipeline, project: project)
+ end
+
+ context 'when pipeline status is running' do
+ let(:pipeline) { create(:ci_pipeline, :running, project: project) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when pipeline status is success' do
+ let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ context 'when pipeline does not have builds with test reports' do
+ before do
+ create(:ci_build, :artifacts, pipeline: pipeline, project: project)
+ end
+
+ let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when retried build has test reports' do
+ before do
+ create(:ci_build, :retried, :test_reports, pipeline: pipeline, project: project)
+ end
+
+ let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#test_reports' do
+ subject { pipeline.test_reports }
+
+ context 'when pipeline has multiple builds with test reports' do
+ let!(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline, project: project) }
+ let!(:build_java) { create(:ci_build, :success, name: 'java', pipeline: pipeline, project: project) }
+
+ before do
+ create(:ci_job_artifact, :junit, job: build_rspec, project: project)
+ create(:ci_job_artifact, :junit_with_ant, job: build_java, project: project)
+ end
+
+ it 'returns test reports with collected data' do
+ expect(subject.total_count).to be(7)
+ expect(subject.success_count).to be(5)
+ expect(subject.failed_count).to be(2)
+ end
+
+ context 'when builds are retried' do
+ let!(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline, project: project) }
+ let!(:build_java) { create(:ci_build, :retried, :success, name: 'java', pipeline: pipeline, project: project) }
+
+ it 'does not take retried builds into account' do
+ expect(subject.total_count).to be(0)
+ expect(subject.success_count).to be(0)
+ expect(subject.failed_count).to be(0)
+ end
+ end
+ end
+
+ context 'when pipeline does not have any builds with test reports' do
+ it 'returns empty test reports' do
+ expect(subject.total_count).to be(0)
+ 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) }
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index f6433234573..953af2c4710 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -105,7 +105,7 @@ describe Ci::Runner do
end
end
- describe '.shared' do
+ describe '.instance_type' do
let(:group) { create(:group) }
let(:project) { create(:project) }
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
@@ -113,7 +113,7 @@ describe Ci::Runner do
let!(:shared_runner) { create(:ci_runner, :instance) }
it 'returns only shared runners' do
- expect(described_class.shared).to contain_exactly(shared_runner)
+ expect(described_class.instance_type).to contain_exactly(shared_runner)
end
end
@@ -155,7 +155,7 @@ describe Ci::Runner do
end
end
- describe '.owned_or_shared' do
+ describe '.owned_or_instance_wide' do
it 'returns a globally shared, a project specific and a group specific runner' do
# group specific
group = create(:group)
@@ -168,7 +168,7 @@ describe Ci::Runner do
# globally shared
shared_runner = create(:ci_runner, :instance)
- expect(described_class.owned_or_shared(project.id)).to contain_exactly(
+ expect(described_class.owned_or_instance_wide(project.id)).to contain_exactly(
group_runner, project_runner, shared_runner
)
end
@@ -202,7 +202,6 @@ describe Ci::Runner do
it 'transitions shared runner to project runner and assigns project' do
expect(subject).to be_truthy
- expect(runner).to be_specific
expect(runner).to be_project_type
expect(runner.projects).to eq([project])
expect(runner.only_for?(project)).to be_truthy
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index 0eb1e3876e2..e5b2bdc8a4e 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -6,13 +6,24 @@ describe Clusters::Applications::Helm do
describe '.installed' do
subject { described_class.installed }
- let!(:cluster) { create(:clusters_applications_helm, :installed) }
+ let!(:installed_cluster) { create(:clusters_applications_helm, :installed) }
before do
create(:clusters_applications_helm, :errored)
end
- it { is_expected.to contain_exactly(cluster) }
+ it { is_expected.to contain_exactly(installed_cluster) }
+ end
+
+ describe '#issue_client_cert' do
+ let(:application) { create(:clusters_applications_helm) }
+ subject { application.issue_client_cert }
+
+ it 'returns a new cert' do
+ is_expected.to be_kind_of(Gitlab::Kubernetes::Helm::Certificate)
+ expect(subject.cert_string).not_to eq(application.ca_cert)
+ expect(subject.key_string).not_to eq(application.ca_key)
+ end
end
describe '#install_command' do
@@ -25,5 +36,16 @@ describe Clusters::Applications::Helm do
it 'should be initialized with 1 arguments' do
expect(subject.name).to eq('helm')
end
+
+ it 'should have cert files' do
+ expect(subject.files[:'ca.pem']).to be_present
+ expect(subject.files[:'ca.pem']).to eq(helm.ca_cert)
+
+ expect(subject.files[:'cert.pem']).to be_present
+ expect(subject.files[:'key.pem']).to be_present
+
+ cert = OpenSSL::X509::Certificate.new(subject.files[:'cert.pem'])
+ expect(cert.not_after).to be > 999.years.from_now
+ end
end
end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index bb5b2ef3a47..21f75ced8c3 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -23,6 +23,20 @@ describe Clusters::Applications::Ingress do
it { is_expected.to contain_exactly(cluster) }
end
+ describe '#make_installing!' do
+ before do
+ application.make_installing!
+ end
+
+ context 'application install previously errored with older version' do
+ let(:application) { create(:clusters_applications_ingress, :scheduled, version: '0.22.0') }
+
+ it 'updates the application version' do
+ expect(application.reload.version).to eq('0.23.0')
+ end
+ end
+ end
+
describe '#make_installed!' do
before do
application.make_installed!
@@ -73,19 +87,53 @@ describe Clusters::Applications::Ingress do
it 'should be initialized with ingress arguments' do
expect(subject.name).to eq('ingress')
expect(subject.chart).to eq('stable/nginx-ingress')
- expect(subject.version).to be_nil
- expect(subject.values).to eq(ingress.values)
+ expect(subject.version).to eq('0.23.0')
+ expect(subject.files).to eq(ingress.files)
+ end
+
+ context 'application failed to install previously' do
+ let(:ingress) { create(:clusters_applications_ingress, :errored, version: 'nginx') }
+
+ it 'should be initialized with the locked version' do
+ expect(subject.version).to eq('0.23.0')
+ end
end
end
- describe '#values' do
- subject { ingress.values }
+ describe '#files' do
+ let(:application) { ingress }
+ let(:values) { subject[:'values.yaml'] }
+
+ subject { application.files }
+
+ it 'should include ingress valid keys in values' do
+ expect(values).to include('image')
+ expect(values).to include('repository')
+ expect(values).to include('stats')
+ expect(values).to include('podAnnotations')
+ end
+
+ context 'when the helm application does not have a ca_cert' do
+ before do
+ application.cluster.application_helm.ca_cert = nil
+ end
+
+ it 'should not include cert files' do
+ expect(subject[:'ca.pem']).not_to be_present
+ expect(subject[:'cert.pem']).not_to be_present
+ expect(subject[:'key.pem']).not_to be_present
+ end
+ end
+
+ it 'should include cert files' do
+ expect(subject[:'ca.pem']).to be_present
+ expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
+
+ expect(subject[:'cert.pem']).to be_present
+ expect(subject[:'key.pem']).to be_present
- it 'should include ingress valid keys' do
- is_expected.to include('image')
- is_expected.to include('repository')
- is_expected.to include('stats')
- is_expected.to include('podAnnotations')
+ cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
+ expect(cert.not_after).to be < 60.minutes.from_now
end
end
end
diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb
index 65750141e65..027b732681b 100644
--- a/spec/models/clusters/applications/jupyter_spec.rb
+++ b/spec/models/clusters/applications/jupyter_spec.rb
@@ -25,6 +25,20 @@ describe Clusters::Applications::Jupyter do
end
end
+ describe '#make_installing!' do
+ before do
+ application.make_installing!
+ end
+
+ context 'application install previously errored with older version' do
+ let(:application) { create(:clusters_applications_jupyter, :scheduled, version: 'v0.5') }
+
+ it 'updates the application version' do
+ expect(application.reload.version).to eq('v0.6')
+ end
+ end
+ end
+
describe '#install_command' do
let!(:ingress) { create(:clusters_applications_ingress, :installed, external_ip: '127.0.0.1') }
let!(:jupyter) { create(:clusters_applications_jupyter, cluster: ingress.cluster) }
@@ -36,25 +50,57 @@ describe Clusters::Applications::Jupyter do
it 'should be initialized with 4 arguments' do
expect(subject.name).to eq('jupyter')
expect(subject.chart).to eq('jupyter/jupyterhub')
- expect(subject.version).to be_nil
+ expect(subject.version).to eq('v0.6')
expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/')
- expect(subject.values).to eq(jupyter.values)
+ expect(subject.files).to eq(jupyter.files)
+ end
+
+ context 'application failed to install previously' do
+ let(:jupyter) { create(:clusters_applications_jupyter, :errored, version: '0.0.1') }
+
+ it 'should be initialized with the locked version' do
+ expect(subject.version).to eq('v0.6')
+ end
end
end
- describe '#values' do
- let(:jupyter) { create(:clusters_applications_jupyter) }
+ describe '#files' do
+ let(:application) { create(:clusters_applications_jupyter) }
+ let(:values) { subject[:'values.yaml'] }
+
+ subject { application.files }
+
+ it 'should include cert files' do
+ expect(subject[:'ca.pem']).to be_present
+ expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
+
+ expect(subject[:'cert.pem']).to be_present
+ expect(subject[:'key.pem']).to be_present
- subject { jupyter.values }
+ cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
+ expect(cert.not_after).to be < 60.minutes.from_now
+ end
+
+ context 'when the helm application does not have a ca_cert' do
+ before do
+ application.cluster.application_helm.ca_cert = nil
+ end
+
+ it 'should not include cert files' do
+ expect(subject[:'ca.pem']).not_to be_present
+ expect(subject[:'cert.pem']).not_to be_present
+ expect(subject[:'key.pem']).not_to be_present
+ end
+ end
it 'should include valid values' do
- is_expected.to include('ingress')
- is_expected.to include('hub')
- is_expected.to include('rbac')
- is_expected.to include('proxy')
- is_expected.to include('auth')
- is_expected.to include("clientId: #{jupyter.oauth_application.uid}")
- is_expected.to include("callbackUrl: #{jupyter.callback_url}")
+ expect(values).to include('ingress')
+ expect(values).to include('hub')
+ expect(values).to include('rbac')
+ expect(values).to include('proxy')
+ expect(values).to include('auth')
+ expect(values).to match(/clientId: '?#{application.oauth_application.uid}/)
+ expect(values).to match(/callbackUrl: '?#{application.callback_url}/)
end
end
end
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index efd57040005..7454be3ab2f 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -16,6 +16,20 @@ describe Clusters::Applications::Prometheus do
it { is_expected.to contain_exactly(cluster) }
end
+ describe '#make_installing!' do
+ before do
+ application.make_installing!
+ end
+
+ context 'application install previously errored with older version' do
+ let(:application) { create(:clusters_applications_prometheus, :scheduled, version: '6.7.2') }
+
+ it 'updates the application version' do
+ expect(application.reload.version).to eq('6.7.3')
+ end
+ end
+ end
+
describe 'transition to installed' do
let(:project) { create(:project) }
let(:cluster) { create(:cluster, projects: [project]) }
@@ -34,6 +48,47 @@ describe Clusters::Applications::Prometheus do
end
end
+ describe '#ready' do
+ let(:project) { create(:project) }
+ let(:cluster) { create(:cluster, projects: [project]) }
+
+ it 'returns true when installed' do
+ application = build(:clusters_applications_prometheus, :installed, cluster: cluster)
+
+ expect(application).to be_ready
+ end
+
+ it 'returns false when not_installable' do
+ application = build(:clusters_applications_prometheus, :not_installable, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+
+ it 'returns false when installable' do
+ application = build(:clusters_applications_prometheus, :installable, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+
+ it 'returns false when scheduled' do
+ application = build(:clusters_applications_prometheus, :scheduled, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+
+ it 'returns false when installing' do
+ application = build(:clusters_applications_prometheus, :installing, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+
+ it 'returns false when errored' do
+ application = build(:clusters_applications_prometheus, :errored, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+ end
+
describe '#prometheus_client' do
context 'cluster is nil' do
it 'returns nil' do
@@ -102,29 +157,63 @@ describe Clusters::Applications::Prometheus do
let(:kubeclient) { double('kubernetes client') }
let(:prometheus) { create(:clusters_applications_prometheus) }
- subject { prometheus.install_command }
-
- it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) }
+ it 'returns an instance of Gitlab::Kubernetes::Helm::InstallCommand' do
+ expect(prometheus.install_command).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand)
+ end
it 'should be initialized with 3 arguments' do
- expect(subject.name).to eq('prometheus')
- expect(subject.chart).to eq('stable/prometheus')
- expect(subject.version).to eq('6.7.3')
- expect(subject.values).to eq(prometheus.values)
+ command = prometheus.install_command
+
+ expect(command.name).to eq('prometheus')
+ expect(command.chart).to eq('stable/prometheus')
+ expect(command.version).to eq('6.7.3')
+ expect(command.files).to eq(prometheus.files)
+ end
+
+ context 'application failed to install previously' do
+ let(:prometheus) { create(:clusters_applications_prometheus, :errored, version: '2.0.0') }
+
+ it 'should be initialized with the locked version' do
+ expect(subject.version).to eq('6.7.3')
+ end
end
end
- describe '#values' do
- let(:prometheus) { create(:clusters_applications_prometheus) }
+ describe '#files' do
+ let(:application) { create(:clusters_applications_prometheus) }
+ let(:values) { subject[:'values.yaml'] }
+
+ subject { application.files }
+
+ it 'should include cert files' do
+ expect(subject[:'ca.pem']).to be_present
+ expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
+
+ expect(subject[:'cert.pem']).to be_present
+ expect(subject[:'key.pem']).to be_present
+
+ cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
+ expect(cert.not_after).to be < 60.minutes.from_now
+ end
+
+ context 'when the helm application does not have a ca_cert' do
+ before do
+ application.cluster.application_helm.ca_cert = nil
+ end
- subject { prometheus.values }
+ it 'should not include cert files' do
+ expect(subject[:'ca.pem']).not_to be_present
+ expect(subject[:'cert.pem']).not_to be_present
+ expect(subject[:'key.pem']).not_to be_present
+ end
+ end
it 'should include prometheus valid values' do
- is_expected.to include('alertmanager')
- is_expected.to include('kubeStateMetrics')
- is_expected.to include('nodeExporter')
- is_expected.to include('pushgateway')
- is_expected.to include('serverFiles')
+ expect(values).to include('alertmanager')
+ expect(values).to include('kubeStateMetrics')
+ expect(values).to include('nodeExporter')
+ expect(values).to include('pushgateway')
+ expect(values).to include('serverFiles')
end
end
end
diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb
index b12500d0acd..d84f125e246 100644
--- a/spec/models/clusters/applications/runner_spec.rb
+++ b/spec/models/clusters/applications/runner_spec.rb
@@ -8,6 +8,20 @@ describe Clusters::Applications::Runner do
it { is_expected.to belong_to(:runner) }
+ describe '#make_installing!' do
+ before do
+ application.make_installing!
+ end
+
+ context 'application install previously errored with older version' do
+ let(:application) { create(:clusters_applications_runner, :scheduled, version: '0.1.30') }
+
+ it 'updates the application version' do
+ expect(application.reload.version).to eq('0.1.31')
+ end
+ end
+ end
+
describe '.installed' do
subject { described_class.installed }
@@ -31,33 +45,65 @@ describe Clusters::Applications::Runner do
it 'should be initialized with 4 arguments' do
expect(subject.name).to eq('runner')
expect(subject.chart).to eq('runner/gitlab-runner')
- expect(subject.version).to be_nil
+ expect(subject.version).to eq('0.1.31')
expect(subject.repository).to eq('https://charts.gitlab.io')
- expect(subject.values).to eq(gitlab_runner.values)
+ expect(subject.files).to eq(gitlab_runner.files)
+ end
+
+ context 'application failed to install previously' do
+ let(:gitlab_runner) { create(:clusters_applications_runner, :errored, runner: ci_runner, version: '0.1.13') }
+
+ it 'should be initialized with the locked version' do
+ expect(subject.version).to eq('0.1.31')
+ end
end
end
- describe '#values' do
- let(:gitlab_runner) { create(:clusters_applications_runner, runner: ci_runner) }
+ describe '#files' do
+ let(:application) { create(:clusters_applications_runner, runner: ci_runner) }
+ let(:values) { subject[:'values.yaml'] }
+
+ subject { application.files }
+
+ it 'should include cert files' do
+ expect(subject[:'ca.pem']).to be_present
+ expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
+
+ expect(subject[:'cert.pem']).to be_present
+ expect(subject[:'key.pem']).to be_present
+
+ cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
+ expect(cert.not_after).to be < 60.minutes.from_now
+ end
- subject { gitlab_runner.values }
+ context 'when the helm application does not have a ca_cert' do
+ before do
+ application.cluster.application_helm.ca_cert = nil
+ end
+
+ it 'should not include cert files' do
+ expect(subject[:'ca.pem']).not_to be_present
+ expect(subject[:'cert.pem']).not_to be_present
+ expect(subject[:'key.pem']).not_to be_present
+ end
+ end
it 'should include runner valid values' do
- is_expected.to include('concurrent')
- is_expected.to include('checkInterval')
- is_expected.to include('rbac')
- is_expected.to include('runners')
- is_expected.to include('privileged: true')
- is_expected.to include('image: ubuntu:16.04')
- is_expected.to include('resources')
- is_expected.to include("runnerToken: #{ci_runner.token}")
- is_expected.to include("gitlabUrl: #{Gitlab::Routing.url_helpers.root_url}")
+ expect(values).to include('concurrent')
+ expect(values).to include('checkInterval')
+ expect(values).to include('rbac')
+ expect(values).to include('runners')
+ expect(values).to include('privileged: true')
+ expect(values).to include('image: ubuntu:16.04')
+ expect(values).to include('resources')
+ expect(values).to match(/runnerToken: '?#{ci_runner.token}/)
+ expect(values).to match(/gitlabUrl: '?#{Gitlab::Routing.url_helpers.root_url}/)
end
context 'without a runner' do
let(:project) { create(:project) }
- let(:cluster) { create(:cluster, projects: [project]) }
- let(:gitlab_runner) { create(:clusters_applications_runner, cluster: cluster) }
+ let(:cluster) { create(:cluster, :with_installed_helm, projects: [project]) }
+ let(:application) { create(:clusters_applications_runner, cluster: cluster) }
it 'creates a runner' do
expect do
@@ -66,18 +112,18 @@ describe Clusters::Applications::Runner do
end
it 'uses the new runner token' do
- expect(subject).to include("runnerToken: #{gitlab_runner.reload.runner.token}")
+ expect(values).to match(/runnerToken: '?#{application.reload.runner.token}/)
end
it 'assigns the new runner to runner' do
subject
- expect(gitlab_runner.reload.runner).to be_project_type
+ expect(application.reload.runner).to be_project_type
end
end
context 'with duplicated values on vendor/runner/values.yaml' do
- let(:values) do
+ let(:stub_values) do
{
"concurrent" => 4,
"checkInterval" => 3,
@@ -96,11 +142,11 @@ describe Clusters::Applications::Runner do
end
before do
- allow(gitlab_runner).to receive(:chart_values).and_return(values)
+ allow(application).to receive(:chart_values).and_return(stub_values)
end
it 'should overwrite values.yaml' do
- is_expected.to include("privileged: #{gitlab_runner.privileged}")
+ expect(values).to match(/privileged: '?#{application.privileged}/)
end
end
end
diff --git a/spec/models/concerns/avatarable_spec.rb b/spec/models/concerns/avatarable_spec.rb
index 9faf21bfbbd..76f734079b7 100644
--- a/spec/models/concerns/avatarable_spec.rb
+++ b/spec/models/concerns/avatarable_spec.rb
@@ -43,6 +43,10 @@ describe Avatarable do
expect(project.avatar_path(only_path: only_path)).to eq(avatar_path)
end
+ it 'returns the expected avatar path with width parameter' do
+ expect(project.avatar_path(only_path: only_path, size: 128)).to eq(avatar_path + "?width=128")
+ end
+
context "when avatar is stored remotely" do
before do
stub_uploads_object_storage(AvatarUploader)
diff --git a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
index c16b245bea8..e5392fe0462 100644
--- a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
+++ b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
@@ -4,8 +4,8 @@ describe BatchDestroyDependentAssociations do
class TestProject < ActiveRecord::Base
self.table_name = 'projects'
- has_many :builds, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
- has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
+ has_many :builds, dependent: :destroy
+ has_many :notification_settings, as: :source, dependent: :delete_all
has_many :pages_domains
has_many :todos
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 2d75422ee68..da26d802688 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -370,4 +370,20 @@ describe CacheMarkdownField do
end
end
end
+
+ describe CacheMarkdownField::MarkdownEngine do
+ subject { lambda { |version| CacheMarkdownField::MarkdownEngine.from_version(version) } }
+
+ it 'returns :common_mark as a default' do
+ expect(subject.call(nil)).to eq :common_mark
+ end
+
+ it 'returns :common_mark' do
+ expect(subject.call(CacheMarkdownField::CACHE_COMMONMARK_VERSION)).to eq :common_mark
+ end
+
+ it 'returns :redcarpet' do
+ expect(subject.call(CacheMarkdownField::CACHE_REDCARPET_VERSION)).to eq :redcarpet
+ end
+ end
end
diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb
index c6331c5ec15..f8c2e29fadd 100644
--- a/spec/models/concerns/cacheable_attributes_spec.rb
+++ b/spec/models/concerns/cacheable_attributes_spec.rb
@@ -52,7 +52,7 @@ describe CacheableAttributes do
describe '.cache_key' do
it 'excludes cache attributes' do
- expect(minimal_test_class.cache_key).to eq("TestClass:#{Gitlab::VERSION}:#{Gitlab.migrations_hash}:#{Rails.version}")
+ expect(minimal_test_class.cache_key).to eq("TestClass:#{Gitlab::VERSION}:#{Rails.version}")
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 1cfd526834c..ec6374f3963 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -549,7 +549,7 @@ describe Issuable do
let(:project) { create(:project, namespace: group) }
let(:other_project) { create(:project) }
let(:owner) { create(:owner) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
@@ -558,7 +558,7 @@ describe Issuable do
before do
group.add_owner(owner)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
project.add_guest(contributor)
@@ -570,8 +570,8 @@ describe Issuable do
let(:merged_mr_other_project) { create(:merge_request, :merged, author: first_time_contributor, target_project: other_project, source_project: other_project) }
context "for merge requests" do
- it "is false for MASTER" do
- mr = create(:merge_request, author: master, target_project: project, source_project: project)
+ it "is false for MAINTAINER" do
+ mr = create(:merge_request, author: maintainer, target_project: project, source_project: project)
expect(mr).not_to be_first_contribution
end
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index c73ea6aa94c..a9b237fa9ea 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -136,7 +136,7 @@ describe Issue, "Mentionable" do
expect(SystemNoteService).not_to receive(:cross_reference)
- issue.update_attributes(description: 'New description')
+ issue.update(description: 'New description')
issue.create_new_cross_references!
end
@@ -145,7 +145,7 @@ describe Issue, "Mentionable" do
expect(SystemNoteService).to receive(:cross_reference).with(issues[1], any_args)
- issue.update_attributes(description: issues[1].to_reference)
+ issue.update(description: issues[1].to_reference)
issue.create_new_cross_references!
end
@@ -155,7 +155,7 @@ describe Issue, "Mentionable" do
expect(SystemNoteService).to receive(:cross_reference).with(issues[1], any_args)
- note.update_attributes(note: issues[1].to_reference)
+ note.update(note: issues[1].to_reference)
note.create_new_cross_references!
end
end
diff --git a/spec/models/concerns/protected_ref_access_spec.rb b/spec/models/concerns/protected_ref_access_spec.rb
index a62ca391e25..ce602337647 100644
--- a/spec/models/concerns/protected_ref_access_spec.rb
+++ b/spec/models/concerns/protected_ref_access_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe ProtectedRefAccess do
subject(:protected_ref_access) do
- create(:protected_branch, :masters_can_push).push_access_levels.first
+ create(:protected_branch, :maintainers_can_push).push_access_levels.first
end
let(:project) { protected_ref_access.project }
@@ -14,11 +14,11 @@ describe ProtectedRefAccess do
expect(protected_ref_access.check_access(admin)).to be_truthy
end
- it 'is true for masters' do
- master = create(:user)
- project.add_master(master)
+ it 'is true for maintainers' do
+ maintainer = create(:user)
+ project.add_maintainer(maintainer)
- expect(protected_ref_access.check_access(master)).to be_truthy
+ expect(protected_ref_access.check_access(maintainer)).to be_truthy
end
it 'is for developers of the project' do
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index 0f156619e9e..97a4c212f1c 100644
--- a/spec/models/concerns/reactive_caching_spec.rb
+++ b/spec/models/concerns/reactive_caching_spec.rb
@@ -85,6 +85,14 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
it { is_expected.to be_nil }
end
+
+ context 'when cache was invalidated' do
+ it 'refreshes cache' do
+ expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666)
+
+ instance.with_reactive_cache { raise described_class::InvalidateReactiveCache }
+ end
+ end
end
end
@@ -125,6 +133,13 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
go!
end
+ it "calls a reactive_cache_updated only once if content did not change on subsequent update" do
+ expect(instance).to receive(:calculate_reactive_cache).twice
+ expect(instance).to receive(:reactive_cache_updated).once
+
+ 2.times { instance.exclusively_update_reactive_cache! }
+ end
+
context 'and #calculate_reactive_cache raises an exception' do
before do
stub_reactive_cache(instance, "preexisting")
diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb
index 2f9f63ce7e0..97b046b0f21 100644
--- a/spec/models/concerns/resolvable_discussion_spec.rb
+++ b/spec/models/concerns/resolvable_discussion_spec.rb
@@ -190,7 +190,7 @@ describe Discussion, ResolvableDiscussion do
context "when the signed in user can push to the project" do
before do
- subject.project.add_master(current_user)
+ subject.project.add_maintainer(current_user)
end
it "returns true" do
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index 8cb50d7465c..565266321d3 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -12,16 +12,6 @@ describe Group, 'Routable' do
it { is_expected.to have_many(:redirect_routes).dependent(:destroy) }
end
- describe 'GitLab read-only instance' do
- it 'does not save route if route is not present' do
- group.route.path = ''
- allow(Gitlab::Database).to receive(:read_only?).and_return(true)
- expect(group).to receive(:update_route).and_call_original
-
- expect { group.full_path }.to change { Route.count }.by(0)
- end
- end
-
describe 'Callbacks' do
it 'creates route record on create' do
expect(group.route.path).to eq(group.path)
@@ -29,7 +19,7 @@ describe Group, 'Routable' do
end
it 'updates route record on path change' do
- group.update_attributes(path: 'wow', name: 'much')
+ group.update(path: 'wow', name: 'much')
expect(group.route.path).to eq('wow')
expect(group.route.name).to eq('much')
@@ -131,29 +121,6 @@ describe Group, 'Routable' do
it { expect(group.full_path).to eq(group.path) }
it { expect(nested_group.full_path).to eq("#{group.full_path}/#{nested_group.path}") }
-
- context 'with RequestStore active', :request_store do
- it 'does not load the route table more than once' do
- group.expires_full_path_cache
- expect(group).to receive(:uncached_full_path).once.and_call_original
-
- 3.times { group.full_path }
- expect(group.full_path).to eq(group.path)
- end
- end
- end
-
- describe '#expires_full_path_cache' do
- context 'with RequestStore active', :request_store do
- it 'expires the full_path cache' do
- expect(group.full_path).to eq('foo')
-
- group.route.update(path: 'bar', name: 'bar')
- group.expires_full_path_cache
-
- expect(group.full_path).to eq('bar')
- end
- end
end
describe '#full_name' do
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index f8d51a95833..3435f93c999 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -62,7 +62,22 @@ describe DeployToken do
end
end
- context "when it hasn't been revoked" do
+ context "when it hasn't been revoked and is not expired" do
+ it 'should return true' do
+ expect(deploy_token.active?).to be_truthy
+ end
+ end
+
+ context "when it hasn't been revoked and is expired" do
+ it 'should return true' do
+ deploy_token.update_attribute(:expires_at, Date.today - 5.days)
+ expect(deploy_token.active?).to be_falsy
+ end
+ end
+
+ context "when it hasn't been revoked and has no expiry" do
+ let(:deploy_token) { create(:deploy_token, expires_at: nil) }
+
it 'should return true' do
expect(deploy_token.active?).to be_truthy
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index e01906f4b6c..b335e0fbeb3 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -157,22 +157,4 @@ describe Deployment do
end
end
end
-
- describe '#stop_action?' do
- subject { deployment.stop_action? }
-
- context 'when no other actions' do
- let(:deployment) { build(:deployment) }
-
- it { is_expected.to be_falsey }
- end
-
- context 'when matching action is defined' do
- let(:build) { create(:ci_build) }
- let(:deployment) { FactoryBot.build(:deployment, deployable: build, on_stop: 'close_app') }
- let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
-
- it { is_expected.to be_truthy }
- end
- end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 25d6597084c..c65e0b81451 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -170,8 +170,8 @@ describe Environment do
end
end
- describe '#stop_action?' do
- subject { environment.stop_action? }
+ describe '#stop_action_available?' do
+ subject { environment.stop_action_available? }
context 'when no other actions' do
it { is_expected.to be_falsey }
@@ -179,8 +179,17 @@ describe Environment do
context 'when matching action is defined' do
let(:build) { create(:ci_build) }
- let!(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
- let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
+
+ let!(:deployment) do
+ create(:deployment, environment: environment,
+ deployable: build,
+ on_stop: 'close_app')
+ end
+
+ let!(:close_action) do
+ create(:ci_build, :manual, pipeline: build.pipeline,
+ name: 'close_app')
+ end
context 'when environment is available' do
before do
@@ -562,7 +571,7 @@ describe Environment do
it "is not regenerated if name changes" do
original_slug = environment.slug
- environment.update_attributes!(name: environment.name.reverse)
+ environment.update!(name: environment.name.reverse)
expect(environment.slug).to eq(original_slug)
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 9fe1186a8c9..0729eb99e78 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -177,7 +177,7 @@ describe Group do
describe 'when the user has access to a group' do
before do
- group.add_user(user, Gitlab::Access::MASTER)
+ group.add_user(user, Gitlab::Access::MAINTAINER)
end
it { is_expected.to eq([group]) }
@@ -229,10 +229,10 @@ describe Group do
let(:user) { create(:user) }
before do
- group.add_user(user, GroupMember::MASTER)
+ group.add_user(user, GroupMember::MAINTAINER)
end
- it { expect(group.group_members.masters.map(&:user)).to include(user) }
+ it { expect(group.group_members.maintainers.map(&:user)).to include(user) }
end
describe '#add_users' do
@@ -254,7 +254,7 @@ describe Group do
let(:user) { create(:user) }
before do
- group.add_user(user, GroupMember::MASTER)
+ group.add_user(user, GroupMember::MAINTAINER)
end
it "is true if avatar is image" do
@@ -274,7 +274,7 @@ describe Group do
context 'when avatar file is uploaded' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'shows correct avatar url' do
@@ -317,7 +317,7 @@ describe Group do
end
it { expect(group.has_owner?(@members[:owner])).to be_truthy }
- it { expect(group.has_owner?(@members[:master])).to be_falsey }
+ it { expect(group.has_owner?(@members[:maintainer])).to be_falsey }
it { expect(group.has_owner?(@members[:developer])).to be_falsey }
it { expect(group.has_owner?(@members[:reporter])).to be_falsey }
it { expect(group.has_owner?(@members[:guest])).to be_falsey }
@@ -325,19 +325,19 @@ describe Group do
it { expect(group.has_owner?(nil)).to be_falsey }
end
- describe '#has_master?' do
+ describe '#has_maintainer?' do
before do
@members = setup_group_members(group)
- create(:group_member, :invited, :master, group: group)
+ create(:group_member, :invited, :maintainer, group: group)
end
- it { expect(group.has_master?(@members[:owner])).to be_falsey }
- it { expect(group.has_master?(@members[:master])).to be_truthy }
- it { expect(group.has_master?(@members[:developer])).to be_falsey }
- it { expect(group.has_master?(@members[:reporter])).to be_falsey }
- it { expect(group.has_master?(@members[:guest])).to be_falsey }
- it { expect(group.has_master?(@members[:requester])).to be_falsey }
- it { expect(group.has_master?(nil)).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:owner])).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:maintainer])).to be_truthy }
+ it { expect(group.has_maintainer?(@members[:developer])).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:reporter])).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:guest])).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:requester])).to be_falsey }
+ it { expect(group.has_maintainer?(nil)).to be_falsey }
end
describe '#lfs_enabled?' do
@@ -401,7 +401,7 @@ describe Group do
def setup_group_members(group)
members = {
owner: create(:user),
- master: create(:user),
+ maintainer: create(:user),
developer: create(:user),
reporter: create(:user),
guest: create(:user),
@@ -409,7 +409,7 @@ describe Group do
}
group.add_user(members[:owner], GroupMember::OWNER)
- group.add_user(members[:master], GroupMember::MASTER)
+ group.add_user(members[:maintainer], GroupMember::MAINTAINER)
group.add_user(members[:developer], GroupMember::DEVELOPER)
group.add_user(members[:reporter], GroupMember::REPORTER)
group.add_user(members[:guest], GroupMember::GUEST)
@@ -439,25 +439,25 @@ describe Group do
describe '#members_with_parents', :nested_groups do
let!(:group) { create(:group, :nested) }
- let!(:master) { group.parent.add_user(create(:user), GroupMember::MASTER) }
+ let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }
it 'returns parents members' do
expect(group.members_with_parents).to include(developer)
- expect(group.members_with_parents).to include(master)
+ expect(group.members_with_parents).to include(maintainer)
end
end
describe '#direct_and_indirect_members', :nested_groups do
let!(:group) { create(:group, :nested) }
let!(:sub_group) { create(:group, parent: group) }
- let!(:master) { group.parent.add_user(create(:user), GroupMember::MASTER) }
+ 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(master)
+ expect(group.direct_and_indirect_members).to include(maintainer)
end
it 'returns descendant members' do
@@ -539,14 +539,14 @@ describe Group do
describe '#user_ids_for_project_authorizations' do
it 'returns the user IDs for which to refresh authorizations' do
- master = create(:user)
+ maintainer = create(:user)
developer = create(:user)
- group.add_user(master, GroupMember::MASTER)
+ group.add_user(maintainer, GroupMember::MAINTAINER)
group.add_user(developer, GroupMember::DEVELOPER)
expect(group.user_ids_for_project_authorizations)
- .to include(master.id, developer.id)
+ .to include(maintainer.id, developer.id)
end
end
@@ -617,7 +617,7 @@ describe Group do
expect(group).to receive(:system_hook_service).and_return(system_hook_service)
expect(system_hook_service).to receive(:execute_hooks_for).with(group, :rename)
- group.update_attributes!(path: new_path)
+ group.update!(path: new_path)
end
end
@@ -625,7 +625,7 @@ describe Group do
it 'does not trigger system hook' do
expect(group).not_to receive(:system_hook_service)
- group.update_attributes!(name: 'new name')
+ group.update!(name: 'new name')
end
end
end
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 8bc45715dcd..01129df1107 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -63,7 +63,7 @@ describe SystemHook do
end
it "project_create hook" do
- project.add_master(user)
+ project.add_maintainer(user)
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_team/,
@@ -72,7 +72,7 @@ describe SystemHook do
end
it "project_destroy hook" do
- project.add_master(user)
+ project.add_maintainer(user)
project.project_members.destroy_all
expect(WebMock).to have_requested(:post, system_hook.url).with(
@@ -100,7 +100,7 @@ describe SystemHook do
end
it 'group member create hook' do
- group.add_master(user)
+ group.add_maintainer(user)
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_group/,
@@ -109,7 +109,7 @@ describe SystemHook do
end
it 'group member destroy hook' do
- group.add_master(user)
+ group.add_maintainer(user)
group.group_members.destroy_all
expect(WebMock).to have_requested(:post, system_hook.url).with(
diff --git a/spec/models/hooks/web_hook_log_spec.rb b/spec/models/hooks/web_hook_log_spec.rb
index 19bc88b1333..744a6ccae8b 100644
--- a/spec/models/hooks/web_hook_log_spec.rb
+++ b/spec/models/hooks/web_hook_log_spec.rb
@@ -9,6 +9,24 @@ describe WebHookLog do
it { is_expected.to validate_presence_of(:web_hook) }
+ describe '.recent' do
+ let(:hook) { create(:project_hook) }
+
+ it 'does not return web hook logs that are too old' do
+ create(:web_hook_log, web_hook: hook, created_at: 91.days.ago)
+
+ expect(described_class.recent.size).to be_zero
+ end
+
+ it 'returns the web hook logs in descending order' do
+ hook1 = create(:web_hook_log, web_hook: hook, created_at: 2.hours.ago)
+ hook2 = create(:web_hook_log, web_hook: hook, created_at: 1.hour.ago)
+ hooks = described_class.recent.to_a
+
+ expect(hooks).to eq([hook2, hook1])
+ end
+ end
+
describe '#success?' do
let(:web_hook_log) { build(:web_hook_log, response_status: status) }
diff --git a/spec/models/import_export_upload_spec.rb b/spec/models/import_export_upload_spec.rb
new file mode 100644
index 00000000000..58af84b8a08
--- /dev/null
+++ b/spec/models/import_export_upload_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe ImportExportUpload do
+ subject { described_class.new(project: create(:project)) }
+
+ shared_examples 'stores the Import/Export file' do |method|
+ it 'stores the import file' do
+ subject.public_send("#{method}=", fixture_file_upload('spec/fixtures/project_export.tar.gz'))
+
+ subject.save!
+
+ url = "/uploads/-/system/import_export_upload/#{method}/#{subject.id}/project_export.tar.gz"
+
+ expect(subject.public_send(method).url).to eq(url)
+ end
+ end
+
+ context 'import' do
+ it_behaves_like 'stores the Import/Export file', :import_file
+ end
+
+ context 'export' do
+ it_behaves_like 'stores the Import/Export file', :export_file
+ end
+end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 581fd0293cc..20600f5fa38 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -79,6 +79,46 @@ describe InternalId do
end
end
+ describe '.track_greatest' do
+ let(:value) { 9001 }
+ subject { described_class.track_greatest(issue, scope, usage, value, init) }
+
+ context 'in the absence of a record' do
+ it 'creates a record if not yet present' do
+ expect { subject }.to change { described_class.count }.from(0).to(1)
+ end
+ end
+
+ it 'stores record attributes' do
+ subject
+
+ described_class.first.tap do |record|
+ expect(record.project).to eq(project)
+ expect(record.usage).to eq(usage.to_s)
+ expect(record.last_value).to eq(value)
+ end
+ end
+
+ context 'with existing issues' do
+ before do
+ create(:issue, project: project)
+ described_class.delete_all
+ end
+
+ it 'still returns the last value to that of the given value' do
+ expect(subject).to eq(value)
+ end
+ end
+
+ context 'when value is less than the current last_value' do
+ it 'returns the current last_value' do
+ described_class.create!(**scope, usage: usage, last_value: 10_001)
+
+ expect(subject).to eq 10_001
+ end
+ end
+ end
+
describe '#increment_and_save!' do
let(:id) { create(:internal_id) }
subject { id.increment_and_save! }
@@ -103,4 +143,30 @@ describe InternalId do
end
end
end
+
+ describe '#track_greatest_and_save!' do
+ let(:id) { create(:internal_id) }
+ let(:new_last_value) { 9001 }
+ subject { id.track_greatest_and_save!(new_last_value) }
+
+ it 'returns new last value' do
+ expect(subject).to eq new_last_value
+ end
+
+ it 'saves the record' do
+ subject
+
+ expect(id.changed?).to be_falsey
+ end
+
+ context 'when new last value is lower than the max' do
+ it 'does not update the last value' do
+ id.update!(last_value: 10_001)
+
+ subject
+
+ expect(id.reload.last_value).to eq 10_001
+ end
+ end
+ end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index e818fbeb9cf..84edfc3ff00 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -669,7 +669,7 @@ describe Issue do
context 'when the user is the project owner' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'returns true for a regular issue' do
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 8914845ea82..99670af786a 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -139,4 +139,20 @@ describe Label do
end
end
end
+
+ describe '.search' do
+ let(:label) { create(:label, title: 'bug', description: 'incorrect behavior') }
+
+ it 'returns labels with a partially matching title' do
+ expect(described_class.search(label.title[0..2])).to eq([label])
+ end
+
+ it 'returns labels with a partially matching description' do
+ expect(described_class.search(label.description[0..5])).to eq([label])
+ end
+
+ it 'returns nothing' do
+ expect(described_class.search('feature')).to be_empty
+ end
+ end
end
diff --git a/spec/models/lfs_file_lock_spec.rb b/spec/models/lfs_file_lock_spec.rb
index ce87b01b49c..e74f342d3eb 100644
--- a/spec/models/lfs_file_lock_spec.rb
+++ b/spec/models/lfs_file_lock_spec.rb
@@ -13,13 +13,13 @@ describe LfsFileLock do
describe '#can_be_unlocked_by?' do
let(:developer) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
before do
project = lfs_file_lock.project
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
end
context "when it's forced" do
@@ -29,8 +29,8 @@ describe LfsFileLock do
expect(lfs_file_lock.can_be_unlocked_by?(user, true)).to eq(true)
end
- it 'can be unlocked by a master' do
- expect(lfs_file_lock.can_be_unlocked_by?(master, true)).to eq(true)
+ it 'can be unlocked by a maintainer' do
+ expect(lfs_file_lock.can_be_unlocked_by?(maintainer, true)).to eq(true)
end
it "can't be unlocked by other user" do
@@ -45,8 +45,8 @@ describe LfsFileLock do
expect(lfs_file_lock.can_be_unlocked_by?(user)).to eq(true)
end
- it "can't be unlocked by a master" do
- expect(lfs_file_lock.can_be_unlocked_by?(master)).to eq(false)
+ it "can't be unlocked by a maintainer" do
+ expect(lfs_file_lock.can_be_unlocked_by?(maintainer)).to eq(false)
end
it "can't be unlocked by other user" do
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index c64cdf8f812..fca1b1f90d9 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -62,16 +62,16 @@ describe Member do
@owner_user = create(:user).tap { |u| group.add_owner(u) }
@owner = group.members.find_by(user_id: @owner_user.id)
- @master_user = create(:user).tap { |u| project.add_master(u) }
- @master = project.members.find_by(user_id: @master_user.id)
+ @maintainer_user = create(:user).tap { |u| project.add_maintainer(u) }
+ @maintainer = project.members.find_by(user_id: @maintainer_user.id)
@blocked_user = create(:user).tap do |u|
- project.add_master(u)
+ project.add_maintainer(u)
project.add_developer(u)
u.block!
end
- @blocked_master = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::MASTER)
+ @blocked_maintainer = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::MAINTAINER)
@blocked_developer = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::DEVELOPER)
@invited_member = create(:project_member, :developer,
@@ -95,10 +95,10 @@ describe Member do
describe '.access_for_user_ids' do
it 'returns the right access levels' do
- users = [@owner_user.id, @master_user.id, @blocked_user.id]
+ users = [@owner_user.id, @maintainer_user.id, @blocked_user.id]
expected = {
@owner_user.id => Gitlab::Access::OWNER,
- @master_user.id => Gitlab::Access::MASTER
+ @maintainer_user.id => Gitlab::Access::MAINTAINER
}
expect(described_class.access_for_user_ids(users)).to eq(expected)
@@ -106,7 +106,7 @@ describe Member do
end
describe '.invite' do
- it { expect(described_class.invite).not_to include @master }
+ it { expect(described_class.invite).not_to include @maintainer }
it { expect(described_class.invite).to include @invited_member }
it { expect(described_class.invite).not_to include @accepted_invite_member }
it { expect(described_class.invite).not_to include @requested_member }
@@ -114,7 +114,7 @@ describe Member do
end
describe '.non_invite' do
- it { expect(described_class.non_invite).to include @master }
+ it { expect(described_class.non_invite).to include @maintainer }
it { expect(described_class.non_invite).not_to include @invited_member }
it { expect(described_class.non_invite).to include @accepted_invite_member }
it { expect(described_class.non_invite).to include @requested_member }
@@ -122,7 +122,7 @@ describe Member do
end
describe '.request' do
- it { expect(described_class.request).not_to include @master }
+ it { expect(described_class.request).not_to include @maintainer }
it { expect(described_class.request).not_to include @invited_member }
it { expect(described_class.request).not_to include @accepted_invite_member }
it { expect(described_class.request).to include @requested_member }
@@ -130,7 +130,7 @@ describe Member do
end
describe '.non_request' do
- it { expect(described_class.non_request).to include @master }
+ it { expect(described_class.non_request).to include @maintainer }
it { expect(described_class.non_request).to include @invited_member }
it { expect(described_class.non_request).to include @accepted_invite_member }
it { expect(described_class.non_request).not_to include @requested_member }
@@ -141,35 +141,35 @@ describe Member do
subject { described_class.developers.to_a }
it { is_expected.not_to include @owner }
- it { is_expected.not_to include @master }
+ it { is_expected.not_to include @maintainer }
it { is_expected.to include @invited_member }
it { is_expected.to include @accepted_invite_member }
it { is_expected.not_to include @requested_member }
it { is_expected.to include @accepted_request_member }
- it { is_expected.not_to include @blocked_master }
+ it { is_expected.not_to include @blocked_maintainer }
it { is_expected.not_to include @blocked_developer }
end
- describe '.owners_and_masters' do
- it { expect(described_class.owners_and_masters).to include @owner }
- it { expect(described_class.owners_and_masters).to include @master }
- it { expect(described_class.owners_and_masters).not_to include @invited_member }
- it { expect(described_class.owners_and_masters).not_to include @accepted_invite_member }
- it { expect(described_class.owners_and_masters).not_to include @requested_member }
- it { expect(described_class.owners_and_masters).not_to include @accepted_request_member }
- it { expect(described_class.owners_and_masters).not_to include @blocked_master }
+ describe '.owners_and_maintainers' do
+ it { expect(described_class.owners_and_maintainers).to include @owner }
+ it { expect(described_class.owners_and_maintainers).to include @maintainer }
+ it { expect(described_class.owners_and_maintainers).not_to include @invited_member }
+ it { expect(described_class.owners_and_maintainers).not_to include @accepted_invite_member }
+ it { expect(described_class.owners_and_maintainers).not_to include @requested_member }
+ it { expect(described_class.owners_and_maintainers).not_to include @accepted_request_member }
+ it { expect(described_class.owners_and_maintainers).not_to include @blocked_maintainer }
end
describe '.has_access' do
subject { described_class.has_access.to_a }
it { is_expected.to include @owner }
- it { is_expected.to include @master }
+ it { is_expected.to include @maintainer }
it { is_expected.to include @invited_member }
it { is_expected.to include @accepted_invite_member }
it { is_expected.not_to include @requested_member }
it { is_expected.to include @accepted_request_member }
- it { is_expected.not_to include @blocked_master }
+ it { is_expected.not_to include @blocked_maintainer }
it { is_expected.not_to include @blocked_developer }
end
end
@@ -187,20 +187,20 @@ describe Member do
let!(:admin) { create(:admin) }
it 'returns a <Source>Member object' do
- member = described_class.add_user(source, user, :master)
+ member = described_class.add_user(source, user, :maintainer)
expect(member).to be_a "#{source_type.classify}Member".constantize
expect(member).to be_persisted
end
it 'sets members.created_by to the given current_user' do
- member = described_class.add_user(source, user, :master, current_user: admin)
+ member = described_class.add_user(source, user, :maintainer, current_user: admin)
expect(member.created_by).to eq(admin)
end
it 'sets members.expires_at to the given expires_at' do
- member = described_class.add_user(source, user, :master, expires_at: Date.new(2016, 9, 22))
+ member = described_class.add_user(source, user, :maintainer, expires_at: Date.new(2016, 9, 22))
expect(member.expires_at).to eq(Date.new(2016, 9, 22))
end
@@ -230,7 +230,7 @@ describe Member do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, user.id, :master)
+ described_class.add_user(source, user.id, :maintainer)
expect(source.users.reload).to include(user)
end
@@ -240,7 +240,7 @@ describe Member do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, 42, :master)
+ described_class.add_user(source, 42, :maintainer)
expect(source.users.reload).not_to include(user)
end
@@ -250,7 +250,7 @@ describe Member do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, user, :master)
+ described_class.add_user(source, user, :maintainer)
expect(source.users.reload).to include(user)
end
@@ -265,7 +265,7 @@ describe Member do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
- expect { described_class.add_user(source, user, :master) }
+ expect { described_class.add_user(source, user, :maintainer) }
.to raise_error(Gitlab::Access::AccessDeniedError)
expect(source.users.reload).not_to include(user)
@@ -277,7 +277,7 @@ describe Member do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, user.email, :master)
+ described_class.add_user(source, user.email, :maintainer)
expect(source.users.reload).to include(user)
end
@@ -287,7 +287,7 @@ describe Member do
it 'creates an invited member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, 'user@example.com', :master)
+ described_class.add_user(source, 'user@example.com', :maintainer)
expect(source.members.invite.pluck(:invite_email)).to include('user@example.com')
end
@@ -298,7 +298,7 @@ describe Member do
it 'creates the member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, user, :master, current_user: admin)
+ described_class.add_user(source, user, :maintainer, current_user: admin)
expect(source.users.reload).to include(user)
end
@@ -312,7 +312,7 @@ describe Member do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
- described_class.add_user(source, user, :master, current_user: admin)
+ described_class.add_user(source, user, :maintainer, current_user: admin)
expect(source.users.reload).to include(user)
expect(source.requesters.reload.exists?(user_id: user)).to be_falsy
@@ -324,7 +324,7 @@ describe Member do
it 'does not create the member' do
expect(source.users).not_to include(user)
- member = described_class.add_user(source, user, :master, current_user: user)
+ member = described_class.add_user(source, user, :maintainer, current_user: user)
expect(source.users.reload).not_to include(user)
expect(member).not_to be_persisted
@@ -339,7 +339,7 @@ describe Member do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
- described_class.add_user(source, user, :master, current_user: user)
+ described_class.add_user(source, user, :maintainer, current_user: user)
expect(source.users.reload).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
@@ -356,9 +356,9 @@ describe Member do
it 'updates the member' do
expect(source.users).to include(user)
- described_class.add_user(source, user, :master)
+ described_class.add_user(source, user, :maintainer)
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MASTER)
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -366,9 +366,9 @@ describe Member do
it 'updates the member' do
expect(source.users).to include(user)
- described_class.add_user(source, user, :master, current_user: admin)
+ described_class.add_user(source, user, :maintainer, current_user: admin)
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MASTER)
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -376,7 +376,7 @@ describe Member do
it 'does not update the member' do
expect(source.users).to include(user)
- described_class.add_user(source, user, :master, current_user: user)
+ described_class.add_user(source, user, :maintainer, current_user: user)
expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER)
end
@@ -395,7 +395,7 @@ describe Member do
let(:user2) { create(:user) }
it 'returns a <Source>Member objects' do
- members = described_class.add_users(source, [user1, user2], :master)
+ members = described_class.add_users(source, [user1, user2], :maintainer)
expect(members).to be_a Array
expect(members.size).to eq(2)
@@ -404,7 +404,7 @@ describe Member do
end
it 'returns an empty array' do
- members = described_class.add_users(source, [], :master)
+ members = described_class.add_users(source, [], :maintainer)
expect(members).to be_a Array
expect(members).to be_empty
@@ -413,7 +413,7 @@ describe Member do
it 'supports differents formats' do
list = ['joe@local.test', admin, user1.id, user2.id.to_s]
- members = described_class.add_users(source, list, :master)
+ members = described_class.add_users(source, list, :maintainer)
expect(members.size).to eq(4)
expect(members.first).to be_invite
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index ffc78015f94..97959ed4304 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -21,7 +21,7 @@ describe GroupMember do
described_class.add_users(
group,
[users.first.id, users.second],
- described_class::MASTER
+ described_class::MAINTAINER
)
expect(group.users).to include(users.first, users.second)
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 574eb468e4c..334d4f95f53 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -28,7 +28,7 @@ describe ProjectMember do
expect(project.users).not_to include(user)
- described_class.add_user(project, user, :master, current_user: project.owner)
+ described_class.add_user(project, user, :maintainer, current_user: project.owner)
expect(project.users.reload).to include(user)
end
@@ -41,9 +41,9 @@ describe ProjectMember do
end
describe "#destroy" do
- let(:owner) { create(:project_member, access_level: ProjectMember::MASTER) }
+ let(:owner) { create(:project_member, access_level: ProjectMember::MAINTAINER) }
let(:project) { owner.project }
- let(:master) { create(:project_member, project: project) }
+ let(:maintainer) { create(:project_member, project: project) }
it "creates an expired event when left due to expiry" do
expired = create(:project_member, project: project, expires_at: Time.now - 6.days)
@@ -52,7 +52,7 @@ describe ProjectMember do
end
it "creates a left event when left due to leave" do
- master.destroy
+ maintainer.destroy
expect(Event.recent.first.action).to eq(Event::LEFT)
end
end
@@ -95,7 +95,7 @@ describe ProjectMember do
described_class.add_users_to_projects(
[projects.first.id, projects.second.id],
[users.first.id, users.second],
- described_class::MASTER)
+ described_class::MAINTAINER)
expect(projects.first.users).to include(users.first)
expect(projects.first.users).to include(users.second)
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 48c01fc4d4e..90cce826b6c 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -82,6 +82,14 @@ describe MergeRequestDiff do
diff.diffs
end
+
+ it 'returns persisted diffs if diff refs does not exist' do
+ expect(diff).to receive(:load_diffs)
+
+ diff.update!(start_commit_sha: nil, base_commit_sha: nil)
+
+ diff.diffs
+ end
end
end
@@ -119,6 +127,13 @@ describe MergeRequestDiff do
expect(diffs.map(&:new_path)).to contain_exactly('files/ruby/popen.rb')
end
+ it 'only serializes diff files found by query' do
+ expect(diff_with_commits.merge_request_diff_files.count).to be > 10
+ expect_any_instance_of(MergeRequestDiffFile).to receive(:to_hash).once
+
+ diffs
+ end
+
it 'uses the diffs from the DB' do
expect(diff_with_commits).to receive(:load_diffs)
@@ -153,6 +168,13 @@ describe MergeRequestDiff do
expect(mr_diff.empty?).to be_truthy
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')
+
+ expect(diff_file.diff).not_to be_empty
+ end
+
it 'saves binary diffs correctly' do
path = 'files/images/icn-time-tracking.pdf'
mr_diff = create(:merge_request, source_branch: 'add-pdf-text-binary', target_branch: 'master').merge_request_diff
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index ec72fefd137..6258bfa232f 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe MergeRequest do
include RepoHelpers
include ProjectForksHelper
+ include ReactiveCachingHelpers
subject { create(:merge_request) }
@@ -310,6 +311,51 @@ describe MergeRequest do
end
end
+ describe '#visible_closing_issues_for' do
+ let(:guest) { create(:user) }
+ let(:developer) { create(:user) }
+ let(:issue_1) { create(:issue, project: subject.source_project) }
+ let(:issue_2) { create(:issue, project: subject.source_project) }
+ let(:confidential_issue) { create(:issue, :confidential, project: subject.source_project) }
+
+ before do
+ subject.project.add_developer(subject.author)
+ subject.target_branch = subject.project.default_branch
+ commit = double('commit1', safe_message: "Fixes #{issue_1.to_reference} #{issue_2.to_reference} #{confidential_issue.to_reference}")
+ allow(subject).to receive(:commits).and_return([commit])
+ end
+
+ it 'shows only allowed issues to guest' do
+ subject.project.add_guest(guest)
+
+ subject.cache_merge_request_closes_issues!
+
+ expect(subject.visible_closing_issues_for(guest)).to match_array([issue_1, issue_2])
+ end
+
+ it 'shows only allowed issues to developer' do
+ subject.project.add_developer(developer)
+
+ subject.cache_merge_request_closes_issues!
+
+ expect(subject.visible_closing_issues_for(developer)).to match_array([issue_1, confidential_issue, issue_2])
+ end
+
+ context 'when external issue tracker is enabled' do
+ before do
+ subject.project.has_external_issue_tracker = true
+ subject.project.save!
+ end
+
+ it 'calls non #closes_issues to retrieve data' do
+ expect(subject).to receive(:closes_issues)
+ expect(subject).not_to receive(:cached_closes_issues)
+
+ subject.visible_closing_issues_for
+ end
+ end
+ end
+
describe '#cache_merge_request_closes_issues!' do
before do
subject.project.add_developer(subject.author)
@@ -324,6 +370,25 @@ describe MergeRequest do
expect { subject.cache_merge_request_closes_issues!(subject.author) }.to change(subject.merge_requests_closing_issues, :count).by(1)
end
+ it 'does not cache closed issues when merge request is closed' do
+ issue = create :issue, project: subject.project
+ commit = double('commit1', safe_message: "Fixes #{issue.to_reference}")
+
+ allow(subject).to receive(:commits).and_return([commit])
+ allow(subject).to receive(:state).and_return("closed")
+
+ expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count)
+ end
+
+ it 'does not cache closed issues when merge request is merged' do
+ issue = create :issue, project: subject.project
+ commit = double('commit1', safe_message: "Fixes #{issue.to_reference}")
+ allow(subject).to receive(:commits).and_return([commit])
+ allow(subject).to receive(:state).and_return("merged")
+
+ expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count)
+ end
+
context 'when both internal and external issue trackers are enabled' do
before do
subject.project.has_external_issue_tracker = true
@@ -632,6 +697,7 @@ describe MergeRequest do
allow(subject).to receive(:commits).and_return([commit])
allow(subject.project).to receive(:default_branch)
.and_return(subject.target_branch)
+ subject.cache_merge_request_closes_issues!
expect(subject.issues_mentioned_but_not_closing(subject.author)).to match_array([mentioned_issue])
end
@@ -649,6 +715,8 @@ describe MergeRequest do
end
it 'detects issues mentioned in description but not closed' do
+ subject.cache_merge_request_closes_issues!
+
expect(subject.issues_mentioned_but_not_closing(subject.author).map(&:to_s)).to match_array(['TEST-2'])
end
end
@@ -724,7 +792,7 @@ describe MergeRequest do
subject { merge_request }
before do
- subject.source_project.add_master(user)
+ subject.source_project.add_maintainer(user)
end
it "can't be removed when its a protected branch" do
@@ -779,9 +847,8 @@ describe MergeRequest do
subject.project.add_developer(subject.author)
subject.description = "This issue Closes #{issue.to_reference}"
-
- allow(subject.project).to receive(:default_branch)
- .and_return(subject.target_branch)
+ allow(subject.project).to receive(:default_branch).and_return(subject.target_branch)
+ subject.cache_merge_request_closes_issues!
expect(subject.merge_commit_message)
.to match("Closes #{issue.to_reference}")
@@ -1079,6 +1146,97 @@ describe MergeRequest do
end
end
+ describe '#has_test_reports?' do
+ subject { merge_request.has_test_reports? }
+
+ let(:project) { create(:project, :repository) }
+
+ context 'when head pipeline has test reports' do
+ let(:merge_request) { create(:merge_request, :with_test_reports, source_project: project) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when head pipeline does not have test reports' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#compare_test_reports' do
+ subject { merge_request.compare_test_reports }
+
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ let!(:base_pipeline) do
+ create(:ci_pipeline,
+ :with_test_reports,
+ project: project,
+ ref: merge_request.target_branch,
+ sha: merge_request.diff_base_sha)
+ end
+
+ before do
+ merge_request.update!(head_pipeline_id: head_pipeline.id)
+ end
+
+ context 'when head pipeline has test reports' do
+ let!(:head_pipeline) do
+ create(:ci_pipeline,
+ :with_test_reports,
+ project: project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha)
+ end
+
+ context 'when reactive cache worker is parsing asynchronously' do
+ it 'returns 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 status and data' do
+ expect_any_instance_of(Ci::CompareTestReportsService)
+ .to receive(:execute).with(base_pipeline, head_pipeline).and_call_original
+
+ subject
+ end
+
+ context 'when cached results is not latest' do
+ before do
+ allow_any_instance_of(Ci::CompareTestReportsService)
+ .to receive(:latest?).and_return(false)
+ end
+
+ it 'raises and InvalidateReactiveCache error' do
+ expect { subject }.to raise_error(ReactiveCaching::InvalidateReactiveCache)
+ end
+ end
+ end
+ end
+
+ context 'when head pipeline does not have test reports' do
+ let!(:head_pipeline) do
+ create(:ci_pipeline,
+ project: project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha)
+ end
+
+ it 'returns status and error message' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:status_reason]).to eq('This merge request does not have test reports')
+ end
+ end
+ end
+
describe '#all_commit_shas' do
context 'when merge request is persisted' do
let(:all_commit_shas) do
@@ -1199,7 +1357,7 @@ describe MergeRequest do
end
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
ProcessCommitWorker.new.perform(project.id,
current_user.id,
@@ -1207,6 +1365,16 @@ describe MergeRequest do
project.default_branch == branch)
end
+ context 'but merged at timestamp cannot be found' do
+ before do
+ allow(subject).to receive(:merged_at) { nil }
+ end
+
+ it 'returns false' do
+ expect(subject.can_be_reverted?(current_user)).to be_falsey
+ end
+ end
+
context 'when the revert commit is mentioned in a note after the MR was merged' do
it 'returns false' do
expect(subject.can_be_reverted?(current_user)).to be_falsey
@@ -1246,6 +1414,63 @@ describe MergeRequest do
end
end
+ describe '#merged_at' do
+ context 'when MR is not merged' do
+ let(:merge_request) { create(:merge_request, :closed) }
+
+ it 'returns nil' do
+ expect(merge_request.merged_at).to be_nil
+ end
+ end
+
+ context 'when metrics has merged_at data' do
+ let(:merge_request) { create(:merge_request, :merged) }
+
+ before do
+ merge_request.metrics.update!(merged_at: 1.day.ago)
+ end
+
+ it 'returns metrics merged_at' do
+ expect(merge_request.merged_at).to eq(merge_request.metrics.merged_at)
+ end
+ end
+
+ context 'when merged event is persisted, but no metrics merged_at is persisted' do
+ let(:user) { create(:user) }
+ let(:merge_request) { create(:merge_request, :merged) }
+
+ before do
+ EventCreateService.new.merge_mr(merge_request, user)
+ end
+
+ it 'returns merged event creation date' do
+ expect(merge_request.merge_event).to be_persisted
+ expect(merge_request.merged_at).to eq(merge_request.merge_event.created_at)
+ end
+ end
+
+ context 'when merging note is persisted, but no metrics or merge event exists' do
+ let(:user) { create(:user) }
+ let(:merge_request) { create(:merge_request, :merged) }
+
+ before do
+ merge_request.metrics.destroy!
+
+ SystemNoteService.change_status(merge_request,
+ merge_request.target_project,
+ user,
+ merge_request.state, nil)
+ end
+
+ it 'returns merging note creation date' do
+ expect(merge_request.reload.metrics).to be_nil
+ expect(merge_request.merge_event).to be_nil
+ expect(merge_request.notes.count).to eq(1)
+ expect(merge_request.merged_at).to eq(merge_request.notes.first.created_at)
+ end
+ end
+ end
+
describe '#participants' do
let(:project) { create(:project, :public) }
@@ -1569,8 +1794,8 @@ describe MergeRequest do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- merge_request.source_project.add_master(user)
- merge_request.target_project.add_master(user)
+ merge_request.source_project.add_maintainer(user)
+ merge_request.target_project.add_maintainer(user)
end
context 'with multiple environments' do
@@ -1891,7 +2116,7 @@ describe MergeRequest do
end
it 'returns false if the merge request is merged' do
- merge_request.update_attributes(state: 'merged')
+ merge_request.update(state: 'merged')
expect(merge_request.reload.reopenable?).to be_falsey
end
@@ -2010,6 +2235,26 @@ describe MergeRequest do
end
end
+ describe '#base_pipeline' do
+ let(:pipeline_arguments) do
+ {
+ project: project,
+ ref: merge_request.target_branch,
+ sha: merge_request.diff_base_sha
+ }
+ end
+
+ let(:project) { create(:project, :public, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ let!(:first_pipeline) { create(:ci_pipeline_without_jobs, pipeline_arguments) }
+ let!(:last_pipeline) { create(:ci_pipeline_without_jobs, pipeline_arguments) }
+
+ it 'returns latest pipeline' do
+ expect(merge_request.base_pipeline).to eq(last_pipeline)
+ end
+ end
+
describe '#has_commits?' do
before do
allow(subject.merge_request_diff).to receive(:commits_count)
@@ -2190,6 +2435,22 @@ describe MergeRequest do
end
end
end
+
+ context 'source branch is missing' do
+ subject { create(:merge_request, :invalid, :opened, merge_status: :unchecked, target_branch: 'master') }
+
+ before do
+ allow(subject.project.repository).to receive(:can_be_merged?).and_call_original
+ end
+
+ it 'does not raise error' do
+ expect(notification_service).not_to receive(:merge_request_unmergeable)
+ expect(todo_service).not_to receive(:merge_request_became_unmergeable)
+
+ expect { subject.mark_as_unmergeable }.not_to raise_error
+ expect(subject.cannot_be_merged?).to eq(true)
+ end
+ end
end
describe 'check_state?' do
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 204d6b47832..55b984faecf 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -310,4 +310,24 @@ describe Milestone do
expect(milestone.participants).to eq [user]
end
end
+
+ describe '.sort_by_attribute' do
+ set(:milestone_1) { create(:milestone, title: 'Foo') }
+ set(:milestone_2) { create(:milestone, title: 'Bar') }
+ set(:milestone_3) { create(:milestone, title: 'Zoo') }
+
+ context 'ordering by name ascending' do
+ it 'sorts by title ascending' do
+ expect(described_class.sort_by_attribute('name_asc'))
+ .to eq([milestone_2, milestone_1, milestone_3])
+ end
+ end
+
+ context 'ordering by name descending' do
+ it 'sorts by title descending' do
+ expect(described_class.sort_by_attribute('name_desc'))
+ .to eq([milestone_3, milestone_1, milestone_2])
+ end
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 70f1a1c8b38..9b7f932ec3a 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -200,11 +200,39 @@ describe Namespace do
end
it "moves dir if path changed" do
- namespace.update_attributes(path: namespace.full_path + '_new')
+ namespace.update(path: namespace.full_path + '_new')
expect(gitlab_shell.exists?(project.repository_storage, "#{namespace.path}/#{project.path}.git")).to be_truthy
end
+ context 'when #write_projects_repository_config raises an error' do
+ context 'in test environment' do
+ it 'raises an exception' do
+ expect(namespace).to receive(:write_projects_repository_config).and_raise('foo')
+
+ expect do
+ namespace.update(path: namespace.full_path + '_new')
+ end.to raise_error('foo')
+ end
+ end
+
+ context 'in production environment' do
+ it 'does not cancel later callbacks' do
+ expect(namespace).to receive(:write_projects_repository_config).and_raise('foo')
+ expect(namespace).to receive(:move_dir).and_wrap_original do |m, *args|
+ move_dir_result = m.call(*args)
+
+ expect(move_dir_result).to be_truthy # Must be truthy, or else later callbacks would be canceled
+
+ move_dir_result
+ end
+ expect(Gitlab::Sentry).to receive(:should_raise?).and_return(false) # like prod
+
+ namespace.update(path: namespace.full_path + '_new')
+ end
+ end
+ end
+
context 'with subgroups', :nested_groups do
let(:parent) { create(:group, name: 'parent', path: 'parent') }
let(:new_parent) { create(:group, name: 'new_parent', path: 'new_parent') }
@@ -279,7 +307,7 @@ describe Namespace do
it "repository directory remains unchanged if path changed" do
before_disk_path = project.disk_path
- namespace.update_attributes(path: namespace.full_path + '_new')
+ namespace.update(path: namespace.full_path + '_new')
expect(before_disk_path).to eq(project.disk_path)
expect(gitlab_shell.exists?(project.repository_storage, "#{project.disk_path}.git")).to be_truthy
@@ -295,6 +323,16 @@ describe Namespace do
parent.update(path: 'mygroup_new')
+ # Routes are loaded when creating the projects, so we need to manually
+ # reload them for the below code to be aware of the above UPDATE.
+ [
+ project_in_parent_group,
+ hashed_project_in_subgroup,
+ legacy_project_in_subgroup
+ ].each do |project|
+ project.route.reload
+ end
+
expect(project_rugged(project_in_parent_group).config['gitlab.fullpath']).to eq "mygroup_new/#{project_in_parent_group.path}"
expect(project_rugged(hashed_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{hashed_project_in_subgroup.path}"
expect(project_rugged(legacy_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{legacy_project_in_subgroup.path}"
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index a2cb716cb93..947be44c903 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -144,8 +144,8 @@ describe Note do
describe 'admin' do
before do
@p1.project_members.create(user: @u1, access_level: ProjectMember::REPORTER)
- @p1.project_members.create(user: @u2, access_level: ProjectMember::MASTER)
- @p2.project_members.create(user: @u3, access_level: ProjectMember::MASTER)
+ @p1.project_members.create(user: @u2, access_level: ProjectMember::MAINTAINER)
+ @p2.project_members.create(user: @u3, access_level: ProjectMember::MAINTAINER)
end
it { expect(Ability.allowed?(@u1, :admin_note, @p1)).to be_falsey }
@@ -225,7 +225,7 @@ describe Note do
describe "cross_reference_not_visible_for?" do
let(:private_user) { create(:user) }
- let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_master(private_user) } }
+ let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_maintainer(private_user) } }
let(:private_issue) { create(:issue, project: private_project) }
let(:ext_proj) { create(:project, :public) }
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 12681a147b4..77c475b9f52 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe NotificationSetting do
1.upto(4) do |i|
setting = create(:notification_setting, user: user)
- setting.project.update_attributes(pending_delete: true) if i.even?
+ setting.project.update(pending_delete: true) if i.even?
end
end
@@ -93,4 +93,10 @@ RSpec.describe NotificationSetting do
end
end
end
+
+ context 'email events' do
+ it 'includes EXCLUDED_WATCHER_EVENTS in EMAIL_EVENTS' do
+ expect(described_class::EMAIL_EVENTS).to include(*described_class::EXCLUDED_WATCHER_EVENTS)
+ end
+ end
end
diff --git a/spec/models/postgresql/replication_slot_spec.rb b/spec/models/postgresql/replication_slot_spec.rb
new file mode 100644
index 00000000000..919a7526803
--- /dev/null
+++ b/spec/models/postgresql/replication_slot_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Postgresql::ReplicationSlot, :postgresql do
+ describe '.lag_too_great?' do
+ it 'returns true when replication lag is too great' do
+ expect(described_class)
+ .to receive(:pluck)
+ .and_return([125.megabytes])
+
+ expect(described_class.lag_too_great?).to eq(true)
+ end
+
+ it 'returns false when more than one replicas is up to date enough' do
+ expect(described_class)
+ .to receive(:pluck)
+ .and_return([125.megabytes, 0.megabytes, 0.megabytes])
+
+ expect(described_class.lag_too_great?).to eq(false)
+ end
+
+ it 'returns false when replication lag is not too great' do
+ expect(described_class)
+ .to receive(:pluck)
+ .and_return([0.megabytes])
+
+ expect(described_class.lag_too_great?).to eq(false)
+ end
+ end
+end
diff --git a/spec/models/programming_language_spec.rb b/spec/models/programming_language_spec.rb
new file mode 100644
index 00000000000..99cd358f863
--- /dev/null
+++ b/spec/models/programming_language_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe ProgrammingLanguage do
+ it { is_expected.to respond_to(:name) }
+ it { is_expected.to respond_to(:color) }
+
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to allow_value("#000000").for(:color) }
+ it { is_expected.not_to allow_value("000000").for(:color) }
+ it { is_expected.not_to allow_value("#0z0000").for(:color) }
+end
diff --git a/spec/models/project_authorization_spec.rb b/spec/models/project_authorization_spec.rb
index 9e7e525b2c0..c289ee0859a 100644
--- a/spec/models/project_authorization_spec.rb
+++ b/spec/models/project_authorization_spec.rb
@@ -8,15 +8,15 @@ describe ProjectAuthorization do
describe '.insert_authorizations' do
it 'inserts the authorizations' do
described_class
- .insert_authorizations([[user.id, project1.id, Gitlab::Access::MASTER]])
+ .insert_authorizations([[user.id, project1.id, Gitlab::Access::MAINTAINER]])
expect(user.project_authorizations.count).to eq(1)
end
it 'inserts rows in batches' do
described_class.insert_authorizations([
- [user.id, project1.id, Gitlab::Access::MASTER],
- [user.id, project2.id, Gitlab::Access::MASTER]
+ [user.id, project1.id, Gitlab::Access::MAINTAINER],
+ [user.id, project2.id, Gitlab::Access::MAINTAINER]
], 1)
expect(user.project_authorizations.count).to eq(2)
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index 63c6fbda3f2..10617edec0f 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -77,7 +77,7 @@ describe ProjectFeature do
context 'repository related features' do
before do
- project.project_feature.update_attributes(
+ project.project_feature.update(
merge_requests_access_level: ProjectFeature::DISABLED,
builds_access_level: ProjectFeature::DISABLED,
repository_access_level: ProjectFeature::PRIVATE
@@ -119,4 +119,46 @@ describe ProjectFeature do
end
end
end
+
+ context 'Site Statistics' do
+ set(:project_with_wiki) { create(:project, :wiki_enabled) }
+ set(:project_without_wiki) { create(:project, :wiki_disabled) }
+
+ context 'when creating a project' do
+ it 'tracks wiki availability when wikis are enabled by default' do
+ expect { create(:project) }.to change { SiteStatistic.fetch.wikis_count }.by(1)
+ end
+
+ it 'does not track wiki availability when wikis are disabled by default' do
+ expect { create(:project, :wiki_disabled) }.not_to change { SiteStatistic.fetch.wikis_count }
+ end
+ end
+
+ context 'when updating a project_feature' do
+ it 'untracks wiki availability when disabling wiki access' do
+ expect { project_with_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) }
+ .to change { SiteStatistic.fetch.wikis_count }.by(-1)
+ end
+
+ it 'tracks again wiki availability when re-enabling wiki access as public' do
+ expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED) }
+ .to change { SiteStatistic.fetch.wikis_count }.by(1)
+ end
+
+ it 'tracks again wiki availability when re-enabling wiki access as private' do
+ expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::PRIVATE) }
+ .to change { SiteStatistic.fetch.wikis_count }.by(1)
+ end
+ end
+
+ context 'when removing a project' do
+ it 'untracks wiki availability when removing a project with previous wiki access' do
+ expect { project_with_wiki.destroy }.to change { SiteStatistic.fetch.wikis_count }.by(-1)
+ end
+
+ it 'does not untrack wiki availability when removing a project without wiki access' do
+ expect { project_without_wiki.destroy }.not_to change { SiteStatistic.fetch.wikis_count }
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
index 85baaccf035..f4f7afb1b92 100644
--- a/spec/models/project_services/bamboo_service_spec.rb
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -120,6 +120,14 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end
end
+ describe '#execute' do
+ it 'runs update and build action' do
+ stub_update_and_build_request
+
+ subject.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA)
+ end
+ end
+
describe '#build_page' do
it 'returns the contents of the reactive cache' do
stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref')
@@ -216,10 +224,20 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end
end
+ def stub_update_and_build_request(status: 200, body: nil)
+ bamboo_full_url = 'http://gitlab.com/bamboo/updateAndBuild.action?buildKey=foo&os_authType=basic'
+
+ stub_bamboo_request(bamboo_full_url, status, body)
+ end
+
def stub_request(status: 200, body: nil)
- bamboo_full_url = 'http://gitlab.com/bamboo/rest/api/latest/result?label=123&os_authType=basic'
+ bamboo_full_url = 'http://gitlab.com/bamboo/rest/api/latest/result/byChangeset/123?os_authType=basic'
+
+ stub_bamboo_request(bamboo_full_url, status, body)
+ end
- WebMock.stub_request(:get, bamboo_full_url).to_return(
+ def stub_bamboo_request(url, status, body)
+ WebMock.stub_request(:get, url).to_return(
status: status,
headers: { 'Content-Type' => 'application/json' },
body: body
diff --git a/spec/models/project_services/hangouts_chat_service_spec.rb b/spec/models/project_services/hangouts_chat_service_spec.rb
new file mode 100644
index 00000000000..cfa55188a64
--- /dev/null
+++ b/spec/models/project_services/hangouts_chat_service_spec.rb
@@ -0,0 +1,246 @@
+require 'spec_helper'
+
+describe HangoutsChatService do
+ describe 'Associations' do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe 'Validations' do
+ context 'when service is active' do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of(:webhook) }
+ it_behaves_like 'issue tracker service URL attribute', :webhook
+ end
+
+ context 'when service is inactive' do
+ before do
+ subject.active = false
+ end
+
+ it { is_expected.not_to validate_presence_of(:webhook) }
+ end
+ end
+
+ describe '#execute' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:webhook_url) { 'https://example.gitlab.com/' }
+
+ before do
+ allow(subject).to receive_messages(
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url)
+ end
+
+ shared_examples 'Hangouts Chat service' do
+ it 'calls Hangouts Chat API' do
+ subject.execute(sample_data)
+
+ expect(WebMock)
+ .to have_requested(:post, webhook_url)
+ .with { |req| req.body =~ /\A{"text":.+}\Z/ }
+ .once
+ end
+ end
+
+ context 'with push events' do
+ let(:sample_data) do
+ Gitlab::DataBuilder::Push.build_sample(project, user)
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+
+ it 'specifies the webhook when it is configured' do
+ expect(HangoutsChat::Sender).to receive(:new).with(webhook_url).and_return(double(:hangouts_chat_service).as_null_object)
+
+ subject.execute(sample_data)
+ end
+
+ context 'with not default branch' do
+ let(:sample_data) do
+ Gitlab::DataBuilder::Push.build(project, user, nil, nil, 'not-the-default-branch')
+ end
+
+ context 'when notify_only_default_branch enabled' do
+ before do
+ subject.notify_only_default_branch = true
+ end
+
+ it 'does not call the Hangouts Chat API' do
+ result = subject.execute(sample_data)
+
+ expect(result).to be_falsy
+ end
+ end
+
+ context 'when notify_only_default_branch disabled' do
+ before do
+ subject.notify_only_default_branch = false
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+ end
+ end
+
+ context 'with issue events' do
+ let(:opts) { { title: 'Awesome issue', description: 'please fix' } }
+ let(:sample_data) do
+ service = Issues::CreateService.new(project, user, opts)
+ issue = service.execute
+ service.hook_data(issue, 'open')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with merge events' do
+ let(:opts) do
+ {
+ title: 'Awesome merge_request',
+ description: 'please fix',
+ source_branch: 'feature',
+ target_branch: 'master'
+ }
+ end
+
+ let(:sample_data) do
+ service = MergeRequests::CreateService.new(project, user, opts)
+ merge_request = service.execute
+ service.hook_data(merge_request, 'open')
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with wiki page events' do
+ let(:opts) do
+ {
+ title: 'Awesome wiki_page',
+ content: 'Some text describing some thing or another',
+ format: 'md',
+ message: 'user created page: Awesome wiki_page'
+ }
+ end
+ let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) }
+ let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with note events' do
+ let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) }
+
+ context 'with commit comment' do
+ let(:note) do
+ create(:note_on_commit, author: user,
+ project: project,
+ commit_id: project.repository.commit.id,
+ note: 'a comment on a commit')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with merge request comment' do
+ let(:note) do
+ create(:note_on_merge_request, project: project,
+ note: 'merge request note')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with issue comment' do
+ let(:note) do
+ create(:note_on_issue, project: project, note: 'issue note')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with snippet comment' do
+ let(:note) do
+ create(:note_on_project_snippet, project: project,
+ note: 'snippet note')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+ end
+
+ context 'with pipeline events' do
+ let(:pipeline) do
+ create(:ci_pipeline,
+ project: project, status: status,
+ sha: project.commit.sha, ref: project.default_branch)
+ end
+ let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
+
+ context 'with failed pipeline' do
+ let(:status) { 'failed' }
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with succeeded pipeline' do
+ let(:status) { 'success' }
+
+ context 'with default notify_only_broken_pipelines' do
+ it 'does not call Hangouts Chat API' do
+ result = subject.execute(sample_data)
+
+ expect(result).to be_falsy
+ end
+ end
+
+ context 'when notify_only_broken_pipelines is false' do
+ before do
+ subject.notify_only_broken_pipelines = false
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+ end
+
+ context 'with not default branch' do
+ let(:pipeline) do
+ create(:ci_pipeline, project: project, status: 'failed', ref: 'not-the-default-branch')
+ end
+
+ context 'when notify_only_default_branch enabled' do
+ before do
+ subject.notify_only_default_branch = true
+ end
+
+ it 'does not call the Hangouts Chat API' do
+ result = subject.execute(sample_data)
+
+ expect(result).to be_falsy
+ end
+ end
+
+ context 'when notify_only_default_branch disabled' do
+ before do
+ subject.notify_only_default_branch = false
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+ 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 6c637533c6b..ac9ff59b9b5 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -30,6 +30,10 @@ describe JiraService do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
+ it { is_expected.to allow_value(nil).for(:jira_issue_transition_id) }
+ it { is_expected.to allow_value("1,2,3").for(:jira_issue_transition_id) }
+ it { is_expected.to allow_value("1;2;3").for(:jira_issue_transition_id) }
+ it { is_expected.not_to allow_value("a,b,cd").for(:jira_issue_transition_id) }
end
describe 'Validations' do
@@ -124,7 +128,7 @@ describe JiraService do
url: 'http://jira.example.com',
username: 'gitlab_jira_username',
password: 'gitlab_jira_password',
- jira_issue_transition_id: "custom-id"
+ jira_issue_transition_id: "999"
)
# These stubs are needed to test JiraService#close_issue.
@@ -226,12 +230,49 @@ describe JiraService do
).once
end
- it "calls the api with jira_issue_transition_id" do
- @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ context '#close_issue' do
+ it "logs exception when transition id is not valid" do
+ allow(Rails.logger).to receive(:info)
+ WebMock.stub_request(:post, @transitions_url).with(basic_auth: %w(gitlab_jira_username gitlab_jira_password)).and_raise("Bad Request")
- expect(WebMock).to have_requested(:post, @transitions_url).with(
- body: /custom-id/
- ).once
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+
+ expect(Rails.logger).to have_received(:info).with("JiraService Issue Transition failed message ERROR: http://jira.example.com - Bad Request")
+ end
+
+ it "calls the api with jira_issue_transition_id" do
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+
+ expect(WebMock).to have_requested(:post, @transitions_url).with(
+ body: /999/
+ ).once
+ end
+
+ context "when have multiple transition ids" do
+ it "calls the api with transition ids separated by comma" do
+ allow(@jira_service).to receive_messages(jira_issue_transition_id: "1,2,3")
+
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+
+ 1.upto(3) do |transition_id|
+ expect(WebMock).to have_requested(:post, @transitions_url).with(
+ body: /#{transition_id}/
+ ).once
+ end
+ end
+
+ it "calls the api with transition ids separated by semicolon" do
+ allow(@jira_service).to receive_messages(jira_issue_transition_id: "1;2;3")
+
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+
+ 1.upto(3) do |transition_id|
+ expect(WebMock).to have_requested(:post, @transitions_url).with(
+ body: /#{transition_id}/
+ ).once
+ end
+ end
+ end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index acc7821a21e..076de06cf99 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -26,6 +26,7 @@ describe Project do
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) }
+ it { is_expected.to have_one(:hangouts_chat_service) }
it { is_expected.to have_one(:packagist_service) }
it { is_expected.to have_one(:pushover_service) }
it { is_expected.to have_one(:asana_service) }
@@ -68,6 +69,7 @@ describe Project do
it { is_expected.to have_many(:pages_domains) }
it { is_expected.to have_many(:labels).class_name('ProjectLabel') }
it { is_expected.to have_many(:users_star_projects) }
+ it { is_expected.to have_many(:repository_languages) }
it { is_expected.to have_many(:environments) }
it { is_expected.to have_many(:deployments) }
it { is_expected.to have_many(:todos) }
@@ -101,6 +103,22 @@ describe Project do
end
end
+ context 'Site Statistics' do
+ context 'when creating a new project' do
+ it 'tracks project in SiteStatistic' do
+ expect { create(:project) }.to change { SiteStatistic.fetch.repositories_count }.by(1)
+ end
+ end
+
+ context 'when deleting a project' do
+ it 'untracks project in SiteStatistic' do
+ project = create(:project)
+
+ expect { project.destroy }.to change { SiteStatistic.fetch.repositories_count }.by(-1)
+ end
+ end
+ end
+
context 'updating cd_cd_settings' do
it 'does not raise an error' do
project = create(:project)
@@ -149,23 +167,25 @@ describe Project do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
-
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_length_of(:path).is_at_most(255) }
-
it { is_expected.to validate_length_of(:description).is_at_most(2000) }
-
it { is_expected.to validate_length_of(:ci_config_path).is_at_most(255) }
it { is_expected.to allow_value('').for(:ci_config_path) }
it { is_expected.not_to allow_value('test/../foo').for(:ci_config_path) }
it { is_expected.not_to allow_value('/test/foo').for(:ci_config_path) }
-
it { is_expected.to validate_presence_of(:creator) }
-
it { is_expected.to validate_presence_of(:namespace) }
-
it { is_expected.to validate_presence_of(:repository_storage) }
+ it 'validates build timeout constraints' do
+ is_expected.to validate_numericality_of(:build_timeout)
+ .only_integer
+ .is_greater_than_or_equal_to(10.minutes)
+ .is_less_than(1.month)
+ .with_message('needs to be beetween 10 minutes and 1 month')
+ end
+
it 'does not allow new projects beyond user limits' do
project2 = build(:project)
allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
@@ -336,7 +356,7 @@ describe Project do
end
describe 'delegation' do
- [:add_guest, :add_reporter, :add_developer, :add_master, :add_user, :add_users].each do |method|
+ [:add_guest, :add_reporter, :add_developer, :add_maintainer, :add_user, :add_users].each do |method|
it { is_expected.to delegate_method(method).to(:team) }
end
@@ -576,15 +596,15 @@ describe Project do
end
it 'returns the most recent timestamp' do
- project.update_attributes(updated_at: nil,
- last_activity_at: timestamp,
- last_repository_updated_at: timestamp - 1.hour)
+ project.update(updated_at: nil,
+ last_activity_at: timestamp,
+ last_repository_updated_at: timestamp - 1.hour)
expect(project.last_activity_date).to be_like_time(timestamp)
- project.update_attributes(updated_at: timestamp,
- last_activity_at: timestamp - 1.hour,
- last_repository_updated_at: nil)
+ project.update(updated_at: timestamp,
+ last_activity_at: timestamp - 1.hour,
+ last_repository_updated_at: nil)
expect(project.last_activity_date).to be_like_time(timestamp)
end
@@ -1139,7 +1159,7 @@ describe Project do
describe 'when a user has access to a project' do
before do
- project.add_user(user, Gitlab::Access::MASTER)
+ project.add_user(user, Gitlab::Access::MAINTAINER)
end
it { is_expected.to eq([project]) }
@@ -1777,7 +1797,7 @@ describe Project do
it 'resets project import_error' do
error_message = 'Some error'
mirror = create(:project_empty_repo, :import_started)
- mirror.import_state.update_attributes(last_error: error_message)
+ mirror.import_state.update(last_error: error_message)
expect { mirror.import_finish }.to change { mirror.import_error }.from(error_message).to(nil)
end
@@ -1938,7 +1958,7 @@ describe Project do
end
it 'returns false when remote mirror is disabled' do
- project.remote_mirrors.first.update_attributes(enabled: false)
+ project.remote_mirrors.first.update(enabled: false)
is_expected.to be_falsy
end
@@ -1968,7 +1988,7 @@ describe Project do
end
it 'does not sync disabled remote mirrors' do
- project.remote_mirrors.first.update_attributes(enabled: false)
+ project.remote_mirrors.first.update(enabled: false)
expect_any_instance_of(RemoteMirror).not_to receive(:sync)
@@ -2301,6 +2321,28 @@ describe Project do
end
end
+ describe '#default_environment' do
+ let(:project) { create(:project) }
+
+ it 'returns production environment when it exists' do
+ production = create(:environment, name: "production", project: project)
+ create(:environment, name: 'staging', project: project)
+
+ expect(project.default_environment).to eq(production)
+ end
+
+ it 'returns first environment when no production environment exists' do
+ create(:environment, name: 'staging', project: project)
+ create(:environment, name: 'foo', project: project)
+
+ expect(project.default_environment).to eq(project.environments.first)
+ end
+
+ it 'returns nil when no available environment exists' do
+ expect(project.default_environment).to be_nil
+ end
+ end
+
describe '#secret_variables_for' do
let(:project) { create(:project) }
@@ -2769,6 +2811,10 @@ describe Project do
let(:legacy_project) { create(:project, :legacy_storage, :with_export) }
let(:project) { create(:project, :with_export) }
+ before do
+ stub_feature_flags(import_export_object_storage: false)
+ end
+
it 'removes the exports directory for the project' do
expect(File.exist?(project.export_path)).to be_truthy
@@ -2817,12 +2863,14 @@ describe Project do
let(:project) { create(:project, :with_export) }
it 'removes the exported project file' do
+ stub_feature_flags(import_export_object_storage: false)
+
exported_file = project.export_project_path
expect(File.exist?(exported_file)).to be_truthy
- allow(FileUtils).to receive(:rm_f).and_call_original
- expect(FileUtils).to receive(:rm_f).with(exported_file).and_call_original
+ allow(FileUtils).to receive(:rm_rf).and_call_original
+ expect(FileUtils).to receive(:rm_rf).with(exported_file).and_call_original
project.remove_exported_project_file
@@ -2920,8 +2968,6 @@ describe Project do
expect(project).to receive(:expire_caches_before_rename)
- expect(project).to receive(:expires_full_path_cache)
-
project.rename_repo
end
@@ -3070,6 +3116,19 @@ describe Project do
allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
end
+ context 'migration to hashed storage' do
+ it 'calls HashedStorageMigrationService with correct options' do
+ project = create(:project, :repository, :legacy_storage)
+ allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
+
+ expect_next_instance_of(::Projects::HashedStorageMigrationService) do |service|
+ expect(service).to receive(:execute).and_return(true)
+ end
+
+ project.rename_repo
+ end
+ end
+
it 'renames a repository' do
stub_container_registry_config(enabled: false)
@@ -3081,8 +3140,6 @@ describe Project do
expect(project).to receive(:expire_caches_before_rename)
- expect(project).to receive(:expires_full_path_cache)
-
project.rename_repo
end
@@ -3118,8 +3175,10 @@ describe Project do
context 'when not rolled out' do
let(:project) { create(:project, :repository, storage_version: 1, skip_disk_validation: true) }
- it 'moves pages folder to new location' do
- expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)
+ it 'moves pages folder to hashed storage' do
+ expect_next_instance_of(Projects::HashedStorage::MigrateAttachmentsService) do |service|
+ expect(service).to receive(:execute)
+ end
project.rename_repo
end
@@ -3257,6 +3316,50 @@ describe Project do
end
end
+ describe '#has_auto_devops_implicitly_enabled?' do
+ set(:project) { create(:project) }
+
+ context 'when disabled in settings' do
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+ end
+
+ it 'does not have auto devops implicitly disabled' do
+ expect(project).not_to have_auto_devops_implicitly_enabled
+ end
+ end
+
+ context 'when enabled in settings' do
+ before do
+ stub_application_setting(auto_devops_enabled: true)
+ end
+
+ it 'auto devops is implicitly disabled' do
+ expect(project).to have_auto_devops_implicitly_enabled
+ end
+
+ context 'when explicitly disabled' do
+ before do
+ create(:project_auto_devops, project: project, enabled: false)
+ end
+
+ it 'does not have auto devops implicitly disabled' do
+ expect(project).not_to have_auto_devops_implicitly_enabled
+ end
+ end
+
+ context 'when explicitly enabled' do
+ before do
+ create(:project_auto_devops, project: project, enabled: true)
+ end
+
+ it 'does not have auto devops implicitly disabled' do
+ expect(project).not_to have_auto_devops_implicitly_enabled
+ end
+ end
+ end
+ end
+
describe '#has_auto_devops_implicitly_disabled?' do
set(:project) { create(:project) }
@@ -3291,7 +3394,7 @@ describe Project do
context 'when explicitly enabled' do
before do
- create(:project_auto_devops, project: project)
+ create(:project_auto_devops, project: project, enabled: true)
end
it 'does not have auto devops implicitly disabled' do
@@ -3477,8 +3580,8 @@ describe Project do
expect(project.protected_branches).not_to be_empty
expect(project.default_branch).to eq(project.protected_branches.first.name)
- expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
- expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
+ expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
end
end
@@ -3714,7 +3817,7 @@ describe Project do
end
it 'does not allow access if the user cannot merge the merge request' do
- create(:protected_branch, :masters_can_push, project: target_project, name: 'target-branch')
+ create(:protected_branch, :maintainers_can_push, project: target_project, name: 'target-branch')
expect(project.branch_allows_collaboration?(user, 'awesome-feature-1'))
.to be_falsy
@@ -3843,6 +3946,16 @@ describe Project do
end
end
+ context '#commits_by' do
+ let(:project) { create(:project, :repository) }
+ let(:commits) { project.repository.commits('HEAD', limit: 3).commits }
+ let(:commit_shas) { commits.map(&:id) }
+
+ it 'retrieves several commits from the repository by oid' do
+ expect(project.commits_by(oids: commit_shas)).to eq commits
+ end
+ end
+
def rugged_config
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
project.repository.rugged.config
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb
index 38a3590ad12..64c39f09e33 100644
--- a/spec/models/project_statistics_spec.rb
+++ b/spec/models/project_statistics_spec.rb
@@ -128,6 +128,12 @@ describe ProjectStatistics do
.by(13)
end
+ it 'increases also storage size by that amount' do
+ expect { described_class.increment_statistic(project.id, :build_artifacts_size, 20) }
+ .to change { statistics.reload.storage_size }
+ .by(20)
+ end
+
context 'when the amount is 0' do
it 'does not execute a query' do
project
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index 9978f3e9566..c4af17f4726 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -1,7 +1,7 @@
require "spec_helper"
describe ProjectTeam do
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:nonmember) { create(:user) }
@@ -10,23 +10,23 @@ describe ProjectTeam do
let(:project) { create(:project) }
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
end
describe 'members collection' do
- it { expect(project.team.masters).to include(master) }
- it { expect(project.team.masters).not_to include(guest) }
- it { expect(project.team.masters).not_to include(reporter) }
- it { expect(project.team.masters).not_to include(nonmember) }
+ it { expect(project.team.maintainers).to include(maintainer) }
+ it { expect(project.team.maintainers).not_to include(guest) }
+ it { expect(project.team.maintainers).not_to include(reporter) }
+ it { expect(project.team.maintainers).not_to include(nonmember) }
end
describe 'access methods' do
- it { expect(project.team.master?(master)).to be_truthy }
- it { expect(project.team.master?(guest)).to be_falsey }
- it { expect(project.team.master?(reporter)).to be_falsey }
- it { expect(project.team.master?(nonmember)).to be_falsey }
+ it { expect(project.team.maintainer?(maintainer)).to be_truthy }
+ it { expect(project.team.maintainer?(guest)).to be_falsey }
+ it { expect(project.team.maintainer?(reporter)).to be_falsey }
+ it { expect(project.team.maintainer?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy }
it { expect(project.team.member?(reporter, Gitlab::Access::REPORTER)).to be_truthy }
@@ -40,35 +40,35 @@ describe ProjectTeam do
let!(:project) { create(:project, group: group) }
before do
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_reporter(reporter)
group.add_guest(guest)
# If user is a group and a project member - GitLab uses highest permission
- # So we add group guest as master and add group master as guest
+ # So we add group guest as maintainer and add group maintainer as guest
# to this project to test highest access
- project.add_master(guest)
- project.add_guest(master)
+ project.add_maintainer(guest)
+ project.add_guest(maintainer)
end
describe 'members collection' do
it { expect(project.team.reporters).to include(reporter) }
- it { expect(project.team.masters).to include(master) }
- it { expect(project.team.masters).to include(guest) }
- it { expect(project.team.masters).not_to include(reporter) }
- it { expect(project.team.masters).not_to include(nonmember) }
+ it { expect(project.team.maintainers).to include(maintainer) }
+ it { expect(project.team.maintainers).to include(guest) }
+ it { expect(project.team.maintainers).not_to include(reporter) }
+ it { expect(project.team.maintainers).not_to include(nonmember) }
end
describe 'access methods' do
it { expect(project.team.reporter?(reporter)).to be_truthy }
- it { expect(project.team.master?(master)).to be_truthy }
- it { expect(project.team.master?(guest)).to be_truthy }
- it { expect(project.team.master?(reporter)).to be_falsey }
- it { expect(project.team.master?(nonmember)).to be_falsey }
+ it { expect(project.team.maintainer?(maintainer)).to be_truthy }
+ it { expect(project.team.maintainer?(guest)).to be_truthy }
+ it { expect(project.team.maintainer?(reporter)).to be_falsey }
+ it { expect(project.team.maintainer?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy }
- it { expect(project.team.member?(guest, Gitlab::Access::MASTER)).to be_truthy }
- it { expect(project.team.member?(reporter, Gitlab::Access::MASTER)).to be_falsey }
+ it { expect(project.team.member?(guest, Gitlab::Access::MAINTAINER)).to be_truthy }
+ it { expect(project.team.member?(reporter, Gitlab::Access::MAINTAINER)).to be_falsey }
it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
end
end
@@ -145,13 +145,13 @@ describe ProjectTeam do
let(:requester) { create(:user) }
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
project.request_access(requester)
end
- it { expect(project.team.find_member(master.id)).to be_a(ProjectMember) }
+ it { expect(project.team.find_member(maintainer.id)).to be_a(ProjectMember) }
it { expect(project.team.find_member(reporter.id)).to be_a(ProjectMember) }
it { expect(project.team.find_member(guest.id)).to be_a(ProjectMember) }
it { expect(project.team.find_member(nonmember.id)).to be_nil }
@@ -164,13 +164,13 @@ describe ProjectTeam do
let(:requester) { create(:user) }
before do
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_reporter(reporter)
group.add_guest(guest)
group.request_access(requester)
end
- it { expect(project.team.find_member(master.id)).to be_a(GroupMember) }
+ it { expect(project.team.find_member(maintainer.id)).to be_a(GroupMember) }
it { expect(project.team.find_member(reporter.id)).to be_a(GroupMember) }
it { expect(project.team.find_member(guest.id)).to be_a(GroupMember) }
it { expect(project.team.find_member(nonmember.id)).to be_nil }
@@ -184,7 +184,7 @@ describe ProjectTeam do
group = create(:group)
project = create(:project, namespace: group)
- group.add_master(user)
+ group.add_maintainer(user)
expect(project.team.human_max_access(user.id)).to eq 'Maintainer'
end
@@ -210,13 +210,13 @@ describe ProjectTeam do
context 'when project is not shared with group' do
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
project.request_access(requester)
end
- it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
+ it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::MAINTAINER) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
it { expect(project.team.max_member_access(nonmember.id)).to eq(Gitlab::Access::NO_ACCESS) }
@@ -230,11 +230,11 @@ describe ProjectTeam do
group: group,
group_access: Gitlab::Access::DEVELOPER)
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_reporter(reporter)
end
- it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::DEVELOPER) }
+ it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::DEVELOPER) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
it { expect(project.team.max_member_access(nonmember.id)).to eq(Gitlab::Access::NO_ACCESS) }
it { expect(project.team.max_member_access(requester.id)).to eq(Gitlab::Access::NO_ACCESS) }
@@ -244,7 +244,7 @@ describe ProjectTeam do
project.namespace.update(share_with_group_lock: true)
end
- it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::NO_ACCESS) }
+ it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::NO_ACCESS) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::NO_ACCESS) }
end
end
@@ -257,13 +257,13 @@ describe ProjectTeam do
end
before do
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_reporter(reporter)
group.add_guest(guest)
group.request_access(requester)
end
- it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
+ it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::MAINTAINER) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
it { expect(project.team.max_member_access(nonmember.id)).to eq(Gitlab::Access::NO_ACCESS) }
@@ -274,7 +274,7 @@ describe ProjectTeam do
describe '#member?' do
let(:group) { create(:group) }
let(:developer) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:personal_project) do
create(:project, namespace: developer.namespace)
@@ -288,11 +288,11 @@ describe ProjectTeam do
let(:shared_project) { create(:project) }
before do
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_developer(developer)
members_project.add_developer(developer)
- members_project.add_master(master)
+ members_project.add_maintainer(maintainer)
create(:project_group_link, project: shared_project, group: group)
end
@@ -318,14 +318,14 @@ describe ProjectTeam do
end
it 'checks for the correct minimum level access' do
- expect(group_project.team.member?(developer, Gitlab::Access::MASTER)).to be(false)
- expect(group_project.team.member?(master, Gitlab::Access::MASTER)).to be(true)
- expect(members_project.team.member?(developer, Gitlab::Access::MASTER)).to be(false)
- expect(members_project.team.member?(master, Gitlab::Access::MASTER)).to be(true)
- expect(shared_project.team.member?(developer, Gitlab::Access::MASTER)).to be(false)
- expect(shared_project.team.member?(master, Gitlab::Access::MASTER)).to be(false)
+ expect(group_project.team.member?(developer, Gitlab::Access::MAINTAINER)).to be(false)
+ expect(group_project.team.member?(maintainer, Gitlab::Access::MAINTAINER)).to be(true)
+ expect(members_project.team.member?(developer, Gitlab::Access::MAINTAINER)).to be(false)
+ expect(members_project.team.member?(maintainer, Gitlab::Access::MAINTAINER)).to be(true)
+ expect(shared_project.team.member?(developer, Gitlab::Access::MAINTAINER)).to be(false)
+ expect(shared_project.team.member?(maintainer, Gitlab::Access::MAINTAINER)).to be(false)
expect(shared_project.team.member?(developer, Gitlab::Access::DEVELOPER)).to be(true)
- expect(shared_project.team.member?(master, Gitlab::Access::DEVELOPER)).to be(true)
+ expect(shared_project.team.member?(maintainer, Gitlab::Access::DEVELOPER)).to be(true)
end
end
@@ -334,7 +334,7 @@ describe ProjectTeam do
let(:group) { create(:group) }
let(:second_group) { create(:group) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
@@ -347,23 +347,23 @@ describe ProjectTeam do
let(:second_user_without_access) { create(:user) }
let(:users) do
- [master, reporter, promoted_guest, guest, group_developer, second_developer, user_without_access].map(&:id)
+ [maintainer, reporter, promoted_guest, guest, group_developer, second_developer, user_without_access].map(&:id)
end
let(:expected) do
{
- master.id => Gitlab::Access::MASTER,
+ maintainer.id => Gitlab::Access::MAINTAINER,
reporter.id => Gitlab::Access::REPORTER,
promoted_guest.id => Gitlab::Access::DEVELOPER,
guest.id => Gitlab::Access::GUEST,
group_developer.id => Gitlab::Access::DEVELOPER,
- second_developer.id => Gitlab::Access::MASTER,
+ second_developer.id => Gitlab::Access::MAINTAINER,
user_without_access.id => Gitlab::Access::NO_ACCESS
}
end
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(promoted_guest)
project.add_guest(guest)
@@ -373,16 +373,16 @@ describe ProjectTeam do
group_access: Gitlab::Access::DEVELOPER
)
- group.add_master(promoted_guest)
+ group.add_maintainer(promoted_guest)
group.add_developer(group_developer)
group.add_developer(second_developer)
project.project_group_links.create(
group: second_group,
- group_access: Gitlab::Access::MASTER
+ group_access: Gitlab::Access::MAINTAINER
)
- second_group.add_master(second_developer)
+ second_group.add_maintainer(second_developer)
end
it 'returns correct roles for different users' do
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index a3c20b3b3c1..528f5b610d7 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
require "spec_helper"
describe ProjectWiki do
@@ -10,7 +11,6 @@ describe ProjectWiki do
subject { project_wiki }
- it { is_expected.to delegate_method(:empty?).to :pages }
it { is_expected.to delegate_method(:repository_storage).to :project }
it { is_expected.to delegate_method(:hashed_storage?).to :project }
@@ -92,11 +92,19 @@ describe ProjectWiki do
context "when the wiki has pages" do
before do
project_wiki.create_page("index", "This is an awesome new Gollum Wiki")
+ project_wiki.create_page("another-page", "This is another page")
end
describe '#empty?' do
subject { super().empty? }
it { is_expected.to be_falsey }
+
+ # Re-enable this when https://gitlab.com/gitlab-org/gitaly/issues/1204 is fixed
+ xit 'only instantiates a Wiki page once' do
+ expect(WikiPage).to receive(:new).once.and_call_original
+
+ subject
+ end
end
end
end
@@ -181,6 +189,22 @@ describe ProjectWiki do
end
end
+ describe '#find_sidebar' do
+ before do
+ create_page(described_class::SIDEBAR, 'This is an awesome Sidebar')
+ end
+
+ after do
+ subject.pages.each { |page| destroy_page(page.page) }
+ end
+
+ it 'finds the page defined as _sidebar' do
+ page = subject.find_page('_sidebar')
+
+ expect(page.content).to eq('This is an awesome Sidebar')
+ end
+ end
+
describe '#find_file' do
shared_examples 'finding a wiki file' do
let(:image) { File.open(Rails.root.join('spec', 'fixtures', 'big-image.png')) }
diff --git a/spec/models/protected_branch/merge_access_level_spec.rb b/spec/models/protected_branch/merge_access_level_spec.rb
index f70503eadbc..612e4a0e332 100644
--- a/spec/models/protected_branch/merge_access_level_spec.rb
+++ b/spec/models/protected_branch/merge_access_level_spec.rb
@@ -1,5 +1,5 @@
require 'spec_helper'
describe ProtectedBranch::MergeAccessLevel do
- it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MASTER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
+ it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MAINTAINER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
end
diff --git a/spec/models/protected_branch/push_access_level_spec.rb b/spec/models/protected_branch/push_access_level_spec.rb
index f161f345761..9ccdc22fd41 100644
--- a/spec/models/protected_branch/push_access_level_spec.rb
+++ b/spec/models/protected_branch/push_access_level_spec.rb
@@ -1,5 +1,5 @@
require 'spec_helper'
describe ProtectedBranch::PushAccessLevel do
- it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MASTER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
+ it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MAINTAINER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
end
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 3597b080021..c2ef0435c8e 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -85,7 +85,7 @@ describe RemoteMirror do
expect(RepositoryRemoveRemoteWorker).to receive(:perform_async).with(mirror.project.id, mirror.remote_name).and_call_original
- mirror.update_attributes(url: 'http://test.com')
+ mirror.update(url: 'http://test.com')
end
end
end
@@ -167,7 +167,7 @@ describe RemoteMirror do
context 'with remote mirroring disabled' do
it 'returns nil' do
- remote_mirror.update_attributes(enabled: false)
+ remote_mirror.update(enabled: false)
expect(remote_mirror.sync).to be_nil
end
@@ -229,7 +229,7 @@ describe RemoteMirror do
end
before do
- remote_mirror.update_attributes(last_update_started_at: Time.now)
+ remote_mirror.update(last_update_started_at: Time.now)
end
context 'when remote mirror does not have status failed' do
@@ -244,7 +244,7 @@ describe RemoteMirror do
context 'when remote mirror has status failed' do
it 'returns false when last update started after the timestamp' do
- remote_mirror.update_attributes(update_status: 'failed')
+ remote_mirror.update(update_status: 'failed')
expect(remote_mirror.updated_since?(timestamp)).to be false
end
diff --git a/spec/models/repository_language_spec.rb b/spec/models/repository_language_spec.rb
new file mode 100644
index 00000000000..e2e4beb512f
--- /dev/null
+++ b/spec/models/repository_language_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe RepositoryLanguage do
+ let(:repository_language) { build(:repository_language) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:programming_language) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to allow_value(0).for(:share) }
+ it { is_expected.to allow_value(100.0).for(:share) }
+ it { is_expected.not_to allow_value(100.1).for(:share) }
+ end
+end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 27a14ff5d5b..52ec8dbe25a 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -151,7 +151,9 @@ describe Repository do
it { is_expected.to eq(['v1.1.0', 'v1.0.0', annotated_tag_name]) }
after do
- repository.rugged.tags.delete(annotated_tag_name)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.tags.delete(annotated_tag_name)
+ end
end
end
end
@@ -294,24 +296,40 @@ describe Repository do
end
describe '#new_commits' do
- let(:new_refs) do
- double(:git_rev_list, new_refs: %w[
- c1acaa58bbcbc3eafe538cb8274ba387047b69f8
- 5937ac0a7beb003549fc5fd26fc247adbce4a52e
- ])
- end
+ shared_examples 'finding unreferenced commits' do
+ set(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
- it 'delegates to Gitlab::Git::RevList' do
- expect(Gitlab::Git::RevList).to receive(:new).with(
- repository.raw,
- newrev: 'aaaabbbbccccddddeeeeffffgggghhhhiiiijjjj').and_return(new_refs)
+ subject { repository.new_commits(rev) }
- commits = repository.new_commits('aaaabbbbccccddddeeeeffffgggghhhhiiiijjjj')
+ context 'when there are no new commits' do
+ let(:rev) { repository.commit.id }
- expect(commits).to eq([
- repository.commit('c1acaa58bbcbc3eafe538cb8274ba387047b69f8'),
- repository.commit('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- ])
+ it 'returns an empty array' do
+ expect(subject).to eq([])
+ end
+ end
+
+ context 'when new commits are found' do
+ let(:branch) { 'orphaned-branch' }
+ let!(:rev) { repository.commit(branch).id }
+
+ it 'returns the commits' do
+ repository.delete_branch(branch)
+
+ expect(subject).not_to be_empty
+ expect(subject).to all( be_a(::Commit) )
+ expect(subject.size).to eq(1)
+ end
+ end
+ end
+
+ context 'when Gitaly handles the request' do
+ it_behaves_like 'finding unreferenced commits'
+ end
+
+ context 'when Gitaly is disabled', :disable_gitaly do
+ it_behaves_like 'finding unreferenced commits'
end
end
@@ -431,6 +449,18 @@ describe Repository do
it { is_expected.to be_falsey }
end
+
+ context 'non merged branch' do
+ subject { repository.merged_to_root_ref?('fix') }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'non existent branch' do
+ subject { repository.merged_to_root_ref?('non_existent_branch') }
+
+ it { is_expected.to be_nil }
+ end
end
describe '#can_be_merged?' do
@@ -452,17 +482,11 @@ describe Repository do
it { is_expected.to be_falsey }
end
- context 'non merged branch' do
- subject { repository.merged_to_root_ref?('fix') }
+ context 'submodule changes that confuse rugged' do
+ subject { repository.can_be_merged?('update-gitlab-shell-v-6-0-1', 'update-gitlab-shell-v-6-0-3') }
it { is_expected.to be_falsey }
end
-
- context 'non existent branch' do
- subject { repository.merged_to_root_ref?('non_existent_branch') }
-
- it { is_expected.to be_nil }
- end
end
describe '#commit' do
@@ -479,6 +503,14 @@ describe Repository do
end
end
+ context 'when ref is not specified' do
+ it 'is using a root ref' do
+ expect(repository).to receive(:find_commit).with('master')
+
+ repository.commit
+ end
+ end
+
context 'when ref is not valid' do
context 'when preceding tree element exists' do
it 'returns nil' do
@@ -1006,24 +1038,6 @@ describe Repository do
end
end
- describe '#find_branch' do
- context 'fresh_repo is true' do
- it 'delegates the call to raw_repository' do
- expect(repository.raw_repository).to receive(:find_branch).with('master', true)
-
- repository.find_branch('master', fresh_repo: true)
- end
- end
-
- context 'fresh_repo is false' do
- it 'delegates the call to raw_repository' do
- expect(repository.raw_repository).to receive(:find_branch).with('master', false)
-
- repository.find_branch('master', fresh_repo: false)
- end
- end
- end
-
describe '#update_branch_with_hooks' do
let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature
let(:new_rev) { 'a74ae73c1ccde9b974a70e82b901588071dc142a' } # commit whose parent is old_rev
@@ -1115,16 +1129,12 @@ describe Repository do
end
it 'raises Rugged::ReferenceError' do
- raise_reference_error = raise_error(Rugged::ReferenceError) do |err|
- expect(err.cause).to be_nil
- end
-
expect do
Gitlab::Git::OperationService.new(git_user, target_project.repository.raw_repository)
.with_branch('feature',
start_repository: project.repository.raw_repository,
&:itself)
- end.to raise_reference_error
+ end.to raise_error(Gitlab::Git::CommandError)
end
end
@@ -1689,19 +1699,29 @@ describe Repository do
end
describe '#after_change_head' do
- it 'flushes the readme cache' do
+ it 'flushes the method caches' do
expect(repository).to receive(:expire_method_caches).with([
- :readme,
+ :size,
+ :commit_count,
+ :rendered_readme,
+ :contribution_guide,
:changelog,
- :license,
- :contributing,
+ :license_blob,
+ :license_key,
:gitignore,
- :koding,
- :gitlab_ci,
+ :koding_yml,
+ :gitlab_ci_yml,
+ :branch_names,
+ :tag_names,
+ :branch_count,
+ :tag_count,
:avatar,
- :issue_template,
- :merge_request_template,
- :xcode_config
+ :exists?,
+ :root_ref,
+ :has_visible_content?,
+ :issue_template_names,
+ :merge_request_template_names,
+ :xcode_project?
])
repository.after_change_head
@@ -1843,155 +1863,61 @@ describe Repository do
describe '#add_tag' do
let(:user) { build_stubbed(:user) }
- shared_examples 'adding tag' do
- context 'with a valid target' do
- it 'creates the tag' do
- repository.add_tag(user, '8.5', 'master', 'foo')
-
- tag = repository.find_tag('8.5')
- expect(tag).to be_present
- expect(tag.message).to eq('foo')
- expect(tag.dereferenced_target.id).to eq(repository.commit('master').id)
- end
-
- it 'returns a Gitlab::Git::Tag object' do
- tag = repository.add_tag(user, '8.5', 'master', 'foo')
+ context 'with a valid target' do
+ it 'creates the tag' do
+ repository.add_tag(user, '8.5', 'master', 'foo')
- expect(tag).to be_a(Gitlab::Git::Tag)
- end
+ tag = repository.find_tag('8.5')
+ expect(tag).to be_present
+ expect(tag.message).to eq('foo')
+ expect(tag.dereferenced_target.id).to eq(repository.commit('master').id)
end
- context 'with an invalid target' do
- it 'returns false' do
- expect(repository.add_tag(user, '8.5', 'bar', 'foo')).to be false
- end
- end
- end
-
- context 'when Gitaly operation_user_add_tag feature is enabled' do
- it_behaves_like 'adding tag'
- end
-
- context 'when Gitaly operation_user_add_tag feature is disabled', :disable_gitaly do
- it_behaves_like 'adding tag'
-
- it 'passes commit SHA to pre-receive and update hooks and tag SHA to post-receive hook' do
- pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', project)
- update_hook = Gitlab::Git::Hook.new('update', project)
- post_receive_hook = Gitlab::Git::Hook.new('post-receive', project)
-
- allow(Gitlab::Git::Hook).to receive(:new)
- .and_return(pre_receive_hook, update_hook, post_receive_hook)
-
- allow(pre_receive_hook).to receive(:trigger).and_call_original
- allow(update_hook).to receive(:trigger).and_call_original
- allow(post_receive_hook).to receive(:trigger).and_call_original
-
+ it 'returns a Gitlab::Git::Tag object' do
tag = repository.add_tag(user, '8.5', 'master', 'foo')
- commit_sha = repository.commit('master').id
- tag_sha = tag.target
-
- expect(pre_receive_hook).to have_received(:trigger)
- .with(anything, anything, anything, commit_sha, anything)
- expect(update_hook).to have_received(:trigger)
- .with(anything, anything, anything, commit_sha, anything)
- expect(post_receive_hook).to have_received(:trigger)
- .with(anything, anything, anything, tag_sha, anything)
+ expect(tag).to be_a(Gitlab::Git::Tag)
end
end
- end
-
- describe '#rm_branch' do
- shared_examples "user deleting a branch" do
- it 'removes a branch' do
- expect(repository).to receive(:before_remove_branch)
- expect(repository).to receive(:after_remove_branch)
- repository.rm_branch(user, 'feature')
+ context 'with an invalid target' do
+ it 'returns false' do
+ expect(repository.add_tag(user, '8.5', 'bar', 'foo')).to be false
end
end
+ end
- context 'with gitaly enabled' do
- it_behaves_like "user deleting a branch"
-
- context 'when pre hooks failed' do
- before do
- allow_any_instance_of(Gitlab::GitalyClient::OperationService)
- .to receive(:user_delete_branch).and_raise(Gitlab::Git::PreReceiveError)
- end
-
- it 'gets an error and does not delete the branch' do
- expect do
- repository.rm_branch(user, 'feature')
- end.to raise_error(Gitlab::Git::PreReceiveError)
+ describe '#rm_branch' do
+ it 'removes a branch' do
+ expect(repository).to receive(:before_remove_branch)
+ expect(repository).to receive(:after_remove_branch)
- expect(repository.find_branch('feature')).not_to be_nil
- end
- end
+ repository.rm_branch(user, 'feature')
end
- context 'with gitaly disabled', :disable_gitaly do
- it_behaves_like "user deleting a branch"
-
- let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature
- let(:blank_sha) { '0000000000000000000000000000000000000000' }
-
- context 'when pre hooks were successful' do
- it 'runs without errors' do
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:execute)
- .with(git_user, repository.raw_repository, old_rev, blank_sha, 'refs/heads/feature')
-
- expect { repository.rm_branch(user, 'feature') }.not_to raise_error
- end
-
- it 'deletes the branch' do
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
-
- expect { repository.rm_branch(user, 'feature') }.not_to raise_error
-
- expect(repository.find_branch('feature')).to be_nil
- end
+ context 'when pre hooks failed' do
+ before do
+ allow_any_instance_of(Gitlab::GitalyClient::OperationService)
+ .to receive(:user_delete_branch).and_raise(Gitlab::Git::PreReceiveError)
end
- context 'when pre hooks failed' do
- it 'gets an error' do
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
-
- expect do
- repository.rm_branch(user, 'feature')
- end.to raise_error(Gitlab::Git::PreReceiveError)
- end
-
- it 'does not delete the branch' do
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
+ it 'gets an error and does not delete the branch' do
+ expect do
+ repository.rm_branch(user, 'feature')
+ end.to raise_error(Gitlab::Git::PreReceiveError)
- expect do
- repository.rm_branch(user, 'feature')
- end.to raise_error(Gitlab::Git::PreReceiveError)
- expect(repository.find_branch('feature')).not_to be_nil
- end
+ expect(repository.find_branch('feature')).not_to be_nil
end
end
end
describe '#rm_tag' do
- shared_examples 'removing tag' do
- it 'removes a tag' do
- expect(repository).to receive(:before_remove_tag)
-
- repository.rm_tag(build_stubbed(:user), 'v1.1.0')
+ it 'removes a tag' do
+ expect(repository).to receive(:before_remove_tag)
- expect(repository.find_tag('v1.1.0')).to be_nil
- end
- end
+ repository.rm_tag(build_stubbed(:user), 'v1.1.0')
- context 'when Gitaly operation_user_delete_tag feature is enabled' do
- it_behaves_like 'removing tag'
- end
-
- context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'removing tag'
+ expect(repository.find_tag('v1.1.0')).to be_nil
end
end
@@ -2295,17 +2221,6 @@ describe Repository do
end
end
- describe '#remote_branches' do
- it 'returns the remote branches' do
- masterrev = repository.find_branch('master').dereferenced_target
- create_remote_branch('joe', 'remote_branch', masterrev)
- repository.add_branch(user, 'local_branch', masterrev.id)
-
- expect(repository.remote_branches('joe').any? { |branch| branch.name == 'local_branch' }).to eq(false)
- expect(repository.remote_branches('joe').any? { |branch| branch.name == 'remote_branch' }).to eq(true)
- end
- end
-
describe '#commit_count' do
context 'with a non-existing repository' do
it 'returns 0' do
diff --git a/spec/models/resource_label_event_spec.rb b/spec/models/resource_label_event_spec.rb
new file mode 100644
index 00000000000..4756caa1b97
--- /dev/null
+++ b/spec/models/resource_label_event_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe ResourceLabelEvent, type: :model do
+ subject { build(:resource_label_event) }
+ let(:issue) { create(:issue) }
+ let(:merge_request) { create(:merge_request) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:user) }
+ it { is_expected.to belong_to(:issue) }
+ it { is_expected.to belong_to(:merge_request) }
+ it { is_expected.to belong_to(:label) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to be_valid }
+ it { is_expected.to validate_presence_of(:label) }
+ it { is_expected.to validate_presence_of(:user) }
+
+ describe 'Issuable validation' do
+ it 'is invalid if issue_id and merge_request_id are missing' do
+ subject.attributes = { issue: nil, merge_request: nil }
+
+ expect(subject).to be_invalid
+ end
+
+ it 'is invalid if issue_id and merge_request_id are set' do
+ subject.attributes = { issue: issue, merge_request: merge_request }
+
+ expect(subject).to be_invalid
+ end
+
+ it 'is valid if only issue_id is set' do
+ subject.attributes = { issue: issue, merge_request: nil }
+
+ expect(subject).to be_valid
+ end
+
+ it 'is valid if only merge_request_id is set' do
+ subject.attributes = { merge_request: merge_request, issue: nil }
+
+ expect(subject).to be_valid
+ end
+ end
+ end
+end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index 01238a89a81..48799781b87 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -29,12 +29,12 @@ describe Route do
context 'after update' do
it 'calls #create_redirect_for_old_path' do
expect(route).to receive(:create_redirect_for_old_path)
- route.update_attributes(path: 'foo')
+ route.update(path: 'foo')
end
it 'calls #delete_conflicting_redirects' do
expect(route).to receive(:delete_conflicting_redirects)
- route.update_attributes(path: 'foo')
+ route.update(path: 'foo')
end
end
@@ -70,7 +70,7 @@ describe Route do
context 'path update' do
context 'when route name is set' do
before do
- route.update_attributes(path: 'bar')
+ route.update(path: 'bar')
end
it 'updates children routes with new path' do
@@ -89,7 +89,7 @@ describe Route do
end
it "does not fail" do
- expect(route.update_attributes(path: 'bar')).to be_truthy
+ expect(route.update(path: 'bar')).to be_truthy
end
end
@@ -100,7 +100,7 @@ describe Route do
let!(:conflicting_redirect3) { route.create_redirect('gitlab-org') }
it 'deletes the conflicting redirects' do
- route.update_attributes(path: 'bar')
+ route.update(path: 'bar')
expect(RedirectRoute.exists?(path: 'bar/test')).to be_falsey
expect(RedirectRoute.exists?(path: 'bar/test/foo')).to be_falsey
@@ -111,7 +111,7 @@ describe Route do
context 'name update' do
it 'updates children routes with new path' do
- route.update_attributes(name: 'bar')
+ route.update(name: 'bar')
expect(described_class.exists?(name: 'bar')).to be_truthy
expect(described_class.exists?(name: 'bar / test')).to be_truthy
@@ -123,7 +123,7 @@ describe Route do
# Note: using `update_columns` to skip all validation and callbacks
route.update_columns(name: nil)
- expect { route.update_attributes(name: 'bar') }
+ expect { route.update(name: 'bar') }
.to change { route.name }.from(nil).to('bar')
end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 28c908ea425..029ad7f3e9f 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -78,7 +78,7 @@ describe Service do
context 'when template is invalid' do
it 'sets service template to inactive when template is invalid' do
project = create(:project)
- template = JiraService.new(template: true, active: true)
+ template = KubernetesService.new(template: true, active: true)
template.save(validate: false)
service = described_class.build_from_template(project.id, template)
@@ -280,7 +280,7 @@ describe Service do
service.save!
expect do
- service.update_attributes(active: false)
+ service.update(active: false)
end.to change { service.project.has_external_issue_tracker }.from(true).to(false)
end
end
diff --git a/spec/models/site_statistic_spec.rb b/spec/models/site_statistic_spec.rb
new file mode 100644
index 00000000000..9b056fbf332
--- /dev/null
+++ b/spec/models/site_statistic_spec.rb
@@ -0,0 +1,83 @@
+require 'spec_helper'
+
+describe SiteStatistic do
+ describe '.fetch' do
+ context 'existing record' do
+ it 'returns existing SiteStatistic model' do
+ statistics = create(:site_statistics)
+
+ expect(described_class.fetch).to be_a(described_class)
+ expect(described_class.fetch).to eq(statistics)
+ end
+ end
+
+ context 'non existing record' do
+ it 'creates a new SiteStatistic model' do
+ expect(described_class.first).to be_nil
+ expect(described_class.fetch).to be_a(described_class)
+ end
+ end
+ end
+
+ describe '.track' do
+ context 'with allowed attributes' do
+ let(:statistics) { create(:site_statistics) }
+
+ it 'increases the attribute counter' do
+ expect { described_class.track('repositories_count') }.to change { statistics.reload.repositories_count }.by(1)
+ expect { described_class.track('wikis_count') }.to change { statistics.reload.wikis_count }.by(1)
+ end
+
+ it 'doesnt increase the attribute counter when an exception happens during transaction' do
+ expect do
+ begin
+ described_class.transaction do
+ described_class.track('repositories_count')
+
+ raise StandardError
+ end
+ rescue StandardError
+ # no-op
+ end
+ end.not_to change { statistics.reload.repositories_count }
+ end
+ end
+
+ context 'with not allowed attributes' do
+ it 'returns error' do
+ expect { described_class.track('something_else') }.to raise_error(ArgumentError).with_message(/Invalid attribute: \'something_else\' to \'track\' method/)
+ end
+ end
+ end
+
+ describe '.untrack' do
+ context 'with allowed attributes' do
+ let(:statistics) { create(:site_statistics) }
+
+ it 'decreases the attribute counter' do
+ expect { described_class.untrack('repositories_count') }.to change { statistics.reload.repositories_count }.by(-1)
+ expect { described_class.untrack('wikis_count') }.to change { statistics.reload.wikis_count }.by(-1)
+ end
+
+ it 'doesnt decrease the attribute counter when an exception happens during transaction' do
+ expect do
+ begin
+ described_class.transaction do
+ described_class.track('repositories_count')
+
+ raise StandardError
+ end
+ rescue StandardError
+ # no-op
+ end
+ end.not_to change { described_class.fetch.repositories_count }
+ end
+ end
+
+ context 'with not allowed attributes' do
+ it 'returns error' do
+ expect { described_class.untrack('something_else') }.to raise_error(ArgumentError).with_message(/Invalid attribute: \'something_else\' to \'untrack\' method/)
+ end
+ end
+ end
+end
diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb
index 0d6b4384ada..90a2caaeb88 100644
--- a/spec/models/spam_log_spec.rb
+++ b/spec/models/spam_log_spec.rb
@@ -22,7 +22,7 @@ describe SpamLog do
spam_log = build(:spam_log)
user = spam_log.user
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
spam_log.remove_user(deleted_by: admin)
end
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index bd498269798..f29abcf536e 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -7,6 +7,7 @@ describe Todo do
it { is_expected.to belong_to(:author).class_name("User") }
it { is_expected.to belong_to(:note) }
it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:target).touch(true) }
it { is_expected.to belong_to(:user) }
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 097144d04ce..f5e2c977104 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -20,6 +20,7 @@ describe User do
describe 'associations' do
it { is_expected.to have_one(:namespace) }
+ it { is_expected.to have_one(:status) }
it { is_expected.to have_many(:snippets).dependent(:destroy) }
it { is_expected.to have_many(:members) }
it { is_expected.to have_many(:project_members) }
@@ -383,7 +384,7 @@ describe User do
let(:secondary) { create(:email, :confirmed, email: 'secondary@example.com', user: user) }
it 'allows a verfied secondary email to be used as the primary without needing reconfirmation' do
- user.update_attributes!(email: secondary.email)
+ user.update!(email: secondary.email)
user.reload
expect(user.email).to eq secondary.email
expect(user.unconfirmed_email).to eq nil
@@ -405,11 +406,11 @@ describe User do
it 'gets called when email updated' do
expect(@user).to receive(:update_emails_with_primary_email)
- @user.update_attributes!(email: 'new_primary@example.com')
+ @user.update!(email: 'new_primary@example.com')
end
it 'adds old primary to secondary emails when secondary is a new email ' do
- @user.update_attributes!(email: 'new_primary@example.com')
+ @user.update!(email: 'new_primary@example.com')
@user.reload
expect(@user.emails.count).to eq 2
@@ -417,7 +418,7 @@ describe User do
end
it 'adds old primary to secondary emails if secondary is becoming a primary' do
- @user.update_attributes!(email: @secondary.email)
+ @user.update!(email: @secondary.email)
@user.reload
expect(@user.emails.count).to eq 1
@@ -425,7 +426,7 @@ describe User do
end
it 'transfers old confirmation values into new secondary' do
- @user.update_attributes!(email: @secondary.email)
+ @user.update!(email: @secondary.email)
@user.reload
expect(@user.emails.count).to eq 1
@@ -494,12 +495,12 @@ describe User do
it 'does nothing when the name is updated' do
expect(user).not_to receive(:update_invalid_gpg_signatures)
- user.update_attributes!(name: 'Bette')
+ user.update!(name: 'Bette')
end
it 'synchronizes the gpg keys when the email is updated' do
expect(user).to receive(:update_invalid_gpg_signatures).at_most(:twice)
- user.update_attributes!(email: 'shawnee.ritchie@denesik.com')
+ user.update!(email: 'shawnee.ritchie@denesik.com')
end
end
end
@@ -617,13 +618,13 @@ describe User do
it 'receives callback when external changes' do
expect(user).to receive(:ensure_user_rights_and_limits)
- user.update_attributes(external: false)
+ user.update(external: false)
end
it 'ensures correct rights and limits for user' do
stub_config_setting(default_can_create_group: true)
- expect { user.update_attributes(external: false) }.to change { user.can_create_group }.to(true)
+ expect { user.update(external: false) }.to change { user.can_create_group }.to(true)
.and change { user.projects_limit }.to(Gitlab::CurrentSettings.default_projects_limit)
end
end
@@ -634,11 +635,11 @@ describe User do
it 'receives callback when external changes' do
expect(user).to receive(:ensure_user_rights_and_limits)
- user.update_attributes(external: true)
+ user.update(external: true)
end
it 'ensures correct rights and limits for user' do
- expect { user.update_attributes(external: true) }.to change { user.can_create_group }.to(false)
+ expect { user.update(external: true) }.to change { user.can_create_group }.to(false)
.and change { user.projects_limit }.to(0)
end
end
@@ -700,7 +701,7 @@ describe User do
@project = create(:project, namespace: @user.namespace)
@project_2 = create(:project, group: create(:group)) do |project|
- project.add_master(@user)
+ project.add_maintainer(@user)
end
@project_3 = create(:project, group: create(:group)) do |project|
project.add_developer(@user)
@@ -836,7 +837,7 @@ describe User do
before do
# add user to project
- project.add_master(user)
+ project.add_maintainer(user)
# create invite to projet
create(:project_member, :developer, project: project, invite_token: '1234', invite_email: 'inviteduser1@example.com')
@@ -949,6 +950,7 @@ describe User do
user = create(:user, email: 'foo@example.com')
expect(described_class.find_by_any_email(user.email)).to eq user
+ expect(described_class.find_by_any_email(user.email, confirmed: true)).to eq user
end
it 'finds by secondary email' do
@@ -956,11 +958,19 @@ describe User do
user = email.user
expect(described_class.find_by_any_email(email.email)).to eq user
+ expect(described_class.find_by_any_email(email.email, confirmed: true)).to eq user
end
it 'returns nil when nothing found' do
expect(described_class.find_by_any_email('')).to be_nil
end
+
+ it 'returns nil when user is not confirmed' do
+ user = create(:user, email: 'foo@example.com', confirmed_at: nil)
+
+ expect(described_class.find_by_any_email(user.email, confirmed: false)).to eq(user)
+ expect(described_class.find_by_any_email(user.email, confirmed: true)).to be_nil
+ end
end
describe '.by_any_email' do
@@ -974,6 +984,12 @@ describe User do
expect(described_class.by_any_email(user.email)).to eq([user])
end
+
+ it 'returns a relation of users for confirmed users' do
+ user = create(:user)
+
+ expect(described_class.by_any_email(user.email, confirmed: true)).to eq([user])
+ end
end
describe '.search' do
@@ -1581,8 +1597,8 @@ describe User do
let!(:merge_event) { create(:event, :created, project: project3, target: merge_request, author: subject) }
before do
- project1.add_master(subject)
- project2.add_master(subject)
+ project1.add_maintainer(subject)
+ project2.add_maintainer(subject)
end
it "includes IDs for projects the user has pushed to" do
@@ -1663,8 +1679,8 @@ describe User do
let!(:project) { create(:project, group: project_group) }
before do
- private_group.add_user(user, Gitlab::Access::MASTER)
- project.add_master(user)
+ private_group.add_user(user, Gitlab::Access::MAINTAINER)
+ project.add_maintainer(user)
end
subject { user.authorized_groups }
@@ -1678,7 +1694,7 @@ describe User do
let!(:child_group) { create(:group, parent: parent_group) }
before do
- parent_group.add_user(user, Gitlab::Access::MASTER)
+ parent_group.add_user(user, Gitlab::Access::MAINTAINER)
end
subject { user.membership_groups }
@@ -1696,7 +1712,7 @@ describe User do
it 'includes projects that belong to a user, but no other projects' do
owned = create(:project, :private, namespace: user.namespace)
- member = create(:project, :private).tap { |p| p.add_master(user) }
+ member = create(:project, :private).tap { |p| p.add_maintainer(user) }
other = create(:project)
expect(subject).to include(owned)
@@ -1726,11 +1742,11 @@ describe User do
.to contain_exactly(project)
end
- it 'includes projects for which the user is a master' do
+ it 'includes projects for which the user is a maintainer' do
user = create(:user)
project = create(:project, :private)
- project.add_master(user)
+ project.add_maintainer(user)
expect(user.authorized_projects(Gitlab::Access::REPORTER))
.to contain_exactly(project)
@@ -1824,10 +1840,10 @@ describe User do
it 'includes projects for which the user access level is above or equal to reporter' do
reporter_project = create(:project) { |p| p.add_reporter(user) }
developer_project = create(:project) { |p| p.add_developer(user) }
- master_project = create(:project) { |p| p.add_master(user) }
+ maintainer_project = create(:project) { |p| p.add_maintainer(user) }
- expect(user.projects_where_can_admin_issues.to_a).to match_array([master_project, developer_project, reporter_project])
- expect(user.can?(:admin_issue, master_project)).to eq(true)
+ expect(user.projects_where_can_admin_issues.to_a).to match_array([maintainer_project, developer_project, reporter_project])
+ expect(user.can?(:admin_issue, maintainer_project)).to eq(true)
expect(user.can?(:admin_issue, developer_project)).to eq(true)
expect(user.can?(:admin_issue, reporter_project)).to eq(true)
end
@@ -1907,9 +1923,9 @@ describe User do
end
shared_examples :member do
- context 'when the user is a master' do
+ context 'when the user is a maintainer' do
before do
- add_user(:master)
+ add_user(:maintainer)
end
it 'loads' do
@@ -2461,18 +2477,20 @@ describe User do
it 'changes the namespace (just to compare to when username is not changed)' do
expect do
- user.update_attributes!(username: new_username)
+ Timecop.freeze(1.second.from_now) do
+ user.update!(username: new_username)
+ end
end.to change { user.namespace.updated_at }
end
it 'updates the namespace name' do
- user.update_attributes!(username: new_username)
+ user.update!(username: new_username)
expect(user.namespace.name).to eq(new_username)
end
it 'updates the namespace path' do
- user.update_attributes!(username: new_username)
+ user.update!(username: new_username)
expect(user.namespace.path).to eq(new_username)
end
@@ -2481,12 +2499,12 @@ describe User do
let!(:conflicting_namespace) { create(:group, path: new_username) }
it 'causes the user save to fail' do
- expect(user.update_attributes(username: new_username)).to be_falsey
+ expect(user.update(username: new_username)).to be_falsey
expect(user.namespace.errors.messages[:path].first).to eq('has already been taken')
end
it 'adds the namespace errors to the user' do
- user.update_attributes(username: new_username)
+ user.update(username: new_username)
expect(user.errors.full_messages.first).to eq('Username has already been taken')
end
@@ -2496,7 +2514,7 @@ describe User do
context 'when the username is not changed' do
it 'does not change the namespace' do
expect do
- user.update_attributes!(email: 'asdf@asdf.com')
+ user.update!(email: 'asdf@asdf.com')
end.not_to change { user.namespace.updated_at }
end
end
@@ -2526,7 +2544,7 @@ describe User do
expect(system_hook_service).to receive(:execute_hooks_for).with(user, :rename)
expect(user).to receive(:system_hook_service).and_return(system_hook_service)
- user.update_attributes!(username: new_username)
+ user.update!(username: new_username)
end
end
@@ -2534,7 +2552,7 @@ describe User do
it 'does not trigger system hook' do
expect(user).not_to receive(:system_hook_service)
- user.update_attributes!(email: 'asdf@asdf.com')
+ user.update!(email: 'asdf@asdf.com')
end
end
end
@@ -2666,20 +2684,20 @@ describe User do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:owner_project) { create(:project, group: group) }
- let(:master_project) { create(:project) }
+ let(:maintainer_project) { create(:project) }
let(:reporter_project) { create(:project) }
let(:developer_project) { create(:project) }
let(:guest_project) { create(:project) }
let(:no_access_project) { create(:project) }
let(:projects) do
- [owner_project, master_project, reporter_project, developer_project, guest_project, no_access_project].map(&:id)
+ [owner_project, maintainer_project, reporter_project, developer_project, guest_project, no_access_project].map(&:id)
end
let(:expected) do
{
owner_project.id => Gitlab::Access::OWNER,
- master_project.id => Gitlab::Access::MASTER,
+ maintainer_project.id => Gitlab::Access::MAINTAINER,
reporter_project.id => Gitlab::Access::REPORTER,
developer_project.id => Gitlab::Access::DEVELOPER,
guest_project.id => Gitlab::Access::GUEST,
@@ -2689,7 +2707,7 @@ describe User do
before do
create(:group_member, user: user, group: group)
- master_project.add_master(user)
+ maintainer_project.add_maintainer(user)
reporter_project.add_reporter(user)
developer_project.add_developer(user)
guest_project.add_guest(user)
@@ -2716,14 +2734,14 @@ describe User do
end
it 'only requests the extra projects when uncached projects are passed' do
- second_master_project = create(:project)
+ second_maintainer_project = create(:project)
second_developer_project = create(:project)
- second_master_project.add_master(user)
+ second_maintainer_project.add_maintainer(user)
second_developer_project.add_developer(user)
- all_projects = projects + [second_master_project.id, second_developer_project.id]
+ all_projects = projects + [second_maintainer_project.id, second_developer_project.id]
- expected_all = expected.merge(second_master_project.id => Gitlab::Access::MASTER,
+ expected_all = expected.merge(second_maintainer_project.id => Gitlab::Access::MAINTAINER,
second_developer_project.id => Gitlab::Access::DEVELOPER)
access_levels(projects)
@@ -2731,7 +2749,7 @@ describe User do
queries = ActiveRecord::QueryRecorder.new { access_levels(all_projects) }
expect(queries.count).to eq(1)
- expect(queries.log_message).to match(/\W(#{second_master_project.id}, #{second_developer_project.id})\W/)
+ expect(queries.log_message).to match(/\W(#{second_maintainer_project.id}, #{second_developer_project.id})\W/)
expect(access_levels(all_projects)).to eq(expected_all)
end
end
@@ -2745,20 +2763,20 @@ describe User do
shared_examples 'max member access for groups' do
let(:user) { create(:user) }
let(:owner_group) { create(:group) }
- let(:master_group) { create(:group) }
+ let(:maintainer_group) { create(:group) }
let(:reporter_group) { create(:group) }
let(:developer_group) { create(:group) }
let(:guest_group) { create(:group) }
let(:no_access_group) { create(:group) }
let(:groups) do
- [owner_group, master_group, reporter_group, developer_group, guest_group, no_access_group].map(&:id)
+ [owner_group, maintainer_group, reporter_group, developer_group, guest_group, no_access_group].map(&:id)
end
let(:expected) do
{
owner_group.id => Gitlab::Access::OWNER,
- master_group.id => Gitlab::Access::MASTER,
+ maintainer_group.id => Gitlab::Access::MAINTAINER,
reporter_group.id => Gitlab::Access::REPORTER,
developer_group.id => Gitlab::Access::DEVELOPER,
guest_group.id => Gitlab::Access::GUEST,
@@ -2768,7 +2786,7 @@ describe User do
before do
owner_group.add_owner(user)
- master_group.add_master(user)
+ maintainer_group.add_maintainer(user)
reporter_group.add_reporter(user)
developer_group.add_developer(user)
guest_group.add_guest(user)
@@ -2795,14 +2813,14 @@ describe User do
end
it 'only requests the extra groups when uncached groups are passed' do
- second_master_group = create(:group)
+ second_maintainer_group = create(:group)
second_developer_group = create(:group)
- second_master_group.add_master(user)
+ second_maintainer_group.add_maintainer(user)
second_developer_group.add_developer(user)
- all_groups = groups + [second_master_group.id, second_developer_group.id]
+ all_groups = groups + [second_maintainer_group.id, second_developer_group.id]
- expected_all = expected.merge(second_master_group.id => Gitlab::Access::MASTER,
+ expected_all = expected.merge(second_maintainer_group.id => Gitlab::Access::MAINTAINER,
second_developer_group.id => Gitlab::Access::DEVELOPER)
access_levels(groups)
@@ -2810,7 +2828,7 @@ describe User do
queries = ActiveRecord::QueryRecorder.new { access_levels(all_groups) }
expect(queries.count).to eq(1)
- expect(queries.log_message).to match(/\W(#{second_master_group.id}, #{second_developer_group.id})\W/)
+ expect(queries.log_message).to match(/\W(#{second_maintainer_group.id}, #{second_developer_group.id})\W/)
expect(access_levels(all_groups)).to eq(expected_all)
end
end
diff --git a/spec/models/user_status_spec.rb b/spec/models/user_status_spec.rb
new file mode 100644
index 00000000000..fcc01cdae3d
--- /dev/null
+++ b/spec/models/user_status_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe UserStatus do
+ it { is_expected.to validate_presence_of(:user) }
+
+ it { is_expected.to allow_value('smirk').for(:emoji) }
+ it { is_expected.not_to allow_value('hello world').for(:emoji) }
+ it { is_expected.not_to allow_value('').for(:emoji) }
+
+ it { is_expected.to validate_length_of(:message).is_at_most(100) }
+ it { is_expected.to allow_value('').for(:message) }
+
+ it 'is expected to be deleted when the user is deleted' do
+ status = create(:user_status)
+
+ expect { status.user.destroy }.to change { described_class.count }.from(1).to(0)
+ end
+end
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 1c765ceac2f..63850939be1 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -554,6 +554,16 @@ describe WikiPage do
end
end
+ describe '#hook_attrs' do
+ it 'adds absolute urls for images in the content' do
+ create_page("test page", "test![WikiPage_Image](/uploads/abc/WikiPage_Image.png)")
+ page = wiki.wiki.page(title: "test page")
+ wiki_page = described_class.new(wiki, page, true)
+
+ expect(wiki_page.hook_attrs['content']).to eq("test![WikiPage_Image](#{Settings.gitlab.url}/uploads/abc/WikiPage_Image.png)")
+ end
+ end
+
private
def remove_temp_repo(path)
diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb
index eead55d33ca..79a616899fa 100644
--- a/spec/policies/ci/build_policy_spec.rb
+++ b/spec/policies/ci/build_policy_spec.rb
@@ -204,18 +204,18 @@ describe Ci::BuildPolicy do
end
end
- context 'when a master erases a build' do
+ context 'when a maintainer erases a build' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
- context 'when masters can push to the branch' do
+ context 'when maintainers can push to the branch' do
before do
- create(:protected_branch, :masters_can_push,
+ create(:protected_branch, :maintainers_can_push,
name: build.ref, project: project)
end
- context 'when the build was created by the master' do
+ context 'when the build was created by the maintainer' do
let(:owner) { user }
it { expect(policy).to be_allowed :erase_build }
diff --git a/spec/policies/ci/pipeline_schedule_policy_spec.rb b/spec/policies/ci/pipeline_schedule_policy_spec.rb
index c0c3eda4911..f1d3cd04e32 100644
--- a/spec/policies/ci/pipeline_schedule_policy_spec.rb
+++ b/spec/policies/ci/pipeline_schedule_policy_spec.rb
@@ -77,9 +77,9 @@ describe Ci::PipelineSchedulePolicy, :models do
end
end
- describe 'rules for a master' do
+ describe 'rules for a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'includes abilities to do do all operations on pipeline schedule' do
@@ -93,8 +93,8 @@ describe Ci::PipelineSchedulePolicy, :models do
let(:owner) { create(:user) }
before do
- project.add_master(owner)
- project.add_master(user)
+ project.add_maintainer(owner)
+ project.add_maintainer(user)
pipeline_schedule.update(owner: owner)
end
diff --git a/spec/policies/ci/trigger_policy_spec.rb b/spec/policies/ci/trigger_policy_spec.rb
index 14630748c90..d8a63066265 100644
--- a/spec/policies/ci/trigger_policy_spec.rb
+++ b/spec/policies/ci/trigger_policy_spec.rb
@@ -43,9 +43,9 @@ describe Ci::TriggerPolicy do
context 'when owner is undefined' do
let(:owner) { nil }
- context 'when user is master of the project' do
+ context 'when user is maintainer of the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'allows to admin and manage trigger'
@@ -67,9 +67,9 @@ describe Ci::TriggerPolicy do
context 'when owner is an user' do
let(:owner) { user }
- context 'when user is master of the project' do
+ context 'when user is maintainer of the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'allows to admin and manage trigger'
@@ -79,9 +79,9 @@ describe Ci::TriggerPolicy do
context 'when owner is another user' do
let(:owner) { create(:user) }
- context 'when user is master of the project' do
+ context 'when user is maintainer of the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'allows to manage trigger'
diff --git a/spec/policies/clusters/cluster_policy_spec.rb b/spec/policies/clusters/cluster_policy_spec.rb
index 4207f42b07f..ced969830d8 100644
--- a/spec/policies/clusters/cluster_policy_spec.rb
+++ b/spec/policies/clusters/cluster_policy_spec.rb
@@ -16,9 +16,9 @@ describe Clusters::ClusterPolicy, :models do
it { expect(policy).to be_disallowed :admin_cluster }
end
- context 'when master' do
+ context 'when maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it { expect(policy).to be_allowed :update_cluster }
diff --git a/spec/policies/concerns/policy_actor_spec.rb b/spec/policies/concerns/policy_actor_spec.rb
new file mode 100644
index 00000000000..27db9710a38
--- /dev/null
+++ b/spec/policies/concerns/policy_actor_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PolicyActor do
+ it 'implements all the methods from user' do
+ methods = subject.instance_methods
+
+ # User.instance_methods do not return all methods until an instance is
+ # initialized. So here we just use an instance
+ expect(build(:user).methods).to include(*methods)
+ end
+end
diff --git a/spec/policies/deploy_key_policy_spec.rb b/spec/policies/deploy_key_policy_spec.rb
index ca7b7fe7ef7..e7263d49613 100644
--- a/spec/policies/deploy_key_policy_spec.rb
+++ b/spec/policies/deploy_key_policy_spec.rb
@@ -12,7 +12,7 @@ describe DeployKeyPolicy do
let(:project) { create(:project_empty_repo) }
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
project.deploy_keys << deploy_key
end
diff --git a/spec/policies/deploy_token_policy_spec.rb b/spec/policies/deploy_token_policy_spec.rb
index eea287d895e..cef5a4a22bc 100644
--- a/spec/policies/deploy_token_policy_spec.rb
+++ b/spec/policies/deploy_token_policy_spec.rb
@@ -8,15 +8,15 @@ describe DeployTokenPolicy do
subject { described_class.new(current_user, deploy_token) }
describe 'creating a deploy key' do
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
end
it { is_expected.to be_allowed(:create_deploy_token) }
end
- context 'when user is not master' do
+ context 'when user is not maintainer' do
before do
project.add_developer(current_user)
end
@@ -26,15 +26,15 @@ describe DeployTokenPolicy do
end
describe 'updating a deploy key' do
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
end
it { is_expected.to be_allowed(:update_deploy_token) }
end
- context 'when user is not master' do
+ context 'when user is not maintainer' do
before do
project.add_developer(current_user)
end
diff --git a/spec/policies/environment_policy_spec.rb b/spec/policies/environment_policy_spec.rb
index de4cb5b30c5..0442b032e89 100644
--- a/spec/policies/environment_policy_spec.rb
+++ b/spec/policies/environment_policy_spec.rb
@@ -1,57 +1,101 @@
require 'spec_helper'
describe EnvironmentPolicy do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
+ using RSpec::Parameterized::TableSyntax
- let(:environment) do
- create(:environment, :with_review_app, project: project)
- end
+ let(:user) { create(:user) }
let(:policy) do
described_class.new(user, environment)
end
describe '#rules' do
- context 'when user does not have access to the project' do
- let(:project) { create(:project, :private, :repository) }
+ shared_examples 'project permissions' do
+ context 'with stop action' do
+ let(:environment) do
+ create(:environment, :with_review_app, project: project)
+ end
- it 'does not include ability to stop environment' do
- expect(policy).to be_disallowed :stop_environment
- end
- end
+ where(:access_level, :allowed?) do
+ nil | false
+ :guest | false
+ :reporter | false
+ :developer | true
+ :maintainer | true
+ end
- context 'when anonymous user has access to the project' do
- let(:project) { create(:project, :public, :repository) }
+ with_them do
+ before do
+ project.add_user(user, access_level) unless access_level.nil?
+ end
- it 'does not include ability to stop environment' do
- expect(policy).to be_disallowed :stop_environment
- end
- end
+ it { expect(policy.allowed?(:stop_environment)).to be allowed? }
+ end
- context 'when team member has access to the project' do
- let(:project) { create(:project, :public, :repository) }
+ context 'when an admin user' do
+ let(:user) { create(:user, :admin) }
- before do
- project.add_developer(user)
- end
+ it { expect(policy).to be_allowed :stop_environment }
+ end
+
+ context 'with protected branch' do
+ with_them do
+ before do
+ project.add_user(user, access_level) unless access_level.nil?
+ create(:protected_branch, :no_one_can_push,
+ name: 'master', project: project)
+ end
- context 'when team member has ability to stop environment' do
- it 'does includes ability to stop environment' do
- expect(policy).to be_allowed :stop_environment
+ it { expect(policy).to be_disallowed :stop_environment }
+ end
+
+ context 'when an admin user' do
+ let(:user) { create(:user, :admin) }
+
+ it { expect(policy).to be_allowed :stop_environment }
+ end
end
end
- context 'when team member has no ability to stop environment' do
- before do
- create(:protected_branch, :no_one_can_push,
- name: 'master', project: project)
+ context 'without stop action' do
+ let(:environment) do
+ create(:environment, project: project)
+ end
+
+ where(:access_level, :allowed?) do
+ nil | false
+ :guest | false
+ :reporter | false
+ :developer | false
+ :maintainer | true
end
- it 'does not include ability to stop environment' do
- expect(policy).to be_disallowed :stop_environment
+ with_them do
+ before do
+ project.add_user(user, access_level) unless access_level.nil?
+ end
+
+ it { expect(policy.allowed?(:stop_environment)).to be allowed? }
+ end
+
+ context 'when an admin user' do
+ let(:user) { create(:user, :admin) }
+
+ it { expect(policy).to be_allowed :stop_environment }
end
end
end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ include_examples 'project permissions'
+ end
+
+ context 'when project is private' do
+ let(:project) { create(:project, :private, :repository) }
+
+ include_examples 'project permissions'
+ end
end
end
diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb
index 873673b50ef..30d68e7dc9d 100644
--- a/spec/policies/global_policy_spec.rb
+++ b/spec/policies/global_policy_spec.rb
@@ -65,12 +65,12 @@ describe GlobalPolicy do
it { is_expected.not_to be_allowed(:create_fork) }
end
- context "when user is a master in a group" do
+ context "when user is a maintainer in a group" do
let(:group) { create(:group) }
let(:current_user) { create(:user, projects_limit: 0) }
before do
- group.add_master(current_user)
+ group.add_maintainer(current_user)
end
it { is_expected.to be_allowed(:create_fork) }
@@ -180,4 +180,38 @@ describe GlobalPolicy do
end
end
end
+
+ describe 'read instance statistics' do
+ context 'regular user' do
+ it { is_expected.to be_allowed(:read_instance_statistics) }
+
+ context 'when instance statistics are set to private' do
+ before do
+ stub_application_setting(instance_statistics_visibility_private: true)
+ end
+
+ it { is_expected.not_to be_allowed(:read_instance_statistics) }
+ end
+ end
+
+ context 'admin' do
+ let(:current_user) { create(:admin) }
+
+ it { is_expected.to be_allowed(:read_instance_statistics) }
+
+ context 'when instance statistics are set to private' do
+ before do
+ stub_application_setting(instance_statistics_visibility_private: true)
+ end
+
+ it { is_expected.to be_allowed(:read_instance_statistics) }
+ end
+ end
+
+ context 'anonymous' do
+ let(:current_user) { nil }
+
+ it { is_expected.not_to be_allowed(:read_instance_statistics) }
+ end
+ end
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 9b5c290b9f9..35951251bc5 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -4,18 +4,22 @@ describe GroupPolicy do
let(:guest) { create(:user) }
let(:reporter) { create(:user) }
let(:developer) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:owner) { create(:user) }
let(:admin) { create(:admin) }
let(:group) { create(:group, :private) }
- let(:guest_permissions) { [:read_label, :read_group, :upload_file, :read_namespace] }
+ let(:guest_permissions) do
+ [:read_label, :read_group, :upload_file, :read_namespace, :read_group_activity,
+ :read_group_issues, :read_group_boards, :read_group_labels, :read_group_milestones,
+ :read_group_merge_requests]
+ end
let(:reporter_permissions) { [:admin_label] }
let(:developer_permissions) { [:admin_milestones] }
- let(:master_permissions) do
+ let(:maintainer_permissions) do
[
:create_projects
]
@@ -35,7 +39,7 @@ describe GroupPolicy do
group.add_guest(guest)
group.add_reporter(reporter)
group.add_developer(developer)
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_owner(owner)
end
@@ -58,7 +62,7 @@ describe GroupPolicy do
expect_disallowed(:upload_file)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
expect_disallowed(:read_namespace)
end
@@ -93,7 +97,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -105,7 +109,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -117,19 +121,19 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
- context 'master' do
- let(:current_user) { master }
+ context 'maintainer' do
+ let(:current_user) { maintainer }
it do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -143,7 +147,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
end
@@ -157,7 +161,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
end
@@ -199,7 +203,7 @@ describe GroupPolicy do
nested_group.add_guest(guest)
nested_group.add_guest(reporter)
nested_group.add_guest(developer)
- nested_group.add_guest(master)
+ nested_group.add_guest(maintainer)
group.owners.destroy_all
@@ -216,7 +220,7 @@ describe GroupPolicy do
expect_disallowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -228,7 +232,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -240,7 +244,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -252,19 +256,19 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
- context 'master' do
- let(:current_user) { master }
+ context 'maintainer' do
+ let(:current_user) { maintainer }
it do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -278,7 +282,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 6d4676c25a5..dd3fa4e6a51 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -4,7 +4,7 @@ describe ProjectPolicy do
set(:guest) { create(:user) }
set(:reporter) { create(:user) }
set(:developer) { create(:user) }
- set(:master) { create(:user) }
+ set(:maintainer) { create(:user) }
set(:owner) { create(:user) }
set(:admin) { create(:admin) }
let(:project) { create(:project, :public, namespace: owner.namespace) }
@@ -42,7 +42,7 @@ describe ProjectPolicy do
]
end
- let(:base_master_permissions) do
+ let(:base_maintainer_permissions) do
%i[
push_to_delete_protected_branch update_project_snippet update_environment
update_deployment admin_project_snippet
@@ -70,15 +70,15 @@ describe ProjectPolicy do
# Used in EE specs
let(:additional_guest_permissions) { [] }
let(:additional_reporter_permissions) { [] }
- let(:additional_master_permissions) { [] }
+ let(:additional_maintainer_permissions) { [] }
let(:guest_permissions) { base_guest_permissions + additional_guest_permissions }
let(:reporter_permissions) { base_reporter_permissions + additional_reporter_permissions }
- let(:master_permissions) { base_master_permissions + additional_master_permissions }
+ let(:maintainer_permissions) { base_maintainer_permissions + additional_maintainer_permissions }
before do
project.add_guest(guest)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_developer(developer)
project.add_reporter(reporter)
end
@@ -276,7 +276,7 @@ describe ProjectPolicy do
expect_disallowed(*reporter_public_build_permissions)
expect_disallowed(*team_member_reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
@@ -326,7 +326,7 @@ describe ProjectPolicy do
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
@@ -347,7 +347,7 @@ describe ProjectPolicy do
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
expect_allowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
@@ -357,23 +357,23 @@ describe ProjectPolicy do
end
end
- shared_examples 'project policies as master' do
+ shared_examples 'project policies as maintainer' do
context 'abilities for non-public projects' do
let(:project) { create(:project, namespace: owner.namespace) }
- subject { described_class.new(master, project) }
+ subject { described_class.new(maintainer, project) }
it do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
it_behaves_like 'archived project policies' do
- let(:regular_abilities) { master_permissions }
+ let(:regular_abilities) { maintainer_permissions }
end
end
end
@@ -389,7 +389,7 @@ describe ProjectPolicy do
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
@@ -410,7 +410,7 @@ describe ProjectPolicy do
expect_allowed(*reporter_permissions)
expect_disallowed(*team_member_reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
@@ -424,7 +424,7 @@ describe ProjectPolicy do
it_behaves_like 'project policies as guest'
it_behaves_like 'project policies as reporter'
it_behaves_like 'project policies as developer'
- it_behaves_like 'project policies as master'
+ it_behaves_like 'project policies as maintainer'
it_behaves_like 'project policies as owner'
it_behaves_like 'project policies as admin'
diff --git a/spec/policies/protected_branch_policy_spec.rb b/spec/policies/protected_branch_policy_spec.rb
index b39de42d721..1587196754d 100644
--- a/spec/policies/protected_branch_policy_spec.rb
+++ b/spec/policies/protected_branch_policy_spec.rb
@@ -8,8 +8,8 @@ describe ProtectedBranchPolicy do
subject { described_class.new(user, protected_branch) }
- it 'branches can be updated via project masters' do
- project.add_master(user)
+ it 'branches can be updated via project maintainers' do
+ project.add_maintainer(user)
is_expected.to be_allowed(:update_protected_branch)
end
diff --git a/spec/policies/user_policy_spec.rb b/spec/policies/user_policy_spec.rb
index a7a77abc3ee..7e0a1824200 100644
--- a/spec/policies/user_policy_spec.rb
+++ b/spec/policies/user_policy_spec.rb
@@ -35,6 +35,10 @@ describe UserPolicy do
end
end
+ describe "updating a user's status" do
+ it_behaves_like 'changing a user', :update_user_status
+ end
+
describe "destroying a user" do
it_behaves_like 'changing a user', :destroy_user
end
diff --git a/spec/presenters/ci/build_runner_presenter_spec.rb b/spec/presenters/ci/build_runner_presenter_spec.rb
new file mode 100644
index 00000000000..e7019b990dd
--- /dev/null
+++ b/spec/presenters/ci/build_runner_presenter_spec.rb
@@ -0,0 +1,86 @@
+require 'spec_helper'
+
+describe Ci::BuildRunnerPresenter do
+ let(:presenter) { described_class.new(build) }
+ let(:archive) { { paths: ['sample.txt'] } }
+ let(:junit) { { junit: ['junit.xml'] } }
+
+ let(:archive_expectation) do
+ {
+ artifact_type: :archive,
+ artifact_format: :zip,
+ paths: archive[:paths],
+ untracked: archive[:untracked]
+ }
+ end
+
+ let(:junit_expectation) do
+ {
+ name: 'junit.xml',
+ artifact_type: :junit,
+ artifact_format: :gzip,
+ paths: ['junit.xml'],
+ when: 'always'
+ }
+ end
+
+ describe '#artifacts' do
+ context "when option contains archive-type artifacts" do
+ let(:build) { create(:ci_build, options: { artifacts: archive } ) }
+
+ it 'presents correct hash' do
+ expect(presenter.artifacts.first).to include(archive_expectation)
+ end
+
+ context "when untracked is specified" do
+ let(:archive) { { untracked: true } }
+
+ it 'presents correct hash' do
+ expect(presenter.artifacts.first).to include(archive_expectation)
+ end
+ end
+
+ context "when untracked and paths are missing" do
+ let(:archive) { { when: 'always' } }
+
+ it 'does not present hash' do
+ expect(presenter.artifacts).to be_empty
+ end
+ end
+ end
+
+ context "when option has 'junit' keyword" do
+ let(:build) { create(:ci_build, options: { artifacts: { reports: junit } } ) }
+
+ it 'presents correct hash' do
+ expect(presenter.artifacts.first).to include(junit_expectation)
+ end
+ end
+
+ context "when option has both archive and reports specification" do
+ let(:build) { create(:ci_build, options: { script: 'echo', artifacts: { **archive, reports: junit } } ) }
+
+ it 'presents correct hash' do
+ expect(presenter.artifacts.first).to include(archive_expectation)
+ expect(presenter.artifacts.second).to include(junit_expectation)
+ end
+
+ context "when archive specifies 'expire_in' keyword" do
+ let(:archive) { { paths: ['sample.txt'], expire_in: '3 mins 4 sec' } }
+
+ it 'inherits expire_in from archive' do
+ expect(presenter.artifacts.first).to include({ **archive_expectation, expire_in: '3 mins 4 sec' })
+ expect(presenter.artifacts.second).to include({ **junit_expectation, expire_in: '3 mins 4 sec' })
+ end
+ end
+ end
+
+ context "when option has no artifact keywords" do
+ let(:build) { create(:ci_build, :no_options) }
+
+ it 'does not present hash' do
+ expect(presenter.artifacts).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/presenters/commit_status_presenter_spec.rb b/spec/presenters/commit_status_presenter_spec.rb
index f81ee44e371..2b7742ddbb8 100644
--- a/spec/presenters/commit_status_presenter_spec.rb
+++ b/spec/presenters/commit_status_presenter_spec.rb
@@ -12,4 +12,30 @@ describe CommitStatusPresenter do
it 'inherits from Gitlab::View::Presenter::Delegated' do
expect(described_class.superclass).to eq(Gitlab::View::Presenter::Delegated)
end
+
+ describe 'covers all failure reasons' do
+ let(:message) { presenter.callout_failure_message }
+
+ CommitStatus.failure_reasons.keys.each do |failure_reason|
+ context failure_reason do
+ before do
+ build.failure_reason = failure_reason
+ end
+
+ it "is a valid status" do
+ expect { message }.not_to raise_error
+ end
+ end
+ end
+
+ context 'invalid failure message' do
+ before do
+ expect(build).to receive(:failure_reason) { 'invalid failure message' }
+ end
+
+ it "is an invalid status" do
+ expect { message }.to raise_error(/key not found:/)
+ end
+ end
+ end
end
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index e3b37739e8e..a1b52d8692d 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -117,9 +117,9 @@ describe MergeRequestPresenter do
before do
project.add_developer(user)
-
allow(resource.project).to receive(:default_branch)
.and_return(resource.target_branch)
+ resource.cache_merge_request_closes_issues!
end
describe '#closing_issues_links' do
@@ -270,7 +270,7 @@ describe MergeRequestPresenter do
context 'when can create issue and issues enabled' do
it 'returns path' do
allow(project).to receive(:issues_enabled?) { true }
- project.add_master(user)
+ project.add_maintainer(user)
is_expected
.to eq("/#{resource.project.full_path}/issues/new?merge_request_to_resolve_discussions_of=#{resource.iid}")
@@ -288,7 +288,7 @@ describe MergeRequestPresenter do
context 'when issues disabled' do
it 'returns nil' do
allow(project).to receive(:issues_enabled?) { false }
- project.add_master(user)
+ project.add_maintainer(user)
is_expected.to be_nil
end
@@ -307,7 +307,7 @@ describe MergeRequestPresenter do
context 'when merge request enabled and has permission' do
it 'has remove_wip_path' do
allow(project).to receive(:merge_requests_enabled?) { true }
- project.add_master(user)
+ project.add_maintainer(user)
is_expected
.to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}/remove_wip")
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index 830d2ee3b20..01085dbcb49 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -326,7 +326,7 @@ describe ProjectPresenter do
context 'when user can admin pipeline and CI yml does not exists' do
it 'returns anchor data' do
- project.add_master(user)
+ project.add_maintainer(user)
allow(project).to receive(:auto_devops_enabled?).and_return(false)
allow(project.repository).to receive(:gitlab_ci_yml).and_return(nil)
@@ -340,7 +340,7 @@ describe ProjectPresenter do
describe '#kubernetes_cluster_anchor_data' do
context 'when user can create Kubernetes cluster' do
it 'returns link to cluster if only one exists' do
- project.add_master(user)
+ project.add_maintainer(user)
cluster = create(:cluster, projects: [project])
expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: true,
@@ -349,7 +349,7 @@ describe ProjectPresenter do
end
it 'returns link to clusters page if more than one exists' do
- project.add_master(user)
+ project.add_maintainer(user)
create(:cluster, :production_environment, projects: [project])
create(:cluster, projects: [project])
@@ -359,7 +359,7 @@ describe ProjectPresenter do
end
it 'returns link to create a cluster if no cluster exists' do
- project.add_master(user)
+ project.add_maintainer(user)
expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: false,
label: 'Add Kubernetes cluster',
diff --git a/spec/requests/api/access_requests_spec.rb b/spec/requests/api/access_requests_spec.rb
index 24389f28b21..e13129967b2 100644
--- a/spec/requests/api/access_requests_spec.rb
+++ b/spec/requests/api/access_requests_spec.rb
@@ -1,15 +1,15 @@
require 'spec_helper'
describe API::AccessRequests do
- set(:master) { create(:user) }
+ set(:maintainer) { create(:user) }
set(:developer) { create(:user) }
set(:access_requester) { create(:user) }
set(:stranger) { create(:user) }
set(:project) do
- create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
+ create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.request_access(access_requester)
end
end
@@ -17,7 +17,7 @@ describe API::AccessRequests do
set(:group) do
create(:group, :public, :access_requestable) do |group|
group.add_developer(developer)
- group.add_owner(master)
+ group.add_owner(maintainer)
group.request_access(access_requester)
end
end
@@ -28,7 +28,7 @@ describe API::AccessRequests do
let(:route) { get api("/#{source_type.pluralize}/#{source.id}/access_requests", stranger) }
end
- context 'when authenticated as a non-master/owner' do
+ context 'when authenticated as a non-maintainer/owner' do
%i[developer access_requester stranger].each do |type|
context "as a #{type}" do
it 'returns 403' do
@@ -41,9 +41,9 @@ describe API::AccessRequests do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'returns access requesters' do
- get api("/#{source_type.pluralize}/#{source.id}/access_requests", master)
+ get api("/#{source_type.pluralize}/#{source.id}/access_requests", maintainer)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
@@ -61,7 +61,7 @@ describe API::AccessRequests do
end
context 'when authenticated as a member' do
- %i[developer master].each do |type|
+ %i[developer maintainer].each do |type|
context "as a #{type}" do
it 'returns 403' do
expect do
@@ -88,7 +88,7 @@ describe API::AccessRequests do
context 'when authenticated as a stranger' do
context "when access request is disabled for the #{source_type}" do
before do
- source.update_attributes(request_access_enabled: false)
+ source.update(request_access_enabled: false)
end
it 'returns 403' do
@@ -128,7 +128,7 @@ describe API::AccessRequests do
let(:route) { put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}/approve", stranger) }
end
- context 'when authenticated as a non-master/owner' do
+ context 'when authenticated as a non-maintainer/owner' do
%i[developer access_requester stranger].each do |type|
context "as a #{type}" do
it 'returns 403' do
@@ -141,11 +141,11 @@ describe API::AccessRequests do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'returns 201' do
expect do
- put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}/approve", master),
- access_level: Member::MASTER
+ put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}/approve", maintainer),
+ access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(201)
end.to change { source.members.count }.by(1)
@@ -158,13 +158,13 @@ describe API::AccessRequests do
expect(json_response['web_url']).to eq(Gitlab::Routing.url_helpers.user_url(access_requester))
# Member attributes
- expect(json_response['access_level']).to eq(Member::MASTER)
+ expect(json_response['access_level']).to eq(Member::MAINTAINER)
end
context 'user_id does not match an existing access requester' do
it 'returns 404' do
expect do
- put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{stranger.id}/approve", master)
+ put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{stranger.id}/approve", maintainer)
expect(response).to have_gitlab_http_status(404)
end.not_to change { source.members.count }
@@ -180,7 +180,7 @@ describe API::AccessRequests do
let(:route) { delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}", stranger) }
end
- context 'when authenticated as a non-master/owner' do
+ context 'when authenticated as a non-maintainer/owner' do
%i[developer stranger].each do |type|
context "as a #{type}" do
it 'returns 403' do
@@ -203,10 +203,10 @@ describe API::AccessRequests do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'deletes the access requester' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}", maintainer)
expect(response).to have_gitlab_http_status(204)
end.to change { source.requesters.count }.by(-1)
@@ -215,7 +215,7 @@ describe API::AccessRequests do
context 'user_id matches a member, not an access requester' do
it 'returns 404' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{developer.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{developer.id}", maintainer)
expect(response).to have_gitlab_http_status(404)
end.not_to change { source.requesters.count }
@@ -225,7 +225,7 @@ describe API::AccessRequests do
context 'user_id does not match an existing access requester' do
it 'returns 404' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{stranger.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{stranger.id}", maintainer)
expect(response).to have_gitlab_http_status(404)
end.not_to change { source.requesters.count }
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index 5adfb33677f..0921fecb933 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -10,7 +10,7 @@ describe API::AwardEmoji do
set(:note) { create(:note, project: project, noteable: issue) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "GET /projects/:id/awardable/:awardable_id/award_emoji" do
diff --git a/spec/requests/api/badges_spec.rb b/spec/requests/api/badges_spec.rb
index ae64a9ca162..e232e2e04ee 100644
--- a/spec/requests/api/badges_spec.rb
+++ b/spec/requests/api/badges_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe API::Badges do
- let(:master) { create(:user, username: 'master_user') }
+ let(:maintainer) { create(:user, username: 'maintainer_user') }
let(:developer) { create(:user) }
let(:access_requester) { create(:user) }
let(:stranger) { create(:user) }
@@ -25,7 +25,7 @@ describe API::Badges do
let(:route) { get api("/#{source_type.pluralize}/#{source.id}/badges", stranger) }
end
- %i[master developer access_requester stranger].each do |type|
+ %i[maintainer developer access_requester stranger].each do |type|
context "when authenticated as a #{type}" do
it 'returns 200' do
user = public_send(type)
@@ -43,16 +43,16 @@ describe API::Badges do
it 'avoids N+1 queries' do
# Establish baseline
- get api("/#{source_type.pluralize}/#{source.id}/badges", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges", maintainer)
control = ActiveRecord::QueryRecorder.new do
- get api("/#{source_type.pluralize}/#{source.id}/badges", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges", maintainer)
end
project.add_developer(create(:user))
expect do
- get api("/#{source_type.pluralize}/#{source.id}/badges", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges", maintainer)
end.not_to exceed_query_limit(control)
end
end
@@ -69,7 +69,7 @@ describe API::Badges do
end
context 'when authenticated as a non-member' do
- %i[master developer access_requester stranger].each do |type|
+ %i[maintainer developer access_requester stranger].each do |type|
let(:badge) { source.badges.first }
context "as a #{type}" do
@@ -122,10 +122,10 @@ describe API::Badges do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'creates a new badge' do
expect do
- post api("/#{source_type.pluralize}/#{source.id}/badges", master),
+ post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
link_url: example_url, image_url: example_url2
expect(response).to have_gitlab_http_status(201)
@@ -138,21 +138,21 @@ describe API::Badges do
end
it 'returns 400 when link_url is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/badges", master),
+ post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
link_url: example_url
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when image_url is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/badges", master),
+ post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
image_url: example_url2
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when link_url or image_url is not valid' do
- post api("/#{source_type.pluralize}/#{source.id}/badges", master),
+ post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
link_url: 'whatever', image_url: 'whatever'
expect(response).to have_gitlab_http_status(400)
@@ -192,9 +192,9 @@ describe API::Badges do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'updates the member' do
- put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master),
+ put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer),
link_url: example_url, image_url: example_url2
expect(response).to have_gitlab_http_status(200)
@@ -205,7 +205,7 @@ describe API::Badges do
end
it 'returns 400 when link_url or image_url is not valid' do
- put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master),
+ put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer),
link_url: 'whatever', image_url: 'whatever'
expect(response).to have_gitlab_http_status(400)
@@ -239,22 +239,22 @@ describe API::Badges do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'deletes the badge' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer)
expect(response).to have_gitlab_http_status(204)
end.to change { source.badges.count }.by(-1)
end
it_behaves_like '412 response' do
- let(:request) { api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master) }
+ let(:request) { api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer) }
end
end
it 'returns 404 if badge does not exist' do
- delete api("/#{source_type.pluralize}/#{source.id}/badges/123", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/badges/123", maintainer)
expect(response).to have_gitlab_http_status(404)
end
@@ -289,9 +289,9 @@ describe API::Badges do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'gets the rendered badge values' do
- get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", maintainer)
expect(response).to have_gitlab_http_status(200)
@@ -304,19 +304,19 @@ describe API::Badges do
end
it 'returns 400 when link_url is not given' do
- get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}", maintainer)
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when image_url is not given' do
- get api("/#{source_type.pluralize}/#{source.id}/badges/render?image_url=#{example_url}", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges/render?image_url=#{example_url}", maintainer)
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when link_url or image_url is not valid' do
- get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=whatever&image_url=whatever", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=whatever&image_url=whatever", maintainer)
expect(response).to have_gitlab_http_status(400)
end
@@ -326,7 +326,7 @@ describe API::Badges do
context 'when deleting a badge' do
context 'and the source is a project' do
it 'cannot delete badges owned by the project group' do
- delete api("/projects/#{project.id}/badges/#{project_group.badges.first.id}", master)
+ delete api("/projects/#{project.id}/badges/#{project_group.badges.first.id}", maintainer)
expect(response).to have_gitlab_http_status(403)
end
@@ -345,9 +345,9 @@ describe API::Badges do
end
def setup_project
- create(:project, :public, :access_requestable, creator_id: master.id, namespace: project_group) do |project|
+ create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: project_group) do |project|
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.request_access(access_requester)
project.project_badges << build(:project_badge, project: project)
project.project_badges << build(:project_badge, project: project)
@@ -358,7 +358,7 @@ describe API::Badges do
def setup_group
create(:group, :public, :access_requestable) do |group|
group.add_developer(developer)
- group.add_owner(master)
+ group.add_owner(maintainer)
group.request_access(access_requester)
group.badges << build(:group_badge, group: group)
group.badges << build(:group_badge, group: group)
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 9bb6ed62393..7fff0a6cce6 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -13,7 +13,7 @@ describe API::Branches do
let(:current_user) { nil }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "GET /projects/:id/repository/branches" do
@@ -75,7 +75,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository branches'
@@ -170,7 +170,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository branch'
@@ -324,7 +324,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
context "when a protected branch doesn't already exist" do
@@ -381,8 +381,8 @@ describe API::Branches do
expect(json_response['protected']).to eq(true)
expect(json_response['developers_can_push']).to eq(false)
expect(json_response['developers_can_merge']).to eq(false)
- expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MASTER)
- expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MASTER)
+ expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MAINTAINER)
+ expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -458,7 +458,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
context "when a protected branch doesn't already exist" do
@@ -534,7 +534,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
context "when a protected branch doesn't already exist" do
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index e73d1a252f5..246947e58a8 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -12,7 +12,7 @@ describe API::Commits do
let(:current_user) { nil }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/repository/commits' do
@@ -55,7 +55,7 @@ describe API::Commits do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'project commits'
@@ -514,6 +514,38 @@ describe API::Commits do
expect(response).to have_gitlab_http_status(400)
end
end
+
+ context 'when committing into a fork as a maintainer' do
+ include_context 'merge request allowing collaboration'
+
+ let(:project_id) { forked_project.id }
+
+ def push_params(branch_name)
+ {
+ branch: branch_name,
+ commit_message: 'Hello world',
+ actions: [
+ {
+ action: 'create',
+ file_path: 'foo/bar/baz.txt',
+ content: 'puts 8'
+ }
+ ]
+ }
+ end
+
+ it 'allows pushing to the source branch of the merge request' do
+ post api(url, user), push_params('feature')
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ it 'denies pushing to another branch' do
+ post api(url, user), push_params('other-branch')
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
end
describe 'GET /projects/:id/repository/commits/:sha/refs' do
@@ -667,7 +699,7 @@ describe API::Commits do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'ref commit'
@@ -785,7 +817,7 @@ describe API::Commits do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'ref diff'
@@ -884,7 +916,7 @@ describe API::Commits do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'ref comments'
@@ -1065,11 +1097,29 @@ describe API::Commits do
it 'returns 400 if you are not allowed to push to the target branch' do
post api(route, current_user), branch: 'feature'
- expect(response).to have_gitlab_http_status(400)
- expect(json_response['message']).to eq('You are not allowed to push into this branch')
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to match(/You are not allowed to push into this branch/)
end
end
end
+
+ context 'when cherry picking to a fork as a maintainer' do
+ include_context 'merge request allowing collaboration'
+
+ let(:project_id) { forked_project.id }
+
+ it 'allows access from a maintainer that to the source branch' do
+ post api(route, user), branch: 'feature'
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ it 'denies cherry picking to another branch' do
+ post api(route, user), branch: 'master'
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
end
describe 'POST /projects/:id/repository/commits/:sha/comments' do
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index 51b70fda148..61ae053cea7 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -5,7 +5,7 @@ describe API::Deployments do
let(:non_member) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/deployments' do
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index fdddca5d0ef..f3db0c122a0 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -7,7 +7,7 @@ describe API::Environments do
let!(:environment) { create(:environment, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/environments' do
@@ -20,7 +20,7 @@ describe API::Environments do
path path_with_namespace
star_count forks_count
created_at last_activity_at
- avatar_url
+ avatar_url namespace
)
get api("/projects/#{project.id}/environments", user)
@@ -126,7 +126,7 @@ describe API::Environments do
end
describe 'DELETE /projects/:id/environments/:environment_id' do
- context 'as a master' do
+ context 'as a maintainer' do
it 'returns a 200 for an existing environment' do
delete api("/projects/#{project.id}/environments/#{environment.id}", user)
@@ -155,7 +155,7 @@ describe API::Environments do
end
describe 'POST /projects/:id/environments/:environment_id/stop' do
- context 'as a master' do
+ context 'as a maintainer' do
context 'with a stoppable environment' do
before do
environment.update(state: :available)
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 4bc5d3ee899..0aec186f738 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -13,6 +13,24 @@ describe API::Files do
let(:author_email) { 'user@example.org' }
let(:author_name) { 'John Doe' }
+ let(:helper) do
+ fake_class = Class.new do
+ include ::API::Helpers::HeadersHelpers
+
+ attr_reader :headers
+
+ def initialize
+ @headers = {}
+ end
+
+ def header(key, value)
+ @headers[key] = value
+ end
+ end
+
+ fake_class.new
+ end
+
before do
project.add_developer(user)
end
@@ -21,6 +39,18 @@ describe API::Files do
"/projects/#{project.id}/repository/files/#{file_path}"
end
+ context 'http headers' do
+ it 'converts value into string' do
+ helper.set_http_headers(test: 1)
+
+ expect(helper.headers).to eq({ 'X-Gitlab-Test' => '1' })
+ end
+
+ it 'raises exception if value is an Enumerable' do
+ expect { helper.set_http_headers(test: [1]) }.to raise_error(ArgumentError)
+ end
+ end
+
describe "HEAD /projects/:id/repository/files/:file_path" do
shared_examples_for 'repository files' do
it 'returns file attributes in headers' do
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb
new file mode 100644
index 00000000000..8f427d71a32
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe 'Setting WIP status of a merge request' do
+ include GraphqlHelpers
+
+ let(:current_user) { create(:user) }
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
+ let(:input) { { wip: true } }
+
+ let(:mutation) do
+ variables = {
+ project_path: project.full_path,
+ iid: merge_request.iid
+ }
+ graphql_mutation(:merge_request_set_wip, variables.merge(input))
+ end
+
+ def mutation_response
+ graphql_mutation_response(:merge_request_set_wip)
+ end
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ it 'returns an error if the user is not allowed to update the merge request' do
+ post_graphql_mutation(mutation, current_user: create(:user))
+
+ expect(graphql_errors).not_to be_empty
+ end
+
+ it 'marks the merge request as WIP' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).to start_with('WIP:')
+ end
+
+ it 'does not do anything if the merge request was already marked `WIP`' do
+ merge_request.update!(title: 'wip: hello world')
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).to start_with('wip:')
+ end
+
+ context 'when passing WIP false as input' do
+ let(:input) { { wip: false } }
+
+ it 'does not do anything if the merge reqeust was not marked wip' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).not_to start_with(/wip\:/)
+ end
+
+ it 'unmarks the merge request as `WIP`' do
+ merge_request.update!(title: 'wip: hello world')
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).not_to start_with('/wip\:/')
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb
index ad57c43bc87..deb6abbc026 100644
--- a/spec/requests/api/graphql/project/merge_request_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request_spec.rb
@@ -67,4 +67,28 @@ describe 'getting merge request information nested in a project' do
expect(merge_request_graphql_data).to be_nil
end
end
+
+ context 'when there are pipelines' do
+ before do
+ pipeline = create(
+ :ci_pipeline,
+ project: merge_request.source_project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha
+ )
+ merge_request.update!(head_pipeline: pipeline)
+ end
+
+ it 'has a head pipeline' do
+ post_graphql(query, current_user: current_user)
+
+ expect(merge_request_graphql_data['headPipeline']).to be_present
+ end
+
+ it 'has pipeline connections' do
+ post_graphql(query, current_user: current_user)
+
+ expect(merge_request_graphql_data['pipelines']['edges'].size).to eq(1)
+ end
+ end
end
diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb
index a2b3dc5d121..0727ada4691 100644
--- a/spec/requests/api/graphql/project_query_spec.rb
+++ b/spec/requests/api/graphql/project_query_spec.rb
@@ -26,6 +26,18 @@ describe 'getting project information' do
post_graphql(query, current_user: current_user)
end
end
+
+ context 'when there are pipelines present' do
+ before do
+ create(:ci_pipeline, project: project)
+ end
+
+ 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)
+ end
+ end
end
context 'when the user does not have access to the project' do
diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
index 64fa7dc824c..f87e035c89d 100644
--- a/spec/requests/api/group_variables_spec.rb
+++ b/spec/requests/api/group_variables_spec.rb
@@ -9,7 +9,7 @@ describe API::GroupVariables do
context 'authorized user with proper permissions' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'returns group variables' do
@@ -42,7 +42,7 @@ describe API::GroupVariables do
context 'authorized user with proper permissions' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'returns group variable details' do
@@ -82,7 +82,7 @@ describe API::GroupVariables do
let!(:variable) { create(:ci_group_variable, group: group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'creates variable' do
@@ -138,7 +138,7 @@ describe API::GroupVariables do
context 'authorized user with proper permissions' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'updates variable data' do
@@ -184,7 +184,7 @@ describe API::GroupVariables do
context 'authorized user with proper permissions' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'deletes variable' do
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index da23fdd7dca..3a8948f8477 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -215,7 +215,7 @@ describe API::Groups do
context 'when using owned in the request' do
it 'returns an array of groups the user owns' do
- group1.add_master(user2)
+ group1.add_maintainer(user2)
get api('/groups', user2), owned: true
@@ -226,6 +226,25 @@ describe API::Groups do
expect(json_response.first['name']).to eq(group2.name)
end
end
+
+ context 'when using min_access_level in the request' do
+ let!(:group3) { create(:group, :private) }
+ let(:response_groups) { json_response.map { |group| group['id'] } }
+
+ before do
+ group1.add_developer(user2)
+ group3.add_master(user2)
+ end
+
+ it 'returns an array of groups the user has at least master access' do
+ get api('/groups', user2), min_access_level: 40
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq([group2.id, group3.id])
+ end
+ end
end
describe "GET /groups/:id" do
@@ -251,14 +270,22 @@ describe API::Groups do
projects
end
+ def response_project_ids(json_response, key)
+ json_response[key].map do |project|
+ project['id'].to_i
+ end
+ end
+
context 'when unauthenticated' do
it 'returns 404 for a private group' do
get api("/groups/#{group2.id}")
+
expect(response).to have_gitlab_http_status(404)
end
it 'returns 200 for a public group' do
get api("/groups/#{group1.id}")
+
expect(response).to have_gitlab_http_status(200)
end
@@ -268,7 +295,7 @@ describe API::Groups do
get api("/groups/#{public_group.id}")
- expect(json_response['projects'].map { |p| p['id'].to_i })
+ expect(response_project_ids(json_response, 'projects'))
.to contain_exactly(projects[:public].id)
end
@@ -278,7 +305,7 @@ describe API::Groups do
get api("/groups/#{group1.id}")
- expect(json_response['shared_projects'].map { |p| p['id'].to_i })
+ expect(response_project_ids(json_response, 'shared_projects'))
.to contain_exactly(projects[:public].id)
end
end
@@ -309,6 +336,17 @@ describe API::Groups do
expect(json_response['shared_projects'][0]['id']).to eq(project.id)
end
+ it "returns one of user1's groups without projects when with_projects option is set to false" do
+ project = create(:project, namespace: group2, path: 'Foo')
+ create(:project_group_link, project: project, group: group1)
+
+ get api("/groups/#{group1.id}", user1), with_projects: false
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['projects']).to be_nil
+ expect(json_response['shared_projects']).to be_nil
+ end
+
it "does not return a non existing group" do
get api("/groups/1328", user1)
@@ -327,7 +365,7 @@ describe API::Groups do
get api("/groups/#{public_group.id}", user2)
- expect(json_response['projects'].map { |p| p['id'].to_i })
+ expect(response_project_ids(json_response, 'projects'))
.to contain_exactly(projects[:public].id, projects[:internal].id)
end
@@ -337,7 +375,7 @@ describe API::Groups do
get api("/groups/#{group1.id}", user2)
- expect(json_response['shared_projects'].map { |p| p['id'].to_i })
+ expect(response_project_ids(json_response, 'shared_projects'))
.to contain_exactly(projects[:public].id, projects[:internal].id)
end
end
@@ -715,9 +753,9 @@ describe API::Groups do
end
end
- context 'as master', :nested_groups do
+ context 'as maintainer', :nested_groups do
before do
- group2.add_master(user1)
+ group2.add_maintainer(user1)
end
it 'cannot create subgroups' do
@@ -793,7 +831,7 @@ describe API::Groups do
it "does not remove a group if not an owner" do
user4 = create(:user)
- group1.add_master(user4)
+ group1.add_maintainer(user4)
delete api("/groups/#{group1.id}", user3)
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index d8a51f36dba..0a789d58fd8 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -201,7 +201,7 @@ describe API::Helpers do
end
it 'does not allow expired tokens' do
- personal_access_token.update_attributes!(expires_at: 1.day.ago)
+ personal_access_token.update!(expires_at: 1.day.ago)
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error Gitlab::Auth::ExpiredError
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index a56b913198c..85c93f35c20 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -152,7 +152,7 @@ describe API::Internal do
context 'user key' do
it 'returns the correct information about the key' do
- lfs_auth(key.id, project)
+ lfs_auth_key(key.id, project)
expect(response).to have_gitlab_http_status(200)
expect(json_response['username']).to eq(user.username)
@@ -161,8 +161,30 @@ describe API::Internal do
expect(json_response['repository_http_path']).to eq(project.http_url_to_repo)
end
+ it 'returns the correct information about the user' do
+ lfs_auth_user(user.id, project)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['username']).to eq(user.username)
+ expect(json_response['lfs_token']).to eq(Gitlab::LfsToken.new(user).token)
+
+ expect(json_response['repository_http_path']).to eq(project.http_url_to_repo)
+ end
+
+ it 'returns a 404 when no key or user is provided' do
+ lfs_auth_project(project)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
it 'returns a 404 when the wrong key is provided' do
- lfs_auth(nil, project)
+ lfs_auth_key(key.id + 12345, project)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns a 404 when the wrong user is provided' do
+ lfs_auth_user(user.id + 12345, project)
expect(response).to have_gitlab_http_status(404)
end
@@ -172,7 +194,7 @@ describe API::Internal do
let(:key) { create(:deploy_key) }
it 'returns the correct information about the key' do
- lfs_auth(key.id, project)
+ lfs_auth_key(key.id, project)
expect(response).to have_gitlab_http_status(200)
expect(json_response['username']).to eq("lfs+deploy-key-#{key.id}")
@@ -183,13 +205,29 @@ describe API::Internal do
end
describe "GET /internal/discover" do
- it do
+ it "finds a user by key id" do
get(api("/internal/discover"), key_id: key.id, secret_token: secret_token)
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq(user.name)
end
+
+ it "finds a user by user id" do
+ get(api("/internal/discover"), user_id: user.id, secret_token: secret_token)
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(json_response['name']).to eq(user.name)
+ end
+
+ it "finds a user by username" do
+ get(api("/internal/discover"), username: user.username, secret_token: secret_token)
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(json_response['name']).to eq(user.name)
+ end
end
describe "GET /internal/authorized_keys" do
@@ -279,7 +317,7 @@ describe API::Internal do
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
- expect(user).not_to have_an_activity_record
+ expect(user.reload.last_activity_on).to be_nil
end
end
@@ -291,7 +329,7 @@ describe API::Internal do
expect(json_response["status"]).to be_truthy
expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
- expect(user).to have_an_activity_record
+ expect(user.reload.last_activity_on).to eql(Date.today)
end
end
@@ -309,7 +347,7 @@ describe API::Internal do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
- expect(user).to have_an_activity_record
+ expect(user.reload.last_activity_on).to eql(Date.today)
end
end
@@ -328,7 +366,7 @@ describe API::Internal do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
- expect(user).not_to have_an_activity_record
+ expect(user.reload.last_activity_on).to be_nil
end
end
end
@@ -345,7 +383,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_falsey
- expect(user).not_to have_an_activity_record
+ expect(user.reload.last_activity_on).to be_nil
end
end
@@ -355,7 +393,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_falsey
- expect(user).not_to have_an_activity_record
+ expect(user.reload.last_activity_on).to be_nil
end
end
end
@@ -373,7 +411,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_falsey
- expect(user).not_to have_an_activity_record
+ expect(user.reload.last_activity_on).to be_nil
end
end
@@ -383,7 +421,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_falsey
- expect(user).not_to have_an_activity_record
+ expect(user.reload.last_activity_on).to be_nil
end
end
end
@@ -391,7 +429,7 @@ describe API::Internal do
context "archived project" do
before do
project.add_developer(user)
- project.archive!
+ ::Projects::UpdateService.new(project, user, archived: true).execute
end
context "git pull" do
@@ -871,7 +909,15 @@ describe API::Internal do
)
end
- def lfs_auth(key_id, project)
+ def lfs_auth_project(project)
+ post(
+ api("/internal/lfs_authenticate"),
+ secret_token: secret_token,
+ project: project.full_path
+ )
+ end
+
+ def lfs_auth_key(key_id, project)
post(
api("/internal/lfs_authenticate"),
key_id: key_id,
@@ -879,4 +925,13 @@ describe API::Internal do
project: project.full_path
)
end
+
+ def lfs_auth_user(user_id, project)
+ post(
+ api("/internal/lfs_authenticate"),
+ user_id: user_id,
+ secret_token: secret_token,
+ project: project.full_path
+ )
+ end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 95eff029f98..28ba00c7293 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -1002,6 +1002,38 @@ describe API::Issues do
end
end
+ context 'an internal ID is provided' do
+ context 'by an admin' do
+ it 'sets the internal ID on the new issue' do
+ post api("/projects/#{project.id}/issues", admin),
+ title: 'new issue', iid: 9001
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['iid']).to eq 9001
+ end
+ end
+
+ context 'by an owner' do
+ it 'sets the internal ID on the new issue' do
+ post api("/projects/#{project.id}/issues", user),
+ title: 'new issue', iid: 9001
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['iid']).to eq 9001
+ end
+ end
+
+ context 'by another user' do
+ it 'ignores the given internal ID' do
+ post api("/projects/#{project.id}/issues", user2),
+ title: 'new issue', iid: 9001
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['iid']).not_to eq 9001
+ end
+ end
+ end
+
it 'creates a new project issue' do
post api("/projects/#{project.id}/issues", user),
title: 'new issue', labels: 'label, label2', weight: 3,
@@ -1083,7 +1115,7 @@ describe API::Issues do
let(:project) { merge_request.source_project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'resolving all discussions in a merge request' do
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 50d6f4b4d99..5814d834572 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -220,6 +220,7 @@ describe API::Jobs do
expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at)
expect(Time.parse(json_response['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
expect(json_response['duration']).to eq(job.duration)
+ expect(json_response['web_url']).to be_present
end
it 'returns pipeline data' do
@@ -535,12 +536,14 @@ describe API::Jobs do
context 'authorized user' do
context 'when trace is in ObjectStorage' do
let!(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }
+ let(:url) { 'http://object-storage/trace' }
+ let(:file_path) { expand_fixture_path('trace/sample_trace') }
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
allow_any_instance_of(JobArtifactUploader).to receive(:file_storage?) { false }
- allow_any_instance_of(JobArtifactUploader).to receive(:url) { remote_trace_url }
- allow_any_instance_of(JobArtifactUploader).to receive(:size) { remote_trace_size }
+ allow_any_instance_of(JobArtifactUploader).to receive(:url) { url }
+ allow_any_instance_of(JobArtifactUploader).to receive(:size) { File.size(file_path) }
end
it 'returns specific job trace' do
@@ -643,7 +646,7 @@ describe API::Jobs do
end
describe 'POST /projects/:id/jobs/:job_id/erase' do
- let(:role) { :master }
+ let(:role) { :maintainer }
before do
project.add_role(user, role)
@@ -652,13 +655,15 @@ describe API::Jobs do
end
context 'job is erasable' do
- let(:job) { create(:ci_build, :trace_artifact, :artifacts, :success, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace_artifact, :artifacts, :test_reports, :success, project: project, pipeline: pipeline) }
it 'erases job content' do
expect(response).to have_gitlab_http_status(201)
+ expect(job.job_artifacts.count).to eq(0)
expect(job.trace.exist?).to be_falsy
expect(job.artifacts_file.exists?).to be_falsy
expect(job.artifacts_metadata.exists?).to be_falsy
+ expect(job.has_test_reports?).to be_falsy
end
it 'updates job' do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 34cbf75f4c1..a4220f5b2be 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -7,7 +7,7 @@ describe API::Labels do
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/labels' do
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index ec500838eb2..93e1c3a2294 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -1,15 +1,15 @@
require 'spec_helper'
describe API::Members do
- let(:master) { create(:user, username: 'master_user') }
+ let(:maintainer) { create(:user, username: 'maintainer_user') }
let(:developer) { create(:user) }
let(:access_requester) { create(:user) }
let(:stranger) { create(:user) }
let(:project) do
- create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
+ create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.request_access(access_requester)
end
end
@@ -17,84 +17,133 @@ describe API::Members do
let!(:group) do
create(:group, :public, :access_requestable) do |group|
group.add_developer(developer)
- group.add_owner(master)
+ group.add_owner(maintainer)
group.request_access(access_requester)
end
end
- shared_examples 'GET /:sources/:id/members' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ shared_examples 'GET /:source_type/:id/members/(all)' do |source_type, all|
+ let(:members_url) do
+ "/#{source_type.pluralize}/#{source.id}/members".tap do |url|
+ url << "/all" if all
+ end
+ end
+
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
- let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members", stranger) }
+ let(:route) { get api(members_url, stranger) }
end
- %i[master developer access_requester stranger].each do |type|
+ %i[maintainer developer access_requester stranger].each do |type|
context "when authenticated as a #{type}" do
it 'returns 200' do
user = public_send(type)
- get api("/#{source_type.pluralize}/#{source.id}/members", user)
+ get api(members_url, user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(2)
- expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id]
end
end
end
it 'avoids N+1 queries' do
# Establish baseline
- get api("/#{source_type.pluralize}/#{source.id}/members", master)
+ get api(members_url, maintainer)
control = ActiveRecord::QueryRecorder.new do
- get api("/#{source_type.pluralize}/#{source.id}/members", master)
+ get api(members_url, maintainer)
end
project.add_developer(create(:user))
expect do
- get api("/#{source_type.pluralize}/#{source.id}/members", master)
+ get api(members_url, maintainer)
end.not_to exceed_query_limit(control)
end
it 'does not return invitees' do
create(:"#{source_type}_member", invite_token: '123', invite_email: 'test@abc.com', source: source, user: nil)
- get api("/#{source_type.pluralize}/#{source.id}/members", developer)
+ get api(members_url, developer)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(2)
- expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id]
end
it 'finds members with query string' do
- get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: master.username
+ get api(members_url, developer), query: maintainer.username
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.count).to eq(1)
- expect(json_response.first['username']).to eq(master.username)
+ expect(json_response.first['username']).to eq(maintainer.username)
end
it 'finds all members with no query specified' do
- get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: ''
+ get api(members_url, developer), query: ''
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.count).to eq(2)
- expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id]
end
end
end
- shared_examples 'GET /:sources/:id/members/:user_id' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ describe 'GET /:source_type/:id/members/all', :nested_groups do
+ let(:nested_user) { create(:user) }
+ let(:project_user) { create(:user) }
+ let(:linked_group_user) { create(:user) }
+ let!(:project_group_link) { create(:project_group_link, project: project, group: linked_group) }
+
+ let(:project) do
+ create(:project, :public, group: nested_group) do |project|
+ project.add_developer(project_user)
+ end
+ end
+
+ let(:linked_group) do
+ create(:group) do |linked_group|
+ linked_group.add_developer(linked_group_user)
+ end
+ end
+
+ let(:nested_group) do
+ create(:group, parent: group) do |nested_group|
+ nested_group.add_developer(nested_user)
+ end
+ end
+
+ it 'finds all project members including inherited members' do
+ get api("/projects/#{project.id}/members/all", developer)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id, nested_user.id, project_user.id, linked_group_user.id]
+ end
+
+ it 'finds all group members including inherited members' do
+ get api("/groups/#{nested_group.id}/members/all", developer)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id, nested_user.id]
+ end
+ end
+
+ shared_examples 'GET /:source_type/:id/members/:user_id' do |source_type|
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger) }
end
@@ -124,12 +173,12 @@ describe API::Members do
end
end
- shared_examples 'POST /:sources/:id/members' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ shared_examples 'POST /:source_type/:id/members' do |source_type|
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
let(:route) do
post api("/#{source_type.pluralize}/#{source.id}/members", stranger),
- user_id: access_requester.id, access_level: Member::MASTER
+ user_id: access_requester.id, access_level: Member::MAINTAINER
end
end
@@ -139,7 +188,7 @@ describe API::Members do
it 'returns 403' do
user = public_send(type)
post api("/#{source_type.pluralize}/#{source.id}/members", user),
- user_id: access_requester.id, access_level: Member::MASTER
+ user_id: access_requester.id, access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(403)
end
@@ -147,24 +196,24 @@ describe API::Members do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
context 'and new member is already a requester' do
it 'transforms the requester into a proper member' do
expect do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
- user_id: access_requester.id, access_level: Member::MASTER
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ user_id: access_requester.id, access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(201)
end.to change { source.members.count }.by(1)
expect(source.requesters.count).to eq(0)
expect(json_response['id']).to eq(access_requester.id)
- expect(json_response['access_level']).to eq(Member::MASTER)
+ expect(json_response['access_level']).to eq(Member::MAINTAINER)
end
end
it 'creates a new member' do
expect do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
user_id: stranger.id, access_level: Member::DEVELOPER, expires_at: '2016-08-05'
expect(response).to have_gitlab_http_status(201)
@@ -176,28 +225,36 @@ describe API::Members do
end
it "returns 409 if member already exists" do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
- user_id: master.id, access_level: Member::MASTER
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ user_id: maintainer.id, access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(409)
end
+ it 'returns 404 when the user_id is not valid' do
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ user_id: 0, access_level: Member::MAINTAINER
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+
it 'returns 400 when user_id is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
- access_level: Member::MASTER
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when access_level is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
user_id: stranger.id
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when access_level is not valid' do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
user_id: stranger.id, access_level: 1234
expect(response).to have_gitlab_http_status(400)
@@ -205,12 +262,12 @@ describe API::Members do
end
end
- shared_examples 'PUT /:sources/:id/members/:user_id' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ shared_examples 'PUT /:source_type/:id/members/:user_id' do |source_type|
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
let(:route) do
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger),
- access_level: Member::MASTER
+ access_level: Member::MAINTAINER
end
end
@@ -220,7 +277,7 @@ describe API::Members do
it 'returns 403' do
user = public_send(type)
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", user),
- access_level: Member::MASTER
+ access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(403)
end
@@ -228,33 +285,33 @@ describe API::Members do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'updates the member' do
- put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master),
- access_level: Member::MASTER, expires_at: '2016-08-05'
+ put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer),
+ access_level: Member::MAINTAINER, expires_at: '2016-08-05'
expect(response).to have_gitlab_http_status(200)
expect(json_response['id']).to eq(developer.id)
- expect(json_response['access_level']).to eq(Member::MASTER)
+ expect(json_response['access_level']).to eq(Member::MAINTAINER)
expect(json_response['expires_at']).to eq('2016-08-05')
end
end
it 'returns 409 if member does not exist' do
- put api("/#{source_type.pluralize}/#{source.id}/members/123", master),
- access_level: Member::MASTER
+ put api("/#{source_type.pluralize}/#{source.id}/members/123", maintainer),
+ access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(404)
end
it 'returns 400 when access_level is not given' do
- put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master)
+ put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer)
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when access level is not valid' do
- put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master),
+ put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer),
access_level: 1234
expect(response).to have_gitlab_http_status(400)
@@ -262,8 +319,8 @@ describe API::Members do
end
end
- shared_examples 'DELETE /:sources/:id/members/:user_id' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ shared_examples 'DELETE /:source_type/:id/members/:user_id' do |source_type|
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
let(:route) { delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger) }
end
@@ -291,11 +348,11 @@ describe API::Members do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
context 'and member is a requester' do
it 'returns 404' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/members/#{access_requester.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/members/#{access_requester.id}", maintainer)
expect(response).to have_gitlab_http_status(404)
end.not_to change { source.requesters.count }
@@ -304,69 +361,71 @@ describe API::Members do
it 'deletes the member' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer)
expect(response).to have_gitlab_http_status(204)
end.to change { source.members.count }.by(-1)
end
it_behaves_like '412 response' do
- let(:request) { api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master) }
+ let(:request) { api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer) }
end
end
it 'returns 404 if member does not exist' do
- delete api("/#{source_type.pluralize}/#{source.id}/members/123", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/members/123", maintainer)
expect(response).to have_gitlab_http_status(404)
end
end
end
- it_behaves_like 'GET /:sources/:id/members', 'project' do
- let(:source) { project }
- end
+ [false, true].each do |all|
+ it_behaves_like 'GET /:source_type/:id/members/(all)', 'project', all do
+ let(:source) { project }
+ end
- it_behaves_like 'GET /:sources/:id/members', 'group' do
- let(:source) { group }
+ it_behaves_like 'GET /:source_type/:id/members/(all)', 'group', all do
+ let(:source) { group }
+ end
end
- it_behaves_like 'GET /:sources/:id/members/:user_id', 'project' do
+ it_behaves_like 'GET /:source_type/:id/members/:user_id', 'project' do
let(:source) { project }
end
- it_behaves_like 'GET /:sources/:id/members/:user_id', 'group' do
+ it_behaves_like 'GET /:source_type/:id/members/:user_id', 'group' do
let(:source) { group }
end
- it_behaves_like 'POST /:sources/:id/members', 'project' do
+ it_behaves_like 'POST /:source_type/:id/members', 'project' do
let(:source) { project }
end
- it_behaves_like 'POST /:sources/:id/members', 'group' do
+ it_behaves_like 'POST /:source_type/:id/members', 'group' do
let(:source) { group }
end
- it_behaves_like 'PUT /:sources/:id/members/:user_id', 'project' do
+ it_behaves_like 'PUT /:source_type/:id/members/:user_id', 'project' do
let(:source) { project }
end
- it_behaves_like 'PUT /:sources/:id/members/:user_id', 'group' do
+ it_behaves_like 'PUT /:source_type/:id/members/:user_id', 'group' do
let(:source) { group }
end
- it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'project' do
+ it_behaves_like 'DELETE /:source_type/:id/members/:user_id', 'project' do
let(:source) { project }
end
- it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'group' do
+ it_behaves_like 'DELETE /:source_type/:id/members/:user_id', 'group' do
let(:source) { group }
end
context 'Adding owner to project' do
it 'returns 403' do
expect do
- post api("/projects/#{project.id}/members", master),
+ post api("/projects/#{project.id}/members", maintainer),
user_id: stranger.id, access_level: Member::OWNER
expect(response).to have_gitlab_http_status(400)
diff --git a/spec/requests/api/merge_request_diffs_spec.rb b/spec/requests/api/merge_request_diffs_spec.rb
index cb647aee70f..6530dc956cb 100644
--- a/spec/requests/api/merge_request_diffs_spec.rb
+++ b/spec/requests/api/merge_request_diffs_spec.rb
@@ -8,7 +8,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
before do
merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/merge_requests/:merge_request_iid/versions' do
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index d4ebfc3f782..4de834bf93a 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -14,6 +14,7 @@ describe API::MergeRequests do
let!(:merge_request) { create(:merge_request, :simple, milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) }
let!(:merge_request_closed) { create(:merge_request, state: "closed", milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) }
let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds, merge_commit_sha: '9999999999999999999999999999999999999999') }
+ let!(:merge_request_locked) { create(:merge_request, state: "locked", milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Locked test", created_at: base_time + 1.second) }
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
let!(:label) do
@@ -85,7 +86,7 @@ describe API::MergeRequests do
get api('/merge_requests', user), scope: :all
- expect_response_contain_exactly(merge_request2, merge_request_merged, merge_request_closed, merge_request)
+ expect_response_contain_exactly(merge_request2, merge_request_merged, merge_request_closed, merge_request, merge_request_locked)
expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request3.id)
end
@@ -158,7 +159,7 @@ describe API::MergeRequests do
it 'returns merge requests with the given source branch' do
get api('/merge_requests', user), source_branch: merge_request_closed.source_branch, state: 'all'
- expect_response_contain_exactly(merge_request_closed, merge_request_merged)
+ expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked)
end
end
@@ -166,7 +167,7 @@ describe API::MergeRequests do
it 'returns merge requests with the given target branch' do
get api('/merge_requests', user), target_branch: merge_request_closed.target_branch, state: 'all'
- expect_response_contain_exactly(merge_request_closed, merge_request_merged)
+ expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked)
end
end
@@ -219,6 +220,14 @@ describe API::MergeRequests do
expect_response_ordered_exactly(merge_request)
end
end
+
+ context 'state param' do
+ it 'returns merge requests with the given state' do
+ get api('/merge_requests', user), state: 'locked'
+
+ expect_response_contain_exactly(merge_request_locked)
+ end
+ end
end
end
@@ -297,6 +306,14 @@ describe API::MergeRequests do
expect(json_response['changes_count']).to eq(merge_request.merge_request_diff.real_size)
end
+ it 'exposes description and title html when render_html is true' do
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), render_html: true
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(json_response).to include('title_html', 'description_html')
+ end
+
context 'merge_request_metrics' do
before do
merge_request.metrics.update!(merged_by: user,
@@ -937,6 +954,7 @@ describe API::MergeRequests do
issue = create(:issue, project: project)
mr = merge_request.tap do |mr|
mr.update_attribute(:description, "Closes #{issue.to_reference(mr.project)}")
+ mr.cache_merge_request_closes_issues!
end
get api("/projects/#{project.id}/merge_requests/#{mr.iid}/closes_issues", user)
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index 98102fcd6a7..e2000ab42e8 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -23,10 +23,10 @@ describe API::Namespaces do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
- expect(group_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path',
- 'parent_id', 'members_count_with_descendants')
+ expect(group_kind_json_response.keys).to include('id', 'kind', 'name', 'path', 'full_path',
+ 'parent_id', 'members_count_with_descendants')
- expect(user_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
+ expect(user_kind_json_response.keys).to include('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
end
it "admin: returns an array of all namespaces" do
@@ -58,8 +58,8 @@ describe API::Namespaces do
owned_group_response = json_response.find { |resource| resource['id'] == group1.id }
- expect(owned_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path',
- 'parent_id', 'members_count_with_descendants')
+ expect(owned_group_response.keys).to include('id', 'kind', 'name', 'path', 'full_path',
+ 'parent_id', 'members_count_with_descendants')
end
it "returns correct attributes when user cannot admin group" do
@@ -69,7 +69,7 @@ describe API::Namespaces do
guest_group_response = json_response.find { |resource| resource['id'] == group1.id }
- expect(guest_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
+ expect(guest_group_response.keys).to include('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
end
it "user: returns an array of namespaces" do
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index dd568c24c72..3fb45449c74 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -44,7 +44,7 @@ describe API::Notes do
# For testing the cross-reference of a private issue in a public project
let(:private_project) do
create(:project, namespace: private_user.namespace)
- .tap { |p| p.add_master(private_user) }
+ .tap { |p| p.add_maintainer(private_user) }
end
let(:private_issue) { create(:issue, project: private_project) }
@@ -71,7 +71,7 @@ describe API::Notes do
context "issue is confidential" do
before do
- ext_issue.update_attributes(confidential: true)
+ ext_issue.update(confidential: true)
end
it "returns 404" do
@@ -104,7 +104,7 @@ describe API::Notes do
context "when issue is confidential" do
before do
- issue.update_attributes(confidential: true)
+ issue.update(confidential: true)
end
it "returns 404" do
diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb
index a9ccbb32666..35b6ed8d5c0 100644
--- a/spec/requests/api/pages_domains_spec.rb
+++ b/spec/requests/api/pages_domains_spec.rb
@@ -80,7 +80,7 @@ describe API::PagesDomains do
context 'when pages is disabled' do
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like '404 response' do
@@ -88,9 +88,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'get pages domains'
@@ -177,7 +177,7 @@ describe API::PagesDomains do
context 'when domain is vacant' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like '404 response' do
@@ -185,9 +185,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'get pages domain'
@@ -270,9 +270,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'post pages domains'
@@ -380,7 +380,7 @@ describe API::PagesDomains do
context 'when domain is vacant' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like '404 response' do
@@ -388,9 +388,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'put pages domain'
@@ -444,7 +444,7 @@ describe API::PagesDomains do
context 'when domain is vacant' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like '404 response' do
@@ -452,9 +452,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'delete pages domain'
diff --git a/spec/requests/api/pipeline_schedules_spec.rb b/spec/requests/api/pipeline_schedules_spec.rb
index 91d4d5d3de9..997d413eb4f 100644
--- a/spec/requests/api/pipeline_schedules_spec.rb
+++ b/spec/requests/api/pipeline_schedules_spec.rb
@@ -22,7 +22,7 @@ describe API::PipelineSchedules do
.each do |pipeline_schedule|
create(:user).tap do |user|
project.add_developer(user)
- pipeline_schedule.update_attributes(owner: user)
+ pipeline_schedule.update(owner: user)
end
pipeline_schedule.pipelines << build(:ci_pipeline, project: project)
end
@@ -270,38 +270,38 @@ describe API::PipelineSchedules do
end
describe 'DELETE /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let!(:pipeline_schedule) do
create(:ci_pipeline_schedule, project: project, owner: developer)
end
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
end
context 'authenticated user with valid permissions' do
it 'deletes pipeline_schedule' do
expect do
- delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", master)
+ delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", maintainer)
end.to change { project.pipeline_schedules.count }.by(-1)
expect(response).to have_gitlab_http_status(204)
end
it 'responds with 404 Not Found if requesting non-existing pipeline_schedule' do
- delete api("/projects/#{project.id}/pipeline_schedules/-5", master)
+ delete api("/projects/#{project.id}/pipeline_schedules/-5", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
it_behaves_like '412 response' do
- let(:request) { api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", master) }
+ let(:request) { api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", maintainer) }
end
end
context 'authenticated user with invalid permissions' do
- let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: master) }
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: maintainer) }
it 'does not delete pipeline_schedule' do
delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer)
@@ -415,7 +415,7 @@ describe API::PipelineSchedules do
end
describe 'DELETE /projects/:id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
set(:pipeline_schedule) do
create(:ci_pipeline_schedule, project: project, owner: developer)
@@ -426,13 +426,13 @@ describe API::PipelineSchedules do
end
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
end
context 'authenticated user with valid permissions' do
it 'deletes pipeline_schedule_variable' do
expect do
- delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/#{pipeline_schedule_variable.key}", master)
+ delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/#{pipeline_schedule_variable.key}", maintainer)
end.to change { Ci::PipelineScheduleVariable.count }.by(-1)
expect(response).to have_gitlab_http_status(:accepted)
@@ -440,14 +440,14 @@ describe API::PipelineSchedules do
end
it 'responds with 404 Not Found if requesting non-existing pipeline_schedule_variable' do
- delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/____", master)
+ delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/____", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'authenticated user with invalid permissions' do
- let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: master) }
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: maintainer) }
it 'does not delete pipeline_schedule_variable' do
delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/#{pipeline_schedule_variable.key}", developer)
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index 78ea77cb3bb..342a97b6a69 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -11,7 +11,7 @@ describe API::Pipelines do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/pipelines ' do
@@ -24,7 +24,8 @@ describe API::Pipelines do
expect(json_response).to be_an Array
expect(json_response.first['sha']).to match /\A\h{40}\z/
expect(json_response.first['id']).to eq pipeline.id
- expect(json_response.first.keys).to contain_exactly(*%w[id sha ref status])
+ expect(json_response.first['web_url']).to be_present
+ expect(json_response.first.keys).to contain_exactly(*%w[id sha ref status web_url])
end
context 'when parameter is passed' do
diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb
index 3834d27d0a9..45e4e35d773 100644
--- a/spec/requests/api/project_export_spec.rb
+++ b/spec/requests/api/project_export_spec.rb
@@ -109,13 +109,13 @@ describe API::ProjectExport do
it_behaves_like 'get project export status ok'
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
- project_none.add_master(user)
- project_started.add_master(user)
- project_finished.add_master(user)
- project_after_export.add_master(user)
+ project.add_maintainer(user)
+ project_none.add_maintainer(user)
+ project_started.add_maintainer(user)
+ project_finished.add_maintainer(user)
+ project_after_export.add_maintainer(user)
end
it_behaves_like 'get project export status ok'
@@ -192,6 +192,13 @@ describe API::ProjectExport do
context 'when upload complete' do
before do
FileUtils.rm_rf(project_after_export.export_path)
+
+ if project_after_export.export_project_object_exists?
+ upload = project_after_export.import_export_upload
+
+ upload.remove_export_file!
+ upload.save
+ end
end
it_behaves_like '404 response' do
@@ -221,13 +228,13 @@ describe API::ProjectExport do
it_behaves_like 'get project download by strategy'
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
- project_none.add_master(user)
- project_started.add_master(user)
- project_finished.add_master(user)
- project_after_export.add_master(user)
+ project.add_maintainer(user)
+ project_none.add_maintainer(user)
+ project_started.add_maintainer(user)
+ project_finished.add_maintainer(user)
+ project_after_export.add_maintainer(user)
end
it_behaves_like 'get project download by strategy'
@@ -261,6 +268,22 @@ describe API::ProjectExport do
it_behaves_like 'get project export download not found'
end
end
+
+ context 'when an uploader is used' do
+ before do
+ stub_uploads_object_storage(ImportExportUploader)
+
+ [project, project_finished, project_after_export].each do |p|
+ p.add_maintainer(user)
+
+ upload = ImportExportUpload.new(project: p)
+ upload.export_file = fixture_file_upload('spec/fixtures/project_export.tar.gz', "`/tar.gz")
+ upload.save!
+ end
+ end
+
+ it_behaves_like 'get project download by strategy'
+ end
end
describe 'POST /projects/:project_id/export' do
@@ -315,13 +338,13 @@ describe API::ProjectExport do
it_behaves_like 'post project export start'
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
- project_none.add_master(user)
- project_started.add_master(user)
- project_finished.add_master(user)
- project_after_export.add_master(user)
+ project.add_maintainer(user)
+ project_none.add_maintainer(user)
+ project_started.add_maintainer(user)
+ project_finished.add_maintainer(user)
+ project_after_export.add_maintainer(user)
end
it_behaves_like 'post project export start'
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index 12a183fed1e..bc45a63d9f1 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -13,7 +13,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user3)
end
@@ -214,7 +214,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
it "returns a 404 if a user attempts to delete project hooks he/she does not own" do
test_user = create(:user)
other_project = create(:project)
- other_project.add_master(test_user)
+ other_project.add_maintainer(test_user)
delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
expect(response).to have_gitlab_http_status(404)
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index 97dffdc9233..e3fb6cecce9 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -7,6 +7,8 @@ describe API::ProjectImport do
let(:namespace) { create(:group) }
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
namespace.add_owner(user)
end
@@ -102,7 +104,7 @@ describe API::ProjectImport do
it 'correctly overrides params during the import' do
override_params = { 'description' => 'Hello world' }
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
post api('/projects/import', user),
path: 'test-import',
file: fixture_file_upload(file),
@@ -146,7 +148,7 @@ describe API::ProjectImport do
describe 'GET /projects/:id/import' do
it 'returns the import status' do
project = create(:project, :import_started)
- project.add_master(user)
+ project.add_maintainer(user)
get api("/projects/#{project.id}/import", user)
@@ -156,8 +158,8 @@ describe API::ProjectImport do
it 'returns the import status and the error if failed' do
project = create(:project, :import_failed)
- project.add_master(user)
- project.import_state.update_attributes(last_error: 'error')
+ project.add_maintainer(user)
+ project.import_state.update(last_error: 'error')
get api("/projects/#{project.id}/import", user)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index abf9ad738bd..eb41750bf47 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -40,7 +40,7 @@ describe API::Projects do
create(:project_member,
user: user4,
project: project3,
- access_level: ProjectMember::MASTER)
+ access_level: ProjectMember::MAINTAINER)
end
let(:project4) do
create(:project,
@@ -225,7 +225,7 @@ describe API::Projects do
path path_with_namespace
star_count forks_count
created_at last_activity_at
- avatar_url
+ avatar_url namespace
)
get api('/projects?simple=true', user)
@@ -237,6 +237,39 @@ describe API::Projects do
end
end
+ context 'and using archived' do
+ let!(:archived_project) { create(:project, creator_id: user.id, namespace: user.namespace, archived: true) }
+
+ it 'returns archived projects' do
+ get api('/projects?archived=true', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(Project.public_or_visible_to_user(user).where(archived: true).size)
+ expect(json_response.map { |project| project['id'] }).to include(archived_project.id)
+ end
+
+ it 'returns non-archived projects' do
+ get api('/projects?archived=false', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(Project.public_or_visible_to_user(user).where(archived: false).size)
+ expect(json_response.map { |project| project['id'] }).not_to include(archived_project.id)
+ end
+
+ it 'returns every project' do
+ get api('/projects', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(*Project.public_or_visible_to_user(user).pluck(:id))
+ end
+ end
+
context 'and using search' do
it_behaves_like 'projects response' do
let(:filter) { { search: project.name } }
@@ -312,7 +345,7 @@ describe API::Projects do
before do
project_member
- user3.update_attributes(starred_projects: [project, project2, project3, public_project])
+ user3.update(starred_projects: [project, project2, project3, public_project])
end
it 'returns the starred projects viewable by the user' do
@@ -333,7 +366,7 @@ describe API::Projects do
let!(:project9) { create(:project, :public, path: 'gitlab9') }
before do
- user.update_attributes(starred_projects: [project5, project7, project8, project9])
+ user.update(starred_projects: [project5, project7, project8, project9])
end
context 'including owned filter' do
@@ -353,7 +386,7 @@ describe API::Projects do
create(:project_member,
user: user,
project: project5,
- access_level: ProjectMember::MASTER)
+ access_level: ProjectMember::MAINTAINER)
end
it 'returns only projects that satisfy all query parameters' do
@@ -367,6 +400,22 @@ describe API::Projects do
end
end
end
+
+ context 'and with min_access_level' do
+ before do
+ project2.add_master(user2)
+ project3.add_developer(user2)
+ project4.add_reporter(user2)
+ end
+
+ it 'returns an array of groups the user has at least developer access' do
+ get api('/projects', user2), { min_access_level: 30 }
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(project2.id, project3.id)
+ end
+ end
end
context 'when authenticated as a different user' do
@@ -648,6 +697,20 @@ describe API::Projects do
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
end
+
+ it 'returns projects filetered by minimal access level' do
+ private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace)
+ private_project2 = create(:project, :private, name: 'private_project2', creator_id: user4.id, namespace: user4.namespace)
+ private_project1.add_developer(user2)
+ private_project2.add_reporter(user2)
+
+ get api("/users/#{user4.id}/projects/", user2), { min_access_level: 30 }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(private_project1.id)
+ end
end
describe 'POST /projects/user/:id' do
@@ -961,7 +1024,7 @@ describe API::Projects do
describe 'permissions' do
context 'all projects' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'contains permission information' do
@@ -969,19 +1032,19 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(200)
expect(json_response.first['permissions']['project_access']['access_level'])
- .to eq(Gitlab::Access::MASTER)
+ .to eq(Gitlab::Access::MAINTAINER)
expect(json_response.first['permissions']['group_access']).to be_nil
end
end
context 'personal project' do
it 'sets project access and returns 200' do
- project.add_master(user)
+ project.add_maintainer(user)
get api("/projects/#{project.id}", user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['permissions']['project_access']['access_level'])
- .to eq(Gitlab::Access::MASTER)
+ .to eq(Gitlab::Access::MAINTAINER)
expect(json_response['permissions']['group_access']).to be_nil
end
end
@@ -1451,7 +1514,7 @@ describe API::Projects do
end
it 'updates visibility_level from public to private' do
- project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
+ project3.update({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
project_param = { visibility: 'private' }
put api("/projects/#{project3.id}", user), project_param
@@ -1526,9 +1589,23 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(400)
end
+
+ it 'updates avatar' do
+ project_param = {
+ avatar: fixture_file_upload('spec/fixtures/banana_sample.gif',
+ 'image/gif')
+ }
+
+ put api("/projects/#{project3.id}", user), project_param
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
+ '-/system/project/avatar/'\
+ "#{project3.id}/banana_sample.gif")
+ end
end
- context 'when authenticated as project master' do
+ context 'when authenticated as project maintainer' do
it 'updates path' do
project_param = { path: 'bar' }
put api("/projects/#{project3.id}", user4), project_param
@@ -1600,7 +1677,7 @@ describe API::Projects do
context 'on an archived project' do
before do
- project.archive!
+ ::Projects::UpdateService.new(project, user, archived: true).execute
end
it 'remains archived' do
@@ -1636,7 +1713,7 @@ describe API::Projects do
context 'on an archived project' do
before do
- project.archive!
+ ::Projects::UpdateService.new(project, user, archived: true).execute
end
it 'unarchives the project' do
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
index 576fde46615..69a601d7b40 100644
--- a/spec/requests/api/protected_branches_spec.rb
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -26,9 +26,9 @@ describe API::ProtectedBranches do
end
end
- context 'when authenticated as a master' do
+ context 'when authenticated as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'protected branches'
@@ -54,8 +54,8 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq(branch_name)
- expect(json_response['push_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MASTER)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MAINTAINER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MAINTAINER)
end
context 'when protected branch does not exist' do
@@ -68,9 +68,9 @@ describe API::ProtectedBranches do
end
end
- context 'when authenticated as a master' do
+ context 'when authenticated as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'protected branch'
@@ -108,9 +108,9 @@ describe API::ProtectedBranches do
expect(json_response['name']).to eq(branch_name)
end
- context 'when authenticated as a master' do
+ context 'when authenticated as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'protects a single branch' do
@@ -118,8 +118,8 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
- expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
end
it 'protects a single branch and developers can push' do
@@ -128,7 +128,7 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
end
it 'protects a single branch and developers can merge' do
@@ -136,7 +136,7 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
- expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
end
@@ -155,7 +155,7 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
end
it 'protects a single branch and no one can merge' do
@@ -163,7 +163,7 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
- expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
end
@@ -189,8 +189,8 @@ describe API::ProtectedBranches do
post post_endpoint, name: branch_name
expect_protection_to_be_successful
- expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -225,7 +225,7 @@ describe API::ProtectedBranches do
let(:delete_endpoint) { api("/projects/#{project.id}/protected_branches/#{branch_name}", user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it "unprotects a single branch" do
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 28f8564ae92..6063afc213d 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -8,7 +8,7 @@ describe API::Repositories do
let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } }
let!(:project) { create(:project, :repository, creator: user) }
- let!(:master) { create(:project_member, :master, user: user, project: project) }
+ let!(:maintainer) { create(:project_member, :maintainer, user: user, project: project) }
describe "GET /projects/:id/repository/tree" do
let(:route) { "/projects/#{project.id}/repository/tree" }
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index e7639599874..43ceb332cfb 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -424,7 +424,9 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
'untracked' => false,
'paths' => %w(out/),
'when' => 'always',
- 'expire_in' => '7d' }]
+ 'expire_in' => '7d',
+ "artifact_type" => "archive",
+ "artifact_format" => "zip" }]
end
let(:expected_cache) do
@@ -851,6 +853,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
it 'does not update job status and job trace' do
update_job(state: 'success', trace: 'BUILD TRACE UPDATED')
+ job.reload
expect(response).to have_gitlab_http_status(403)
expect(response.header['Job-Status']).to eq 'failed'
expect(job.trace.raw).to eq 'Job failed'
@@ -1419,6 +1422,56 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
end
end
+
+ context 'when artifact_type is archive' do
+ context 'when artifact_format is zip' do
+ let(:params) { { artifact_type: :archive, artifact_format: :zip } }
+
+ it 'stores junit test report' do
+ upload_artifacts(file_upload, headers_with_token, params)
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(job.reload.job_artifacts_archive).not_to be_nil
+ end
+ end
+
+ context 'when artifact_format is gzip' do
+ let(:params) { { artifact_type: :archive, artifact_format: :gzip } }
+
+ it 'returns an error' do
+ upload_artifacts(file_upload, headers_with_token, params)
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(job.reload.job_artifacts_archive).to be_nil
+ end
+ end
+ end
+
+ context 'when artifact_type is junit' do
+ context 'when artifact_format is gzip' do
+ let(:file_upload) { fixture_file_upload('spec/fixtures/junit/junit.xml.gz') }
+ let(:params) { { artifact_type: :junit, artifact_format: :gzip } }
+
+ it 'stores junit test report' do
+ upload_artifacts(file_upload, headers_with_token, params)
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(job.reload.job_artifacts_junit).not_to be_nil
+ end
+ end
+
+ context 'when artifact_format is raw' do
+ let(:file_upload) { fixture_file_upload('spec/fixtures/junit/junit.xml.gz') }
+ let(:params) { { artifact_type: :junit, artifact_format: :raw } }
+
+ it 'returns an error' do
+ upload_artifacts(file_upload, headers_with_token, params)
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(job.reload.job_artifacts_junit).to be_nil
+ end
+ end
+ end
end
context 'when artifacts are being stored outside of tmp path' do
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index 0c7937feed6..3ebdb54f71f 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -18,8 +18,8 @@ describe API::Runners do
before do
# Set project access for users
- create(:project_member, :master, user: user, project: project)
- create(:project_member, :master, user: user, project: project2)
+ create(:project_member, :maintainer, user: user, project: project)
+ create(:project_member, :maintainer, user: user, project: project2)
create(:project_member, :reporter, user: user2, project: project)
end
@@ -90,6 +90,17 @@ describe API::Runners do
end
it 'filters runners by scope' do
+ get api('/runners/all?scope=shared', admin)
+
+ shared = json_response.all? { |r| r['is_shared'] }
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response[0]).to have_key('ip_address')
+ expect(shared).to be_truthy
+ end
+
+ it 'filters runners by scope' do
get api('/runners/all?scope=specific', admin)
shared = json_response.any? { |r| r['is_shared'] }
@@ -136,7 +147,7 @@ describe API::Runners do
delete api("/runners/#{unused_project_runner.id}", admin)
expect(response).to have_gitlab_http_status(204)
- end.to change { Ci::Runner.specific.count }.by(-1)
+ end.to change { Ci::Runner.project_type.count }.by(-1)
end
end
@@ -200,6 +211,69 @@ describe API::Runners do
describe 'PUT /runners/:id' do
context 'admin user' do
+ # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48625
+ context 'single parameter update' do
+ it 'runner description' do
+ description = shared_runner.description
+ update_runner(shared_runner.id, admin, description: "#{description}_updated")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.description).to eq("#{description}_updated")
+ end
+
+ it 'runner active state' do
+ active = shared_runner.active
+ update_runner(shared_runner.id, admin, active: !active)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.active).to eq(!active)
+ end
+
+ it 'runner tag list' do
+ update_runner(shared_runner.id, admin, tag_list: ['ruby2.1', 'pgsql', 'mysql'])
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.tag_list).to include('ruby2.1', 'pgsql', 'mysql')
+ end
+
+ it 'runner untagged flag' do
+ # Ensure tag list is non-empty before setting untagged to false.
+ update_runner(shared_runner.id, admin, tag_list: ['ruby2.1', 'pgsql', 'mysql'])
+ update_runner(shared_runner.id, admin, run_untagged: 'false')
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.run_untagged?).to be(false)
+ end
+
+ it 'runner unlocked flag' do
+ update_runner(shared_runner.id, admin, locked: 'true')
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.locked?).to be(true)
+ end
+
+ it 'runner access level' do
+ update_runner(shared_runner.id, admin, access_level: 'ref_protected')
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.ref_protected?).to be_truthy
+ end
+
+ it 'runner maximum timeout' do
+ update_runner(shared_runner.id, admin, maximum_timeout: 1234)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.maximum_timeout).to eq(1234)
+ end
+
+ it 'fails with no parameters' do
+ put api("/runners/#{shared_runner.id}", admin)
+
+ shared_runner.reload
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
+
context 'when runner is shared' do
it 'updates runner' do
description = shared_runner.description
@@ -300,7 +374,7 @@ describe API::Runners do
delete api("/runners/#{shared_runner.id}", admin)
expect(response).to have_gitlab_http_status(204)
- end.to change { Ci::Runner.shared.count }.by(-1)
+ end.to change { Ci::Runner.instance_type.count }.by(-1)
end
it_behaves_like '412 response' do
@@ -314,7 +388,7 @@ describe API::Runners do
delete api("/runners/#{project_runner.id}", admin)
expect(response).to have_http_status(204)
- end.to change { Ci::Runner.specific.count }.by(-1)
+ end.to change { Ci::Runner.project_type.count }.by(-1)
end
end
@@ -349,7 +423,7 @@ describe API::Runners do
delete api("/runners/#{project_runner.id}", user)
expect(response).to have_http_status(204)
- end.to change { Ci::Runner.specific.count }.by(-1)
+ end.to change { Ci::Runner.project_type.count }.by(-1)
end
it_behaves_like '412 response' do
@@ -502,7 +576,7 @@ describe API::Runners do
end
describe 'GET /projects/:id/runners' do
- context 'authorized user with master privileges' do
+ context 'authorized user with maintainer privileges' do
it "returns project's runners" do
get api("/projects/#{project.id}/runners", user)
@@ -515,7 +589,7 @@ describe API::Runners do
end
end
- context 'authorized user without master privileges' do
+ context 'authorized user without maintainer privileges' do
it "does not return project's runners" do
get api("/projects/#{project.id}/runners", user2)
@@ -584,12 +658,12 @@ describe API::Runners do
end
end
- it 'enables a shared runner' do
+ it 'enables a instance type runner' do
expect do
post api("/projects/#{project.id}/runners", admin), runner_id: shared_runner.id
end.to change { project.runners.count }.by(1)
- expect(shared_runner.reload).not_to be_shared
+ expect(shared_runner.reload).not_to be_instance_type
expect(response).to have_gitlab_http_status(201)
end
end
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 57adc3ca7a6..3e0f47b84a1 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -25,6 +25,7 @@ describe API::Settings, 'Settings' do
expect(json_response['ed25519_key_restriction']).to eq(0)
expect(json_response['circuitbreaker_failure_count_threshold']).not_to be_nil
expect(json_response['performance_bar_allowed_group_id']).to be_nil
+ expect(json_response['instance_statistics_visibility_private']).to be(false)
expect(json_response).not_to have_key('performance_bar_allowed_group_path')
expect(json_response).not_to have_key('performance_bar_enabled')
end
@@ -64,7 +65,8 @@ describe API::Settings, 'Settings' do
circuitbreaker_check_interval: 2,
enforce_terms: true,
terms: 'Hello world!',
- performance_bar_allowed_group_path: group.full_path
+ performance_bar_allowed_group_path: group.full_path,
+ instance_statistics_visibility_private: true
expect(response).to have_gitlab_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
@@ -89,6 +91,7 @@ describe API::Settings, 'Settings' do
expect(json_response['enforce_terms']).to be(true)
expect(json_response['terms']).to eq('Hello world!')
expect(json_response['performance_bar_allowed_group_id']).to eq(group.id)
+ expect(json_response['instance_statistics_visibility_private']).to be(true)
end
end
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index 969710d6613..98f995df06f 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -10,7 +10,7 @@ describe API::Tags do
let(:current_user) { nil }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/repository/tags' do
@@ -86,7 +86,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository tags'
@@ -109,7 +109,7 @@ describe API::Tags do
before do
release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update_attributes(description: description)
+ release.update(description: description)
end
it 'returns an array of project tags with release info' do
@@ -168,7 +168,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository tag'
@@ -222,7 +222,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
context "when a protected branch doesn't already exist" do
@@ -341,7 +341,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository delete tag'
@@ -386,7 +386,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository new release'
@@ -400,7 +400,7 @@ describe API::Tags do
context 'on tag with existing release' do
before do
release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update_attributes(description: description)
+ release.update(description: description)
end
it 'returns 409 if there is already a release' do
@@ -422,7 +422,7 @@ describe API::Tags do
context 'on tag with existing release' do
before do
release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update_attributes(description: description)
+ release.update(description: description)
end
it 'updates the release description' do
@@ -452,7 +452,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository update release'
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index 2ee8d150dc8..b5cf04e7f22 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -1,7 +1,8 @@
require 'spec_helper'
describe API::Todos do
- let(:project_1) { create(:project, :repository) }
+ let(:group) { create(:group) }
+ let(:project_1) { create(:project, :repository, group: group) }
let(:project_2) { create(:project) }
let(:author_1) { create(:user) }
let(:author_2) { create(:user) }
@@ -92,6 +93,17 @@ describe API::Todos do
end
end
+ context 'and using the group filter' do
+ it 'filters based on project_id param' do
+ get api('/todos', john_doe), { group_id: group.id, sort: :target_id }
+
+ expect(response.status).to eq(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ end
+ end
+
context 'and using the action filter' do
it 'filters based on action param' do
get api('/todos', john_doe), { action: 'mentioned' }
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb
index b2c56f7af2c..0ae6796d1e4 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/triggers_spec.rb
@@ -6,7 +6,7 @@ describe API::Triggers do
let!(:trigger_token) { 'secure_token' }
let!(:trigger_token_2) { 'secure_token_2' }
let!(:project) { create(:project, :repository, creator: user) }
- let!(:master) { create(:project_member, :master, user: user, project: project) }
+ let!(:maintainer) { create(:project_member, :maintainer, user: user, project: project) }
let!(:developer) { create(:project_member, :developer, user: user2, project: project) }
let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token, owner: user) }
let!(:trigger2) { create(:ci_trigger, project: project, token: trigger_token_2, owner: user2) }
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index a97c3f3461a..d48d577afa1 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -11,6 +11,28 @@ describe API::Users do
let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 }
let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 }
+ let(:private_user) { create(:user, private_profile: true) }
+
+ shared_examples 'rendering user status' do
+ it 'returns the status if there was one' do
+ create(:user_status, user: user)
+
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response['message']).to be_present
+ expect(json_response['message_html']).to be_present
+ expect(json_response['emoji']).to be_present
+ end
+
+ it 'returns an empty response if there was no status' do
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response['message']).to be_nil
+ expect(json_response['emoji']).to be_nil
+ end
+ end
describe 'GET /users' do
context "when unauthenticated" do
@@ -254,6 +276,13 @@ describe API::Users do
expect(response).to match_response_schema('public_api/v4/user/admin')
expect(json_response['is_admin']).to be(false)
end
+
+ it "includes the `created_at` field for private users" do
+ get api("/users/#{private_user.id}", admin)
+
+ expect(response).to match_response_schema('public_api/v4/user/admin')
+ expect(json_response.keys).to include 'created_at'
+ end
end
context 'for an anonymous user' do
@@ -272,6 +301,20 @@ describe API::Users do
expect(response).to have_gitlab_http_status(404)
end
+
+ it "returns the `created_at` field for public users" do
+ get api("/users/#{user.id}")
+
+ expect(response).to match_response_schema('public_api/v4/user/basic')
+ expect(json_response.keys).to include 'created_at'
+ end
+
+ it "does not return the `created_at` field for private users" do
+ get api("/users/#{private_user.id}")
+
+ expect(response).to match_response_schema('public_api/v4/user/basic')
+ expect(json_response.keys).not_to include 'created_at'
+ end
end
it "returns a 404 error if user id not found" do
@@ -288,6 +331,20 @@ describe API::Users do
end
end
+ describe 'GET /users/:id_or_username/status' do
+ context 'when finding the user by id' do
+ it_behaves_like 'rendering user status' do
+ let(:path) { "/users/#{user.id}/status" }
+ end
+ end
+
+ context 'when finding the user by username' do
+ it_behaves_like 'rendering user status' do
+ let(:path) { "/users/#{user.username}/status" }
+ end
+ end
+ end
+
describe "POST /users" do
before do
admin
@@ -374,6 +431,18 @@ describe API::Users do
expect(new_user.recently_sent_password_reset?).to eq(true)
end
+ it "creates user with private profile" do
+ post api('/users', admin), attributes_for(:user, private_profile: true)
+
+ expect(response).to have_gitlab_http_status(201)
+
+ user_id = json_response['id']
+ new_user = User.find(user_id)
+
+ expect(new_user).not_to eq(nil)
+ expect(new_user.private_profile?).to eq(true)
+ end
+
it "does not create user with invalid email" do
post api('/users', admin),
email: 'invalid email',
@@ -583,6 +652,13 @@ describe API::Users do
expect(user.reload.external?).to be_truthy
end
+ it "updates private profile" do
+ put api("/users/#{user.id}", admin), { private_profile: true }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(user.reload.private_profile).to eq(true)
+ end
+
it "does not update admin status" do
put api("/users/#{admin_user.id}", admin), { can_create_group: false }
@@ -1067,7 +1143,7 @@ describe API::Users do
end
it "deletes user" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
expect(response).to have_gitlab_http_status(204)
expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
@@ -1079,30 +1155,30 @@ describe API::Users do
end
it "does not delete for unauthenticated user" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}") }
+ perform_enqueued_jobs { delete api("/users/#{user.id}") }
expect(response).to have_gitlab_http_status(401)
end
it "is not available for non admin users" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}", user) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}", user) }
expect(response).to have_gitlab_http_status(403)
end
it "returns 404 for non-existing user" do
- Sidekiq::Testing.inline! { delete api("/users/999999", admin) }
+ perform_enqueued_jobs { delete api("/users/999999", admin) }
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
it "returns a 404 for invalid ID" do
- Sidekiq::Testing.inline! { delete api("/users/ASDF", admin) }
+ perform_enqueued_jobs { delete api("/users/ASDF", admin) }
expect(response).to have_gitlab_http_status(404)
end
context "hard delete disabled" do
it "moves contributions to the ghost user" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
expect(response).to have_gitlab_http_status(204)
expect(issue.reload).to be_persisted
@@ -1112,7 +1188,7 @@ describe API::Users do
context "hard delete enabled" do
it "removes contributions" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}?hard_delete=true", admin) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
expect(response).to have_gitlab_http_status(204)
expect(Issue.exists?(issue.id)).to be_falsy
@@ -1733,6 +1809,34 @@ describe API::Users do
end
end
+ describe 'GET /user/status' do
+ let(:path) { '/user/status' }
+ it_behaves_like 'rendering user status'
+ end
+
+ describe 'PUT /user/status' do
+ it 'saves the status' do
+ put api('/user/status', user), { emoji: 'smirk', message: 'hello world' }
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response['emoji']).to eq('smirk')
+ end
+
+ it 'renders errors when the status was invalid' do
+ put api('/user/status', user), { emoji: 'does not exist', message: 'hello world' }
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']['emoji']).to be_present
+ end
+
+ it 'deletes the status when passing empty values' do
+ put api('/user/status', user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(user.reload.status).to be_nil
+ end
+ end
+
describe 'GET /users/:user_id/impersonation_tokens' do
let!(:active_personal_access_token) { create(:personal_access_token, user: user) }
let!(:revoked_personal_access_token) { create(:personal_access_token, :revoked, user: user) }
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb
index 62215ea3d7d..be333df1d78 100644
--- a/spec/requests/api/variables_spec.rb
+++ b/spec/requests/api/variables_spec.rb
@@ -4,7 +4,7 @@ describe API::Variables do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
- let!(:master) { create(:project_member, :master, user: user, project: project) }
+ let!(:maintainer) { create(:project_member, :maintainer, user: user, project: project) }
let!(:developer) { create(:project_member, :developer, user: user2, project: project) }
let!(:variable) { create(:ci_variable, project: project) }
diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb
index 850ba696098..489cb001b82 100644
--- a/spec/requests/api/wikis_spec.rb
+++ b/spec/requests/api/wikis_spec.rb
@@ -7,7 +7,7 @@ require 'spec_helper'
# Every state is tested for 3 user roles:
# - guest
# - developer
-# - master
+# - maintainer
# because they are 3 edge cases of using wiki pages.
describe API::Wikis do
@@ -163,9 +163,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
get api(url, user)
end
@@ -193,9 +193,9 @@ describe API::Wikis do
include_examples 'returns list of wiki pages'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
include_examples 'returns list of wiki pages'
@@ -221,9 +221,9 @@ describe API::Wikis do
include_examples 'returns list of wiki pages'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
include_examples 'returns list of wiki pages'
@@ -256,9 +256,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
get api(url, user)
end
@@ -293,9 +293,9 @@ describe API::Wikis do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
get api(url, user)
end
@@ -337,9 +337,9 @@ describe API::Wikis do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
get api(url, user)
end
@@ -379,9 +379,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
post(api(url, user), payload)
end
@@ -408,9 +408,9 @@ describe API::Wikis do
include_examples 'creates wiki page'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
include_examples 'creates wiki page'
@@ -436,9 +436,9 @@ describe API::Wikis do
include_examples 'creates wiki page'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
include_examples 'creates wiki page'
@@ -472,9 +472,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
put(api(url, user), payload)
end
@@ -510,9 +510,9 @@ describe API::Wikis do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
put(api(url, user), payload)
end
@@ -554,9 +554,9 @@ describe API::Wikis do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
put(api(url, user), payload)
end
@@ -607,9 +607,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
delete(api(url, user))
end
@@ -639,9 +639,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
delete(api(url, user))
end
@@ -671,9 +671,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
delete(api(url, user))
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 92fcfb65269..c71eae9164a 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -5,7 +5,6 @@ describe 'Git HTTP requests' do
include TermsHelper
include GitHttpHelpers
include WorkhorseHelpers
- include UserActivitiesHelpers
shared_examples 'pulls require Basic HTTP Authentication' do
context "when no credentials are provided" do
@@ -312,7 +311,7 @@ describe 'Git HTTP requests' do
let(:project) { fork_project(canonical_project, nil, repository: true) }
before do
- canonical_project.add_master(user)
+ canonical_project.add_maintainer(user)
create(:merge_request,
source_project: project,
target_project: canonical_project,
@@ -382,6 +381,10 @@ describe 'Git HTTP requests' do
context "when authentication fails" do
context "when the user is IP banned" do
+ before do
+ Gitlab.config.rack_attack.git_basic_auth['enabled'] = true
+ end
+
it "responds with status 401" do
expect(Rack::Attack::Allow2Ban).to receive(:filter).and_return(true)
allow_any_instance_of(Rack::Request).to receive(:ip).and_return('1.2.3.4')
@@ -398,13 +401,13 @@ describe 'Git HTTP requests' do
context "when the user has access to the project" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "when the user is blocked" do
it "rejects pulls with 401 Unauthorized" do
user.block
- project.add_master(user)
+ project.add_maintainer(user)
download(path, env) do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
@@ -421,6 +424,10 @@ describe 'Git HTTP requests' do
end
context "when the user isn't blocked" do
+ before do
+ Gitlab.config.rack_attack.git_basic_auth['enabled'] = true
+ end
+
it "resets the IP in Rack Attack on download" do
expect(Rack::Attack::Allow2Ban).to receive(:reset).twice
@@ -440,10 +447,10 @@ describe 'Git HTTP requests' do
end
it 'updates the user last activity', :clean_gitlab_redis_shared_state do
- expect(user_activity(user)).to be_nil
+ expect(user.last_activity_on).to be_nil
download(path, env) do |response|
- expect(user_activity(user)).to be_present
+ expect(user.reload.last_activity_on).to eql(Date.today)
end
end
end
@@ -467,7 +474,7 @@ describe 'Git HTTP requests' do
let(:path) { "#{project.full_path}.git" }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when username and password are provided' do
@@ -827,7 +834,7 @@ describe 'Git HTTP requests' do
context 'and the user is on the team' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it "responds with status 200" do
@@ -850,7 +857,7 @@ describe 'Git HTTP requests' do
let(:env) { { user: user.username, password: user.password } }
before do
- project.add_master(user)
+ project.add_maintainer(user)
enforce_terms
end
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index 6f40a02aaa9..e042d772718 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -70,6 +70,25 @@ describe JwtController do
it { expect(service_class).to have_received(:new).with(nil, user, 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'
+ }
+ end
+
+ let(:service_parameters) do
+ { service: service_name, scopes: %w(scope1 scope2) }
+ end
+
+ it { expect(service_class).to have_received(:new).with(nil, user, service_parameters) }
+ end
+
context 'when user has 2FA enabled' do
let(:user) { create(:user, :two_factor) }
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 4d30b99262e..e349181b794 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -63,7 +63,7 @@ describe 'Git LFS API and storage' do
context 'with LFS disabled globally' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
end
@@ -106,7 +106,7 @@ describe 'Git LFS API and storage' do
context 'with LFS enabled globally' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
enable_lfs
end
@@ -236,7 +236,7 @@ describe 'Git LFS API and storage' do
context 'and does have project access' do
let(:update_permissions) do
- project.add_master(user)
+ project.add_maintainer(user)
project.lfs_objects << lfs_object
end
@@ -293,7 +293,7 @@ describe 'Git LFS API and storage' do
context 'when user allowed' do
let(:update_permissions) do
- project.add_master(user)
+ project.add_maintainer(user)
project.lfs_objects << lfs_object
end
@@ -575,6 +575,40 @@ describe 'Git LFS API and storage' do
end
end
+ context 'when using Deploy Tokens' do
+ let(:project) { create(:project, :repository) }
+ let(:authorization) { authorize_deploy_token }
+ let(:update_user_permissions) { nil }
+ let(:role) { nil }
+ let(:update_lfs_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ context 'when Deploy Token is valid' do
+ let(:deploy_token) { create(:deploy_token, projects: [project]) }
+
+ it_behaves_like 'an authorized requests'
+ end
+
+ context 'when Deploy Token is not valid' do
+ let(:deploy_token) { create(:deploy_token, projects: [project], read_repository: false) }
+
+ it 'responds with access denied' do
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+
+ context 'when Deploy Token is not related to the project' do
+ let(:another_project) { create(:project, :repository) }
+ let(:deploy_token) { create(:deploy_token, projects: [another_project]) }
+
+ it 'responds with access forbidden' do
+ # We render 404, to prevent data leakage about existence of the project
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
context 'when build is authorized as' do
let(:authorization) { authorize_ci_project }
@@ -698,7 +732,7 @@ describe 'Git LFS API and storage' do
expect(json_response['objects'].first['oid']).to eq(sample_oid)
expect(json_response['objects'].first['size']).to eq(sample_size)
expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
- expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
+ expect(json_response['objects'].first['actions']['upload']['header']).to eq({ 'Authorization' => authorization, 'Content-Type' => 'application/octet-stream' })
end
end
@@ -727,7 +761,7 @@ describe 'Git LFS API and storage' do
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("#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
- expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
+ expect(json_response['objects'].first['actions']['upload']['header']).to eq({ 'Authorization' => authorization, 'Content-Type' => 'application/octet-stream' })
end
end
@@ -762,7 +796,7 @@ describe 'Git LFS API and storage' do
expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(json_response['objects'].first['size']).to eq(1575078)
expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
- expect(json_response['objects'].first['actions']['upload']['header']).to eq("Authorization" => authorization)
+ expect(json_response['objects'].first['actions']['upload']['header']).to eq({ 'Authorization' => authorization, 'Content-Type' => 'application/octet-stream' })
expect(json_response['objects'].last['oid']).to eq(sample_oid)
expect(json_response['objects'].last['size']).to eq(sample_size)
@@ -829,7 +863,7 @@ describe 'Git LFS API and storage' do
context 'when user is not authenticated' do
context 'when user has push access' do
let(:update_user_permissions) do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'responds with status 401' do
@@ -874,7 +908,7 @@ describe 'Git LFS API and storage' do
before do
allow(Gitlab::Database).to receive(:read_only?) { true }
- project.add_master(user)
+ project.add_maintainer(user)
enable_lfs
end
@@ -1307,7 +1341,7 @@ describe 'Git LFS API and storage' do
let(:authorization) { authorize_user }
before do
- second_project.add_master(user)
+ second_project.add_maintainer(user)
upstream_project.lfs_objects << lfs_object
end
@@ -1381,6 +1415,10 @@ describe 'Git LFS API and storage' do
ActionController::HttpAuthentication::Basic.encode_credentials(user.username, Gitlab::LfsToken.new(user).token)
end
+ def authorize_deploy_token
+ ActionController::HttpAuthentication::Basic.encode_credentials(deploy_token.username, deploy_token.token)
+ end
+
def post_lfs_json(url, body = nil, headers = nil)
post(url, body.try(:to_json), (headers || {}).merge('Content-Type' => LfsRequest::CONTENT_TYPE))
end
diff --git a/spec/requests/lfs_locks_api_spec.rb b/spec/requests/lfs_locks_api_spec.rb
index e44a11a7232..a44b43a591f 100644
--- a/spec/requests/lfs_locks_api_spec.rb
+++ b/spec/requests/lfs_locks_api_spec.rb
@@ -4,7 +4,7 @@ describe 'Git LFS File Locking API' do
include WorkhorseHelpers
let(:project) { create(:project) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:developer) { create(:user) }
let(:guest) { create(:user) }
let(:path) { 'README.md' }
@@ -29,7 +29,7 @@ describe 'Git LFS File Locking API' do
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
- project.add_developer(master)
+ project.add_developer(maintainer)
project.add_developer(developer)
project.add_guest(guest)
end
@@ -99,7 +99,7 @@ describe 'Git LFS File Locking API' do
include_examples 'unauthorized request'
it 'returns the list of locked files grouped by owner' do
- lock_file('README.md', master)
+ lock_file('README.md', maintainer)
lock_file('README', developer)
post_lfs_json url, nil, headers
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index 179fc9733ad..98df5f787f7 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -79,7 +79,7 @@ end
# edit_admin_hook GET /admin/hooks/:id(.:format) admin/hooks#edit
describe Admin::HooksController, "routing" do
it "to #test" do
- expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1')
+ expect(post("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1')
end
it "to #index" do
diff --git a/spec/routing/instance_statistics_routing_spec.rb b/spec/routing/instance_statistics_routing_spec.rb
new file mode 100644
index 00000000000..b94faabfa1d
--- /dev/null
+++ b/spec/routing/instance_statistics_routing_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Instance Statistics', 'routing' do
+ include RSpec::Rails::RequestExampleGroup
+
+ it "routes '/-/instance_statistics' to conversational development index" do
+ expect(get('/-/instance_statistics')).to redirect_to('/-/instance_statistics/conversational_development_index')
+ end
+end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 56d93095a85..70a7707826e 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -389,7 +389,7 @@ describe 'project routing' do
# DELETE /:project_id/hooks/:id(.:format) hooks#destroy
describe Projects::HooksController, 'routing' do
it 'to #test' do
- expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ expect(post('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
it_behaves_like 'RESTful project resources' do
diff --git a/spec/rubocop/cop/migration/add_reference_spec.rb b/spec/rubocop/cop/migration/add_reference_spec.rb
new file mode 100644
index 00000000000..8f795bb561e
--- /dev/null
+++ b/spec/rubocop/cop/migration/add_reference_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../../rubocop/cop/migration/add_reference'
+
+describe RuboCop::Cop::Migration::AddReference do
+ include CopHelper
+
+ let(:cop) { described_class.new }
+
+ context 'outside of a migration' do
+ it 'does not register any offenses' do
+ expect_no_offenses(<<~RUBY)
+ def up
+ add_reference(:projects, :users)
+ end
+ RUBY
+ end
+ end
+
+ context 'in a migration' do
+ before do
+ allow(cop).to receive(:in_migration?).and_return(true)
+ end
+
+ it 'registers an offense when using add_reference without index' do
+ expect_offense(<<~RUBY)
+ call do
+ add_reference(:projects, :users)
+ ^^^^^^^^^^^^^ `add_reference` requires `index: true`
+ end
+ RUBY
+ end
+
+ it 'registers an offense when using add_reference index disabled' do
+ expect_offense(<<~RUBY)
+ def up
+ add_reference(:projects, :users, index: false)
+ ^^^^^^^^^^^^^ `add_reference` requires `index: true`
+ end
+ RUBY
+ end
+
+ it 'does not register an offense when using add_reference with index enabled' do
+ expect_no_offenses(<<~RUBY)
+ def up
+ add_reference(:projects, :users, index: true)
+ end
+ RUBY
+ end
+ end
+end
diff --git a/spec/serializers/deploy_key_entity_spec.rb b/spec/serializers/deploy_key_entity_spec.rb
index 2bd8162d1b7..01264cf7fb5 100644
--- a/spec/serializers/deploy_key_entity_spec.rb
+++ b/spec/serializers/deploy_key_entity_spec.rb
@@ -44,9 +44,9 @@ describe DeployKeyEntity do
it { expect(entity.as_json).to eq(expected_result) }
end
- describe 'returns can_edit true if user is a master of project' do
+ describe 'returns can_edit true if user is a maintainer of project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it { expect(entity.as_json).to include(can_edit: true) }
diff --git a/spec/serializers/diff_file_entity_spec.rb b/spec/serializers/diff_file_entity_spec.rb
index c4a6c117b76..00b2146dc86 100644
--- a/spec/serializers/diff_file_entity_spec.rb
+++ b/spec/serializers/diff_file_entity_spec.rb
@@ -25,6 +25,20 @@ describe DiffFileEntity do
:context_lines_path
)
end
+
+ # Converted diff files from GitHub import does not contain blob file
+ # and content sha.
+ context 'when diff file does not have a blob and content sha' do
+ it 'exposes some attributes as nil' do
+ allow(diff_file).to receive(:content_sha).and_return(nil)
+ allow(diff_file).to receive(:blob).and_return(nil)
+
+ expect(subject[:context_lines_path]).to be_nil
+ expect(subject[:view_path]).to be_nil
+ expect(subject[:highlighted_diff_lines]).to be_nil
+ expect(subject[:can_modify_blob]).to be_nil
+ end
+ end
end
context 'when there is no merge request' do
diff --git a/spec/serializers/discussion_entity_spec.rb b/spec/serializers/discussion_entity_spec.rb
index 44d8cc69d9b..378540a35b6 100644
--- a/spec/serializers/discussion_entity_spec.rb
+++ b/spec/serializers/discussion_entity_spec.rb
@@ -36,6 +36,25 @@ describe DiscussionEntity do
)
end
+ context 'when is LegacyDiffDiscussion' do
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:discussion) { create(:legacy_diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
+
+ it 'exposes correct attributes' do
+ expect(subject.keys.sort).to include(
+ :diff_discussion,
+ :expanded,
+ :id,
+ :individual_note,
+ :notes,
+ :discussion_path,
+ :for_commit,
+ :commit_id
+ )
+ end
+ end
+
context 'when diff file is present' do
let(:note) { create(:diff_note_on_merge_request) }
diff --git a/spec/serializers/environment_entity_spec.rb b/spec/serializers/environment_entity_spec.rb
index 8f32c5639a1..b7324a26ed2 100644
--- a/spec/serializers/environment_entity_spec.rb
+++ b/spec/serializers/environment_entity_spec.rb
@@ -1,8 +1,9 @@
require 'spec_helper'
describe EnvironmentEntity do
+ let(:request) { double('request') }
let(:entity) do
- described_class.new(environment, request: double)
+ described_class.new(environment, request: spy('request'))
end
let(:environment) { create(:environment) }
diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb
index ca9b520fb38..0f0ab5ac796 100644
--- a/spec/serializers/environment_serializer_spec.rb
+++ b/spec/serializers/environment_serializer_spec.rb
@@ -54,7 +54,9 @@ describe EnvironmentSerializer do
context 'when representing environments within folders' do
let(:serializer) do
- described_class.new(project: project).within_folders
+ described_class
+ .new(current_user: user, project: project)
+ .within_folders
end
let(:resource) { Environment.all }
@@ -123,7 +125,8 @@ describe EnvironmentSerializer do
let(:pagination) { { page: 1, per_page: 2 } }
let(:serializer) do
- described_class.new(project: project)
+ described_class
+ .new(current_user: user, project: project)
.with_pagination(request, response)
end
@@ -169,7 +172,8 @@ describe EnvironmentSerializer do
context 'when grouping environments within folders' do
let(:serializer) do
- described_class.new(project: project)
+ described_class
+ .new(current_user: user, project: project)
.with_pagination(request, response)
.within_folders
end
diff --git a/spec/serializers/group_child_entity_spec.rb b/spec/serializers/group_child_entity_spec.rb
index 505a9eaac5a..dbc40bddc30 100644
--- a/spec/serializers/group_child_entity_spec.rb
+++ b/spec/serializers/group_child_entity_spec.rb
@@ -42,7 +42,7 @@ describe GroupChildEntity do
end
before do
- object.add_master(user)
+ object.add_maintainer(user)
end
it 'has the correct type' do
diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index d2072198d83..0ba2539a717 100644
--- a/spec/serializers/merge_request_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -11,6 +11,21 @@ describe MergeRequestWidgetEntity do
described_class.new(resource, request: request).as_json
end
+ describe 'source_project_full_path' do
+ it 'includes the full path of the source project' do
+ expect(subject[:source_project_full_path]).to be_present
+ end
+
+ context 'when the source project is missing' do
+ it 'returns `nil` for the source project' do
+ resource.allow_broken = true
+ resource.update!(source_project: nil)
+
+ expect(subject[:source_project_full_path]).to be_nil
+ end
+ end
+ end
+
describe 'pipeline' do
let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: resource.source_branch, sha: resource.source_branch_sha, head_pipeline_of: resource) }
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index eb4235e3ee6..cf57776346a 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -125,7 +125,7 @@ describe PipelineSerializer do
it 'verifies number of queries', :request_store do
recorded = ActiveRecord::QueryRecorder.new { subject }
- expect(recorded.count).to be_within(2).of(27)
+ expect(recorded.count).to be_within(2).of(31)
expect(recorded.cached_count).to eq(0)
end
end
@@ -144,7 +144,7 @@ describe PipelineSerializer do
# pipeline. With the same ref this check is cached but if refs are
# different then there is an extra query per ref
# https://gitlab.com/gitlab-org/gitlab-ce/issues/46368
- expect(recorded.count).to be_within(2).of(30)
+ expect(recorded.count).to be_within(2).of(34)
expect(recorded.cached_count).to eq(0)
end
end
diff --git a/spec/serializers/test_case_entity_spec.rb b/spec/serializers/test_case_entity_spec.rb
new file mode 100644
index 00000000000..a55910f98bb
--- /dev/null
+++ b/spec/serializers/test_case_entity_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe TestCaseEntity do
+ include TestReportsHelper
+
+ let(:entity) { described_class.new(test_case) }
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ context 'when test case is success' do
+ let(:test_case) { create_test_case_rspec_success }
+
+ it 'contains correct test case details' do
+ expect(subject[:status]).to eq('success')
+ expect(subject[:name]).to eq('Test#sum when a is 1 and b is 3 returns summary')
+ expect(subject[:execution_time]).to eq(1.11)
+ end
+ end
+
+ context 'when test case is failed' do
+ let(:test_case) { create_test_case_rspec_failed }
+
+ it 'contains correct test case details' do
+ expect(subject[:status]).to eq('failed')
+ expect(subject[:name]).to eq('Test#sum when a is 2 and b is 2 returns summary')
+ expect(subject[:execution_time]).to eq(2.22)
+ end
+ end
+ end
+end
diff --git a/spec/serializers/test_reports_comparer_entity_spec.rb b/spec/serializers/test_reports_comparer_entity_spec.rb
new file mode 100644
index 00000000000..59c058fe368
--- /dev/null
+++ b/spec/serializers/test_reports_comparer_entity_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe TestReportsComparerEntity do
+ include TestReportsHelper
+
+ let(:entity) { described_class.new(comparer) }
+ let(:comparer) { Gitlab::Ci::Reports::TestReportsComparer.new(base_reports, head_reports) }
+ let(:base_reports) { Gitlab::Ci::Reports::TestReports.new }
+ let(:head_reports) { Gitlab::Ci::Reports::TestReports.new }
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ context 'when head and base reports include two test suites' do
+ context 'when the status of head report is success' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'contains correct compared test reports details' do
+ expect(subject[:status]).to eq('success')
+ expect(subject[:summary]).to include(total: 2, resolved: 0, failed: 0)
+ expect(subject[:suites].first[:name]).to eq('rspec')
+ expect(subject[:suites].first[:status]).to eq('success')
+ expect(subject[:suites].second[:name]).to eq('junit')
+ expect(subject[:suites].second[:status]).to eq('success')
+ end
+ end
+
+ context 'when the status of head report is failed' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'contains correct compared test reports details' do
+ expect(subject[:status]).to eq('failed')
+ expect(subject[:summary]).to include(total: 2, resolved: 0, failed: 1)
+ expect(subject[:suites].first[:name]).to eq('rspec')
+ expect(subject[:suites].first[:status]).to eq('success')
+ expect(subject[:suites].second[:name]).to eq('junit')
+ expect(subject[:suites].second[:status]).to eq('failed')
+ end
+ end
+
+ context 'when the status of head report is resolved' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_resolved)
+ end
+
+ let(:create_test_case_java_resolved) do
+ create_test_case_java_failed.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ it 'contains correct compared test reports details' do
+ expect(subject[:status]).to eq('success')
+ expect(subject[:summary]).to include(total: 2, resolved: 1, failed: 0)
+ expect(subject[:suites].first[:name]).to eq('rspec')
+ expect(subject[:suites].first[:status]).to eq('success')
+ expect(subject[:suites].second[:name]).to eq('junit')
+ expect(subject[:suites].second[:status]).to eq('success')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/serializers/test_reports_comparer_serializer_spec.rb b/spec/serializers/test_reports_comparer_serializer_spec.rb
new file mode 100644
index 00000000000..9ea86c0dd83
--- /dev/null
+++ b/spec/serializers/test_reports_comparer_serializer_spec.rb
@@ -0,0 +1,62 @@
+require 'spec_helper'
+
+describe TestReportsComparerSerializer do
+ include TestReportsHelper
+
+ let(:project) { double(:project) }
+ let(:serializer) { described_class.new(project: project).represent(comparer) }
+ let(:comparer) { Gitlab::Ci::Reports::TestReportsComparer.new(base_reports, head_reports) }
+ let(:base_reports) { Gitlab::Ci::Reports::TestReports.new }
+ let(:head_reports) { Gitlab::Ci::Reports::TestReports.new }
+
+ describe '#to_json' do
+ subject { serializer.to_json }
+
+ context 'when head and base reports include two test suites' do
+ context 'when the status of head report is success' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'matches the schema' do
+ expect(subject).to match_schema('entities/test_reports_comparer')
+ end
+ end
+
+ context 'when the status of head report is failed' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'matches the schema' do
+ expect(subject).to match_schema('entities/test_reports_comparer')
+ end
+ end
+
+ context 'when the status of head report is resolved' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_resolved)
+ end
+
+ let(:create_test_case_java_resolved) do
+ create_test_case_java_failed.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ it 'matches the schema' do
+ expect(subject).to match_schema('entities/test_reports_comparer')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/serializers/test_suite_comparer_entity_spec.rb b/spec/serializers/test_suite_comparer_entity_spec.rb
new file mode 100644
index 00000000000..f61331f53a0
--- /dev/null
+++ b/spec/serializers/test_suite_comparer_entity_spec.rb
@@ -0,0 +1,86 @@
+require 'spec_helper'
+
+describe TestSuiteComparerEntity do
+ include TestReportsHelper
+
+ let(:entity) { described_class.new(comparer) }
+ let(:comparer) { Gitlab::Ci::Reports::TestSuiteComparer.new(name, base_suite, head_suite) }
+ let(:name) { 'rpsec' }
+ let(:base_suite) { Gitlab::Ci::Reports::TestSuite.new(name) }
+ let(:head_suite) { Gitlab::Ci::Reports::TestSuite.new(name) }
+ let(:test_case_success) { create_test_case_rspec_success }
+ let(:test_case_failed) { create_test_case_rspec_failed }
+
+ let(:test_case_resolved) do
+ create_test_case_rspec_failed.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ context 'when head sutie has a newly failed test case which does not exist in base' do
+ before do
+ base_suite.add_test_case(test_case_success)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'contains correct compared test suite details' do
+ expect(subject[:name]).to eq(name)
+ expect(subject[:status]).to eq('failed')
+ expect(subject[:summary]).to include(total: 1, resolved: 0, failed: 1)
+ subject[:new_failures].first.tap do |new_failure|
+ expect(new_failure[:status]).to eq(test_case_failed.status)
+ expect(new_failure[:name]).to eq(test_case_failed.name)
+ expect(new_failure[:execution_time]).to eq(test_case_failed.execution_time)
+ expect(new_failure[:system_output]).to eq(test_case_failed.system_output)
+ end
+ expect(subject[:resolved_failures]).to be_empty
+ expect(subject[:existing_failures]).to be_empty
+ end
+ end
+
+ context 'when head sutie still has a failed test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'contains correct compared test suite details' do
+ expect(subject[:name]).to eq(name)
+ expect(subject[:status]).to eq('failed')
+ expect(subject[:summary]).to include(total: 1, resolved: 0, failed: 1)
+ expect(subject[:new_failures]).to be_empty
+ expect(subject[:resolved_failures]).to be_empty
+ subject[:existing_failures].first.tap do |existing_failure|
+ expect(existing_failure[:status]).to eq(test_case_failed.status)
+ expect(existing_failure[:name]).to eq(test_case_failed.name)
+ expect(existing_failure[:execution_time]).to eq(test_case_failed.execution_time)
+ expect(existing_failure[:system_output]).to eq(test_case_failed.system_output)
+ end
+ end
+ end
+
+ context 'when head sutie has a success test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_resolved)
+ end
+
+ it 'contains correct compared test suite details' do
+ expect(subject[:name]).to eq(name)
+ expect(subject[:status]).to eq('success')
+ expect(subject[:summary]).to include(total: 1, resolved: 1, failed: 0)
+ expect(subject[:new_failures]).to be_empty
+ subject[:resolved_failures].first.tap do |resolved_failure|
+ expect(resolved_failure[:status]).to eq(test_case_resolved.status)
+ expect(resolved_failure[:name]).to eq(test_case_resolved.name)
+ expect(resolved_failure[:execution_time]).to eq(test_case_resolved.execution_time)
+ expect(resolved_failure[:system_output]).to eq(test_case_resolved.system_output)
+ end
+ expect(subject[:existing_failures]).to be_empty
+ 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 fce73e0ac1f..c7f88e45c84 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -142,7 +142,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'for registry catalog' do
let(:current_params) do
- { scope: "registry:catalog:*" }
+ { scopes: ["registry:catalog:*"] }
end
context 'disallow browsing for users without Gitlab admin rights' do
@@ -164,7 +164,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
let(:current_params) do
- { scope: "repository:#{project.full_path}:push" }
+ { scopes: ["repository:#{project.full_path}:push"] }
end
it_behaves_like 'a pushable'
@@ -177,7 +177,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
let(:current_params) do
- { scope: "repository:#{project.full_path}:*" }
+ { scopes: ["repository:#{project.full_path}:*"] }
end
it_behaves_like 'an inaccessible'
@@ -191,7 +191,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pulling from root level repository' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull" }
+ { scopes: ["repository:#{project.full_path}:pull"] }
end
it_behaves_like 'a pullable'
@@ -205,7 +205,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
let(:current_params) do
- { scope: "repository:#{project.full_path}:*" }
+ { scopes: ["repository:#{project.full_path}:*"] }
end
it_behaves_like 'an inaccessible'
@@ -218,7 +218,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
let(:current_params) do
- { scope: "repository:#{project.full_path}:push,pull" }
+ { scopes: ["repository:#{project.full_path}:push,pull"] }
end
it_behaves_like 'a pullable'
@@ -231,7 +231,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull,push" }
+ { scopes: ["repository:#{project.full_path}:pull,push"] }
end
it_behaves_like 'an inaccessible'
@@ -244,7 +244,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
let(:current_params) do
- { scope: "repository:#{project.full_path}:*" }
+ { scopes: ["repository:#{project.full_path}:*"] }
end
it_behaves_like 'an inaccessible'
@@ -257,7 +257,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'allow anyone to pull images' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull" }
+ { scopes: ["repository:#{project.full_path}:pull"] }
end
it_behaves_like 'a pullable'
@@ -266,7 +266,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow anyone to push images' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:push" }
+ { scopes: ["repository:#{project.full_path}:push"] }
end
it_behaves_like 'an inaccessible'
@@ -275,7 +275,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow anyone to delete images' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:*" }
+ { scopes: ["repository:#{project.full_path}:*"] }
end
it_behaves_like 'an inaccessible'
@@ -284,7 +284,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when repository name is invalid' do
let(:current_params) do
- { scope: 'repository:invalid:push' }
+ { scopes: ['repository:invalid:push'] }
end
it_behaves_like 'an inaccessible'
@@ -298,7 +298,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'for internal user' do
context 'allow anyone to pull images' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull" }
+ { scopes: ["repository:#{project.full_path}:pull"] }
end
it_behaves_like 'a pullable'
@@ -307,7 +307,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow anyone to push images' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:push" }
+ { scopes: ["repository:#{project.full_path}:push"] }
end
it_behaves_like 'an inaccessible'
@@ -316,7 +316,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow anyone to delete images' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:*" }
+ { scopes: ["repository:#{project.full_path}:*"] }
end
it_behaves_like 'an inaccessible'
@@ -328,7 +328,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow anyone to pull or push images' do
let(:current_user) { create(:user, external: true) }
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull,push" }
+ { scopes: ["repository:#{project.full_path}:pull,push"] }
end
it_behaves_like 'an inaccessible'
@@ -338,7 +338,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow anyone to delete images' do
let(:current_user) { create(:user, external: true) }
let(:current_params) do
- { scope: "repository:#{project.full_path}:*" }
+ { scopes: ["repository:#{project.full_path}:*"] }
end
it_behaves_like 'an inaccessible'
@@ -348,7 +348,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
end
- context 'delete authorized as master' do
+ context 'delete authorized as maintainer' do
let(:current_project) { create(:project) }
let(:current_user) { create(:user) }
@@ -357,14 +357,14 @@ describe Auth::ContainerRegistryAuthenticationService do
end
before do
- current_project.add_master(current_user)
+ current_project.add_maintainer(current_user)
end
it_behaves_like 'a valid token'
context 'allow to delete images' do
let(:current_params) do
- { scope: "repository:#{current_project.full_path}:*" }
+ { scopes: ["repository:#{current_project.full_path}:*"] }
end
it_behaves_like 'a deletable' do
@@ -397,7 +397,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'allow to pull and push images' do
let(:current_params) do
- { scope: "repository:#{current_project.full_path}:pull,push" }
+ { scopes: ["repository:#{current_project.full_path}:pull,push"] }
end
it_behaves_like 'a pullable and pushable' do
@@ -411,7 +411,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow to delete images' do
let(:current_params) do
- { scope: "repository:#{current_project.full_path}:*" }
+ { scopes: ["repository:#{current_project.full_path}:*"] }
end
it_behaves_like 'an inaccessible' do
@@ -422,7 +422,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'for other projects' do
context 'when pulling' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull" }
+ { scopes: ["repository:#{project.full_path}:pull"] }
end
context 'allow for public' do
@@ -489,7 +489,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pushing' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:push" }
+ { scopes: ["repository:#{project.full_path}:push"] }
end
context 'disallow for all' do
@@ -523,7 +523,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow when pulling' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull" }
+ { scopes: ["repository:#{project.full_path}:pull"] }
end
it_behaves_like 'an inaccessible'
@@ -534,14 +534,66 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'registry catalog browsing authorized as admin' do
let(:current_user) { create(:user, :admin) }
+ let(:project) { create(:project, :public) }
let(:current_params) do
- { scope: "registry:catalog:*" }
+ { scopes: ["registry:catalog:*"] }
end
it_behaves_like 'a browsable'
end
+ context 'support for multiple scopes' do
+ let(:internal_project) { create(:project, :internal) }
+ let(: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(:current_user) { create(:user, :admin) }
+
+ 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(: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'
@@ -550,7 +602,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'for invalid scope' do
let(:current_params) do
- { scope: 'invalid:aa:bb' }
+ { scopes: ['invalid:aa:bb'] }
end
it_behaves_like 'a forbidden'
@@ -561,7 +613,7 @@ describe Auth::ContainerRegistryAuthenticationService do
let(:project) { create(:project, :private) }
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull" }
+ { scopes: ["repository:#{project.full_path}:pull"] }
end
it_behaves_like 'a forbidden'
@@ -572,7 +624,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pulling and pushing' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull,push" }
+ { scopes: ["repository:#{project.full_path}:pull,push"] }
end
it_behaves_like 'a pullable'
@@ -581,7 +633,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pushing' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:push" }
+ { scopes: ["repository:#{project.full_path}:push"] }
end
it_behaves_like 'a forbidden'
@@ -591,7 +643,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'for registry catalog' do
let(:current_params) do
- { scope: "registry:catalog:*" }
+ { scopes: ["registry:catalog:*"] }
end
it_behaves_like 'a forbidden'
@@ -601,7 +653,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'for deploy tokens' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:pull" }
+ { scopes: ["repository:#{project.full_path}:pull"] }
end
context 'when deploy token has read_registry as a scope' do
@@ -616,7 +668,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pushing' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:push" }
+ { scopes: ["repository:#{project.full_path}:push"] }
end
it_behaves_like 'an inaccessible'
@@ -632,7 +684,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pushing' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:push" }
+ { scopes: ["repository:#{project.full_path}:push"] }
end
it_behaves_like 'an inaccessible'
@@ -648,7 +700,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pushing' do
let(:current_params) do
- { scope: "repository:#{project.full_path}:push" }
+ { scopes: ["repository:#{project.full_path}:push"] }
end
it_behaves_like 'an inaccessible'
@@ -734,4 +786,26 @@ describe Auth::ContainerRegistryAuthenticationService do
end
end
end
+
+ context 'user authorization' do
+ let(:current_user) { create(:user) }
+
+ context 'with multiple scopes' do
+ let(:project) { create(:project) }
+ let(:project2) { create }
+
+ context 'allow developer to push images' do
+ before 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/services/ci/compare_test_reports_service_spec.rb b/spec/services/ci/compare_test_reports_service_spec.rb
new file mode 100644
index 00000000000..a26c970a8f0
--- /dev/null
+++ b/spec/services/ci/compare_test_reports_service_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe Ci::CompareTestReportsService 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 test reports' do
+ let!(:base_pipeline) { nil }
+ let!(:head_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) }
+
+ it 'returns status and data' do
+ expect(subject[:status]).to eq(:parsed)
+ expect(subject[:data]).to match_schema('entities/test_reports_comparer')
+ end
+ end
+
+ context 'when base and head pipelines have test reports' do
+ let!(:base_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) }
+ let!(:head_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) }
+
+ it 'returns status and data' do
+ expect(subject[:status]).to eq(:parsed)
+ expect(subject[:data]).to match_schema('entities/test_reports_comparer')
+ end
+ end
+
+ context 'when head pipeline has corrupted test reports' do
+ let!(:base_pipeline) { nil }
+ let!(:head_pipeline) { create(:ci_pipeline, project: project) }
+
+ before do
+ build = create(:ci_build, pipeline: head_pipeline, project: head_pipeline.project)
+ create(:ci_job_artifact, :junit_with_corrupted_data, job: build, project: project)
+ end
+
+ it 'returns status and error message' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:status_reason]).to include('XML parsing failed')
+ 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_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 2b88fcc9a96..054b7b1561c 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -462,11 +462,11 @@ describe Ci::CreatePipelineService do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
let(:pipeline) { execute_service }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'creates a protected pipeline' do
@@ -503,13 +503,13 @@ describe Ci::CreatePipelineService do
end
end
- context 'when trigger belongs to a master' do
+ context 'when trigger belongs to a maintainer' do
let(:user) { create(:user) }
let(:trigger) { create(:ci_trigger, owner: user) }
let(:trigger_request) { create(:ci_trigger_request, trigger: trigger) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'creates a pipeline' do
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 3816bd0deb5..a6565709641 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -351,6 +351,38 @@ module Ci
end
end
+ context 'runner feature set is verified' do
+ let!(:pending_job) { create(:ci_build, :pending, pipeline: pipeline) }
+
+ before do
+ expect_any_instance_of(Ci::Build).to receive(:runner_required_feature_names) do
+ [:runner_required_feature]
+ end
+ end
+
+ subject { execute(specific_runner, params) }
+
+ context 'when feature is missing by runner' do
+ let(:params) { {} }
+
+ it 'does not pick the build and drops the build' do
+ expect(subject).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job).to be_runner_unsupported
+ end
+ end
+
+ context 'when feature is supported by runner' do
+ let(:params) do
+ { info: { features: { runner_required_feature: true } } }
+ end
+
+ it 'does pick job' do
+ expect(subject).not_to be_nil
+ end
+ end
+ end
+
context 'when "dependencies" keyword is specified' do
shared_examples 'not pick' do
it 'does not pick the build and drops the build' do
@@ -403,6 +435,7 @@ module Ci
it { expect(subject).to eq(pending_job) }
end
+
context 'when artifacts of depended job has been expired' do
let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
@@ -548,8 +581,21 @@ module Ci
end
end
- def execute(runner)
- described_class.new(runner).execute.build
+ context 'when runner_session params are' do
+ it 'present sets runner session configuration in the build' do
+ runner_session_params = { session: { 'url' => 'https://example.com' } }
+
+ expect(execute(specific_runner, runner_session_params).runner_session.attributes)
+ .to include(runner_session_params[:session])
+ end
+
+ it 'not present it does not configure the runner session' do
+ expect(execute(specific_runner).runner_session).to be_nil
+ end
+ end
+
+ def execute(runner, params = {})
+ described_class.new(runner).execute(params).build
end
end
end
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index e1cb7ed8110..18d52082399 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -24,7 +24,7 @@ describe Ci::RetryBuildService do
artifacts_file artifacts_metadata artifacts_size created_at
updated_at started_at finished_at queued_at erased_by
erased_at auto_canceled_by job_artifacts job_artifacts_archive
- job_artifacts_metadata job_artifacts_trace].freeze
+ job_artifacts_metadata job_artifacts_trace job_artifacts_junit].freeze
IGNORE_ACCESSORS =
%i[type lock_version target_url base_tags trace_sections
@@ -32,13 +32,13 @@ describe Ci::RetryBuildService do
runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried failure_reason
artifacts_file_store artifacts_metadata_store
- metadata trace_chunks].freeze
+ metadata runner_session trace_chunks].freeze
shared_examples 'build duplication' do
let(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
let(:build) do
- create(:ci_build, :failed, :artifacts, :expired, :erased,
+ create(:ci_build, :failed, :artifacts, :test_reports, :expired, :erased,
:queued, :coverage, :tags, :allowed_to_fail, :on_tag,
:triggered, :trace_artifact, :teardown_environment,
description: 'my-job', stage: 'test', stage_id: stage.id,
@@ -49,7 +49,7 @@ describe Ci::RetryBuildService do
# Make sure that build has both `stage_id` and `stage` because FactoryBot
# can reset one of the fields when assigning another. We plan to deprecate
# and remove legacy `stage` column in the future.
- build.update_attributes(stage: 'test', stage_id: stage.id)
+ build.update(stage: 'test', stage_id: stage.id)
end
describe 'clone accessors' do
@@ -100,7 +100,11 @@ describe Ci::RetryBuildService do
end
describe '#execute' do
- let(:new_build) { service.execute(build) }
+ let(:new_build) do
+ Timecop.freeze(1.second.from_now) do
+ service.execute(build)
+ end
+ end
context 'when user has ability to execute build' do
before do
@@ -150,7 +154,11 @@ describe Ci::RetryBuildService do
end
describe '#reprocess' do
- let(:new_build) { service.reprocess!(build) }
+ let(:new_build) do
+ Timecop.freeze(1.second.from_now) do
+ service.reprocess!(build)
+ end
+ end
context 'when user has ability to execute build' do
before do
diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb
index 688d3b8c038..55445e71539 100644
--- a/spec/services/ci/retry_pipeline_service_spec.rb
+++ b/spec/services/ci/retry_pipeline_service_spec.rb
@@ -237,7 +237,7 @@ describe Ci::RetryPipelineService, '#execute' do
context 'when user is not allowed to trigger manual action' do
before do
project.add_developer(user)
- create(:protected_branch, :masters_can_push,
+ create(:protected_branch, :maintainers_can_push,
name: pipeline.ref, project: project)
end
@@ -275,7 +275,7 @@ describe Ci::RetryPipelineService, '#execute' do
let(:pipeline) { create(:ci_pipeline, project: forked_project, ref: 'fixes') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
create(:merge_request,
source_project: forked_project,
target_project: project,
diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb
index 3fc4e499b0c..cdd3d851f61 100644
--- a/spec/services/ci/stop_environments_service_spec.rb
+++ b/spec/services/ci/stop_environments_service_spec.rb
@@ -86,7 +86,7 @@ describe Ci::StopEnvironmentsService do
context 'when user has permission to stop environments' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'does not stop environment' do
diff --git a/spec/services/clusters/applications/check_installation_progress_service_spec.rb b/spec/services/clusters/applications/check_installation_progress_service_spec.rb
index 6894c1797b0..986f11410fd 100644
--- a/spec/services/clusters/applications/check_installation_progress_service_spec.rb
+++ b/spec/services/clusters/applications/check_installation_progress_service_spec.rb
@@ -43,7 +43,7 @@ describe Clusters::Applications::CheckInstallationProgressService do
service.execute
expect(application).to be_errored
- expect(application.status_reason).to match(/\btimeouted\b/)
+ expect(application.status_reason).to match(/\btimed out\b/)
end
end
end
diff --git a/spec/services/clusters/applications/install_service_spec.rb b/spec/services/clusters/applications/install_service_spec.rb
index 93199964a0e..a744ec30b65 100644
--- a/spec/services/clusters/applications/install_service_spec.rb
+++ b/spec/services/clusters/applications/install_service_spec.rb
@@ -47,7 +47,7 @@ describe Clusters::Applications::InstallService do
end
context 'when application cannot be persisted' do
- let(:application) { build(:clusters_applications_helm, :scheduled) }
+ let(:application) { create(:clusters_applications_helm, :scheduled) }
it 'make the application errored' do
expect(application).to receive(:make_installing!).once.and_raise(ActiveRecord::RecordInvalid)
diff --git a/spec/services/discussions/resolve_service_spec.rb b/spec/services/discussions/resolve_service_spec.rb
index 3895a0b3aea..4e0d4749239 100644
--- a/spec/services/discussions/resolve_service_spec.rb
+++ b/spec/services/discussions/resolve_service_spec.rb
@@ -9,7 +9,7 @@ describe Discussions::ResolveService do
let(:service) { described_class.new(discussion.noteable.project, user, merge_request: merge_request) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it "doesn't resolve discussions the user can't resolve" do
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index 13395a7cac3..68e310b0506 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe EventCreateService do
- include UserActivitiesHelpers
-
let(:service) { described_class.new }
describe 'Issues' do
@@ -146,7 +144,7 @@ describe EventCreateService do
it 'updates user last activity' do
expect { service.push(project, user, push_data) }
- .to change { user_activity(user) }
+ .to change { user.last_activity_on }.to(Date.today)
end
it 'caches the last push event for the user' do
diff --git a/spec/services/files/create_service_spec.rb b/spec/services/files/create_service_spec.rb
index abe99b9e794..30d94e4318d 100644
--- a/spec/services/files/create_service_spec.rb
+++ b/spec/services/files/create_service_spec.rb
@@ -23,7 +23,7 @@ describe Files::CreateService do
subject { described_class.new(project, user, commit_params) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "#execute" do
diff --git a/spec/services/files/delete_service_spec.rb b/spec/services/files/delete_service_spec.rb
index ace5f293097..73566afe8c8 100644
--- a/spec/services/files/delete_service_spec.rb
+++ b/spec/services/files/delete_service_spec.rb
@@ -37,7 +37,7 @@ describe Files::DeleteService do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "#execute" do
diff --git a/spec/services/files/multi_service_spec.rb b/spec/services/files/multi_service_spec.rb
index 59984c10990..3bdedaf3770 100644
--- a/spec/services/files/multi_service_spec.rb
+++ b/spec/services/files/multi_service_spec.rb
@@ -38,7 +38,7 @@ describe Files::MultiService do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
index 16bfbdf3089..e01fe487ffa 100644
--- a/spec/services/files/update_service_spec.rb
+++ b/spec/services/files/update_service_spec.rb
@@ -24,7 +24,7 @@ describe Files::UpdateService do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "#execute" do
@@ -71,17 +71,5 @@ describe Files::UpdateService do
expect(results.data).to eq(new_contents)
end
end
-
- context 'with gitaly disabled', :skip_gitaly_mock do
- context 'when target branch is different than source branch' do
- let(:branch_name) { "#{project.default_branch}-new" }
-
- it 'fires hooks only once' do
- expect(Gitlab::Git::HooksService).to receive(:new).once.and_call_original
-
- subject.execute
- end
- end
- end
end
end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 35826de5814..a3c9a660c2f 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -3,15 +3,15 @@ require 'spec_helper'
describe GitPushService, services: true do
include RepoHelpers
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
+ set(:user) { create(:user) }
+ set(:project) { create(:project, :repository) }
let(:blankrev) { Gitlab::Git::BLANK_SHA }
let(:oldrev) { sample_commit.parent_id }
let(:newrev) { sample_commit.id }
let(:ref) { 'refs/heads/master' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'with remote mirrors' do
@@ -267,8 +267,8 @@ describe GitPushService, services: true do
expect(project.default_branch).to eq("master")
execute_service(project, user, blankrev, 'newrev', ref)
expect(project.protected_branches).not_to be_empty
- expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
- expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
+ expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
it "when pushing a branch for the first time with default branch protection disabled" do
@@ -290,7 +290,7 @@ describe GitPushService, services: true do
expect(project.protected_branches).not_to be_empty
expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
- expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
it "when pushing a branch for the first time with an existing branch permission configured" do
@@ -315,7 +315,7 @@ describe GitPushService, services: true do
expect(project.default_branch).to eq("master")
execute_service(project, user, blankrev, 'newrev', ref)
expect(project.protected_branches).not_to be_empty
- expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
end
@@ -442,7 +442,7 @@ describe GitPushService, services: true do
allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
.and_return(closing_commit)
- project.add_master(commit_author)
+ project.add_maintainer(commit_author)
end
context "to default branches" do
@@ -761,7 +761,7 @@ describe GitPushService, services: true do
end
it 'does not queue a CreateGpgSignatureWorker' do
- expect(CreateGpgSignatureWorker).not_to receive(:perform_async).with(sample_commit.id, project.id)
+ expect(CreateGpgSignatureWorker).not_to receive(:perform_async)
execute_service(project, user, oldrev, newrev, ref)
end
@@ -769,7 +769,15 @@ describe GitPushService, services: true do
context 'when the signature is not yet cached' do
it 'queues a CreateGpgSignatureWorker' do
- expect(CreateGpgSignatureWorker).to receive(:perform_async).with(sample_commit.id, project.id)
+ expect(CreateGpgSignatureWorker).to receive(:perform_async).with([sample_commit.id], project.id)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+
+ it 'can queue several commits to create the gpg signature' do
+ allow(Gitlab::Git::Commit).to receive(:shas_with_signatures).and_return([sample_commit.id, another_sample_commit.id])
+
+ expect(CreateGpgSignatureWorker).to receive(:perform_async).with([sample_commit.id, another_sample_commit.id], project.id)
execute_service(project, user, oldrev, newrev, ref)
end
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index a9baccd061a..b54491cf5f9 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -49,7 +49,7 @@ describe Groups::DestroyService do
context 'Sidekiq inline' do
before do
# Run sidekiq immediately to check that renamed dir will be removed
- Sidekiq::Testing.inline! { destroy_group(group, user, async) }
+ perform_enqueued_jobs { destroy_group(group, user, async) }
end
it 'verifies that paths have been deleted' do
diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb
index 1737fd0a9fc..7c5c7409cc1 100644
--- a/spec/services/groups/update_service_spec.rb
+++ b/spec/services/groups/update_service_spec.rb
@@ -12,13 +12,17 @@ describe Groups::UpdateService do
let!(:service) { described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
before do
- public_group.add_user(user, Gitlab::Access::MASTER)
+ public_group.add_user(user, Gitlab::Access::OWNER)
create(:project, :public, group: public_group)
+
+ expect(TodosDestroyer::GroupPrivateWorker).not_to receive(:perform_in)
end
it "does not change permission level" do
service.execute
expect(public_group.errors.count).to eq(1)
+
+ expect(TodosDestroyer::GroupPrivateWorker).not_to receive(:perform_in)
end
end
@@ -26,8 +30,10 @@ describe Groups::UpdateService do
let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
- internal_group.add_user(user, Gitlab::Access::MASTER)
+ internal_group.add_user(user, Gitlab::Access::OWNER)
create(:project, :internal, group: internal_group)
+
+ expect(TodosDestroyer::GroupPrivateWorker).not_to receive(:perform_in)
end
it "does not change permission level" do
@@ -35,6 +41,24 @@ describe Groups::UpdateService do
expect(internal_group.errors.count).to eq(1)
end
end
+
+ context "internal group with private project" do
+ let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
+
+ before do
+ internal_group.add_user(user, Gitlab::Access::OWNER)
+ create(:project, :private, group: internal_group)
+
+ expect(TodosDestroyer::GroupPrivateWorker).to receive(:perform_in)
+ .with(1.hour, internal_group.id)
+ end
+
+ it "changes permission level to private" do
+ service.execute
+ expect(internal_group.visibility_level)
+ .to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
end
context "with parent_id user doesn't have permissions for" do
@@ -55,7 +79,7 @@ describe Groups::UpdateService do
context "unauthorized visibility_level validation" do
let!(:service) { described_class.new(internal_group, user, visibility_level: 99) }
before do
- internal_group.add_user(user, Gitlab::Access::MASTER)
+ internal_group.add_user(user, Gitlab::Access::MAINTAINER)
end
it "does not change permission level" do
@@ -68,7 +92,7 @@ describe Groups::UpdateService do
let!(:service) { described_class.new(internal_group, user, path: SecureRandom.hex) }
before do
- internal_group.add_user(user, Gitlab::Access::MASTER)
+ internal_group.add_user(user, Gitlab::Access::MAINTAINER)
create(:project, :internal, group: internal_group)
end
diff --git a/spec/services/import_export_clean_up_service_spec.rb b/spec/services/import_export_clean_up_service_spec.rb
index 1875d0448cd..d5fcef1246f 100644
--- a/spec/services/import_export_clean_up_service_spec.rb
+++ b/spec/services/import_export_clean_up_service_spec.rb
@@ -11,7 +11,6 @@ describe ImportExportCleanUpService do
path = '/invalid/path/'
stub_repository_downloads_path(path)
- expect(File).to receive(:directory?).with(path + tmp_import_export_folder).and_return(false).at_least(:once)
expect(service).not_to receive(:clean_up_export_files)
service.execute
@@ -38,6 +37,24 @@ describe ImportExportCleanUpService do
end
end
+ context 'with uploader exports' do
+ it 'removes old files' do
+ upload = create(:import_export_upload,
+ updated_at: 2.days.ago,
+ export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz'))
+
+ expect { service.execute }.to change { upload.reload.export_file.file.nil? }.to(true)
+ end
+
+ it 'does not remove new files' do
+ upload = create(:import_export_upload,
+ updated_at: 1.hour.ago,
+ export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz'))
+
+ expect { service.execute }.not_to change { upload.reload.export_file.file.nil? }
+ end
+ end
+
def in_directory_with_files(mtime:)
Dir.mktmpdir do |tmpdir|
stub_repository_downloads_path(tmpdir)
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 7ae49c06896..5e38d0aeb6a 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -9,7 +9,7 @@ describe Issues::CloseService do
let!(:todo) { create(:todo, :assigned, user: user, project: project, target: issue, author: user2) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_guest(guest)
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 79bcdc41fb0..c61c1ddcb3d 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -13,8 +13,8 @@ describe Issues::CreateService do
let(:labels) { create_pair(:label, project: project) }
before do
- project.add_master(user)
- project.add_master(assignee)
+ project.add_maintainer(user)
+ project.add_maintainer(assignee)
end
let(:opts) do
@@ -130,7 +130,7 @@ describe Issues::CreateService do
end
it 'invalidates open issues counter for assignees when issue is assigned' do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
described_class.new(project, user, opts).execute
@@ -160,7 +160,7 @@ describe Issues::CreateService do
context 'issue create service' do
context 'assignees' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'removes assignee when user id is invalid' do
@@ -180,7 +180,7 @@ describe Issues::CreateService do
end
it 'saves assignee when user id is valid' do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
opts = { title: 'Title', description: 'Description', assignee_ids: [assignee.id] }
issue = described_class.new(project, user, opts).execute
@@ -224,8 +224,8 @@ describe Issues::CreateService do
end
before do
- project.add_master(user)
- project.add_master(assignee)
+ project.add_maintainer(user)
+ project.add_maintainer(assignee)
end
it 'assigns and sets milestone to issuable from command' do
@@ -242,7 +242,7 @@ describe Issues::CreateService do
let(:project) { merge_request.source_project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'for a single discussion' do
diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb
index 42e5d544f4c..2a56075419b 100644
--- a/spec/services/issues/reopen_service_spec.rb
+++ b/spec/services/issues/reopen_service_spec.rb
@@ -20,11 +20,11 @@ describe Issues::ReopenService do
end
end
- context 'when user is authrized to reopen issue' do
+ context 'when user is authorized to reopen issue' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'invalidates counter cache for assignees' do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 158541d36e3..5bcfef46b75 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -18,7 +18,7 @@ describe Issues::UpdateService, :mailer do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_developer(user3)
end
@@ -55,6 +55,8 @@ describe Issues::UpdateService, :mailer do
end
it 'updates the issue with the given params' do
+ expect(TodosDestroyer::ConfidentialIssueWorker).not_to receive(:perform_in)
+
update_issue(opts)
expect(issue).to be_valid
@@ -74,6 +76,21 @@ describe Issues::UpdateService, :mailer do
.to change { project.open_issues_count }.from(1).to(0)
end
+ it 'enqueues ConfidentialIssueWorker when an issue is made confidential' do
+ expect(TodosDestroyer::ConfidentialIssueWorker).to receive(:perform_in).with(1.hour, issue.id)
+
+ update_issue(confidential: true)
+ end
+
+ it 'does not enqueue ConfidentialIssueWorker when an issue is made non confidential' do
+ # set confidentiality to true before the actual update
+ issue.update!(confidential: true)
+
+ expect(TodosDestroyer::ConfidentialIssueWorker).not_to receive(:perform_in)
+
+ update_issue(confidential: false)
+ end
+
it 'updates open issue counter for assignees when issue is reassigned' do
update_issue(assignee_ids: [user2.id])
@@ -501,7 +518,7 @@ describe Issues::UpdateService, :mailer do
let(:params) { { label_ids: [], remove_label_ids: [label.id] } }
before do
- issue.update_attributes(labels: [label, label3])
+ issue.update(labels: [label, label3])
end
it 'ignores the label_ids parameter' do
@@ -517,7 +534,7 @@ describe Issues::UpdateService, :mailer do
let(:params) { { add_label_ids: [label3.id], remove_label_ids: [label.id] } }
before do
- issue.update_attributes(labels: [label])
+ issue.update(labels: [label])
end
it 'adds the passed labels' do
@@ -596,7 +613,7 @@ describe Issues::UpdateService, :mailer do
context 'valid project' do
before do
- target_project.add_master(user)
+ target_project.add_maintainer(user)
end
it 'calls the move service with the proper issue and project' do
diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb
index 68d5660445a..97ba2742392 100644
--- a/spec/services/labels/find_or_create_service_spec.rb
+++ b/spec/services/labels/find_or_create_service_spec.rb
@@ -44,6 +44,26 @@ describe Labels::FindOrCreateService do
expect(service.execute).to eq project_label
end
end
+
+ context 'when include_ancestor_groups is true' do
+ let(:group) { create(:group, :nested) }
+ let(:params) do
+ {
+ title: 'Audit',
+ include_ancestor_groups: true
+ }
+ end
+
+ it 'returns the ancestor group labels' do
+ group_label = create(:group_label, group: group.parent, title: 'Audit')
+
+ expect(service.execute).to eq group_label
+ end
+
+ it 'creates new labels if labels are not found' do
+ expect { service.execute }.to change(project.labels, :count).by(1)
+ end
+ end
end
context 'when finding labels on group level' do
diff --git a/spec/services/lfs/unlock_file_service_spec.rb b/spec/services/lfs/unlock_file_service_spec.rb
index 948401d7bdc..539417644db 100644
--- a/spec/services/lfs/unlock_file_service_spec.rb
+++ b/spec/services/lfs/unlock_file_service_spec.rb
@@ -62,11 +62,11 @@ describe Lfs::UnlockFileService do
context 'when forced' do
let(:developer) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
before do
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
end
context 'by a regular user' do
@@ -86,7 +86,7 @@ describe Lfs::UnlockFileService do
end
context 'by a maintainer user' do
- let(:current_user) { master }
+ let(:current_user) { maintainer }
let(:params) do
{ id: lock.id,
force: true }
diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb
index 7076571b753..5c30f5b6a61 100644
--- a/spec/services/members/approve_access_request_service_spec.rb
+++ b/spec/services/members/approve_access_request_service_spec.rb
@@ -34,9 +34,9 @@ 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::MASTER).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::MASTER)
+ expect(member.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
end
@@ -97,7 +97,7 @@ describe Members::ApproveAccessRequestService do
context 'when current user can approve access request to the project' do
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
group.add_owner(current_user)
end
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index 1831c62d788..5c01463d757 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -6,7 +6,7 @@ describe Members::CreateService do
let(:project_user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'adds user to members' do
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
index 36b6e5a701e..0a5220c7c61 100644
--- a/spec/services/members/destroy_service_spec.rb
+++ b/spec/services/members/destroy_service_spec.rb
@@ -20,6 +20,11 @@ describe Members::DestroyService do
end
shared_examples 'a service destroying a member' do
+ before do
+ type = member.is_a?(GroupMember) ? 'Group' : 'Project'
+ expect(TodosDestroyer::EntityLeaveWorker).to receive(:perform_in).with(1.hour, member.user_id, member.source_id, type)
+ end
+
it 'destroys the member' do
expect { described_class.new(current_user).execute(member, opts) }.to change { member.source.members_and_requesters.count }.by(-1)
end
@@ -114,7 +119,7 @@ describe Members::DestroyService do
context 'when current user can destroy the given member' do
before do
- group_project.add_master(current_user)
+ group_project.add_maintainer(current_user)
group.add_owner(current_user)
end
@@ -142,8 +147,8 @@ describe Members::DestroyService do
context 'with an access requester' do
before do
- group_project.update_attributes(request_access_enabled: true)
- group.update_attributes(request_access_enabled: true)
+ group_project.update(request_access_enabled: true)
+ group.update(request_access_enabled: true)
group_project.request_access(member_user)
group.request_access(member_user)
end
@@ -170,7 +175,7 @@ describe Members::DestroyService do
context 'when current user can destroy the given access requester' do
before do
- group_project.add_master(current_user)
+ group_project.add_maintainer(current_user)
group.add_owner(current_user)
end
@@ -210,7 +215,7 @@ describe Members::DestroyService do
context 'when current user can destroy the given invited user' do
before do
- group_project.add_master(current_user)
+ group_project.add_maintainer(current_user)
group.add_owner(current_user)
end
diff --git a/spec/services/members/update_service_spec.rb b/spec/services/members/update_service_spec.rb
index a451272dd1f..6d19a95ffeb 100644
--- a/spec/services/members/update_service_spec.rb
+++ b/spec/services/members/update_service_spec.rb
@@ -8,7 +8,7 @@ describe Members::UpdateService do
let(:permission) { :update }
let(:member) { source.members_and_requesters.find_by!(user_id: member_user.id) }
let(:params) do
- { access_level: Gitlab::Access::MASTER }
+ { access_level: Gitlab::Access::MAINTAINER }
end
shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do
@@ -23,7 +23,7 @@ describe Members::UpdateService do
updated_member = described_class.new(current_user, params).execute(member, permission: permission)
expect(updated_member).to be_valid
- expect(updated_member.access_level).to eq(Gitlab::Access::MASTER)
+ expect(updated_member.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -44,7 +44,7 @@ describe Members::UpdateService do
context 'when current user can update the given member' do
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
group.add_owner(current_user)
end
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 216e0cd4266..433ffbd97f0 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -9,7 +9,7 @@ describe MergeRequests::CloseService do
let!(:todo) { create(:todo, :assigned, user: user, project: project, target: merge_request, author: user2) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_guest(guest)
end
diff --git a/spec/services/merge_requests/conflicts/list_service_spec.rb b/spec/services/merge_requests/conflicts/list_service_spec.rb
index d57852615d9..c81fa95e4b7 100644
--- a/spec/services/merge_requests/conflicts/list_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/list_service_spec.rb
@@ -2,8 +2,8 @@ require 'spec_helper'
describe MergeRequests::Conflicts::ListService do
describe '#can_be_resolved_in_ui?' do
- def create_merge_request(source_branch)
- create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', merge_status: :unchecked) do |mr|
+ def create_merge_request(source_branch, target_branch = 'conflict-start')
+ create(:merge_request, source_branch: source_branch, target_branch: target_branch, merge_status: :unchecked) do |mr|
mr.mark_as_unmergeable
end
end
@@ -34,7 +34,7 @@ describe MergeRequests::Conflicts::ListService do
it 'returns a falsey value when the MR does not support new diff notes' do
merge_request = create_merge_request('conflict-resolvable')
- merge_request.merge_request_diff.update_attributes(start_commit_sha: nil)
+ merge_request.merge_request_diff.update(start_commit_sha: nil)
expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
end
@@ -85,22 +85,10 @@ describe MergeRequests::Conflicts::ListService do
expect(service.can_be_resolved_in_ui?).to be_falsey
end
- context 'with gitaly disabled', :skip_gitaly_mock do
- it 'returns a falsey value when the MR has a missing ref after a force push' do
- merge_request = create_merge_request('conflict-resolvable')
- service = conflicts_service(merge_request)
- allow_any_instance_of(Rugged::Repository).to receive(:merge_commits).and_raise(Rugged::OdbError)
+ it 'returns a falsey value when the conflict is in a submodule revision' do
+ merge_request = create_merge_request('update-gitlab-shell-v-6-0-3', 'update-gitlab-shell-v-6-0-1')
- expect(service.can_be_resolved_in_ui?).to be_falsey
- end
-
- it 'returns a falsey value when the MR has a missing revision after a force push' do
- merge_request = create_merge_request('conflict-resolvable')
- service = conflicts_service(merge_request)
- allow(merge_request).to receive_message_chain(:target_branch_head, :raw, :id).and_return(Gitlab::Git::BLANK_SHA)
-
- expect(service.can_be_resolved_in_ui?).to be_falsey
- end
+ expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
end
end
end
diff --git a/spec/services/merge_requests/conflicts/resolve_service_spec.rb b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
index cff09237005..7edf8a96c94 100644
--- a/spec/services/merge_requests/conflicts/resolve_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
@@ -123,17 +123,6 @@ describe MergeRequests::Conflicts::ResolveService do
expect(merge_request_from_fork.source_branch_head.parents.map(&:id))
.to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813', target_head])
end
-
- context 'when gitaly is disabled', :skip_gitaly_mock do
- it 'gets conflicts from the source project' do
- # REFACTOR NOTE: We used to test that `project.repository.rugged` wasn't
- # used in this case, but since the refactor, for simplification,
- # we always use that repository for read only operations.
- expect(forked_project.repository.rugged).to receive(:merge_commits).and_call_original
-
- subject
- end
- end
end
end
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index 736a50b2c15..06fb61baf33 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -23,7 +23,7 @@ describe MergeRequests::CreateService do
let(:merge_request) { service.execute }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(assignee)
allow(service).to receive(:execute_hooks)
end
@@ -185,8 +185,8 @@ describe MergeRequests::CreateService do
end
before do
- project.add_master(user)
- project.add_master(assignee)
+ project.add_maintainer(user)
+ project.add_maintainer(assignee)
end
it 'assigns and sets milestone to issuable from command' do
@@ -202,7 +202,7 @@ describe MergeRequests::CreateService do
let(:assignee) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'removes assignee_id when user id is invalid' do
@@ -222,7 +222,7 @@ describe MergeRequests::CreateService do
end
it 'saves assignee when user id is valid' do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
opts = { title: 'Title', description: 'Description', assignee_id: assignee.id }
merge_request = described_class.new(project, user, opts).execute
@@ -242,7 +242,7 @@ describe MergeRequests::CreateService do
end
it 'invalidates open merge request counter for assignees when merge request is assigned' do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
described_class.new(project, user, opts).execute
@@ -286,7 +286,7 @@ describe MergeRequests::CreateService do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(assignee)
end
@@ -316,7 +316,7 @@ describe MergeRequests::CreateService do
context 'when user can not access source project' do
before do
target_project.add_developer(assignee)
- target_project.add_master(user)
+ target_project.add_maintainer(user)
end
it 'raises an error' do
@@ -328,7 +328,7 @@ describe MergeRequests::CreateService do
context 'when user can not access target project' do
before do
target_project.add_developer(assignee)
- target_project.add_master(user)
+ target_project.add_maintainer(user)
end
it 'raises an error' do
@@ -372,7 +372,7 @@ describe MergeRequests::CreateService do
before do
project.add_developer(assignee)
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'ignores source_project_id' do
diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb
index 28f56d19657..fe673de46aa 100644
--- a/spec/services/merge_requests/ff_merge_service_spec.rb
+++ b/spec/services/merge_requests/ff_merge_service_spec.rb
@@ -13,7 +13,7 @@ describe MergeRequests::FfMergeService do
let(:project) { merge_request.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
end
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index ef2738ef504..5d96b5ce27c 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -7,7 +7,7 @@ describe MergeRequests::MergeService do
let(:project) { merge_request.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
end
@@ -49,6 +49,7 @@ describe MergeRequests::MergeService do
issue = create :issue, project: project
commit = double('commit', safe_message: "Fixes #{issue.to_reference}")
allow(merge_request).to receive(:commits).and_return([commit])
+ merge_request.cache_merge_request_closes_issues!
service.execute(merge_request)
@@ -63,7 +64,7 @@ describe MergeRequests::MergeService do
let(:commit) { double('commit', safe_message: "Fixes #{jira_issue.to_reference}") }
before do
- project.update_attributes!(has_external_issue_tracker: true)
+ project.update!(has_external_issue_tracker: true)
jira_service_settings
stub_jira_urls(jira_issue.id)
allow(merge_request).to receive(:commits).and_return([commit])
diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb
index 46e4e3559dc..5ad6f5528f9 100644
--- a/spec/services/merge_requests/post_merge_service_spec.rb
+++ b/spec/services/merge_requests/post_merge_service_spec.rb
@@ -6,7 +6,7 @@ describe MergeRequests::PostMergeService do
let(:project) { merge_request.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
@@ -53,7 +53,7 @@ describe MergeRequests::PostMergeService do
allow(project).to receive(:default_branch).and_return('foo')
issue = create(:issue, project: project)
- allow(merge_request).to receive(:closes_issues).and_return([issue])
+ allow(merge_request).to receive(:visible_closing_issues_for).and_return([issue])
allow_any_instance_of(Issues::CloseService).to receive(:execute).with(issue, commit: merge_request).and_raise
expect { described_class.new(project, user, {}).execute(merge_request) }.to raise_error
diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb
index 757c31ab692..2703da7ae44 100644
--- a/spec/services/merge_requests/rebase_service_spec.rb
+++ b/spec/services/merge_requests/rebase_service_spec.rb
@@ -15,7 +15,7 @@ describe MergeRequests::RebaseService do
subject(:service) { described_class.new(project, user, {}) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
@@ -36,9 +36,9 @@ describe MergeRequests::RebaseService do
end
end
- context 'when unexpected error occurs', :disable_gitaly do
+ context 'when unexpected error occurs' do
before do
- allow(repository).to receive(:run_git!).and_raise('Something went wrong')
+ allow(repository).to receive(:gitaly_operation_client).and_raise('Something went wrong')
end
it 'saves a generic error message' do
@@ -53,9 +53,9 @@ describe MergeRequests::RebaseService do
end
end
- context 'with git command failure', :disable_gitaly do
+ context 'with git command failure' do
before do
- allow(repository).to receive(:run_git!).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong')
+ allow(repository).to receive(:gitaly_operation_client).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong')
end
it 'saves a generic error message' do
@@ -71,7 +71,7 @@ describe MergeRequests::RebaseService do
end
context 'valid params' do
- shared_examples 'successful rebase' do
+ describe 'successful rebase' do
before do
service.execute(merge_request)
end
@@ -97,26 +97,8 @@ describe MergeRequests::RebaseService do
end
end
- context 'when Gitaly rebase feature is enabled' do
- it_behaves_like 'successful rebase'
- end
-
- context 'when Gitaly rebase feature is disabled', :disable_gitaly do
- it_behaves_like 'successful rebase'
- end
-
- context 'git commands', :disable_gitaly do
- it 'sets GL_REPOSITORY env variable when calling git commands' do
- expect(repository).to receive(:popen).exactly(3)
- .with(anything, anything, hash_including('GL_REPOSITORY'), anything)
- .and_return(['', 0])
-
- service.execute(merge_request)
- end
- end
-
context 'fork' do
- shared_examples 'successful fork rebase' do
+ describe 'successful fork rebase' do
let(:forked_project) do
fork_project(project, user, repository: true)
end
@@ -140,14 +122,6 @@ describe MergeRequests::RebaseService do
expect(parent_sha).to eq(target_branch_sha)
end
end
-
- context 'when Gitaly rebase feature is enabled' do
- it_behaves_like 'successful fork rebase'
- end
-
- context 'when Gitaly rebase feature is disabled', :disable_gitaly do
- it_behaves_like 'successful fork rebase'
- end
end
end
end
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index 9ee37c51d95..21e71509ed6 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -8,7 +8,7 @@ describe MergeRequests::ReopenService do
let(:project) { merge_request.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_guest(guest)
end
@@ -47,6 +47,12 @@ describe MergeRequests::ReopenService do
end
end
+ it 'caches merge request closing issues' do
+ expect(merge_request).to receive(:cache_merge_request_closes_issues!)
+
+ described_class.new(project, user, {}).execute(merge_request)
+ end
+
it 'updates metrics' do
metrics = merge_request.metrics
service = double(MergeRequestMetricsService)
diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb
index ded17fa92a4..8ab09412f55 100644
--- a/spec/services/merge_requests/squash_service_spec.rb
+++ b/spec/services/merge_requests/squash_service_spec.rb
@@ -124,51 +124,6 @@ describe MergeRequests::SquashService do
message: a_string_including('squash'))
end
end
-
- context 'with Gitaly disabled', :skip_gitaly_mock do
- stages = {
- 'add worktree for squash' => 'worktree',
- 'configure sparse checkout' => 'config',
- 'get files in diff' => 'diff --name-only',
- 'check out target branch' => 'checkout',
- 'apply patch' => 'diff --binary',
- 'commit squashed changes' => 'commit',
- 'get SHA of squashed commit' => 'rev-parse'
- }
-
- stages.each do |stage, command|
- context "when the #{stage} stage fails" do
- before do
- git_command = a_collection_containing_exactly(
- a_string_starting_with("#{Gitlab.config.git.bin_path} #{command}")
- ).or(
- a_collection_starting_with([Gitlab.config.git.bin_path] + command.split)
- )
-
- allow(repository).to receive(:popen).and_return(['', 0])
- allow(repository).to receive(:popen).with(git_command, anything, anything, anything).and_return([error, 1])
- end
-
- it 'logs the stage and output' do
- expect(service).to receive(:log_error).with(log_error)
- expect(service).to receive(:log_error).with(error)
-
- service.execute(merge_request)
- end
-
- it 'returns an error' do
- expect(service.execute(merge_request)).to match(status: :error,
- message: a_string_including('squash'))
- end
-
- it 'cleans up the temporary directory' do
- expect(File.exist?(squash_dir_path)).to be(false)
-
- service.execute(merge_request)
- end
- end
- end
- end
end
context 'when any other exception is thrown' do
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 443dcd92a8b..f0029af83cc 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -19,7 +19,7 @@ describe MergeRequests::UpdateService, :mailer do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_developer(user3)
end
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
index adad73f7e11..3f7a544ea0a 100644
--- a/spec/services/milestones/close_service_spec.rb
+++ b/spec/services/milestones/close_service_spec.rb
@@ -6,7 +6,7 @@ describe Milestones::CloseService do
let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb
index f2a18c7295a..0c91112026f 100644
--- a/spec/services/milestones/create_service_spec.rb
+++ b/spec/services/milestones/create_service_spec.rb
@@ -7,7 +7,7 @@ describe Milestones::CreateService do
describe '#execute' do
context "valid params" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
opts = {
title: 'v2.1.9',
diff --git a/spec/services/milestones/destroy_service_spec.rb b/spec/services/milestones/destroy_service_spec.rb
index 9703780b0e9..6f3612501f4 100644
--- a/spec/services/milestones/destroy_service_spec.rb
+++ b/spec/services/milestones/destroy_service_spec.rb
@@ -8,7 +8,7 @@ describe Milestones::DestroyService do
let!(:merge_request) { create(:merge_request, source_project: project, milestone: milestone) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
def service
diff --git a/spec/services/milestones/promote_service_spec.rb b/spec/services/milestones/promote_service_spec.rb
index a0a2843b676..df212d912e9 100644
--- a/spec/services/milestones/promote_service_spec.rb
+++ b/spec/services/milestones/promote_service_spec.rb
@@ -10,7 +10,7 @@ describe Milestones::PromoteService do
describe '#execute' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
context 'validations' do
diff --git a/spec/services/milestones/update_service_spec.rb b/spec/services/milestones/update_service_spec.rb
new file mode 100644
index 00000000000..3b91442c0ba
--- /dev/null
+++ b/spec/services/milestones/update_service_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Milestones::UpdateService do
+ let(:project) { create(:project) }
+ let(:user) { build(:user) }
+ let(:milestone) { create(:milestone, project: project) }
+
+ describe '#execute' do
+ context "valid params" do
+ let(:inner_service) { double(:service) }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ subject { described_class.new(project, user, { title: 'new_title' }).execute(milestone) }
+
+ it { expect(subject).to be_valid }
+ it { expect(subject.title).to eq('new_title') }
+
+ context 'state_event is activate' do
+ it 'calls ReopenService' do
+ expect(Milestones::ReopenService).to receive(:new).with(project, user, {}).and_return(inner_service)
+ expect(inner_service).to receive(:execute).with(milestone)
+
+ described_class.new(project, user, { state_event: 'activate' }).execute(milestone)
+ end
+ end
+
+ context 'state_event is close' do
+ it 'calls ReopenService' do
+ expect(Milestones::CloseService).to receive(:new).with(project, user, {}).and_return(inner_service)
+ expect(inner_service).to receive(:execute).with(milestone)
+
+ described_class.new(project, user, { state_event: 'close' }).execute(milestone)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 2b2b983494f..b1290fd0d47 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -10,7 +10,7 @@ describe Notes::CreateService do
describe '#execute' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "valid params" do
@@ -145,7 +145,9 @@ describe Notes::CreateService do
let(:note_text) { %(HELLO\n/close\n/assign @#{user.username}\nWORLD) }
it 'saves the note and does not alter the note text' do
- expect_any_instance_of(Issues::UpdateService).to receive(:execute).and_call_original
+ service = double(:service)
+ allow(Issues::UpdateService).to receive(:new).and_return(service)
+ expect(service).to receive(:execute)
note = described_class.new(project, user, opts.merge(note: note_text)).execute
diff --git a/spec/services/notes/post_process_service_spec.rb b/spec/services/notes/post_process_service_spec.rb
index 4e2ab919f0f..5aae0d711c3 100644
--- a/spec/services/notes/post_process_service_spec.rb
+++ b/spec/services/notes/post_process_service_spec.rb
@@ -7,7 +7,7 @@ describe Notes::PostProcessService do
describe '#execute' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
note_opts = {
note: 'Awesome comment',
noteable_type: 'Issue',
diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index b1e218821d2..784dac55454 100644
--- a/spec/services/notes/quick_actions_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -3,11 +3,11 @@ require 'spec_helper'
describe Notes::QuickActionsService do
shared_context 'note on noteable' do
let(:project) { create(:project) }
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:assignee) { create(:user) }
before do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
end
end
@@ -184,7 +184,7 @@ describe Notes::QuickActionsService do
include_context 'note on noteable'
it 'delegates to the class method' do
- service = described_class.new(project, master)
+ service = described_class.new(project, maintainer)
note = create(:note_on_issue, project: project)
expect(described_class).to receive(:supported?).with(note)
@@ -194,7 +194,7 @@ describe Notes::QuickActionsService do
end
describe '#execute' do
- let(:service) { described_class.new(project, master) }
+ let(:service) { described_class.new(project, maintainer) }
it_behaves_like 'note on noteable that supports quick actions' do
let(:note) { build(:note_on_issue, project: project) }
@@ -212,19 +212,19 @@ describe Notes::QuickActionsService do
context 'CE restriction for issue assignees' do
describe '/assign' do
let(:project) { create(:project) }
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:assignee) { create(:user) }
- let(:master) { create(:user) }
- let(:service) { described_class.new(project, master) }
+ let(:maintainer) { create(:user) }
+ let(:service) { described_class.new(project, maintainer) }
let(:note) { create(:note_on_issue, note: note_text, project: project) }
let(:note_text) do
- %(/assign @#{assignee.username} @#{master.username}\n")
+ %(/assign @#{assignee.username} @#{maintainer.username}\n")
end
before do
- project.add_master(master)
- project.add_master(assignee)
+ project.add_maintainer(maintainer)
+ project.add_maintainer(assignee)
end
it 'adds only one assignee from the list' do
diff --git a/spec/services/notes/update_service_spec.rb b/spec/services/notes/update_service_spec.rb
index 65b1d613998..533dcdcd6cd 100644
--- a/spec/services/notes/update_service_spec.rb
+++ b/spec/services/notes/update_service_spec.rb
@@ -9,7 +9,7 @@ describe Notes::UpdateService do
let(:note) { create(:note, project: project, noteable: issue, author: user, note: "Old note #{user2.to_reference}") }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_developer(user3)
end
diff --git a/spec/services/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb
index 7f536ce4e68..14ba6b7bed2 100644
--- a/spec/services/notification_recipient_service_spec.rb
+++ b/spec/services/notification_recipient_service_spec.rb
@@ -12,15 +12,15 @@ describe NotificationRecipientService do
def create_watcher
watcher = create(:user)
- create(:notification_setting, project: project, user: watcher, level: :watch)
+ create(:notification_setting, source: project, user: watcher, level: :watch)
other_projects.each do |other_project|
- create(:notification_setting, project: other_project, user: watcher, level: :watch)
+ create(:notification_setting, source: other_project, user: watcher, level: :watch)
end
end
it 'avoids N+1 queries', :request_store do
- Gitlab::GitalyClient.allow_n_plus_1_calls { create_watcher }
+ create_watcher
service.build_new_note_recipients(note)
@@ -28,7 +28,7 @@ describe NotificationRecipientService do
service.build_new_note_recipients(note)
end
- Gitlab::GitalyClient.allow_n_plus_1_calls { create_watcher }
+ create_watcher
expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count)
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 0eadc83bfe3..c442f6fe32f 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -172,9 +172,9 @@ describe NotificationService, :mailer do
before do
build_team(note.project)
- project.add_master(issue.author)
- project.add_master(assignee)
- project.add_master(note.author)
+ project.add_maintainer(issue.author)
+ project.add_maintainer(assignee)
+ project.add_maintainer(note.author)
@u_custom_off = create_user_with_notification(:custom, 'custom_off')
project.add_guest(@u_custom_off)
@@ -255,8 +255,8 @@ describe NotificationService, :mailer do
describe 'new note on issue in project that belongs to a group' do
before do
note.project.namespace_id = group.id
- group.add_user(@u_watcher, GroupMember::MASTER)
- group.add_user(@u_custom_global, GroupMember::MASTER)
+ group.add_user(@u_watcher, GroupMember::MAINTAINER)
+ group.add_user(@u_custom_global, GroupMember::MAINTAINER)
note.project.save
@u_watcher.notification_settings_for(note.project).participating!
@@ -373,7 +373,7 @@ describe NotificationService, :mailer do
before do
build_team(note.project)
build_group(note.project)
- note.project.add_master(note.author)
+ note.project.add_maintainer(note.author)
add_users_with_subscription(note.project, issue)
reset_delivered_emails!
end
@@ -436,7 +436,7 @@ describe NotificationService, :mailer do
project.add_guest(@u_guest_watcher)
project.add_guest(@u_guest_custom)
add_member_for_parent_group(@pg_watcher, project)
- note.project.add_master(note.author)
+ note.project.add_maintainer(note.author)
reset_delivered_emails!
end
@@ -577,8 +577,8 @@ describe NotificationService, :mailer do
before do
build_team(note.project)
- project.add_master(merge_request.author)
- project.add_master(merge_request.assignee)
+ project.add_maintainer(merge_request.author)
+ project.add_maintainer(merge_request.assignee)
end
describe '#new_note' do
@@ -1088,8 +1088,8 @@ describe NotificationService, :mailer do
let(:merge_request) { create :merge_request, source_project: project, assignee: create(:user), description: 'cc @participant' }
before do
- project.add_master(merge_request.author)
- project.add_master(merge_request.assignee)
+ project.add_maintainer(merge_request.author)
+ project.add_maintainer(merge_request.assignee)
build_team(merge_request.target_project)
add_users_with_subscription(merge_request.target_project, merge_request)
update_custom_notification(:new_merge_request, @u_guest_custom, resource: project)
@@ -1314,7 +1314,7 @@ describe NotificationService, :mailer do
describe 'when merge_when_pipeline_succeeds is true' do
before do
- merge_request.update_attributes(
+ merge_request.update(
merge_when_pipeline_succeeds: true,
merge_user: create(:user)
)
@@ -1529,13 +1529,13 @@ describe NotificationService, :mailer do
let(:added_user) { create(:user) }
describe '#new_access_request' do
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:owner) { create(:user) }
let(:developer) { create(:user) }
let!(:group) do
create(:group, :public, :access_requestable) do |group|
group.add_owner(owner)
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_developer(developer)
end
end
@@ -1544,11 +1544,11 @@ describe NotificationService, :mailer do
reset_delivered_emails!
end
- it 'sends notification to group owners_and_masters' do
+ it 'sends notification to group owners_and_maintainers' do
group.request_access(added_user)
should_email(owner)
- should_email(master)
+ should_email(maintainer)
should_not_email(developer)
end
end
@@ -1601,11 +1601,11 @@ describe NotificationService, :mailer do
context 'for a project in a user namespace' do
let(:project) do
create(:project, :public, :access_requestable) do |project|
- project.add_master(project.owner)
+ project.add_maintainer(project.owner)
end
end
- it 'sends notification to project owners_and_masters' do
+ it 'sends notification to project owners_and_maintainers' do
project.request_access(added_user)
should_only_email(project.owner)
@@ -1621,7 +1621,7 @@ describe NotificationService, :mailer do
reset_delivered_emails!
end
- it 'sends notification to group owners_and_masters' do
+ it 'sends notification to group owners_and_maintainers' do
project.request_access(added_user)
should_only_email(group_owner)
@@ -1759,11 +1759,11 @@ describe NotificationService, :mailer do
end
before do
- project.add_master(u_member)
- project.add_master(u_watcher)
- project.add_master(u_custom_notification_unset)
- project.add_master(u_custom_notification_enabled)
- project.add_master(u_custom_notification_disabled)
+ project.add_maintainer(u_member)
+ project.add_maintainer(u_watcher)
+ project.add_maintainer(u_custom_notification_unset)
+ project.add_maintainer(u_custom_notification_enabled)
+ project.add_maintainer(u_custom_notification_disabled)
reset_delivered_emails!
end
@@ -1903,15 +1903,15 @@ describe NotificationService, :mailer do
set(:u_blocked) { create(:user, :blocked) }
set(:u_silence) { create_user_with_notification(:disabled, 'silent', project) }
set(:u_owner) { project.owner }
- set(:u_master1) { create(:user) }
- set(:u_master2) { create(:user) }
+ set(:u_maintainer1) { create(:user) }
+ set(:u_maintainer2) { create(:user) }
set(:u_developer) { create(:user) }
before do
- project.add_master(u_blocked)
- project.add_master(u_silence)
- project.add_master(u_master1)
- project.add_master(u_master2)
+ project.add_maintainer(u_blocked)
+ project.add_maintainer(u_silence)
+ project.add_maintainer(u_maintainer1)
+ project.add_maintainer(u_maintainer2)
project.add_developer(u_developer)
reset_delivered_emails!
@@ -1926,12 +1926,12 @@ describe NotificationService, :mailer do
describe "##{sym}" do
subject(:notify!) { notification.send(sym, domain) }
- it 'emails current watching masters' do
+ it 'emails current watching maintainers' do
expect(Notify).to receive(:"#{sym}_email").at_least(:once).and_call_original
notify!
- should_only_email(u_master1, u_master2, u_owner)
+ should_only_email(u_maintainer1, u_maintainer2, u_owner)
end
it 'emails nobody if the project is missing' do
@@ -1945,26 +1945,26 @@ describe NotificationService, :mailer do
end
describe '#pages_domain_verification_failed' do
- it 'emails current watching masters' do
+ it 'emails current watching maintainers' do
notification.pages_domain_verification_failed(domain)
- should_only_email(u_master1, u_master2, u_owner)
+ should_only_email(u_maintainer1, u_maintainer2, u_owner)
end
end
describe '#pages_domain_enabled' do
- it 'emails current watching masters' do
+ it 'emails current watching maintainers' do
notification.pages_domain_enabled(domain)
- should_only_email(u_master1, u_master2, u_owner)
+ should_only_email(u_maintainer1, u_maintainer2, u_owner)
end
end
describe '#pages_domain_disabled' do
- it 'emails current watching masters' do
+ it 'emails current watching maintainers' do
notification.pages_domain_disabled(domain)
- should_only_email(u_master1, u_master2, u_owner)
+ should_only_email(u_maintainer1, u_maintainer2, u_owner)
end
end
end
@@ -1988,15 +1988,15 @@ describe NotificationService, :mailer do
@u_guest_watcher = create_user_with_notification(:watch, 'guest_watching')
@u_guest_custom = create_user_with_notification(:custom, 'guest_custom')
- project.add_master(@u_watcher)
- project.add_master(@u_participating)
- project.add_master(@u_participant_mentioned)
- project.add_master(@u_disabled)
- project.add_master(@u_mentioned)
- project.add_master(@u_committer)
- project.add_master(@u_not_mentioned)
- project.add_master(@u_lazy_participant)
- project.add_master(@u_custom_global)
+ project.add_maintainer(@u_watcher)
+ project.add_maintainer(@u_participating)
+ project.add_maintainer(@u_participant_mentioned)
+ project.add_maintainer(@u_disabled)
+ project.add_maintainer(@u_mentioned)
+ project.add_maintainer(@u_committer)
+ project.add_maintainer(@u_not_mentioned)
+ project.add_maintainer(@u_lazy_participant)
+ project.add_maintainer(@u_custom_global)
end
# Users in the project's group but not part of project's team
@@ -2011,7 +2011,7 @@ describe NotificationService, :mailer do
# Group member: global=watch, group=global
@g_global_watcher ||= create_global_setting_for(create(:user), :watch)
- group.add_users([@g_watcher, @g_global_watcher], :master)
+ group.add_users([@g_watcher, @g_global_watcher], :maintainer)
group
end
@@ -2050,7 +2050,7 @@ describe NotificationService, :mailer do
project.reload
- project.group.parent.add_master(user)
+ project.group.parent.add_maintainer(user)
end
def should_email_nested_group_user(user, times: 1, recipients: email_recipients)
@@ -2072,11 +2072,11 @@ describe NotificationService, :mailer do
@subscribed_participant = create_global_setting_for(create(:user, username: 'subscribed_participant'), :participating)
@watcher_and_subscriber = create_global_setting_for(create(:user), :watch)
- project.add_master(@subscribed_participant)
- project.add_master(@subscriber)
- project.add_master(@unsubscriber)
- project.add_master(@watcher_and_subscriber)
- project.add_master(@unsubscribed_mentioned)
+ project.add_maintainer(@subscribed_participant)
+ project.add_maintainer(@subscriber)
+ project.add_maintainer(@unsubscriber)
+ project.add_maintainer(@watcher_and_subscriber)
+ project.add_maintainer(@unsubscribed_mentioned)
issuable.subscriptions.create(user: @unsubscribed_mentioned, project: project, subscribed: false)
issuable.subscriptions.create(user: @subscriber, project: project, subscribed: true)
diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb
index 64a9559791f..81dc7c57f4a 100644
--- a/spec/services/preview_markdown_service_spec.rb
+++ b/spec/services/preview_markdown_service_spec.rb
@@ -64,4 +64,16 @@ describe PreviewMarkdownService do
expect(result[:commands]).to eq 'Sets time estimate to 2y.'
end
end
+
+ it 'sets correct markdown engine' do
+ service = described_class.new(project, user, { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION })
+ result = service.execute
+
+ expect(result[:markdown_engine]).to eq :redcarpet
+
+ service = described_class.new(project, user, { markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION })
+ result = service.execute
+
+ expect(result[:markdown_engine]).to eq :common_mark
+ end
end
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index 6fd73a50511..e98df375d48 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -131,4 +131,58 @@ describe Projects::AutocompleteService do
end
end
end
+
+ describe '#labels_as_hash' do
+ def expect_labels_to_equal(labels, expected_labels)
+ expect(labels.size).to eq(expected_labels.size)
+ extract_title = lambda { |label| label['title'] }
+ expect(labels.map(&extract_title)).to eq(expected_labels.map(&extract_title))
+ end
+
+ let(:user) { create(:user) }
+ let(:group) { create(:group, :nested) }
+ let!(:sub_group) { create(:group, parent: group) }
+ let(:project) { create(:project, :public, group: group) }
+ let(:issue) { create(:issue, project: project) }
+
+ let!(:label1) { create(:label, project: project) }
+ let!(:label2) { create(:label, project: project) }
+ let!(:sub_group_label) { create(:group_label, group: sub_group) }
+ let!(:parent_group_label) { create(:group_label, group: group.parent) }
+
+ before do
+ create(:group_member, group: group, user: user)
+ end
+
+ it 'returns labels from project and ancestor groups' do
+ service = described_class.new(project, user)
+ results = service.labels_as_hash
+ expected_labels = [label1, label2, parent_group_label]
+
+ expect_labels_to_equal(results, expected_labels)
+ end
+
+ context 'some labels are already assigned' do
+ before do
+ issue.labels << label1
+ end
+
+ it 'marks already assigned as set' do
+ service = described_class.new(project, user)
+ results = service.labels_as_hash(issue)
+ expected_labels = [label1, label2, parent_group_label]
+
+ expect_labels_to_equal(results, expected_labels)
+
+ assigned_label_titles = issue.labels.map(&:title)
+ results.each do |hash|
+ if assigned_label_titles.include?(hash['title'])
+ expect(hash[:set]).to eq(true)
+ else
+ expect(hash.key?(:set)).to eq(false)
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb
index 9aa9237d875..141ccf7c4d8 100644
--- a/spec/services/projects/create_from_template_service_spec.rb
+++ b/spec/services/projects/create_from_template_service_spec.rb
@@ -2,10 +2,11 @@ require 'spec_helper'
describe Projects::CreateFromTemplateService do
let(:user) { create(:user) }
+ let(:template_name) { 'rails' }
let(:project_params) do
{
path: user.to_param,
- template_name: 'rails',
+ template_name: template_name,
description: 'project description',
visibility_level: Gitlab::VisibilityLevel::PUBLIC
}
@@ -14,7 +15,10 @@ describe Projects::CreateFromTemplateService do
subject { described_class.new(user, project_params) }
it 'calls the importer service' do
- expect_any_instance_of(Projects::GitlabProjectsImportService).to receive(:execute)
+ import_service_double = double
+
+ allow(Projects::GitlabProjectsImportService).to receive(:new).and_return(import_service_double)
+ expect(import_service_double).to receive(:execute)
subject.execute
end
@@ -26,9 +30,34 @@ describe Projects::CreateFromTemplateService do
expect(project.import_scheduled?).to be(true)
end
+ context 'when template is not present' do
+ let(:template_name) { 'non_existent' }
+ let(:project) { subject.execute }
+
+ before do
+ expect(project).to be_saved
+ end
+
+ it 'does not set import set import type' do
+ expect(project.import_type).to be nil
+ end
+
+ it 'does not set import set import source' do
+ expect(project.import_source).to be nil
+ end
+
+ it 'is not scheduled' do
+ expect(project.import_scheduled?).to be(false)
+ end
+
+ it 'repository is empty' do
+ expect(project.repository.empty?).to be(true)
+ end
+ end
+
context 'the result project' do
before do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
@project = subject.execute
end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index e8cbf84e3be..bb3f1501f0e 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -23,7 +23,7 @@ describe Projects::CreateService, '#execute' do
expect(project).to be_valid
expect(project.owner).to eq(user)
- expect(project.team.masters).to include(user)
+ expect(project.team.maintainers).to include(user)
expect(project.namespace).to eq(user.namespace)
end
end
@@ -47,7 +47,7 @@ describe Projects::CreateService, '#execute' do
expect(project).to be_persisted
expect(project.owner).to eq(user)
- expect(project.team.masters).to contain_exactly(user)
+ expect(project.team.maintainers).to contain_exactly(user)
expect(project.namespace).to eq(user.namespace)
end
end
@@ -114,6 +114,17 @@ describe Projects::CreateService, '#execute' do
end
end
+ context 'import data' do
+ it 'stores import data and URL' do
+ import_data = { data: { 'test' => 'some data' } }
+ project = create_project(user, { name: 'test', import_url: 'http://import-url', import_data: import_data })
+
+ expect(project.import_data).to be_persisted
+ expect(project.import_data.data).to eq(import_data[:data])
+ expect(project.import_url).to eq('http://import-url')
+ end
+ end
+
context 'builds_enabled global setting' do
let(:project) { create_project(user, opts) }
@@ -236,6 +247,18 @@ describe Projects::CreateService, '#execute' do
end
end
+ context 'when readme initialization is requested' do
+ it 'creates README.md' do
+ opts[:initialize_with_readme] = '1'
+
+ project = create_project(user, opts)
+
+ expect(project.repository.commit_count).to be(1)
+ expect(project.repository.readme.name).to eql('README.md')
+ expect(project.repository.readme.data).to include('# GitLab')
+ end
+ end
+
context 'when there is an active service template' do
before do
create(:service, project: nil, template: true, active: true)
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 38660ad7a01..e428808ab68 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -45,18 +45,18 @@ describe Projects::DestroyService do
shared_examples 'handles errors thrown during async destroy' do |error_message|
it 'does not allow the error to bubble up' do
expect do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end.not_to raise_error
end
it 'unmarks the project as "pending deletion"' do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
expect(project.reload.pending_delete).to be(false)
end
it 'stores an error message in `projects.delete_error`' do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
expect(project.reload.delete_error).to be_present
expect(project.delete_error).to include(error_message)
@@ -66,7 +66,7 @@ describe Projects::DestroyService do
context 'Sidekiq inline' do
before do
# Run sidekiq immediatly to check that renamed repository will be removed
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end
context 'when has remote mirrors' do
@@ -110,7 +110,7 @@ describe Projects::DestroyService do
end
it 'keeps project team intact upon an error' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
begin
destroy_project(project, user, {})
rescue ::Redis::CannotConnectError
@@ -128,7 +128,7 @@ describe Projects::DestroyService do
before do
project.project_feature.update_attribute("issues_access_level", ProjectFeature::PRIVATE)
# Run sidekiq immediately to check that renamed repository will be removed
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end
it_behaves_like 'deleting the project'
@@ -172,7 +172,7 @@ describe Projects::DestroyService do
it 'allows error to bubble up and rolls back project deletion' do
expect do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end.to raise_error(Exception, 'Other error message')
expect(project.reload.pending_delete).to be(false)
diff --git a/spec/services/projects/detect_repository_languages_service_spec.rb b/spec/services/projects/detect_repository_languages_service_spec.rb
new file mode 100644
index 00000000000..f90d558938f
--- /dev/null
+++ b/spec/services/projects/detect_repository_languages_service_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+describe Projects::DetectRepositoryLanguagesService, :clean_gitlab_redis_shared_state do
+ set(:project) { create(:project, :repository) }
+
+ subject { described_class.new(project, project.owner) }
+
+ before do
+ allow(Feature).to receive(:disabled?).and_return(false)
+ end
+
+ describe '#execute' do
+ context 'without previous detection' do
+ it 'inserts new programming languages in the database' do
+ subject.execute
+
+ expect(ProgrammingLanguage.exists?(name: 'Ruby')).to be(true)
+ expect(ProgrammingLanguage.count).to be(4)
+ end
+
+ it 'inserts the repository langauges' do
+ names = subject.execute.map(&:name)
+
+ expect(names).to eq(%w[Ruby JavaScript HTML CoffeeScript])
+ end
+ end
+
+ context 'with a previous detection' do
+ before do
+ subject.execute
+
+ allow(project.repository).to receive(:languages).and_return(
+ [{ value: 99.63, label: "Ruby", color: "#701516", highlight: "#701516" },
+ { value: 0.3, label: "D", color: "#701516", highlight: "#701516" }]
+ )
+ end
+
+ it 'updates the repository languages' do
+ repository_languages = subject.execute.map(&:name)
+
+ expect(repository_languages).to eq(%w[Ruby D])
+ end
+ end
+
+ context 'when no repository exists' do
+ set(:project) { create(:project) }
+
+ it 'has no languages' do
+ expect(subject.execute).to be_empty
+ expect(project.repository_languages).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index c15f5120b8a..f89f9b54f53 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -135,7 +135,7 @@ describe Projects::ForkService do
context "when project has restricted visibility level" do
context "and only one visibility level is restricted" do
before do
- @from_project.update_attributes(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ @from_project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
end
diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb
index 0a898e9b89b..b5f2c826c97 100644
--- a/spec/services/projects/gitlab_projects_import_service_spec.rb
+++ b/spec/services/projects/gitlab_projects_import_service_spec.rb
@@ -3,63 +3,13 @@ require 'spec_helper'
describe Projects::GitlabProjectsImportService do
set(:namespace) { create(:namespace) }
let(:path) { 'test-path' }
- let(:file) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') }
+ let(:file) { fixture_file_upload('spec/fixtures/project_export.tar.gz') }
let(:overwrite) { false }
let(:import_params) { { namespace_id: namespace.id, path: path, file: file, overwrite: overwrite } }
+
subject { described_class.new(namespace.owner, import_params) }
describe '#execute' do
- context 'with an invalid path' do
- let(:path) { '/invalid-path/' }
-
- it 'returns an invalid project' do
- project = subject.execute
-
- expect(project).not_to be_persisted
- expect(project).not_to be_valid
- end
- end
-
- context 'with a valid path' do
- it 'creates a project' do
- project = subject.execute
-
- expect(project).to be_persisted
- expect(project).to be_valid
- end
- end
-
- context 'override params' do
- it 'stores them as import data when passed' do
- project = described_class
- .new(namespace.owner, import_params, description: 'Hello')
- .execute
-
- expect(project.import_data.data['override_params']['description']).to eq('Hello')
- end
- end
-
- context 'when there is a project with the same path' do
- let(:existing_project) { create(:project, namespace: namespace) }
- let(:path) { existing_project.path}
-
- it 'does not create the project' do
- project = subject.execute
-
- expect(project).to be_invalid
- expect(project).not_to be_persisted
- end
-
- context 'when overwrite param is set' do
- let(:overwrite) { true }
-
- it 'creates a project in a temporary full_path' do
- project = subject.execute
-
- expect(project).to be_valid
- expect(project).to be_persisted
- end
- end
- end
+ it_behaves_like 'gitlab projects import validations'
end
end
diff --git a/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb b/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
index fb6d7171ac3..28d8a95fe07 100644
--- a/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
@@ -1,21 +1,22 @@
require 'spec_helper'
describe Projects::HashedStorage::MigrateAttachmentsService do
- subject(:service) { described_class.new(project) }
+ subject(:service) { described_class.new(project, project.full_path, logger: nil) }
+
let(:project) { create(:project, :legacy_storage) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) }
let!(:upload) { Upload.find_by(path: file_uploader.upload_path) }
let(:file_uploader) { build(:file_uploader, project: project) }
- let(:old_path) { File.join(base_path(legacy_storage), upload.path) }
- let(:new_path) { File.join(base_path(hashed_storage), upload.path) }
+ let(:old_disk_path) { File.join(base_path(legacy_storage), upload.path) }
+ let(:new_disk_path) { File.join(base_path(hashed_storage), upload.path) }
context '#execute' do
context 'when succeeds' do
it 'moves attachments to hashed storage layout' do
- expect(File.file?(old_path)).to be_truthy
- expect(File.file?(new_path)).to be_falsey
+ expect(File.file?(old_disk_path)).to be_truthy
+ expect(File.file?(new_disk_path)).to be_falsey
expect(File.exist?(base_path(legacy_storage))).to be_truthy
expect(File.exist?(base_path(hashed_storage))).to be_falsey
expect(FileUtils).to receive(:mv).with(base_path(legacy_storage), base_path(hashed_storage)).and_call_original
@@ -24,8 +25,8 @@ describe Projects::HashedStorage::MigrateAttachmentsService do
expect(File.exist?(base_path(hashed_storage))).to be_truthy
expect(File.exist?(base_path(legacy_storage))).to be_falsey
- expect(File.file?(old_path)).to be_falsey
- expect(File.file?(new_path)).to be_truthy
+ expect(File.file?(old_disk_path)).to be_falsey
+ expect(File.file?(new_disk_path)).to be_truthy
end
end
@@ -40,7 +41,7 @@ describe Projects::HashedStorage::MigrateAttachmentsService do
service.execute
expect(File.exist?(base_path(hashed_storage))).to be_falsey
- expect(File.file?(new_path)).to be_falsey
+ expect(File.file?(new_disk_path)).to be_falsey
end
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 ed4930313c5..5f67c325223 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -3,10 +3,11 @@ require 'spec_helper'
describe Projects::HashedStorage::MigrateRepositoryService do
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) }
- let(:service) { described_class.new(project) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) }
+ subject(:service) { described_class.new(project, project.full_path) }
+
describe '#execute' do
before do
allow(service).to receive(:gitlab_shell) { gitlab_shell }
diff --git a/spec/services/projects/hashed_storage_migration_service_spec.rb b/spec/services/projects/hashed_storage_migration_service_spec.rb
index e8e18bb3ac0..5368c3828dd 100644
--- a/spec/services/projects/hashed_storage_migration_service_spec.rb
+++ b/spec/services/projects/hashed_storage_migration_service_spec.rb
@@ -2,14 +2,19 @@ require 'spec_helper'
describe Projects::HashedStorageMigrationService do
let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) }
- subject(:service) { described_class.new(project) }
+ let(:logger) { double }
+
+ subject(:service) { described_class.new(project, project.full_path, logger: logger) }
describe '#execute' do
context 'repository migration' do
- let(:repository_service) { Projects::HashedStorage::MigrateRepositoryService.new(project, subject.logger) }
+ let(:repository_service) { Projects::HashedStorage::MigrateRepositoryService.new(project, project.full_path, logger: logger) }
it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do
- expect(Projects::HashedStorage::MigrateRepositoryService).to receive(:new).with(project, subject.logger).and_return(repository_service)
+ expect(Projects::HashedStorage::MigrateRepositoryService)
+ .to receive(:new)
+ .with(project, project.full_path, logger: logger)
+ .and_return(repository_service)
expect(repository_service).to receive(:execute)
service.execute
@@ -24,10 +29,13 @@ describe Projects::HashedStorageMigrationService do
end
context 'attachments migration' do
- let(:attachments_service) { Projects::HashedStorage::MigrateAttachmentsService.new(project, subject.logger) }
+ let(:attachments_service) { Projects::HashedStorage::MigrateAttachmentsService.new(project, project.full_path, logger: logger) }
it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do
- expect(Projects::HashedStorage::MigrateAttachmentsService).to receive(:new).with(project, subject.logger).and_return(attachments_service)
+ expect(Projects::HashedStorage::MigrateAttachmentsService)
+ .to receive(:new)
+ .with(project, project.full_path, logger: logger)
+ .and_return(attachments_service)
expect(attachments_service).to receive(:execute)
service.execute
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index 1cf373d1d72..18ecef1c0a1 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -35,7 +35,7 @@ describe Projects::HousekeepingService do
allow(subject).to receive(:gc_period).and_return(1)
project.increment_pushes_since_gc
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect { subject.execute }.to change { project.pushes_since_gc }.to(0)
end
end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index b3815045792..e2a600d12d1 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -69,7 +69,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The repository could not be created."
+ expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - The repository could not be created."
end
context 'when repository creation succeeds' do
@@ -141,7 +141,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository"
+ expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - Failed to import the repository"
end
context 'when repository import scheduled' do
diff --git a/spec/services/projects/move_access_service_spec.rb b/spec/services/projects/move_access_service_spec.rb
index a820ebd91f4..88d9d93c33b 100644
--- a/spec/services/projects/move_access_service_spec.rb
+++ b/spec/services/projects/move_access_service_spec.rb
@@ -4,18 +4,18 @@ describe Projects::MoveAccessService do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project_with_access) { create(:project, namespace: user.namespace) }
- let(:master_user) { create(:user) }
+ let(:maintainer_user) { create(:user) }
let(:reporter_user) { create(:user) }
let(:developer_user) { create(:user) }
- let(:master_group) { create(:group) }
+ let(:maintainer_group) { create(:group) }
let(:reporter_group) { create(:group) }
let(:developer_group) { create(:group) }
before do
- project_with_access.add_master(master_user)
+ project_with_access.add_maintainer(maintainer_user)
project_with_access.add_developer(developer_user)
project_with_access.add_reporter(reporter_user)
- project_with_access.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ project_with_access.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
project_with_access.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
project_with_access.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER)
end
@@ -87,7 +87,7 @@ describe Projects::MoveAccessService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining memberships' do
- target_project.add_master(master_user)
+ target_project.add_maintainer(maintainer_user)
subject.execute(project_with_access, options)
@@ -95,7 +95,7 @@ describe Projects::MoveAccessService do
end
it 'does not remove remaining group links' do
- target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ target_project.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
subject.execute(project_with_access, options)
diff --git a/spec/services/projects/move_project_authorizations_service_spec.rb b/spec/services/projects/move_project_authorizations_service_spec.rb
index f7262b9b887..b4408393624 100644
--- a/spec/services/projects/move_project_authorizations_service_spec.rb
+++ b/spec/services/projects/move_project_authorizations_service_spec.rb
@@ -4,7 +4,7 @@ describe Projects::MoveProjectAuthorizationsService do
let!(:user) { create(:user) }
let(:project_with_users) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
- let(:master_user) { create(:user) }
+ let(:maintainer_user) { create(:user) }
let(:reporter_user) { create(:user) }
let(:developer_user) { create(:user) }
@@ -12,7 +12,7 @@ describe Projects::MoveProjectAuthorizationsService do
describe '#execute' do
before do
- project_with_users.add_master(master_user)
+ project_with_users.add_maintainer(maintainer_user)
project_with_users.add_developer(developer_user)
project_with_users.add_reporter(reporter_user)
end
@@ -28,7 +28,7 @@ describe Projects::MoveProjectAuthorizationsService do
end
it 'does not move existent authorizations to the current project' do
- target_project.add_master(developer_user)
+ target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
expect(project_with_users.authorized_users.count).to eq 4
@@ -44,7 +44,7 @@ describe Projects::MoveProjectAuthorizationsService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining project authorizations' do
- target_project.add_master(developer_user)
+ target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
subject.execute(project_with_users, options)
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 e3d06e6d3d7..7ca8cf304fe 100644
--- a/spec/services/projects/move_project_group_links_service_spec.rb
+++ b/spec/services/projects/move_project_group_links_service_spec.rb
@@ -4,7 +4,7 @@ describe Projects::MoveProjectGroupLinksService do
let!(:user) { create(:user) }
let(:project_with_groups) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
- let(:master_group) { create(:group) }
+ let(:maintainer_group) { create(:group) }
let(:reporter_group) { create(:group) }
let(:developer_group) { create(:group) }
@@ -12,7 +12,7 @@ describe Projects::MoveProjectGroupLinksService do
describe '#execute' do
before do
- project_with_groups.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ project_with_groups.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
project_with_groups.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
project_with_groups.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER)
end
@@ -28,7 +28,7 @@ describe Projects::MoveProjectGroupLinksService do
end
it 'does not move existent group links in the current project' do
- target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ 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)
expect(project_with_groups.project_group_links.count).to eq 3
@@ -53,7 +53,7 @@ describe Projects::MoveProjectGroupLinksService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining project group links' do
- target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ 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)
diff --git a/spec/services/projects/move_project_members_service_spec.rb b/spec/services/projects/move_project_members_service_spec.rb
index 9c9a2d2fde1..c8c0eac1f13 100644
--- a/spec/services/projects/move_project_members_service_spec.rb
+++ b/spec/services/projects/move_project_members_service_spec.rb
@@ -4,7 +4,7 @@ describe Projects::MoveProjectMembersService do
let!(:user) { create(:user) }
let(:project_with_users) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
- let(:master_user) { create(:user) }
+ let(:maintainer_user) { create(:user) }
let(:reporter_user) { create(:user) }
let(:developer_user) { create(:user) }
@@ -12,7 +12,7 @@ describe Projects::MoveProjectMembersService do
describe '#execute' do
before do
- project_with_users.add_master(master_user)
+ project_with_users.add_maintainer(maintainer_user)
project_with_users.add_developer(developer_user)
project_with_users.add_reporter(reporter_user)
end
@@ -28,7 +28,7 @@ describe Projects::MoveProjectMembersService do
end
it 'does not move existent members to the current project' do
- target_project.add_master(developer_user)
+ target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
expect(project_with_users.project_members.count).to eq 4
@@ -53,7 +53,7 @@ describe Projects::MoveProjectMembersService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining project members' do
- target_project.add_master(developer_user)
+ target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
subject.execute(project_with_users, options)
diff --git a/spec/services/projects/overwrite_project_service_spec.rb b/spec/services/projects/overwrite_project_service_spec.rb
index 252c61f4224..c7900629f5f 100644
--- a/spec/services/projects/overwrite_project_service_spec.rb
+++ b/spec/services/projects/overwrite_project_service_spec.rb
@@ -96,10 +96,10 @@ describe Projects::OverwriteProjectService do
context 'when project with elements' do
it_behaves_like 'overwrite actions' do
- let(:master_user) { create(:user) }
+ let(:maintainer_user) { create(:user) }
let(:reporter_user) { create(:user) }
let(:developer_user) { create(:user) }
- let(:master_group) { create(:group) }
+ let(:maintainer_group) { create(:group) }
let(:reporter_group) { create(:group) }
let(:developer_group) { create(:group) }
@@ -107,10 +107,10 @@ describe Projects::OverwriteProjectService do
create_list(:deploy_keys_project, 2, project: project_from)
create_list(:notification_setting, 2, source: project_from)
create_list(:users_star_project, 2, project: project_from)
- project_from.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ project_from.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
project_from.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
project_from.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER)
- project_from.add_master(master_user)
+ project_from.add_maintainer(maintainer_user)
project_from.add_developer(developer_user)
project_from.add_reporter(reporter_user)
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 5100987c2fe..1a85c52fc97 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -31,12 +31,6 @@ describe Projects::TransferService do
transfer_project(project, user, group)
end
- it 'expires full_path cache' do
- expect(project).to receive(:expires_full_path_cache)
-
- transfer_project(project, user, group)
- end
-
it 'invalidates the user\'s personal_project_count cache' do
expect(user).to receive(:invalidate_personal_projects_count)
@@ -247,7 +241,7 @@ describe Projects::TransferService do
let(:group_member) { create(:user) }
before do
- group.add_user(owner, GroupMember::MASTER)
+ group.add_user(owner, GroupMember::MAINTAINER)
group.add_user(group_member, GroupMember::DEVELOPER)
end
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index 1bffeee6790..36b619ba9be 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -24,8 +24,8 @@ describe Projects::UpdatePagesService do
let(:extension) { 'zip' }
before do
- build.update_attributes(legacy_artifacts_file: file)
- build.update_attributes(legacy_artifacts_metadata: metadata)
+ build.update(legacy_artifacts_file: file)
+ build.update(legacy_artifacts_metadata: metadata)
end
describe 'pages artifacts' do
@@ -62,13 +62,13 @@ describe Projects::UpdatePagesService do
end
it 'fails if sha on branch is not latest' do
- build.update_attributes(ref: 'feature')
+ build.update(ref: 'feature')
expect(execute).not_to eq(:success)
end
it 'fails for empty file fails' do
- build.update_attributes(legacy_artifacts_file: empty_file)
+ build.update(legacy_artifacts_file: empty_file)
expect { execute }
.to raise_error(Projects::UpdatePagesService::FailedToExtractError)
@@ -79,7 +79,7 @@ describe Projects::UpdatePagesService do
context "for a valid job" do
before do
create(:ci_job_artifact, file: file, job: build)
- create(:ci_job_artifact, file_type: :metadata, file: metadata, job: build)
+ create(:ci_job_artifact, file_type: :metadata, file_format: :gzip, file: metadata, job: build)
build.reload
end
@@ -118,7 +118,7 @@ describe Projects::UpdatePagesService do
end
it 'fails if sha on branch is not latest' do
- build.update_attributes(ref: 'feature')
+ build.update(ref: 'feature')
expect(execute).not_to eq(:success)
end
@@ -188,7 +188,7 @@ describe Projects::UpdatePagesService do
end
it 'fails for invalid archive' do
- build.update_attributes(legacy_artifacts_file: invalid_file)
+ build.update(legacy_artifacts_file: invalid_file)
expect(execute).not_to eq(:success)
end
@@ -199,8 +199,8 @@ describe Projects::UpdatePagesService do
file = fixture_file_upload('spec/fixtures/pages.zip')
metafile = fixture_file_upload('spec/fixtures/pages.zip.meta')
- build.update_attributes(legacy_artifacts_file: file)
- build.update_attributes(legacy_artifacts_metadata: metafile)
+ build.update(legacy_artifacts_file: file)
+ build.update(legacy_artifacts_metadata: metafile)
allow(build).to receive(:artifacts_metadata_entry)
.and_return(metadata)
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index ecf1ba05618..9572b4110d5 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -15,6 +15,8 @@ describe Projects::UpdateService do
context 'when changing visibility level' do
context 'when visibility_level is INTERNAL' do
it 'updates the project to internal' do
+ expect(TodosDestroyer::ProjectPrivateWorker).not_to receive(:perform_in)
+
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
expect(result).to eq({ status: :success })
@@ -24,12 +26,30 @@ describe Projects::UpdateService do
context 'when visibility_level is PUBLIC' do
it 'updates the project to public' do
+ expect(TodosDestroyer::ProjectPrivateWorker).not_to receive(:perform_in)
+
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+
expect(result).to eq({ status: :success })
expect(project).to be_public
end
end
+ context 'when visibility_level is PRIVATE' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'updates the project to private' do
+ expect(TodosDestroyer::ProjectPrivateWorker).to receive(:perform_in).with(1.hour, project.id)
+
+ result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+
+ expect(result).to eq({ status: :success })
+ expect(project).to be_private
+ end
+ end
+
context 'when visibility levels are restricted to PUBLIC only' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
@@ -38,6 +58,7 @@ describe Projects::UpdateService do
context 'when visibility_level is INTERNAL' do
it 'updates the project to internal' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+
expect(result).to eq({ status: :success })
expect(project).to be_internal
end
@@ -54,6 +75,7 @@ describe Projects::UpdateService do
context 'when updated by an admin' do
it 'updates the project to public' do
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+
expect(result).to eq({ status: :success })
expect(project).to be_public
end
@@ -166,6 +188,20 @@ describe Projects::UpdateService do
end
end
+ context 'when changing feature visibility to private' do
+ it 'updates the visibility correctly' do
+ expect(TodosDestroyer::PrivateFeaturesWorker)
+ .to receive(:perform_in).with(1.hour, project.id)
+
+ result = update_project(project, user, project_feature_attributes:
+ { issues_access_level: ProjectFeature::PRIVATE }
+ )
+
+ expect(result).to eq({ status: :success })
+ expect(project.project_feature.issues_access_level).to be(ProjectFeature::PRIVATE)
+ end
+ end
+
context 'when updating a project that contains container images' do
before do
stub_container_registry_config(enabled: true)
@@ -212,6 +248,21 @@ describe Projects::UpdateService do
expect(project.errors.messages).to have_key(:base)
expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk')
end
+
+ context 'when hashed storage enabled' do
+ before do
+ stub_application_setting(hashed_storage_enabled: true)
+ end
+
+ it 'migrates project to a hashed storage instead of renaming the repo to another legacy name' do
+ result = update_project(project, admin, path: 'new-path')
+
+ expect(result).not_to include(status: :error)
+ expect(project).to be_valid
+ expect(project.errors).to be_empty
+ expect(project.reload.hashed_storage?(:repository)).to be_truthy
+ end
+ end
end
context 'with hashed storage' do
diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb
index 786493c3577..79b744142c6 100644
--- a/spec/services/protected_branches/create_service_spec.rb
+++ b/spec/services/protected_branches/create_service_spec.rb
@@ -6,8 +6,8 @@ describe ProtectedBranches::CreateService do
let(:params) do
{
name: 'master',
- merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }],
- push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
}
end
@@ -16,8 +16,8 @@ describe ProtectedBranches::CreateService do
it 'creates a new protected branch' do
expect { service.execute }.to change(ProtectedBranch, :count).by(1)
- expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
- expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
+ expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
context 'when user does not have permission' do
diff --git a/spec/services/protected_tags/create_service_spec.rb b/spec/services/protected_tags/create_service_spec.rb
index c3ed95aaebf..b16acf1d36c 100644
--- a/spec/services/protected_tags/create_service_spec.rb
+++ b/spec/services/protected_tags/create_service_spec.rb
@@ -6,7 +6,7 @@ describe ProtectedTags::CreateService do
let(:params) do
{
name: 'master',
- create_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]
+ create_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
}
end
@@ -15,7 +15,7 @@ describe ProtectedTags::CreateService do
it 'creates a new protected tag' do
expect { service.execute }.to change(ProtectedTag, :count).by(1)
- expect(project.protected_tags.last.create_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_tags.last.create_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
end
end
diff --git a/spec/services/reset_project_cache_service_spec.rb b/spec/services/reset_project_cache_service_spec.rb
index de475d16586..1490ad5fe3b 100644
--- a/spec/services/reset_project_cache_service_spec.rb
+++ b/spec/services/reset_project_cache_service_spec.rb
@@ -18,7 +18,7 @@ describe ResetProjectCacheService do
context 'when project cache_index is a numeric value' do
before do
- project.update_attributes(jobs_cache_index: 1)
+ project.update(jobs_cache_index: 1)
end
it 'increments project cache index' do
diff --git a/spec/services/resource_events/change_labels_service_spec.rb b/spec/services/resource_events/change_labels_service_spec.rb
new file mode 100644
index 00000000000..41b0fb3eea3
--- /dev/null
+++ b/spec/services/resource_events/change_labels_service_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ResourceEvents::ChangeLabelsService do
+ set(:project) { create(:project) }
+ set(:author) { create(:user) }
+ let(:resource) { create(:issue, project: project) }
+
+ 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) }
+
+ def expect_label_event(event, label, action)
+ expect(event.user).to eq(author)
+ expect(event.label).to eq(label)
+ expect(event.action).to eq(action)
+ end
+
+ context 'when adding a label' do
+ let(:added) { [labels[0]] }
+ let(:removed) { [] }
+
+ it 'creates new label event' do
+ expect { subject }.to change { resource.resource_label_events.count }.from(0).to(1)
+
+ expect_label_event(resource.resource_label_events.first, labels[0], 'add')
+ end
+ end
+
+ context 'when removing a label' do
+ let(:added) { [] }
+ let(:removed) { [labels[1]] }
+
+ it 'creates new label event' do
+ expect { subject }.to change { resource.resource_label_events.count }.from(0).to(1)
+
+ expect_label_event(resource.resource_label_events.first, labels[1], 'remove')
+ end
+ end
+
+ context 'when both adding and removing labels' do
+ let(:added) { [labels[0]] }
+ let(:removed) { [labels[1]] }
+
+ it 'creates all label events in a single query' do
+ expect(Gitlab::Database).to receive(:bulk_insert).once.and_call_original
+ expect { subject }.to change { resource.resource_label_events.count }.from(0).to(2)
+ end
+ end
+ end
+end
diff --git a/spec/services/search/global_service_spec.rb b/spec/services/search/global_service_spec.rb
index d8dba26e194..980545b8083 100644
--- a/spec/services/search/global_service_spec.rb
+++ b/spec/services/search/global_service_spec.rb
@@ -10,7 +10,7 @@ describe Search::GlobalService do
let!(:public_project) { create(:project, :public, name: 'searchable_public_project') }
before do
- found_project.add_master(user)
+ found_project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index 02de83a2df8..e5e036c7d44 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -16,7 +16,7 @@ describe SearchService do
let(:public_project) { create(:project, :public, name: 'public_project') }
before do
- accessible_project.add_master(user)
+ accessible_project.add_maintainer(user)
end
describe '#project' do
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 51396d34f8f..e0335880e8e 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -75,7 +75,7 @@ describe SystemHooksService do
end
it 'handles nil datetime columns' do
- user.update_attributes(created_at: nil, updated_at: nil)
+ user.update(created_at: nil, updated_at: nil)
data = event_data(user, :destroy)
expect(data[:created_at]).to be(nil)
diff --git a/spec/services/todos/destroy/confidential_issue_service_spec.rb b/spec/services/todos/destroy/confidential_issue_service_spec.rb
new file mode 100644
index 00000000000..3294f7509aa
--- /dev/null
+++ b/spec/services/todos/destroy/confidential_issue_service_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Todos::Destroy::ConfidentialIssueService do
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+ let(:author) { create(:user) }
+ let(:assignee) { create(:user) }
+ let(:guest) { create(:user) }
+ let(:project_member) { create(:user) }
+ let(:issue) { create(:issue, project: project, author: author, assignees: [assignee]) }
+
+ let!(:todo_issue_non_member) { create(:todo, user: user, target: issue, project: project) }
+ let!(:todo_issue_member) { create(:todo, user: project_member, target: issue, project: project) }
+ let!(:todo_issue_author) { create(:todo, user: author, target: issue, project: project) }
+ let!(:todo_issue_asignee) { create(:todo, user: assignee, target: issue, project: project) }
+ let!(:todo_issue_guest) { create(:todo, user: guest, target: issue, project: project) }
+ let!(:todo_another_non_member) { create(:todo, user: user, project: project) }
+
+ describe '#execute' do
+ before do
+ project.add_developer(project_member)
+ project.add_guest(guest)
+ end
+
+ subject { described_class.new(issue.id).execute }
+
+ context 'when provided issue is confidential' do
+ before do
+ issue.update!(confidential: true)
+ end
+
+ it 'removes issue todos for users who can not access the confidential issue' do
+ expect { subject }.to change { Todo.count }.from(6).to(4)
+ end
+ end
+
+ context 'when provided issue is not confidential' do
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+ end
+end
diff --git a/spec/services/todos/destroy/entity_leave_service_spec.rb b/spec/services/todos/destroy/entity_leave_service_spec.rb
new file mode 100644
index 00000000000..8cb91e7c1b9
--- /dev/null
+++ b/spec/services/todos/destroy/entity_leave_service_spec.rb
@@ -0,0 +1,290 @@
+require 'spec_helper'
+
+describe Todos::Destroy::EntityLeaveService do
+ let(:group) { create(:group, :private) }
+ let(:project) { create(:project, group: group) }
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+ let(:issue) { create(:issue, project: project, confidential: true) }
+ let(:mr) { create(:merge_request, source_project: project) }
+
+ let!(:todo_mr_user) { create(:todo, user: user, target: mr, project: project) }
+ let!(:todo_issue_user) { create(:todo, user: user, target: issue, project: project) }
+ let!(:todo_group_user) { create(:todo, user: user, group: group) }
+ let!(:todo_issue_user2) { create(:todo, user: user2, target: issue, project: project) }
+ let!(:todo_group_user2) { create(:todo, user: user2, group: group) }
+
+ describe '#execute' do
+ context 'when a user leaves a project' do
+ subject { described_class.new(user.id, project.id, 'Project').execute }
+
+ context 'when project is private' do
+ it 'removes project todos for the provided user' do
+ expect { subject }.to change { Todo.count }.from(5).to(3)
+
+ expect(user.todos).to match_array([todo_group_user])
+ expect(user2.todos).to match_array([todo_issue_user2, todo_group_user2])
+ end
+
+ context 'when the user is member of the project' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when the user is a project guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'removes only confidential issues todos' do
+ expect { subject }.to change { Todo.count }.from(5).to(4)
+ end
+ end
+
+ context 'when the user is member of a parent group' do
+ before do
+ group.add_developer(user)
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when the user is guest of a parent group' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'removes only confidential issues todos' do
+ expect { subject }.to change { Todo.count }.from(5).to(4)
+ end
+ end
+ end
+
+ context 'when project is not private' do
+ before do
+ group.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ end
+
+ context 'confidential issues' do
+ context 'when a user is not an author of confidential issue' do
+ it 'removes only confidential issues todos' do
+ expect { subject }.to change { Todo.count }.from(5).to(4)
+ end
+ end
+
+ context 'when a user is an author of confidential issue' do
+ before do
+ issue.update!(author: user)
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when a user is an assignee of confidential issue' do
+ before do
+ issue.assignees << user
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when a user is a project guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'removes only confidential issues todos' do
+ expect { subject }.to change { Todo.count }.from(5).to(4)
+ end
+ end
+
+ context 'when a user is a project guest but group developer' do
+ before do
+ project.add_guest(user)
+ group.add_developer(user)
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+ end
+
+ context 'feature visibility check' do
+ context 'when issues are visible only to project members' do
+ before do
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'removes only users issue todos' do
+ expect { subject }.to change { Todo.count }.from(5).to(4)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when a user leaves a group' do
+ subject { described_class.new(user.id, group.id, 'Group').execute }
+
+ context 'when group is private' do
+ it 'removes group and subproject todos for the user' do
+ expect { subject }.to change { Todo.count }.from(5).to(2)
+
+ expect(user.todos).to be_empty
+ expect(user2.todos).to match_array([todo_issue_user2, todo_group_user2])
+ end
+
+ context 'when the user is member of the group' do
+ before do
+ group.add_developer(user)
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when the user is member of the group project but not the group' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'with nested groups', :nested_groups do
+ let(:subgroup) { create(:group, :private, parent: group) }
+ let(:subgroup2) { create(:group, :private, parent: group) }
+ let(:subproject) { create(:project, group: subgroup) }
+ let(:subproject2) { create(:project, group: subgroup2) }
+
+ let!(:todo_subproject_user) { create(:todo, user: user, project: subproject) }
+ let!(:todo_subproject2_user) { create(:todo, user: user, project: subproject2) }
+ let!(:todo_subgroup_user) { create(:todo, user: user, group: subgroup) }
+ let!(:todo_subgroup2_user) { create(:todo, user: user, group: subgroup2) }
+ let!(:todo_subproject_user2) { create(:todo, user: user2, project: subproject) }
+ let!(:todo_subpgroup_user2) { create(:todo, user: user2, group: subgroup) }
+
+ context 'when the user is not a member of any groups/projects' do
+ it 'removes todos for the user including subprojects todos' do
+ expect { subject }.to change { Todo.count }.from(11).to(4)
+
+ expect(user.todos).to be_empty
+ expect(user2.todos)
+ .to match_array(
+ [todo_issue_user2, todo_group_user2, todo_subproject_user2, todo_subpgroup_user2]
+ )
+ end
+ end
+
+ context 'when the user is member of a parent group' do
+ before do
+ parent_group = create(:group)
+ group.update!(parent: parent_group)
+ parent_group.add_developer(user)
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when the user is member of a subgroup' do
+ before do
+ subgroup.add_developer(user)
+ end
+
+ it 'does not remove group and subproject todos' do
+ expect { subject }.to change { Todo.count }.from(11).to(7)
+
+ expect(user.todos).to match_array([todo_group_user, todo_subgroup_user, todo_subproject_user])
+ expect(user2.todos)
+ .to match_array(
+ [todo_issue_user2, todo_group_user2, todo_subproject_user2, todo_subpgroup_user2]
+ )
+ end
+ end
+
+ context 'when the user is member of a child project' do
+ before do
+ subproject.add_developer(user)
+ end
+
+ it 'does not remove subproject and group todos' do
+ expect { subject }.to change { Todo.count }.from(11).to(7)
+
+ expect(user.todos).to match_array([todo_subgroup_user, todo_group_user, todo_subproject_user])
+ expect(user2.todos)
+ .to match_array(
+ [todo_issue_user2, todo_group_user2, todo_subproject_user2, todo_subpgroup_user2]
+ )
+ end
+ end
+ end
+ end
+
+ context 'when group is not private' do
+ before do
+ group.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ end
+
+ context 'when user is not member' do
+ it 'removes only confidential issues todos' do
+ expect { subject }.to change { Todo.count }.from(5).to(4)
+ end
+ end
+
+ context 'when user is a project guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'removes only confidential issues todos' do
+ expect { subject }.to change { Todo.count }.from(5).to(4)
+ end
+ end
+
+ context 'when user is a project guest & group developer' do
+ before do
+ project.add_guest(user)
+ group.add_developer(user)
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+ end
+ end
+
+ context 'when entity type is not valid' do
+ it 'raises an exception' do
+ expect { described_class.new(user.id, group.id, 'GroupWrongly').execute }
+ .to raise_error(ArgumentError)
+ end
+ end
+
+ context 'when entity was not found' do
+ it 'does not remove any todos' do
+ expect { described_class.new(user.id, 999999, 'Group').execute }
+ .not_to change { Todo.count }
+ end
+ end
+ end
+end
diff --git a/spec/services/todos/destroy/group_private_service_spec.rb b/spec/services/todos/destroy/group_private_service_spec.rb
new file mode 100644
index 00000000000..2f49b68f544
--- /dev/null
+++ b/spec/services/todos/destroy/group_private_service_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe Todos::Destroy::GroupPrivateService do
+ let(:group) { create(:group, :public) }
+ let(:project) { create(:project, group: group) }
+ let(:user) { create(:user) }
+ let(:group_member) { create(:user) }
+ let(:project_member) { create(:user) }
+
+ let!(:todo_non_member) { create(:todo, user: user, group: group) }
+ let!(:todo_another_non_member) { create(:todo, user: user, group: group) }
+ let!(:todo_group_member) { create(:todo, user: group_member, group: group) }
+ let!(:todo_project_member) { create(:todo, user: project_member, group: group) }
+
+ describe '#execute' do
+ before do
+ group.add_developer(group_member)
+ project.add_developer(project_member)
+ end
+
+ subject { described_class.new(group.id).execute }
+
+ context 'when a group set to private' do
+ before do
+ group.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'removes todos only for users who are not group users' do
+ expect { subject }.to change { Todo.count }.from(4).to(2)
+
+ expect(user.todos).to be_empty
+ expect(group_member.todos).to match_array([todo_group_member])
+ expect(project_member.todos).to match_array([todo_project_member])
+ end
+
+ context 'with nested groups', :nested_groups do
+ let(:parent_group) { create(:group) }
+ let(:subgroup) { create(:group, :private, parent: group) }
+ let(:subproject) { create(:project, group: subgroup) }
+
+ let(:parent_member) { create(:user) }
+ let(:subgroup_member) { create(:user) }
+ let(:subgproject_member) { create(:user) }
+
+ let!(:todo_parent_member) { create(:todo, user: parent_member, group: group) }
+ let!(:todo_subgroup_member) { create(:todo, user: subgroup_member, group: group) }
+ let!(:todo_subproject_member) { create(:todo, user: subgproject_member, group: group) }
+
+ before do
+ group.update!(parent: parent_group)
+
+ parent_group.add_developer(parent_member)
+ subgroup.add_developer(subgroup_member)
+ subproject.add_developer(subgproject_member)
+ end
+
+ it 'removes todos only for users who are not group users' do
+ expect { subject }.to change { Todo.count }.from(7).to(5)
+ end
+ end
+ end
+
+ context 'when group is not private' do
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+ end
+end
diff --git a/spec/services/todos/destroy/private_features_service_spec.rb b/spec/services/todos/destroy/private_features_service_spec.rb
new file mode 100644
index 00000000000..be8b5bb3979
--- /dev/null
+++ b/spec/services/todos/destroy/private_features_service_spec.rb
@@ -0,0 +1,143 @@
+require 'spec_helper'
+
+describe Todos::Destroy::PrivateFeaturesService do
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+ let(:another_user) { create(:user) }
+ let(:project_member) { create(:user) }
+ let(:issue) { create(:issue, project: project) }
+ let(:mr) { create(:merge_request, source_project: project) }
+
+ let!(:todo_mr_non_member) { create(:todo, user: user, target: mr, project: project) }
+ let!(:todo_mr_non_member2) { create(:todo, user: another_user, target: mr, project: project) }
+ let!(:todo_mr_member) { create(:todo, user: project_member, target: mr, project: project) }
+ let!(:todo_issue_non_member) { create(:todo, user: user, target: issue, project: project) }
+ let!(:todo_issue_non_member2) { create(:todo, user: another_user, target: issue, project: project) }
+ let!(:todo_issue_member) { create(:todo, user: project_member, target: issue, project: project) }
+ let!(:commit_todo_non_member) { create(:on_commit_todo, user: user, project: project) }
+ let!(:commit_todo_non_member2) { create(:on_commit_todo, user: another_user, project: project) }
+ let!(:commit_todo_member) { create(:on_commit_todo, user: project_member, project: project) }
+
+ before do
+ project.add_developer(project_member)
+ end
+
+ context 'when user_id is provided' do
+ subject { described_class.new(project.id, user.id).execute }
+
+ context 'when all feaures have same visibility as the project' do
+ it 'removes only user issue todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when issues are visible only to project members but the user is a member' do
+ before do
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ project.add_developer(user)
+ end
+
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when issues are visible only to project members' do
+ before do
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'removes only user issue todos' do
+ expect { subject }.to change { Todo.count }.from(9).to(8)
+ end
+ end
+
+ context 'when mrs, builds and repository are visible only to project members' do
+ before do
+ # builds and merge requests cannot have higher visibility than repository
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(repository_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'removes only user mr and commit todos' do
+ expect { subject }.to change { Todo.count }.from(9).to(7)
+ end
+ end
+
+ context 'when mrs are visible only to project members' do
+ before do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'removes only user merge request todo' do
+ expect { subject }.to change { Todo.count }.from(9).to(8)
+ end
+ end
+
+ context 'when mrs and issues are visible only to project members' do
+ before do
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'removes only user merge request and issue todos' do
+ expect { subject }.to change { Todo.count }.from(9).to(7)
+ end
+ end
+ end
+
+ context 'when user_id is not provided' do
+ subject { described_class.new(project.id).execute }
+
+ context 'when all feaures have same visibility as the project' do
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when issues are visible only to project members' do
+ before do
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'removes only non members issue todos' do
+ expect { subject }.to change { Todo.count }.from(9).to(7)
+ end
+ end
+
+ context 'when mrs, builds and repository are visible only to project members' do
+ before do
+ # builds and merge requests cannot have higher visibility than repository
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(repository_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'removes only non members mr and commit todos' do
+ expect { subject }.to change { Todo.count }.from(9).to(5)
+ end
+ end
+
+ context 'when mrs are visible only to project members' do
+ before do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'removes only non members merge request todos' do
+ expect { subject }.to change { Todo.count }.from(9).to(7)
+ end
+ end
+
+ context 'when mrs and issues are visible only to project members' do
+ before do
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'removes only non members merge request and issue todos' do
+ expect { subject }.to change { Todo.count }.from(9).to(5)
+ end
+ end
+ end
+end
diff --git a/spec/services/todos/destroy/project_private_service_spec.rb b/spec/services/todos/destroy/project_private_service_spec.rb
new file mode 100644
index 00000000000..128d3487514
--- /dev/null
+++ b/spec/services/todos/destroy/project_private_service_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Todos::Destroy::ProjectPrivateService do
+ let(:group) { create(:group, :public) }
+ let(:project) { create(:project, :public, group: group) }
+ let(:user) { create(:user) }
+ let(:project_member) { create(:user) }
+ let(:group_member) { create(:user) }
+
+ let!(:todo_non_member) { create(:todo, user: user, project: project) }
+ let!(:todo2_non_member) { create(:todo, user: user, project: project) }
+ let!(:todo_member) { create(:todo, user: project_member, project: project) }
+ let!(:todo_group_member) { create(:todo, user: group_member, project: project) }
+
+ describe '#execute' do
+ before do
+ project.add_developer(project_member)
+ group.add_developer(group_member)
+ end
+
+ subject { described_class.new(project.id).execute }
+
+ context 'when a project set to private' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'removes issue todos for a user who is not a member' do
+ expect { subject }.to change { Todo.count }.from(4).to(2)
+
+ expect(user.todos).to be_empty
+ expect(project_member.todos).to match_array([todo_member])
+ expect(group_member.todos).to match_array([todo_group_member])
+ end
+ end
+
+ context 'when project is not private' do
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+ end
+end
diff --git a/spec/services/users/activity_service_spec.rb b/spec/services/users/activity_service_spec.rb
index 17eabad73be..719b4adf212 100644
--- a/spec/services/users/activity_service_spec.rb
+++ b/spec/services/users/activity_service_spec.rb
@@ -1,60 +1,73 @@
require 'spec_helper'
describe Users::ActivityService do
- include UserActivitiesHelpers
+ include ExclusiveLeaseHelpers
- let(:user) { create(:user) }
+ let(:user) { create(:user, last_activity_on: last_activity_on) }
- subject(:service) { described_class.new(user, 'type') }
+ subject { described_class.new(user, 'type') }
describe '#execute', :clean_gitlab_redis_shared_state do
context 'when last activity is nil' do
- before do
- service.execute
- end
+ let(:last_activity_on) { nil }
- it 'sets the last activity timestamp for the user' do
- expect(last_hour_user_ids).to eq([user.id])
+ it 'updates last_activity_on for the user' do
+ expect { subject.execute }
+ .to change(user, :last_activity_on).from(last_activity_on).to(Date.today)
end
+ end
- it 'updates the same user' do
- service.execute
+ context 'when last activity is in the past' do
+ let(:last_activity_on) { Date.today - 1.week }
- expect(last_hour_user_ids).to eq([user.id])
+ it 'updates last_activity_on for the user' do
+ expect { subject.execute }
+ .to change(user, :last_activity_on)
+ .from(last_activity_on)
+ .to(Date.today)
end
+ end
+
+ context 'when a bad object is passed' do
+ let(:fake_object) { double(username: 'hello') }
+
+ it 'does not record activity' do
+ service = described_class.new(fake_object, 'pull')
- it 'updates the timestamp of an existing user' do
- Timecop.freeze(Date.tomorrow) do
- expect { service.execute }.to change { user_activity(user) }.to(Time.now.to_i.to_s)
- end
+ expect(service).not_to receive(:record_activity)
+
+ service.execute
end
+ end
- describe 'other user' do
- it 'updates other user' do
- other_user = create(:user)
- described_class.new(other_user, 'type').execute
+ context 'when last activity is today' do
+ let(:last_activity_on) { Date.today }
- expect(last_hour_user_ids).to match_array([user.id, other_user.id])
- end
+ it 'does not update last_activity_on' do
+ expect { subject.execute }.not_to change(user, :last_activity_on)
end
end
context 'when in GitLab read-only instance' do
+ let(:last_activity_on) { nil }
+
before do
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
end
- it 'does not update last_activity_at' do
- service.execute
-
- expect(last_hour_user_ids).to eq([])
+ it 'does not update last_activity_on' do
+ expect { subject.execute }.not_to change(user, :last_activity_on)
end
end
- end
- def last_hour_user_ids
- Gitlab::UserActivities.new
- .select { |k, v| v >= 1.hour.ago.to_i.to_s }
- .map { |k, _| k.to_i }
+ context 'when a lease could not be obtained' do
+ let(:last_activity_on) { nil }
+
+ it 'does not update last_activity_on' do
+ stub_exclusive_lease_taken("acitvity_service:#{user.id}", timeout: 1.minute.to_i)
+
+ expect { subject.execute }.not_to change(user, :last_activity_on)
+ end
+ end
end
end
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index f82d4b483e7..3bae8bfbd42 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -173,7 +173,7 @@ describe Users::DestroyService do
describe "user personal's repository removal" do
before do
- Sidekiq::Testing.inline! { service.execute(user) }
+ perform_enqueued_jobs { service.execute(user) }
end
context 'legacy storage' do
diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb
index e5fde07a6eb..122b96ef216 100644
--- a/spec/services/users/refresh_authorized_projects_service_spec.rb
+++ b/spec/services/users/refresh_authorized_projects_service_spec.rb
@@ -30,10 +30,10 @@ describe Users::RefreshAuthorizedProjectsService do
it 'updates the authorized projects of the user' do
project2 = create(:project)
to_remove = user.project_authorizations
- .create!(project: project2, access_level: Gitlab::Access::MASTER)
+ .create!(project: project2, access_level: Gitlab::Access::MAINTAINER)
expect(service).to receive(:update_authorizations)
- .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
+ .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MAINTAINER]])
service.execute_without_lease
end
@@ -45,7 +45,7 @@ describe Users::RefreshAuthorizedProjectsService do
.create!(project: project, access_level: Gitlab::Access::DEVELOPER)
expect(service).to receive(:update_authorizations)
- .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
+ .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MAINTAINER]])
service.execute_without_lease
end
@@ -76,14 +76,14 @@ describe Users::RefreshAuthorizedProjectsService do
it 'inserts authorizations that should be added' do
user.project_authorizations.delete_all
- service.update_authorizations([], [[user.id, project.id, Gitlab::Access::MASTER]])
+ service.update_authorizations([], [[user.id, project.id, Gitlab::Access::MAINTAINER]])
authorizations = user.project_authorizations
expect(authorizations.length).to eq(1)
expect(authorizations[0].user_id).to eq(user.id)
expect(authorizations[0].project_id).to eq(project.id)
- expect(authorizations[0].access_level).to eq(Gitlab::Access::MASTER)
+ expect(authorizations[0].access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -99,12 +99,12 @@ describe Users::RefreshAuthorizedProjectsService do
end
it 'sets the values to the access levels' do
- expect(hash.values).to eq([Gitlab::Access::MASTER])
+ expect(hash.values).to eq([Gitlab::Access::MAINTAINER])
end
context 'personal projects' do
it 'includes the project with the right access level' do
- expect(hash[project.id]).to eq(Gitlab::Access::MASTER)
+ expect(hash[project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -139,11 +139,11 @@ describe Users::RefreshAuthorizedProjectsService do
let!(:other_project) { create(:project, group: nested_group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'includes the project with the right access level' do
- expect(hash[other_project.id]).to eq(Gitlab::Access::MASTER)
+ expect(hash[other_project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -153,7 +153,7 @@ describe Users::RefreshAuthorizedProjectsService do
let!(:project_group_link) { create(:project_group_link, project: other_project, group: group, group_access: Gitlab::Access::GUEST) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'includes the project with the right access level' do
@@ -168,7 +168,7 @@ describe Users::RefreshAuthorizedProjectsService do
let!(:project_group_link) { create(:project_group_link, project: other_project, group: nested_group, group_access: Gitlab::Access::DEVELOPER) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'includes the project with the right access level' do
@@ -194,7 +194,7 @@ describe Users::RefreshAuthorizedProjectsService do
value = hash.values[0]
expect(value.project_id).to eq(project.id)
- expect(value.access_level).to eq(Gitlab::Access::MASTER)
+ expect(value.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -219,7 +219,7 @@ describe Users::RefreshAuthorizedProjectsService do
end
it 'includes the access level for every row' do
- expect(row.access_level).to eq(Gitlab::Access::MASTER)
+ expect(row.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
end
@@ -235,7 +235,7 @@ describe Users::RefreshAuthorizedProjectsService do
rows = service.fresh_authorizations.to_a
expect(rows.length).to eq(1)
- expect(rows.first.access_level).to eq(Gitlab::Access::MASTER)
+ expect(rows.first.access_level).to eq(Gitlab::Access::MAINTAINER)
end
context 'every returned row' do
@@ -246,7 +246,7 @@ describe Users::RefreshAuthorizedProjectsService do
end
it 'includes the access level' do
- expect(row.access_level).to eq(Gitlab::Access::MASTER)
+ expect(row.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
end
diff --git a/spec/services/users/set_status_service_spec.rb b/spec/services/users/set_status_service_spec.rb
new file mode 100644
index 00000000000..8a8458ab9de
--- /dev/null
+++ b/spec/services/users/set_status_service_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Users::SetStatusService do
+ let(:current_user) { create(:user) }
+ subject(:service) { described_class.new(current_user, params) }
+
+ describe '#execute' do
+ context 'when when params are set' do
+ let(:params) { { emoji: 'taurus', message: 'a random status' } }
+
+ it 'creates a status' do
+ service.execute
+
+ expect(current_user.status.emoji).to eq('taurus')
+ expect(current_user.status.message).to eq('a random status')
+ end
+
+ it 'updates a status if it already existed' do
+ create(:user_status, user: current_user)
+
+ expect { service.execute }.not_to change { UserStatus.count }
+ expect(current_user.status.message).to eq('a random status')
+ end
+
+ context 'for another user' do
+ let(:target_user) { create(:user) }
+ let(:params) do
+ { emoji: 'taurus', message: 'a random status', user: target_user }
+ end
+
+ context 'the current user is admin' do
+ let(:current_user) { create(:admin) }
+
+ it 'changes the status when the current user is allowed to do that' do
+ expect { service.execute }.to change { target_user.status }
+ end
+ end
+
+ it 'does not update the status if the current user is not allowed' do
+ expect { service.execute }.not_to change { target_user.status }
+ end
+ end
+ end
+
+ context 'without params' do
+ let(:params) { {} }
+
+ 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
+ end
+end
diff --git a/spec/services/users/update_service_spec.rb b/spec/services/users/update_service_spec.rb
index a4b7fe4674f..529c8485202 100644
--- a/spec/services/users/update_service_spec.rb
+++ b/spec/services/users/update_service_spec.rb
@@ -30,6 +30,27 @@ describe Users::UpdateService do
expect(result[:message]).to eq('Username has already been taken')
end
+ it 'updates the status if status params were given' do
+ update_user(user, status: { message: "On a call" })
+
+ expect(user.status.message).to eq("On a call")
+ end
+
+ it 'does not delete the status if no status param was passed' do
+ create(:user_status, user: user, message: 'Busy!')
+
+ update_user(user, name: 'New name')
+
+ expect(user.status.message).to eq('Busy!')
+ end
+
+ it 'includes status error messages' do
+ result = update_user(user, status: { emoji: "Moo!" })
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq("Emoji is not included in the list")
+ end
+
def update_user(user, opts)
described_class.new(user, opts.merge(user: user)).execute
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index fdce8e84620..bd564cc60a6 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -4,7 +4,7 @@ SimpleCovEnv.start!
ENV["RAILS_ENV"] = 'test'
ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true'
-require File.expand_path("../../config/environment", __FILE__)
+require File.expand_path('../config/environment', __dir__)
require 'rspec/rails'
require 'shoulda/matchers'
require 'rspec/retry'
@@ -170,6 +170,17 @@ RSpec.configure do |config|
redis_queues_cleanup!
end
+ config.around(:each, :use_clean_rails_memory_store_fragment_caching) do |example|
+ caching_store = ActionController::Base.cache_store
+ ActionController::Base.cache_store = ActiveSupport::Cache::MemoryStore.new
+ ActionController::Base.perform_caching = true
+
+ example.run
+
+ ActionController::Base.perform_caching = false
+ ActionController::Base.cache_store = caching_store
+ end
+
# The :each scope runs "inside" the example, so this hook ensures the DB is in the
# correct state before any examples' before hooks are called. This prevents a
# problem where `ScheduleIssuesClosedAtTypeChange` (or any migration that depends
diff --git a/spec/support/api/repositories_shared_context.rb b/spec/support/api/repositories_shared_context.rb
index ea38fe4f5b8..f1341804e56 100644
--- a/spec/support/api/repositories_shared_context.rb
+++ b/spec/support/api/repositories_shared_context.rb
@@ -1,6 +1,6 @@
shared_context 'disabled repository' do
before do
- project.project_feature.update_attributes!(
+ project.project_feature.update!(
repository_access_level: ProjectFeature::DISABLED,
merge_requests_access_level: ProjectFeature::DISABLED,
builds_access_level: ProjectFeature::DISABLED
diff --git a/spec/support/api/time_tracking_shared_examples.rb b/spec/support/api/time_tracking_shared_examples.rb
index 52e1bc55191..fee464c15a3 100644
--- a/spec/support/api/time_tracking_shared_examples.rb
+++ b/spec/support/api/time_tracking_shared_examples.rb
@@ -85,7 +85,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
it 'subtracts time of the total spent time' do
Timecop.travel(1.minute.from_now) do
expect do
- issuable.update_attributes!(spend_time: { duration: 7200, user_id: user.id })
+ issuable.update!(spend_time: { duration: 7200, user_id: user.id })
end.to change { issuable.reload.updated_at }
end
@@ -99,7 +99,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
context 'when time to subtract is greater than the total spent time' do
it 'does not modify the total time spent' do
- issuable.update_attributes!(spend_time: { duration: 7200, user_id: user.id })
+ issuable.update!(spend_time: { duration: 7200, user_id: user.id })
Timecop.travel(1.minute.from_now) do
expect do
@@ -135,8 +135,8 @@ shared_examples 'time tracking endpoints' do |issuable_name|
describe "GET /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_stats" do
it "returns the time stats for #{issuable_name}" do
- issuable.update_attributes!(spend_time: { duration: 1800, user_id: user.id },
- time_estimate: 3600)
+ issuable.update!(spend_time: { duration: 1800, user_id: user.id },
+ time_estimate: 3600)
get api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_stats", user)
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
index 1bd6c25100e..9b44c532ff6 100644
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ b/spec/support/features/issuable_slash_commands_shared_examples.rb
@@ -4,7 +4,7 @@
shared_examples 'issuable record that supports quick actions in its description and notes' do |issuable_type|
include Spec::Support::Helpers::Features::NotesHelpers
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:project) do
case issuable_type
when :merge_request
@@ -19,9 +19,9 @@ shared_examples 'issuable record that supports quick actions in its description
let(:new_url_opts) { {} }
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
- gitlab_sign_in(master)
+ gitlab_sign_in(maintainer)
end
after do
@@ -210,31 +210,31 @@ shared_examples 'issuable record that supports quick actions in its description
expect(page).not_to have_content '/todo'
expect(page).to have_content 'Commands applied'
- todos = TodosFinder.new(master).execute
+ todos = TodosFinder.new(maintainer).execute
todo = todos.first
expect(todos.size).to eq 1
expect(todo).to be_pending
expect(todo.target).to eq issuable
- expect(todo.author).to eq master
- expect(todo.user).to eq master
+ expect(todo.author).to eq maintainer
+ expect(todo.user).to eq maintainer
end
end
context "with a note marking the #{issuable_type} as done" do
before do
- TodoService.new.mark_todo(issuable, master)
+ TodoService.new.mark_todo(issuable, maintainer)
end
it "creates a new todo for the #{issuable_type}" do
- todos = TodosFinder.new(master).execute
+ todos = TodosFinder.new(maintainer).execute
todo = todos.first
expect(todos.size).to eq 1
expect(todos.first).to be_pending
expect(todo.target).to eq issuable
- expect(todo.author).to eq master
- expect(todo.user).to eq master
+ expect(todo.author).to eq maintainer
+ expect(todo.user).to eq maintainer
add_note("/done")
@@ -247,31 +247,31 @@ shared_examples 'issuable record that supports quick actions in its description
context "with a note subscribing to the #{issuable_type}" do
it "creates a new todo for the #{issuable_type}" do
- expect(issuable.subscribed?(master, project)).to be_falsy
+ expect(issuable.subscribed?(maintainer, project)).to be_falsy
add_note("/subscribe")
expect(page).not_to have_content '/subscribe'
expect(page).to have_content 'Commands applied'
- expect(issuable.subscribed?(master, project)).to be_truthy
+ expect(issuable.subscribed?(maintainer, project)).to be_truthy
end
end
context "with a note unsubscribing to the #{issuable_type} as done" do
before do
- issuable.subscribe(master, project)
+ issuable.subscribe(maintainer, project)
end
it "creates a new todo for the #{issuable_type}" do
- expect(issuable.subscribed?(master, project)).to be_truthy
+ expect(issuable.subscribed?(maintainer, project)).to be_truthy
add_note("/unsubscribe")
expect(page).not_to have_content '/unsubscribe'
expect(page).to have_content 'Commands applied'
- expect(issuable.subscribed?(master, project)).to be_falsy
+ expect(issuable.subscribed?(maintainer, project)).to be_falsy
end
end
@@ -282,7 +282,7 @@ shared_examples 'issuable record that supports quick actions in its description
expect(page).not_to have_content '/assign me'
expect(page).to have_content 'Commands applied'
- expect(issuable.reload.assignees).to eq [master]
+ expect(issuable.reload.assignees).to eq [maintainer]
end
end
end
diff --git a/spec/support/generate-seed-repo-rb b/spec/support/generate-seed-repo-rb
index 44b3de23b99..bee9d419376 100755
--- a/spec/support/generate-seed-repo-rb
+++ b/spec/support/generate-seed-repo-rb
@@ -15,7 +15,7 @@
require 'erb'
require 'tempfile'
-SOURCE = File.expand_path('../gitlab-git-test.git', __FILE__).freeze
+SOURCE = File.expand_path('gitlab-git-test.git', __dir__).freeze
SCRIPT_NAME = 'generate-seed-repo-rb'.freeze
REPO_NAME = 'gitlab-git-test.git'.freeze
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index 32d9807f06a..c228bd2393b 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -125,7 +125,8 @@ module CycleAnalyticsHelpers
_, opts = args
commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- raw_repository.commit(branch_update.newrev).rugged_commit
+ rugged = raw_repository.rugged
+ rugged.rev_parse(branch_update.newrev)
end
branch_update.newrev = commit.amend(
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index 0930b9da368..75827df80dc 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -1,4 +1,6 @@
module GraphqlHelpers
+ MutationDefinition = Struct.new(:query, :variables)
+
# makes an underscored string look like a fieldname
# "merge_request" => "mergeRequest"
def self.fieldnamerize(underscored_field_name)
@@ -41,6 +43,37 @@ module GraphqlHelpers
QUERY
end
+ def graphql_mutation(name, input, fields = nil)
+ mutation_name = GraphqlHelpers.fieldnamerize(name)
+ input_variable_name = "$#{input_variable_name_for_mutation(name)}"
+ mutation_field = GitlabSchema.mutation.fields[mutation_name]
+ fields ||= all_graphql_fields_for(mutation_field.type)
+
+ query = <<~MUTATION
+ mutation(#{input_variable_name}: #{mutation_field.arguments['input'].type}) {
+ #{mutation_name}(input: #{input_variable_name}) {
+ #{fields}
+ }
+ }
+ MUTATION
+ variables = variables_for_mutation(name, input)
+
+ MutationDefinition.new(query, variables)
+ end
+
+ def variables_for_mutation(name, input)
+ graphql_input = input.map { |name, value| [GraphqlHelpers.fieldnamerize(name), value] }.to_h
+ { input_variable_name_for_mutation(name) => graphql_input }.to_json
+ end
+
+ def input_variable_name_for_mutation(mutation_name)
+ mutation_name = GraphqlHelpers.fieldnamerize(mutation_name)
+ mutation_field = GitlabSchema.mutation.fields[mutation_name]
+ input_type = field_type(mutation_field.arguments['input'])
+
+ GraphqlHelpers.fieldnamerize(input_type)
+ end
+
def query_graphql_field(name, attributes = {}, fields = nil)
fields ||= all_graphql_fields_for(name.classify)
attributes = attributes_to_graphql(attributes)
@@ -57,12 +90,12 @@ module GraphqlHelpers
type.fields.map do |name, field|
# We can't guess arguments, so skip fields that require them
- next if field.arguments.any?
+ next if required_arguments?(field)
- if scalar?(field)
- name
- else
+ if nested_fields?(field)
"#{name} { #{all_graphql_fields_for(field_type(field))} }"
+ else
+ name
end
end.compact.join("\n")
end
@@ -73,8 +106,12 @@ module GraphqlHelpers
end.join(", ")
end
- def post_graphql(query, current_user: nil)
- post api('/', current_user, version: 'graphql'), query: query
+ def post_graphql(query, current_user: nil, variables: nil)
+ post api('/', current_user, version: 'graphql'), query: query, variables: variables
+ end
+
+ def post_graphql_mutation(mutation, current_user: nil)
+ post_graphql(mutation.query, current_user: current_user, variables: mutation.variables)
end
def graphql_data
@@ -82,18 +119,38 @@ module GraphqlHelpers
end
def graphql_errors
- json_response['data']
+ json_response['errors']
+ end
+
+ def graphql_mutation_response(mutation_name)
+ graphql_data[GraphqlHelpers.fieldnamerize(mutation_name)]
+ end
+
+ def nested_fields?(field)
+ !scalar?(field) && !enum?(field)
end
def scalar?(field)
field_type(field).kind.scalar?
end
+ def enum?(field)
+ field_type(field).kind.enum?
+ end
+
+ def required_arguments?(field)
+ field.arguments.values.any? { |argument| argument.type.non_null? }
+ end
+
def field_type(field)
- if field.type.respond_to?(:of_type)
- field.type.of_type
- else
- field.type
- end
+ field_type = 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
diff --git a/spec/support/helpers/jira_service_helper.rb b/spec/support/helpers/jira_service_helper.rb
index 88a7aeba461..f4d5343c4ed 100644
--- a/spec/support/helpers/jira_service_helper.rb
+++ b/spec/support/helpers/jira_service_helper.rb
@@ -12,7 +12,7 @@ module JiraServiceHelper
jira_issue_transition_id: '1'
}
- jira_tracker.update_attributes(properties: properties, active: true)
+ jira_tracker.update(properties: properties, active: true)
end
def jira_issue_comments
diff --git a/spec/support/helpers/key_generator_helper.rb b/spec/support/helpers/key_generator_helper.rb
index b1c289ffef7..d55d8312c65 100644
--- a/spec/support/helpers/key_generator_helper.rb
+++ b/spec/support/helpers/key_generator_helper.rb
@@ -24,7 +24,7 @@ module Spec
private
# Encodes an openssh-mpi-encoded integer.
- def encode_mpi(n)
+ def encode_mpi(n) # rubocop:disable Naming/UncommunicativeMethodParamName
chars, n = [], n.to_i
chars << (n & 0xff) && n >>= 8 while n != 0
chars << 0 if chars.empty? || chars.last >= 0x80
diff --git a/spec/support/helpers/markdown_feature.rb b/spec/support/helpers/markdown_feature.rb
index 39e94ad53de..346f5b1cc4d 100644
--- a/spec/support/helpers/markdown_feature.rb
+++ b/spec/support/helpers/markdown_feature.rb
@@ -24,7 +24,7 @@ class MarkdownFeature
def project
@project ||= create(:project, :repository, group: group).tap do |project|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
diff --git a/spec/support/helpers/reactive_caching_helpers.rb b/spec/support/helpers/reactive_caching_helpers.rb
index e22dd974c6a..a575aa99b79 100644
--- a/spec/support/helpers/reactive_caching_helpers.rb
+++ b/spec/support/helpers/reactive_caching_helpers.rb
@@ -14,8 +14,8 @@ module ReactiveCachingHelpers
end
def synchronous_reactive_cache(subject)
- allow(service).to receive(:with_reactive_cache) do |*args, &block|
- block.call(service.calculate_reactive_cache(*args))
+ allow(subject).to receive(:with_reactive_cache) do |*args, &block|
+ block.call(subject.calculate_reactive_cache(*args))
end
end
diff --git a/spec/support/helpers/seed_helper.rb b/spec/support/helpers/seed_helper.rb
index 8fd107260cc..25781f5e679 100644
--- a/spec/support/helpers/seed_helper.rb
+++ b/spec/support/helpers/seed_helper.rb
@@ -101,10 +101,4 @@ bla/bla.txt
handle.write('# hello'.encode(enc))
end
end
-
- # Prevent developer git configurations from being persisted to test
- # repositories
- def git_env
- { 'GIT_TEMPLATE_DIR' => '' }
- end
end
diff --git a/spec/support/helpers/stub_metrics.rb b/spec/support/helpers/stub_metrics.rb
new file mode 100644
index 00000000000..64983fdf222
--- /dev/null
+++ b/spec/support/helpers/stub_metrics.rb
@@ -0,0 +1,27 @@
+module StubMetrics
+ def authentication_metrics
+ Gitlab::Auth::Activity
+ end
+
+ def stub_authentication_activity_metrics(debug: false)
+ authentication_metrics.each_counter do |name, metric, description|
+ allow(authentication_metrics).to receive(name)
+ .and_return(double("#{metric} - #{description}"))
+ end
+
+ debug_authentication_activity_metrics if debug
+ end
+
+ def debug_authentication_activity_metrics
+ authentication_metrics.tap do |metrics|
+ metrics.each_counter do |name, metric|
+ "#{name}_increment!".tap do |incrementer|
+ allow(metrics).to receive(incrementer).and_wrap_original do |method|
+ puts "Authentication activity metric incremented: #{name}"
+ method.call
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index 471b0a74a19..58b5c6a6435 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -25,6 +25,11 @@ module StubObjectStorage
::Fog::Storage.new(connection_params).tap do |connection|
begin
connection.directories.create(key: remote_directory)
+
+ # Cleanup remaining files
+ connection.directories.each do |directory|
+ directory.files.map(&:destroy)
+ end
rescue Excon::Error::Conflict
end
end
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 05a8e6206ae..f392660d2c7 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -8,7 +8,7 @@ module TestEnv
# When developing the seed repository, comment out the branch you will modify.
BRANCH_SHA = {
- 'signed-commits' => '2d1096e',
+ 'signed-commits' => '6101e87',
'not-merged-branch' => 'b83d6e3',
'branch-merged' => '498214d',
'empty-branch' => '7efb185',
@@ -49,7 +49,10 @@ module TestEnv
'add-pdf-file' => 'e774ebd',
'squash-large-files' => '54cec52',
'add-pdf-text-binary' => '79faa7b',
- 'add_images_and_changes' => '010d106'
+ 'add_images_and_changes' => '010d106',
+ 'update-gitlab-shell-v-6-0-1' => '2f61d70',
+ 'update-gitlab-shell-v-6-0-3' => 'de78448',
+ '2-mb-file' => 'bf12d25'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
@@ -241,6 +244,14 @@ module TestEnv
set_repo_refs(target_repo_path, refs)
end
+ def create_bare_repository(path)
+ FileUtils.mkdir_p(path)
+
+ system(git_env, *%W(#{Gitlab.config.git.bin_path} -C #{path} init --bare),
+ out: '/dev/null',
+ err: '/dev/null')
+ end
+
def repos_path
@repos_path ||= Gitlab.config.repositories.storages[REPOS_STORAGE].legacy_disk_path
end
diff --git a/spec/support/helpers/user_activities_helpers.rb b/spec/support/helpers/user_activities_helpers.rb
deleted file mode 100644
index 44feb104644..00000000000
--- a/spec/support/helpers/user_activities_helpers.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module UserActivitiesHelpers
- def user_activity(user)
- Gitlab::UserActivities.new
- .find { |k, _| k == user.id.to_s }&.
- second
- end
-end
diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb
index fda0e29f983..c7f878b7371 100644
--- a/spec/support/helpers/wait_for_requests.rb
+++ b/spec/support/helpers/wait_for_requests.rb
@@ -24,7 +24,9 @@ module WaitForRequests
# Wait for client-side AJAX requests
def wait_for_requests
- wait_for('JS requests complete') { finished_all_js_requests? }
+ wait_for('JS requests complete', max_wait_time: 2 * Capybara.default_max_wait_time) do
+ finished_all_js_requests?
+ end
end
# Wait for active Rack requests and client-side AJAX requests
diff --git a/spec/support/http_io/http_io_helpers.rb b/spec/support/http_io/http_io_helpers.rb
index 2c68c2cd9a6..42144870eb5 100644
--- a/spec/support/http_io/http_io_helpers.rb
+++ b/spec/support/http_io/http_io_helpers.rb
@@ -1,65 +1,49 @@
module HttpIOHelpers
- def stub_remote_trace_206
- WebMock.stub_request(:get, remote_trace_url)
- .to_return { |request| remote_trace_response(request, 206) }
+ def stub_remote_url_206(url, file_path)
+ WebMock.stub_request(:get, url)
+ .to_return { |request| remote_url_response(file_path, request, 206) }
end
- def stub_remote_trace_200
- WebMock.stub_request(:get, remote_trace_url)
- .to_return { |request| remote_trace_response(request, 200) }
+ def stub_remote_url_200(url, file_path)
+ WebMock.stub_request(:get, url)
+ .to_return { |request| remote_url_response(file_path, request, 200) }
end
- def stub_remote_trace_500
- WebMock.stub_request(:get, remote_trace_url)
+ def stub_remote_url_500(url)
+ WebMock.stub_request(:get, url)
.to_return(status: [500, "Internal Server Error"])
end
- def remote_trace_url
- "http://trace.com/trace"
- end
-
- def remote_trace_response(request, responce_status)
+ def remote_url_response(file_path, request, response_status)
range = request.headers['Range'].match(/bytes=(\d+)-(\d+)/)
+ body = File.read(file_path).force_encoding(Encoding::BINARY)
+ size = body.bytesize
+
{
- status: responce_status,
- headers: remote_trace_response_headers(responce_status, range[1].to_i, range[2].to_i),
- body: range_trace_body(range[1].to_i, range[2].to_i)
+ status: response_status,
+ headers: remote_url_response_headers(response_status, range[1].to_i, range[2].to_i, size),
+ body: body[range[1].to_i..range[2].to_i]
}
end
- def remote_trace_response_headers(responce_status, from, to)
- headers = { 'Content-Type' => 'text/plain' }
-
- if responce_status == 206
- headers.merge('Content-Range' => "bytes #{from}-#{to}/#{remote_trace_size}")
+ def remote_url_response_headers(response_status, from, to, size)
+ { 'Content-Type' => 'text/plain' }.tap do |headers|
+ if response_status == 206
+ headers.merge('Content-Range' => "bytes #{from}-#{to}/#{size}")
+ end
end
-
- headers
- end
-
- def range_trace_body(from, to)
- remote_trace_body[from..to]
- end
-
- def remote_trace_body
- @remote_trace_body ||= File.read(expand_fixture_path('trace/sample_trace'))
- .force_encoding(Encoding::BINARY)
- end
-
- def remote_trace_size
- remote_trace_body.bytesize
end
def set_smaller_buffer_size_than(file_size)
blocks = (file_size / 128)
new_size = (blocks / 2) * 128
- stub_const("Gitlab::Ci::Trace::HttpIO::BUFFER_SIZE", new_size)
+ stub_const("Gitlab::HttpIO::BUFFER_SIZE", new_size)
end
def set_larger_buffer_size_than(file_size)
blocks = (file_size / 128)
new_size = (blocks * 2) * 128
- stub_const("Gitlab::Ci::Trace::HttpIO::BUFFER_SIZE", new_size)
+ stub_const("Gitlab::HttpIO::BUFFER_SIZE", new_size)
end
end
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index 562423afc2a..4d925ac77f4 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -37,7 +37,7 @@ module ExportFileHelper
event = create(:event, :created, target: milestone, project: project, author: user, action: 5)
create(:push_event_payload, event: event)
- create(:project_member, :master, user: user, project: project)
+ create(:project_member, :maintainer, user: user, project: project)
create(:ci_variable, project: project)
create(:ci_trigger, project: project)
key = create(:deploy_key)
diff --git a/spec/support/matchers/access_matchers_for_controller.rb b/spec/support/matchers/access_matchers_for_controller.rb
index 42a9ed9ff34..429401a5da8 100644
--- a/spec/support/matchers/access_matchers_for_controller.rb
+++ b/spec/support/matchers/access_matchers_for_controller.rb
@@ -24,7 +24,7 @@ module AccessMatchersForController
when User
user = role
sign_in(user)
- when *Gitlab::Access.sym_options_with_owner.keys # owner, master, developer, reporter, guest
+ when *Gitlab::Access.sym_options_with_owner.keys # owner, maintainer, developer, reporter, guest
raise ArgumentError, "cannot emulate #{role} without membership parent" unless membership
user = create_user_by_membership(role, membership)
diff --git a/spec/support/matchers/disallow_request_matchers.rb b/spec/support/matchers/disallow_request_matchers.rb
new file mode 100644
index 00000000000..db4d90e4fd0
--- /dev/null
+++ b/spec/support/matchers/disallow_request_matchers.rb
@@ -0,0 +1,15 @@
+RSpec::Matchers.define :disallow_request do
+ match do |middleware|
+ alert = middleware.env['rack.session'].to_hash
+ .dig('flash', 'flashes', 'alert')
+
+ alert&.include?('You cannot perform write operations')
+ end
+end
+
+RSpec::Matchers.define :disallow_request_in_json do
+ match do |response|
+ json_response = JSON.parse(response.body)
+ response.body.include?('You cannot perform write operations') && json_response.key?('message')
+ end
+end
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index be6fa4c71a0..7be84838e00 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -34,6 +34,15 @@ RSpec::Matchers.define :have_graphql_field do |field_name|
end
end
+RSpec::Matchers.define :have_graphql_mutation do |mutation_class|
+ match do |mutation_type|
+ field = mutation_type.fields[GraphqlHelpers.fieldnamerize(mutation_class.graphql_name)]
+
+ expect(field).to be_present
+ expect(field.resolver).to eq(mutation_class)
+ end
+end
+
RSpec::Matchers.define :have_graphql_arguments do |*expected|
include GraphqlHelpers
diff --git a/spec/support/matchers/metric_counter_matcher.rb b/spec/support/matchers/metric_counter_matcher.rb
new file mode 100644
index 00000000000..22d5cd17e3f
--- /dev/null
+++ b/spec/support/matchers/metric_counter_matcher.rb
@@ -0,0 +1,11 @@
+RSpec::Matchers.define :increment do |counter|
+ match do |adapter|
+ expect(adapter.send(counter))
+ .to receive(:increment)
+ .exactly(@exactly || :once)
+ end
+
+ chain :twice do
+ @exactly = :twice
+ end
+end
diff --git a/spec/support/matchers/user_activity_matchers.rb b/spec/support/matchers/user_activity_matchers.rb
deleted file mode 100644
index ce3b683b6d2..00000000000
--- a/spec/support/matchers/user_activity_matchers.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-RSpec::Matchers.define :have_an_activity_record do |expected|
- match do |user|
- expect(Gitlab::UserActivities.new.find { |k, _| k == user.id.to_s }).to be_present
- end
-end
diff --git a/spec/support/matchers/user_status_matcher.rb b/spec/support/matchers/user_status_matcher.rb
new file mode 100644
index 00000000000..3cf240d874a
--- /dev/null
+++ b/spec/support/matchers/user_status_matcher.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :show_user_status do |status|
+ match do |page|
+ expect(page).to have_selector(".user-status-emoji[title='#{status.message}']")
+
+ # The same user status might be displayed multiple times on the page
+ emoji_span = page.first(".user-status-emoji[title='#{status.message}']")
+ page.within(emoji_span) do
+ expect(page).to have_emoji(status.emoji)
+ end
+ end
+end
diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb
index c7c3346d39e..0fd67531c3b 100644
--- a/spec/support/prometheus/additional_metrics_shared_examples.rb
+++ b/spec/support/prometheus/additional_metrics_shared_examples.rb
@@ -25,7 +25,7 @@ RSpec.shared_examples 'additional metrics query' do
shared_examples 'query context containing environment slug and filter' do
it 'contains ci_environment_slug' do
- expect(subject).to receive(:query_metrics).with(project, hash_including(ci_environment_slug: environment.slug))
+ expect(subject).to receive(:query_metrics).with(project, environment, hash_including(ci_environment_slug: environment.slug))
subject.query(*query_params)
end
@@ -33,6 +33,7 @@ RSpec.shared_examples 'additional metrics query' do
it 'contains environment filter' do
expect(subject).to receive(:query_metrics).with(
project,
+ environment,
hash_including(
environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\""
)
@@ -50,7 +51,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter'
it 'query context contains kube_namespace' do
- expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: kube_namespace))
+ expect(subject).to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: kube_namespace))
subject.query(*query_params)
end
@@ -74,7 +75,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter'
it 'query context contains empty kube_namespace' do
- expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: ''))
+ expect(subject).to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: ''))
subject.query(*query_params)
end
diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb
index 54b8df7aa19..9b8bcebcb3a 100644
--- a/spec/support/rspec.rb
+++ b/spec/support/rspec.rb
@@ -1,4 +1,5 @@
require_relative "helpers/stub_configuration"
+require_relative "helpers/stub_metrics"
require_relative "helpers/stub_object_storage"
require_relative "helpers/stub_env"
@@ -7,6 +8,7 @@ RSpec.configure do |config|
config.raise_errors_for_deprecations!
config.include StubConfiguration
+ config.include StubMetrics
config.include StubObjectStorage
config.include StubENV
diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
index 7b064162726..8b4cffaac19 100644
--- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
+++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
@@ -3,7 +3,7 @@
shared_examples 'new issuable record that supports quick actions' do
let!(:project) { create(:project, :repository) }
- let(:user) { create(:user).tap { |u| project.add_master(u) } }
+ let(:user) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:assignee) { create(:user) }
let!(:milestone) { create(:milestone, project: project) }
let!(:labels) { create_list(:label, 3, project: project) }
@@ -12,7 +12,7 @@ shared_examples 'new issuable record that supports quick actions' do
let(:issuable) { described_class.new(project, user, params).execute }
before do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
end
context 'with labels in command only' do
diff --git a/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb b/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb
new file mode 100644
index 00000000000..05424d08b9d
--- /dev/null
+++ b/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb
@@ -0,0 +1,15 @@
+shared_context 'merge request allowing collaboration' do
+ include ProjectForksHelper
+
+ let(:canonical) { create(:project, :public, :repository) }
+ let(:forked_project) { fork_project(canonical, nil, repository: true) }
+
+ before do
+ canonical.add_maintainer(user)
+ create(:merge_request,
+ target_project: canonical,
+ source_project: forked_project,
+ source_branch: 'feature',
+ allow_collaboration: true)
+ end
+end
diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb
index db723a323f8..94e82b8ce90 100644
--- a/spec/support/shared_examples/ci_trace_shared_examples.rb
+++ b/spec/support/shared_examples/ci_trace_shared_examples.rb
@@ -138,6 +138,28 @@ shared_examples_for 'common trace features' do
end
end
+ describe '#write' do
+ subject { trace.send(:write, mode) { } }
+
+ let(:mode) { 'wb' }
+
+ context 'when arhicved trace does not exist yet' do
+ it 'does not raise an error' do
+ expect { subject }.not_to raise_error
+ end
+ end
+
+ context 'when arhicved trace already exists' do
+ before do
+ create(:ci_job_artifact, :trace, job: build)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Ci::Trace::AlreadyArchivedError)
+ end
+ end
+ end
+
describe '#set' do
before do
trace.set("12")
@@ -574,7 +596,7 @@ shared_examples_for 'trace with disabled live trace feature' do
it 'does not archive' do
expect_any_instance_of(described_class).not_to receive(:archive_stream!)
- expect { subject }.to raise_error('Already archived')
+ expect { subject }.to raise_error(Gitlab::Ci::Trace::AlreadyArchivedError)
expect(build.job_artifacts_trace.file.exists?).to be_truthy
end
end
@@ -589,6 +611,55 @@ shared_examples_for 'trace with disabled live trace feature' do
end
end
end
+
+ describe '#erase!' do
+ subject { trace.erase! }
+
+ context 'when it is a live trace' do
+ context 'when trace is stored in database' do
+ let(:build) { create(:ci_build) }
+
+ before do
+ build.update_column(:trace, 'sample trace')
+ end
+
+ it { expect(trace.raw).not_to be_nil }
+
+ it "removes trace" do
+ subject
+
+ expect(trace.raw).to be_nil
+ end
+ end
+
+ context 'when trace is stored in file storage' do
+ let(:build) { create(:ci_build, :trace_live) }
+
+ it { expect(trace.raw).not_to be_nil }
+
+ it "removes trace" do
+ subject
+
+ expect(trace.raw).to be_nil
+ end
+ end
+ end
+
+ context 'when it is an archived trace' do
+ let(:build) { create(:ci_build, :trace_artifact) }
+
+ it "has trace at first" do
+ expect(trace.raw).not_to be_nil
+ end
+
+ it "removes trace" do
+ subject
+
+ build.reload
+ expect(trace.raw).to be_nil
+ end
+ end
+ end
end
shared_examples_for 'trace with enabled live trace feature' do
@@ -761,7 +832,7 @@ shared_examples_for 'trace with enabled live trace feature' do
it 'does not archive' do
expect_any_instance_of(described_class).not_to receive(:archive_stream!)
- expect { subject }.to raise_error('Already archived')
+ expect { subject }.to raise_error(Gitlab::Ci::Trace::AlreadyArchivedError)
expect(build.job_artifacts_trace.file.exists?).to be_truthy
end
end
@@ -776,4 +847,35 @@ shared_examples_for 'trace with enabled live trace feature' do
end
end
end
+
+ describe '#erase!' do
+ subject { trace.erase! }
+
+ context 'when it is a live trace' do
+ let(:build) { create(:ci_build, :trace_live) }
+
+ it { expect(trace.raw).not_to be_nil }
+
+ it "removes trace" do
+ subject
+
+ expect(trace.raw).to be_nil
+ end
+ end
+
+ context 'when it is an archived trace' do
+ let(:build) { create(:ci_build, :trace_artifact) }
+
+ it "has trace at first" do
+ expect(trace.raw).not_to be_nil
+ end
+
+ it "removes trace" do
+ subject
+
+ build.reload
+ expect(trace.raw).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/controllers/todos_shared_examples.rb b/spec/support/shared_examples/controllers/todos_shared_examples.rb
new file mode 100644
index 00000000000..bafd9bac8d0
--- /dev/null
+++ b/spec/support/shared_examples/controllers/todos_shared_examples.rb
@@ -0,0 +1,43 @@
+shared_examples 'todos actions' do
+ context 'when authorized' do
+ before do
+ sign_in(user)
+ parent.add_developer(user)
+ end
+
+ it 'creates todo' do
+ expect do
+ post_create
+ end.to change { user.todos.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'returns todo path and pending count' do
+ post_create
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['count']).to eq 1
+ expect(json_response['delete_path']).to match(%r{/dashboard/todos/\d{1}})
+ end
+ end
+
+ context 'when not authorized for project/group' do
+ it 'does not create todo for resource that user has no access to' do
+ sign_in(user)
+ expect do
+ post_create
+ end.to change { user.todos.count }.by(0)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'does not create todo when user is not logged in' do
+ expect do
+ post_create
+ end.to change { user.todos.count }.by(0)
+
+ expect(response).to have_gitlab_http_status(parent.is_a?(Group) ? 401 : 302)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
index bbbad86dcd5..7088fb1e5fb 100644
--- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
@@ -260,4 +260,83 @@ shared_examples 'handle uploads' do
end
end
end
+
+ describe "POST #authorize" do
+ context 'when a user is not authorized to upload a file' do
+ it 'returns 404 status' do
+ post_authorize
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when a user can upload a file' do
+ before do
+ sign_in(user)
+ model.add_developer(user)
+ end
+
+ context 'and the request bypassed workhorse' do
+ it 'raises an exception' do
+ expect { post_authorize(verified: false) }.to raise_error JWT::DecodeError
+ end
+ end
+
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ shared_examples 'a valid response' do
+ before do
+ post_authorize
+ end
+
+ it 'responds with status 200' do
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'uses the gitlab-workhorse content type' do
+ expect(response.headers["Content-Type"]).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ end
+ end
+
+ shared_examples 'a local file' do
+ it_behaves_like 'a valid response' do
+ it 'responds with status 200, location of uploads store and object details' do
+ expect(json_response['TempPath']).to eq(uploader_class.workhorse_local_upload_path)
+ expect(json_response['RemoteObject']).to be_nil
+ end
+ end
+ end
+
+ context 'when using local storage' do
+ it_behaves_like 'a local file'
+ end
+
+ context 'when using remote storage' do
+ context 'when direct upload is enabled' do
+ before do
+ stub_uploads_object_storage(uploader_class, direct_upload: true)
+ end
+
+ it_behaves_like 'a valid response' do
+ it 'responds with status 200, location of uploads remote store and object details' do
+ expect(json_response['TempPath']).to eq(uploader_class.workhorse_local_upload_path)
+ 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
+ end
+
+ context 'when direct upload is disabled' do
+ before do
+ stub_uploads_object_storage(uploader_class, direct_upload: false)
+ end
+
+ it_behaves_like 'a local file'
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
index 5a569d233bc..7038a366144 100644
--- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
@@ -10,9 +10,9 @@ RSpec.shared_examples 'a creatable merge request' do
let!(:label2) { create(:label, project: target_project) }
before do
- source_project.add_master(user)
- target_project.add_master(user)
- target_project.add_master(user2)
+ source_project.add_maintainer(user)
+ target_project.add_maintainer(user)
+ target_project.add_maintainer(user2)
sign_in(user)
visit project_new_merge_request_path(
diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
index 645db41cddc..3057845061b 100644
--- a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
@@ -15,9 +15,9 @@ RSpec.shared_examples 'an editable merge request' do
end
before do
- source_project.add_master(user)
- target_project.add_master(user)
- target_project.add_master(user2)
+ source_project.add_maintainer(user)
+ target_project.add_maintainer(user)
+ target_project.add_maintainer(user2)
sign_in(user)
visit edit_project_merge_request_path(target_project, merge_request)
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 b29bb3c2fc0..75ad948e42c 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
@@ -1,20 +1,20 @@
-RSpec.shared_examples 'Master manages access requests' do
+RSpec.shared_examples 'Maintainer manages access requests' do
let(:user) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
before do
entity.request_access(user)
- entity.respond_to?(:add_owner) ? entity.add_owner(master) : entity.add_master(master)
- sign_in(master)
+ entity.respond_to?(:add_owner) ? entity.add_owner(maintainer) : entity.add_maintainer(maintainer)
+ sign_in(maintainer)
end
- it 'master can see access requests' do
+ it 'maintainer can see access requests' do
visit members_page_path
expect_visible_access_request(entity, user)
end
- it 'master can grant access', :js do
+ it 'maintainer can grant access', :js do
visit members_page_path
expect_visible_access_request(entity, user)
@@ -28,7 +28,7 @@ RSpec.shared_examples 'Master manages access requests' do
end
end
- it 'master can deny access', :js do
+ it 'maintainer can deny access', :js do
visit members_page_path
expect_visible_access_request(entity, user)
diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb
index 56e86a87ab9..ef9bb7f5533 100644
--- a/spec/support/shared_examples/helm_generated_script.rb
+++ b/spec/support/shared_examples/helm_generated_script.rb
@@ -6,8 +6,8 @@ shared_examples 'helm commands' do
ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
+ apk add -U wget ca-certificates openssl >/dev/null
+ wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.2-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
mv /tmp/linux-amd64/helm /usr/bin/
EOS
end
diff --git a/spec/support/shared_examples/instance_statistics_controllers_shared_examples.rb b/spec/support/shared_examples/instance_statistics_controllers_shared_examples.rb
new file mode 100644
index 00000000000..5334af841e1
--- /dev/null
+++ b/spec/support/shared_examples/instance_statistics_controllers_shared_examples.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+shared_examples 'instance statistics availability' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'GET #index' do
+ it 'is available when the feature is available publicly' do
+ get :index
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+
+ it 'renders a 404 when the feature is not available publicly' do
+ stub_application_setting(instance_statistics_visibility_private: true)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'for admins' do
+ let(:user) { create(:admin) }
+
+ it 'allows access when the feature is not available publicly' do
+ stub_application_setting(instance_statistics_visibility_private: true)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb
index 7ab1041d17c..c659be8f13a 100644
--- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb
+++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb
@@ -60,6 +60,20 @@ shared_examples_for 'AtomicInternalId' do |validate_presence: true|
expect { subject }.not_to change { instance.public_send(internal_id_attribute) }
end
+
+ context 'when the instance has an internal ID set' do
+ let(:internal_id) { 9001 }
+
+ it 'calls InternalId.update_last_value and sets the `last_value` to that of the instance' do
+ instance.send("#{internal_id_attribute}=", internal_id)
+
+ expect(InternalId)
+ .to receive(:track_greatest)
+ .with(instance, scope_attrs, usage, internal_id, any_args)
+ .and_return(internal_id)
+ subject
+ end
+ end
end
end
end
diff --git a/spec/support/shared_examples/models/members_notifications_shared_example.rb b/spec/support/shared_examples/models/members_notifications_shared_example.rb
index 76611e54306..ef5cea3f2a5 100644
--- a/spec/support/shared_examples/models/members_notifications_shared_example.rb
+++ b/spec/support/shared_examples/models/members_notifications_shared_example.rb
@@ -21,7 +21,7 @@ RSpec.shared_examples 'members notifications' do |entity_type|
it "calls NotificationService.update_#{entity_type}_member" do
expect(notification_service).to receive(:"update_#{entity_type}_member").with(member)
- member.update_attribute(:access_level, Member::MASTER)
+ member.update_attribute(:access_level, Member::MAINTAINER)
end
it "does not send an email when the access level has not changed" do
diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb
index d176d3fa425..5fb9ced3b63 100644
--- a/spec/support/shared_examples/notify_shared_examples.rb
+++ b/spec/support/shared_examples/notify_shared_examples.rb
@@ -77,7 +77,7 @@ shared_examples 'a thread answer email with reply-by-email enabled' do
aggregate_failures do
is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/)
is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>")
- is_expected.to have_header('References', /\A<#{route_key}@#{host}> <reply\-.*@#{host}>\Z/ )
+ is_expected.to have_header('References', /\A<reply\-.*@#{host}> <#{route_key}@#{host}>\Z/ )
is_expected.to have_subject(/^Re: /)
end
end
@@ -87,6 +87,10 @@ shared_examples 'an email starting a new thread with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details'
include_examples 'a new thread email with reply-by-email enabled'
+ it 'includes "Reply to this email directly or <View it on GitLab>"' do
+ expect(subject.default_part.body).to include(%(Reply to this email directly or <a href="#{Gitlab::UrlBuilder.build(model)}">view it on GitLab</a>.))
+ end
+
context 'when reply-by-email is enabled with incoming address with %{key}' do
it 'has a Reply-To header' do
is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/
diff --git a/spec/support/shared_examples/requests/api/merge_requests_list.rb b/spec/support/shared_examples/requests/api/merge_requests_list.rb
index d5e22b8cb56..1aed8ab0113 100644
--- a/spec/support/shared_examples/requests/api/merge_requests_list.rb
+++ b/spec/support/shared_examples/requests/api/merge_requests_list.rb
@@ -29,7 +29,7 @@ shared_examples 'merge requests list' do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.length).to eq(3)
+ expect(json_response.length).to eq(4)
expect(json_response.last['title']).to eq(merge_request.title)
expect(json_response.last).to have_key('web_url')
expect(json_response.last['sha']).to eq(merge_request.diff_head_sha)
@@ -53,7 +53,7 @@ shared_examples 'merge requests list' do
expect(response).to include_pagination_headers
expect(json_response.last.keys).to match_array(%w(id iid title web_url created_at description project_id state updated_at))
expect(json_response).to be_an Array
- expect(json_response.length).to eq(3)
+ expect(json_response.length).to eq(4)
expect(json_response.last['iid']).to eq(merge_request.iid)
expect(json_response.last['title']).to eq(merge_request.title)
expect(json_response.last).to have_key('web_url')
@@ -70,7 +70,7 @@ shared_examples 'merge requests list' do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.length).to eq(3)
+ expect(json_response.length).to eq(4)
expect(json_response.last['title']).to eq(merge_request.title)
end
@@ -136,8 +136,9 @@ shared_examples 'merge requests list' do
it 'returns an array of merge requests in given milestone' do
get api(endpoint_path, user), milestone: '0.9'
- expect(json_response.first['title']).to eq merge_request_closed.title
- expect(json_response.first['id']).to eq merge_request_closed.id
+ closed_issues = json_response.select { |mr| mr['id'] == merge_request_closed.id }
+ expect(closed_issues.length).to eq(1)
+ expect(closed_issues.first['title']).to eq merge_request_closed.title
end
it 'returns an array of merge requests matching state in milestone' do
@@ -216,7 +217,7 @@ shared_examples 'merge requests list' do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.length).to eq(3)
+ expect(json_response.length).to eq(4)
response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort)
end
@@ -229,7 +230,7 @@ shared_examples 'merge requests list' do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.length).to eq(3)
+ expect(json_response.length).to eq(4)
response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort.reverse)
end
@@ -242,7 +243,7 @@ shared_examples 'merge requests list' do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.length).to eq(3)
+ expect(json_response.length).to eq(4)
response_dates = json_response.map { |merge_request| merge_request['updated_at'] }
expect(response_dates).to eq(response_dates.sort.reverse)
end
@@ -255,7 +256,7 @@ shared_examples 'merge requests list' do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.length).to eq(3)
+ expect(json_response.length).to eq(4)
response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort)
end
@@ -265,7 +266,7 @@ shared_examples 'merge requests list' do
it 'returns merge requests with the given source branch' do
get api(endpoint_path, user), source_branch: merge_request_closed.source_branch, state: 'all'
- expect_response_contain_exactly(merge_request_closed, merge_request_merged)
+ expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked)
end
end
@@ -273,7 +274,7 @@ shared_examples 'merge requests list' do
it 'returns merge requests with the given target branch' do
get api(endpoint_path, user), target_branch: merge_request_closed.target_branch, state: 'all'
- expect_response_contain_exactly(merge_request_closed, merge_request_merged)
+ expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked)
end
end
end
diff --git a/spec/support/shared_examples/requests/api/notes.rb b/spec/support/shared_examples/requests/api/notes.rb
index 79b2196660c..1b563021244 100644
--- a/spec/support/shared_examples/requests/api/notes.rb
+++ b/spec/support/shared_examples/requests/api/notes.rb
@@ -121,6 +121,7 @@ shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
expect(json_response['body']).to eq('hi!')
expect(json_response['author']['username']).to eq(user.username)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time)
end
end
diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb
index fe7b7bc306f..04140cad3f0 100644
--- a/spec/support/shared_examples/requests/graphql_shared_examples.rb
+++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb
@@ -5,7 +5,7 @@ shared_examples 'a working graphql query' do
it 'returns a successful response', :aggregate_failures do
expect(response).to have_gitlab_http_status(:success)
- expect(graphql_errors['errors']).to be_nil
+ expect(graphql_errors).to be_nil
expect(json_response.keys).to include('data')
end
end
diff --git a/spec/support/shared_examples/services/boards/issues_list_service.rb b/spec/support/shared_examples/services/boards/issues_list_service.rb
index 3e744323cea..8b879cef084 100644
--- a/spec/support/shared_examples/services/boards/issues_list_service.rb
+++ b/spec/support/shared_examples/services/boards/issues_list_service.rb
@@ -7,6 +7,16 @@ shared_examples 'issues list service' do
described_class.new(parent, user, params).execute
end
+ context '#metadata' do
+ it 'returns issues count for list' do
+ params = { board_id: board.id, id: list1.id }
+
+ metadata = described_class.new(parent, user, params).metadata
+
+ expect(metadata[:size]).to eq(3)
+ end
+ end
+
context 'issues are ordered by priority' do
it 'returns opened issues when list_id is missing' do
params = { board_id: board.id }
diff --git a/spec/support/shared_examples/services/boards/issues_move_service.rb b/spec/support/shared_examples/services/boards/issues_move_service.rb
index 737863ea411..6d29a97c56d 100644
--- a/spec/support/shared_examples/services/boards/issues_move_service.rb
+++ b/spec/support/shared_examples/services/boards/issues_move_service.rb
@@ -4,7 +4,9 @@ shared_examples 'issues move service' do |group|
let(:params) { { board_id: board1.id, from_list_id: list1.id, to_list_id: list2.id } }
it 'delegates the label changes to Issues::UpdateService' do
- expect_any_instance_of(Issues::UpdateService).to receive(:execute).with(issue).once
+ service = double(:service)
+ expect(Issues::UpdateService).to receive(:new).and_return(service)
+ expect(service).to receive(:execute).with(issue).once
described_class.new(parent, user, params).execute(issue)
end
diff --git a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb
new file mode 100644
index 00000000000..b8db35a6ef9
--- /dev/null
+++ b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb
@@ -0,0 +1,54 @@
+shared_examples 'gitlab projects import validations' do
+ context 'with an invalid path' do
+ let(:path) { '/invalid-path/' }
+
+ it 'returns an invalid project' do
+ project = subject.execute
+
+ expect(project).not_to be_persisted
+ expect(project).not_to be_valid
+ end
+ end
+
+ context 'with a valid path' do
+ it 'creates a project' do
+ project = subject.execute
+
+ expect(project).to be_persisted
+ expect(project).to be_valid
+ end
+ end
+
+ context 'override params' do
+ it 'stores them as import data when passed' do
+ project = described_class
+ .new(namespace.owner, import_params, description: 'Hello')
+ .execute
+
+ expect(project.import_data.data['override_params']['description']).to eq('Hello')
+ end
+ end
+
+ context 'when there is a project with the same path' do
+ let(:existing_project) { create(:project, namespace: namespace) }
+ let(:path) { existing_project.path}
+
+ it 'does not create the project' do
+ project = subject.execute
+
+ expect(project).to be_invalid
+ expect(project).not_to be_persisted
+ end
+
+ context 'when overwrite param is set' do
+ let(:overwrite) { true }
+
+ it 'creates a project in a temporary full_path' do
+ project = subject.execute
+
+ expect(project).to be_valid
+ expect(project).to be_persisted
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/showing_user_status_shared_examples.rb b/spec/support/shared_examples/showing_user_status_shared_examples.rb
new file mode 100644
index 00000000000..eef769de2fc
--- /dev/null
+++ b/spec/support/shared_examples/showing_user_status_shared_examples.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+shared_examples 'showing user status' do
+ let!(:status) { create(:user_status, user: user_with_status, emoji: 'smirk', message: 'Authoring this object') }
+
+ it 'shows the status' do
+ subject
+
+ expect(page).to show_user_status(status)
+ end
+end
diff --git a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
index 7c34c7b4977..940c24c8d67 100644
--- a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
@@ -130,7 +130,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
context "event channels" do
it "uses the right channel for push event" do
- chat_service.update_attributes(push_channel: "random")
+ chat_service.update(push_channel: "random")
expect(Slack::Notifier).to receive(:new)
.with(webhook_url, channel: "random")
@@ -142,7 +142,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses the right channel for merge request event" do
- chat_service.update_attributes(merge_request_channel: "random")
+ chat_service.update(merge_request_channel: "random")
expect(Slack::Notifier).to receive(:new)
.with(webhook_url, channel: "random")
@@ -154,7 +154,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses the right channel for issue event" do
- chat_service.update_attributes(issue_channel: "random")
+ chat_service.update(issue_channel: "random")
expect(Slack::Notifier).to receive(:new)
.with(webhook_url, channel: "random")
@@ -169,7 +169,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
let(:issue_service_options) { { title: 'Secret', confidential: true } }
it "uses confidential issue channel" do
- chat_service.update_attributes(confidential_issue_channel: 'confidential')
+ chat_service.update(confidential_issue_channel: 'confidential')
expect(Slack::Notifier).to execute_with_options(channel: 'confidential')
@@ -177,7 +177,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it 'falls back to issue channel' do
- chat_service.update_attributes(issue_channel: 'fallback_channel')
+ chat_service.update(issue_channel: 'fallback_channel')
expect(Slack::Notifier).to execute_with_options(channel: 'fallback_channel')
@@ -186,7 +186,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses the right channel for wiki event" do
- chat_service.update_attributes(wiki_page_channel: "random")
+ chat_service.update(wiki_page_channel: "random")
expect(Slack::Notifier).to receive(:new)
.with(webhook_url, channel: "random")
@@ -203,7 +203,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses the right channel" do
- chat_service.update_attributes(note_channel: "random")
+ chat_service.update(note_channel: "random")
note_data = Gitlab::DataBuilder::Note.build(issue_note, user)
@@ -222,7 +222,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses confidential channel" do
- chat_service.update_attributes(confidential_note_channel: "confidential")
+ chat_service.update(confidential_note_channel: "confidential")
note_data = Gitlab::DataBuilder::Note.build(issue_note, user)
@@ -232,7 +232,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it 'falls back to note channel' do
- chat_service.update_attributes(note_channel: "fallback_channel")
+ chat_service.update(note_channel: "fallback_channel")
note_data = Gitlab::DataBuilder::Note.build(issue_note, user)
diff --git a/spec/support/stored_repositories.rb b/spec/support/stored_repositories.rb
index 21995c89a6e..26f823cb6ef 100644
--- a/spec/support/stored_repositories.rb
+++ b/spec/support/stored_repositories.rb
@@ -1,8 +1,4 @@
RSpec.configure do |config|
- config.before(:each, :repository) do
- TestEnv.clean_test_path
- end
-
config.before(:all, :broken_storage) do
FileUtils.rm_rf Gitlab.config.repositories.storages.broken.legacy_disk_path
end
diff --git a/spec/support/test_reports/test_reports_helper.rb b/spec/support/test_reports/test_reports_helper.rb
new file mode 100644
index 00000000000..45c6e04dbf3
--- /dev/null
+++ b/spec/support/test_reports/test_reports_helper.rb
@@ -0,0 +1,93 @@
+module TestReportsHelper
+ def create_test_case_rspec_success
+ Gitlab::Ci::Reports::TestCase.new(
+ name: 'Test#sum when a is 1 and b is 3 returns summary',
+ classname: 'spec.test_spec',
+ file: './spec/test_spec.rb',
+ execution_time: 1.11,
+ status: Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+
+ def create_test_case_rspec_failed
+ Gitlab::Ci::Reports::TestCase.new(
+ name: 'Test#sum when a is 2 and b is 2 returns summary',
+ classname: 'spec.test_spec',
+ file: './spec/test_spec.rb',
+ execution_time: 2.22,
+ system_output: sample_rspec_failed_message,
+ status: Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+
+ def create_test_case_rspec_skipped
+ Gitlab::Ci::Reports::TestCase.new(
+ name: 'Test#sum when a is 3 and b is 3 returns summary',
+ classname: 'spec.test_spec',
+ file: './spec/test_spec.rb',
+ execution_time: 3.33,
+ status: Gitlab::Ci::Reports::TestCase::STATUS_SKIPPED)
+ end
+
+ def create_test_case_rspec_error
+ Gitlab::Ci::Reports::TestCase.new(
+ name: 'Test#sum when a is 4 and b is 4 returns summary',
+ classname: 'spec.test_spec',
+ file: './spec/test_spec.rb',
+ execution_time: 4.44,
+ status: Gitlab::Ci::Reports::TestCase::STATUS_ERROR)
+ end
+
+ def sample_rspec_failed_message
+ <<-EOF.strip_heredoc
+ Failure/Error: is_expected.to eq(3)
+
+ expected: 3
+ got: -1
+
+ (compared using ==)
+ ./spec/test_spec.rb:12:in `block (4 levels) in &lt;top (required)&gt;&apos;
+ EOF
+ end
+
+ def create_test_case_java_success
+ Gitlab::Ci::Reports::TestCase.new(
+ name: 'addTest',
+ classname: 'CalculatorTest',
+ execution_time: 5.55,
+ status: Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+
+ def create_test_case_java_failed
+ Gitlab::Ci::Reports::TestCase.new(
+ name: 'subtractTest',
+ classname: 'CalculatorTest',
+ execution_time: 6.66,
+ system_output: sample_java_failed_message,
+ status: Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+
+ def create_test_case_java_skipped
+ Gitlab::Ci::Reports::TestCase.new(
+ name: 'multiplyTest',
+ classname: 'CalculatorTest',
+ execution_time: 7.77,
+ status: Gitlab::Ci::Reports::TestCase::STATUS_SKIPPED)
+ end
+
+ def create_test_case_java_error
+ Gitlab::Ci::Reports::TestCase.new(
+ name: 'divideTest',
+ classname: 'CalculatorTest',
+ execution_time: 8.88,
+ status: Gitlab::Ci::Reports::TestCase::STATUS_ERROR)
+ end
+
+ def sample_java_failed_message
+ <<-EOF.strip_heredoc
+ junit.framework.AssertionFailedError: expected:&lt;1&gt; but was:&lt;3&gt;
+ at CalculatorTest.subtractExpression(Unknown Source)
+ at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+ at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
+ at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
+ EOF
+ end
+end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 93a436cb2b5..3ba6caf1337 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -87,6 +87,27 @@ describe 'gitlab:app namespace rake task' do
expect { run_rake_task('gitlab:backup:restore') }.to output.to_stdout
end
end
+
+ context 'when the restore directory is not empty' do
+ before do
+ # We only need a backup of the repositories for this test
+ stub_env('SKIP', 'db,uploads,builds,artifacts,lfs,registry')
+ end
+
+ it 'removes stale data' do
+ expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout
+
+ excluded_project = create(:project, :repository, name: 'mepmep')
+
+ expect { run_rake_task('gitlab:backup:restore') }.to output.to_stdout
+
+ raw_repo = excluded_project.repository.raw
+
+ # The restore will not find the repository in the backup, but will create
+ # an empty one in its place
+ expect(raw_repo.empty?).to be(true)
+ end
+ end
end # backup_restore task
describe 'backup' do
diff --git a/spec/tasks/gitlab/cleanup_rake_spec.rb b/spec/tasks/gitlab/cleanup_rake_spec.rb
index 2bf873c923f..cc2cca10f58 100644
--- a/spec/tasks/gitlab/cleanup_rake_spec.rb
+++ b/spec/tasks/gitlab/cleanup_rake_spec.rb
@@ -5,7 +5,7 @@ describe 'gitlab:cleanup rake tasks' do
Rake.application.rake_require 'tasks/gitlab/cleanup'
end
- describe 'cleanup' do
+ describe 'cleanup namespaces and repos' do
let(:storages) do
{
'default' => Gitlab::GitalyClient::StorageSettings.new(@default_storage_hash.merge('path' => 'tmp/tests/default_storage'))
@@ -67,4 +67,88 @@ describe 'gitlab:cleanup rake tasks' do
end
end
end
+
+ # A single integration test that is redundant with one part of the
+ # Gitlab::Cleanup::ProjectUploads spec.
+ #
+ # Additionally, this tests DRY_RUN env var values, and the extra line of
+ # output that says you can disable DRY_RUN if it's enabled.
+ describe 'cleanup:project_uploads' do
+ let!(:logger) { double(:logger) }
+
+ before do
+ expect(main_object).to receive(:logger).and_return(logger).at_least(1).times
+
+ allow(logger).to receive(:info).at_least(1).times
+ allow(logger).to receive(:debug).at_least(1).times
+ end
+
+ context 'with a fixable orphaned project upload file' do
+ let(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) }
+ let(:new_path) { orphaned.absolute_path }
+ let(:path) { File.join(FileUploader.root, 'some', 'wrong', 'location', orphaned.path) }
+
+ before do
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.mv(new_path, path)
+ end
+
+ context 'with DRY_RUN disabled' do
+ before do
+ stub_env('DRY_RUN', 'false')
+ end
+
+ it 'moves the file to its proper location' do
+ run_rake_task('gitlab:cleanup:project_uploads')
+
+ expect(File.exist?(path)).to be_falsey
+ expect(File.exist?(new_path)).to be_truthy
+ end
+
+ it 'logs action as done' do
+ expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up...")
+ expect(logger).to receive(:info).with("Did fix #{path} -> #{new_path}")
+
+ run_rake_task('gitlab:cleanup:project_uploads')
+ end
+ end
+
+ shared_examples_for 'does not move the file' do
+ it 'does not move the file' do
+ run_rake_task('gitlab:cleanup:project_uploads')
+
+ expect(File.exist?(path)).to be_truthy
+ expect(File.exist?(new_path)).to be_falsey
+ end
+
+ it 'logs action as able to be done' do
+ expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up. Dry run...")
+ expect(logger).to receive(:info).with("Can fix #{path} -> #{new_path}")
+ expect(logger).to receive(:info).with(/To clean up these files run this command with DRY_RUN=false/)
+
+ run_rake_task('gitlab:cleanup:project_uploads')
+ end
+ end
+
+ context 'with DRY_RUN explicitly enabled' do
+ before do
+ stub_env('DRY_RUN', 'true')
+ end
+
+ it_behaves_like 'does not move the file'
+ end
+
+ context 'with DRY_RUN set to an unknown value' do
+ before do
+ stub_env('DRY_RUN', 'foo')
+ end
+
+ it_behaves_like 'does not move the file'
+ end
+
+ context 'with DRY_RUN unset' do
+ it_behaves_like 'does not move the file'
+ end
+ end
+ end
end
diff --git a/spec/tasks/gitlab/git_rake_spec.rb b/spec/tasks/gitlab/git_rake_spec.rb
index 1efaecc63a5..57b006e1a39 100644
--- a/spec/tasks/gitlab/git_rake_spec.rb
+++ b/spec/tasks/gitlab/git_rake_spec.rb
@@ -1,41 +1,20 @@
require 'rake_helper'
describe 'gitlab:git rake tasks' do
- before(:all) do
- @default_storage_hash = Gitlab.config.repositories.storages.default.to_h
- end
+ let(:base_path) { 'tmp/tests/default_storage' }
+ let!(:project) { create(:project, :repository) }
before do
Rake.application.rake_require 'tasks/gitlab/git'
- storages = { 'default' => Gitlab::GitalyClient::StorageSettings.new(@default_storage_hash.merge('path' => 'tmp/tests/default_storage')) }
- FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git'))
- allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
allow_any_instance_of(String).to receive(:color) { |string, _color| string }
stub_warn_user_is_not_gitlab
end
- after do
- FileUtils.rm_rf(Settings.absolute('tmp/tests/default_storage'))
- end
-
describe 'fsck' do
it 'outputs the integrity check for a repo' do
- expect { run_rake_task('gitlab:git:fsck') }.to output(%r{Performed Checking integrity at .*@hashed/1/2/test.git}).to_stdout
- end
-
- it 'errors out about config.lock issues' do
- FileUtils.touch(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/config.lock'))
-
- expect { run_rake_task('gitlab:git:fsck') }.to output(/file exists\? ... yes/).to_stdout
- end
-
- it 'errors out about ref lock issues' do
- FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/refs/heads'))
- FileUtils.touch(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/refs/heads/blah.lock'))
-
- expect { run_rake_task('gitlab:git:fsck') }.to output(/Ref lock files exist:/).to_stdout
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/Performed integrity check for/).to_stdout
end
end
end
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index 59013a02938..7e24efda5dd 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -80,6 +80,59 @@ describe FileUploader do
end
end
+ describe 'copy_to' do
+ shared_examples 'returns a valid uploader' do
+ describe 'returned uploader' do
+ let(:new_project) { create(:project) }
+ let(:moved) { described_class.copy_to(subject, new_project) }
+
+ it 'generates a new secret' do
+ expect(subject).to be
+ expect(described_class).to receive(:generate_secret).once.and_call_original
+ expect(moved).to be
+ end
+
+ it 'create new upload' do
+ expect(moved.upload).not_to eq(subject.upload)
+ end
+
+ it 'copies the file' do
+ expect(subject.file).to exist
+ expect(moved.file).to exist
+ expect(subject.file).not_to eq(moved.file)
+ expect(subject.object_store).to eq(moved.object_store)
+ end
+ end
+ end
+
+ context 'files are stored locally' do
+ before do
+ subject.store!(fixture_file_upload('spec/fixtures/dk.png'))
+ end
+
+ include_examples 'returns a valid uploader'
+ end
+
+ context 'files are stored remotely' do
+ before do
+ stub_uploads_object_storage
+ subject.store!(fixture_file_upload('spec/fixtures/dk.png'))
+ subject.migrate!(ObjectStorage::Store::REMOTE)
+ end
+
+ include_examples 'returns a valid uploader'
+ end
+ end
+
+ describe '.extract_dynamic_path' do
+ it 'works with hashed storage' do
+ path = 'export/4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a/test/uploads/72a497a02fe3ee09edae2ed06d390038/dummy.txt'
+
+ expect(described_class.extract_dynamic_path(path)[:identifier]).to eq('dummy.txt')
+ expect(described_class.extract_dynamic_path(path)[:secret]).to eq('72a497a02fe3ee09edae2ed06d390038')
+ end
+ end
+
describe '#secret' do
it 'generates a secret if none is provided' do
expect(described_class).to receive(:generate_secret).and_return('secret')
@@ -113,4 +166,50 @@ describe FileUploader do
uploader.upload = upload
end
end
+
+ describe '#cache!' do
+ subject do
+ uploader.store!(uploaded_file)
+ end
+
+ context 'when remote file is used' do
+ let(:temp_file) { Tempfile.new("test") }
+
+ let!(:fog_connection) do
+ stub_uploads_object_storage(described_class)
+ end
+
+ let(:uploaded_file) do
+ UploadedFile.new(temp_file.path, filename: "my file.txt", remote_id: "test/123123")
+ end
+
+ let!(:fog_file) do
+ fog_connection.directories.get('uploads').files.create(
+ key: 'tmp/uploads/test/123123',
+ body: 'content'
+ )
+ end
+
+ before do
+ FileUtils.touch(temp_file)
+ end
+
+ after do
+ FileUtils.rm_f(temp_file)
+ end
+
+ it 'file is stored remotely in permament location with sanitized name' do
+ subject
+
+ expect(uploader).to be_exists
+ expect(uploader).not_to be_cached
+ expect(uploader).not_to be_file_storage
+ expect(uploader.path).not_to be_nil
+ expect(uploader.path).not_to include('tmp/upload')
+ expect(uploader.path).not_to include('tmp/cache')
+ expect(uploader.url).to include('/my_file.txt')
+ expect(uploader.object_store).to eq(described_class::Store::REMOTE)
+ end
+ end
+ end
end
diff --git a/spec/uploaders/gitlab_uploader_spec.rb b/spec/uploaders/gitlab_uploader_spec.rb
index 362f89424d4..44718ed1212 100644
--- a/spec/uploaders/gitlab_uploader_spec.rb
+++ b/spec/uploaders/gitlab_uploader_spec.rb
@@ -68,4 +68,66 @@ describe GitlabUploader do
expect(subject.file.path).to match(/#{subject.cache_dir}/)
end
end
+
+ describe '#open' do
+ context 'when trace is stored in File storage' do
+ context 'when file exists' do
+ let(:file) do
+ fixture_file_upload('spec/fixtures/trace/sample_trace', 'text/plain')
+ end
+
+ before do
+ subject.store!(file)
+ end
+
+ it 'returns io stream' do
+ expect(subject.open).to be_a(IO)
+ end
+
+ it 'when passing block it yields' do
+ expect { |b| subject.open(&b) }.to yield_control
+ end
+ end
+
+ context 'when file does not exist' do
+ it 'returns nil' do
+ expect(subject.open).to be_nil
+ end
+
+ it 'when passing block it does not yield' do
+ expect { |b| subject.open(&b) }.not_to yield_control
+ end
+ end
+ end
+
+ context 'when trace is stored in Object storage' do
+ before do
+ allow(subject).to receive(:file_storage?) { false }
+ end
+
+ context 'when file exists' do
+ before do
+ allow(subject).to receive(:url) { 'http://object_storage.com/trace' }
+ end
+
+ it 'returns http io stream' do
+ expect(subject.open).to be_a(Gitlab::HttpIO)
+ end
+
+ it 'when passing block it yields' do
+ expect { |b| subject.open(&b) }.to yield_control.once
+ end
+ end
+
+ context 'when file does not exist' do
+ it 'returns nil' do
+ expect(subject.open).to be_nil
+ end
+
+ it 'when passing block it does not yield' do
+ expect { |b| subject.open(&b) }.not_to yield_control
+ end
+ end
+ end
+ end
end
diff --git a/spec/uploaders/import_export_uploader_spec.rb b/spec/uploaders/import_export_uploader_spec.rb
new file mode 100644
index 00000000000..51b173b682d
--- /dev/null
+++ b/spec/uploaders/import_export_uploader_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe ImportExportUploader do
+ let(:model) { build_stubbed(:import_export_upload) }
+ let(:upload) { create(:upload, model: model) }
+
+ subject { described_class.new(model, :import_file) }
+
+ context "object_store is REMOTE" do
+ before do
+ stub_uploads_object_storage
+ end
+
+ include_context 'with storage', described_class::Store::REMOTE
+
+ it_behaves_like 'builds correct paths',
+ store_dir: %r[import_export_upload/import_file/],
+ upload_path: %r[import_export_upload/import_file/]
+ end
+end
diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb
index 026e4356ed6..3ad5fe7e3b3 100644
--- a/spec/uploaders/job_artifact_uploader_spec.rb
+++ b/spec/uploaders/job_artifact_uploader_spec.rb
@@ -23,43 +23,6 @@ describe JobArtifactUploader do
store_dir: %r[\h{2}/\h{2}/\h{64}/\d{4}_\d{1,2}_\d{1,2}/\d+/\d+\z]
end
- describe '#open' do
- subject { uploader.open }
-
- context 'when trace is stored in File storage' do
- context 'when file exists' do
- let(:file) do
- fixture_file_upload('spec/fixtures/trace/sample_trace', 'text/plain')
- end
-
- before do
- uploader.store!(file)
- end
-
- it 'returns io stream' do
- is_expected.to be_a(IO)
- end
- end
-
- context 'when file does not exist' do
- it 'returns nil' do
- is_expected.to be_nil
- end
- end
- end
-
- context 'when trace is stored in Object storage' do
- before do
- allow(uploader).to receive(:file_storage?) { false }
- allow(uploader).to receive(:url) { 'http://object_storage.com/trace' }
- end
-
- it 'returns http io stream' do
- is_expected.to be_a(Gitlab::Ci::Trace::HttpIO)
- end
- end
- end
-
context 'file is stored in valid local_path' do
let(:file) do
fixture_file_upload('spec/fixtures/ci_build_artifacts.zip', 'application/zip')
diff --git a/spec/views/layouts/_head.html.haml_spec.rb b/spec/views/layouts/_head.html.haml_spec.rb
index e8e6d2e7a75..9d1efcabb80 100644
--- a/spec/views/layouts/_head.html.haml_spec.rb
+++ b/spec/views/layouts/_head.html.haml_spec.rb
@@ -29,6 +29,39 @@ describe 'layouts/_head' do
expect(rendered).to match(%{content="foo&quot; http-equiv=&quot;refresh"})
end
+ context 'when an asset_host is set and feature is activated in the config it will' do
+ let(:asset_host) { 'http://assets' }
+
+ before do
+ stub_feature_flags(asset_host_prefetch: true)
+ allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
+ end
+
+ it 'add a link dns-prefetch tag' do
+ render
+ expect(rendered).to match('<link href="http://assets" rel="dns-prefetch">')
+ end
+
+ it 'add a link preconnect tag' do
+ render
+ expect(rendered).to match('<link crossorigin="" href="http://assets" rel="preconnnect">')
+ end
+ end
+
+ context 'when an asset_host is set and feature is not activated in the config it will' do
+ let(:asset_host) { 'http://assets' }
+
+ before do
+ stub_feature_flags(asset_host_prefetch: false)
+ allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
+ end
+
+ it 'not add a link dns-prefetch tag' do
+ render
+ expect(rendered).not_to match('<link href="http://assets" rel="dns-prefetch">')
+ end
+ end
+
def stub_helper_with_safe_string(method)
allow_any_instance_of(PageLayoutHelper).to receive(method)
.and_return(%q{foo" http-equiv="refresh}.html_safe)
diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb
index 15fce65979b..b56940a9613 100644
--- a/spec/views/projects/_home_panel.html.haml_spec.rb
+++ b/spec/views/projects/_home_panel.html.haml_spec.rb
@@ -1,55 +1,48 @@
require 'spec_helper'
describe 'projects/_home_panel' do
- let(:group) { create(:group) }
- let(:project) { create(:project, :public, namespace: group) }
+ context 'notifications' do
+ let(:project) { create(:project) }
- let(:notification_settings) do
- user&.notification_settings_for(project)
- end
+ before do
+ assign(:project, project)
- before do
- assign(:project, project)
- assign(:notification_setting, notification_settings)
+ allow(view).to receive(:current_user).and_return(user)
+ allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
+ end
- allow(view).to receive(:current_user).and_return(user)
- allow(view).to receive(:can?).and_return(false)
- end
+ context 'when user is signed in' do
+ let(:user) { create(:user) }
- context 'when user is signed in' do
- let(:user) { create(:user) }
+ before do
+ notification_settings = user.notification_settings_for(project)
+ assign(:notification_setting, notification_settings)
+ end
- it 'makes it possible to set notification level' do
- render
+ it 'makes it possible to set notification level' do
+ render
- expect(view).to render_template('shared/notifications/_button')
- expect(rendered).to have_selector('.notification-dropdown')
+ expect(view).to render_template('shared/notifications/_button')
+ expect(rendered).to have_selector('.notification-dropdown')
+ end
end
- end
-
- context 'when user is signed out' do
- let(:user) { nil }
- it 'is not possible to set notification level' do
- render
+ context 'when user is signed out' do
+ let(:user) { nil }
- expect(rendered).not_to have_selector('.notification_dropdown')
- end
- end
-
- context 'when project' do
- let!(:user) { create(:user) }
- let(:badges) { project.badges }
+ before do
+ assign(:notification_setting, nil)
+ end
- context 'has no badges' do
- it 'should not render any badge' do
+ it 'is not possible to set notification level' do
render
- expect(rendered).to have_selector('.project-badges')
- expect(rendered).not_to have_selector('.project-badges > a')
+ expect(rendered).not_to have_selector('.notification_dropdown')
end
end
+ end
+ context 'badges' do
shared_examples 'show badges' do
it 'should render the all badges' do
render
@@ -62,7 +55,31 @@ describe 'projects/_home_panel' do
end
end
+ let(:user) { create(:user) }
+ let(:badges) { project.badges }
+
+ before do
+ assign(:project, project)
+
+ allow(view).to receive(:current_user).and_return(user)
+ allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
+ end
+
+ context 'has no badges' do
+ let(:project) { create(:project) }
+
+ it 'should not render any badge' do
+ render
+
+ expect(rendered).to have_selector('.project-badges')
+ expect(rendered).not_to have_selector('.project-badges > a')
+ end
+ end
+
context 'only has group badges' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
+
before do
create(:group_badge, group: project.group)
end
@@ -71,6 +88,8 @@ describe 'projects/_home_panel' do
end
context 'only has project badges' do
+ let(:project) { create(:project) }
+
before do
create(:project_badge, project: project)
end
@@ -79,6 +98,9 @@ describe 'projects/_home_panel' do
end
context 'has both group and project badges' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
+
before do
create(:project_badge, project: project)
create(:group_badge, group: project.group)
@@ -87,4 +109,35 @@ describe 'projects/_home_panel' do
it_behaves_like 'show badges'
end
end
+
+ context 'project id' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before do
+ assign(:project, project)
+
+ allow(view).to receive(:current_user).and_return(user)
+ end
+
+ context 'user can read project' do
+ it 'is shown' do
+ allow(view).to receive(:can?).with(user, :read_project, project).and_return(true)
+
+ render
+
+ expect(rendered).to have_content("Project ID: #{project.id}")
+ end
+ end
+
+ context 'user cannot read project' do
+ it 'is not shown' do
+ allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
+
+ render
+
+ expect(rendered).not_to have_content("Project ID: #{project.id}")
+ end
+ end
+ end
end
diff --git a/spec/views/projects/imports/new.html.haml_spec.rb b/spec/views/projects/imports/new.html.haml_spec.rb
index 32d73d0c5ab..11fe144d1d2 100644
--- a/spec/views/projects/imports/new.html.haml_spec.rb
+++ b/spec/views/projects/imports/new.html.haml_spec.rb
@@ -7,9 +7,9 @@ describe "projects/imports/new.html.haml" do
let(:project) { create(:project_empty_repo, :import_failed, import_type: :gitlab_project, import_source: '/var/opt/gitlab/gitlab-rails/shared/tmp/project_exports/uploads/t.tar.gz', import_url: nil) }
before do
- project.import_state.update_attributes(last_error: '<a href="http://googl.com">Foo</a>')
+ project.import_state.update(last_error: '<a href="http://googl.com">Foo</a>')
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
it "escapes HTML in import errors" do
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 264e0ce0b40..fa6c4ce4ac8 100644
--- a/spec/views/projects/merge_requests/show.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/show.html.haml_spec.rb
@@ -17,6 +17,13 @@ describe 'projects/merge_requests/show.html.haml' 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)
@@ -26,6 +33,8 @@ describe 'projects/merge_requests/show.html.haml' do
assign(:notes, [])
assign(:pipelines, Ci::Pipeline.none)
+ preload_view_requirements
+
allow(view).to receive_messages(current_user: user,
can?: true,
current_application_settings: Gitlab::CurrentSettings.current_application_settings)
@@ -42,6 +51,7 @@ 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
render
@@ -52,10 +62,11 @@ describe 'projects/merge_requests/show.html.haml' do
context 'when the merge request is open' do
it 'closes the merge request if the source project does not exist' do
- closed_merge_request.update_attributes(state: 'open')
+ closed_merge_request.update(state: 'open')
forked_project.destroy
# Reload merge request so MergeRequest#source_project turns to `nil`
closed_merge_request.reload
+ preload_view_requirements
render
diff --git a/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb b/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb
index 6e7d8db99c4..5d60d6bc5e7 100644
--- a/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb
+++ b/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'projects/pipeline_schedules/_pipeline_schedule' do
let(:owner) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:project) { create(:project) }
let(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project) }
@@ -17,10 +17,10 @@ describe 'projects/pipeline_schedules/_pipeline_schedule' do
context 'taking ownership of schedule' do
context 'when non-owner is signed in' do
- let(:user) { master }
+ let(:user) { maintainer }
before do
- allow(view).to receive(:can?).with(master, :take_ownership_pipeline_schedule, pipeline_schedule).and_return(true)
+ allow(view).to receive(:can?).with(maintainer, :take_ownership_pipeline_schedule, pipeline_schedule).and_return(true)
end
it 'non-owner can take ownership of pipeline' do
diff --git a/spec/views/shared/notes/_form.html.haml_spec.rb b/spec/views/shared/notes/_form.html.haml_spec.rb
index 50980718e66..c57319869f3 100644
--- a/spec/views/shared/notes/_form.html.haml_spec.rb
+++ b/spec/views/shared/notes/_form.html.haml_spec.rb
@@ -7,7 +7,7 @@ describe 'shared/notes/_form' do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
assign(:project, project)
assign(:note, note)
diff --git a/spec/workers/background_migration_worker_spec.rb b/spec/workers/background_migration_worker_spec.rb
index d67e7698635..3bd072e7125 100644
--- a/spec/workers/background_migration_worker_spec.rb
+++ b/spec/workers/background_migration_worker_spec.rb
@@ -3,6 +3,12 @@ require 'spec_helper'
describe BackgroundMigrationWorker, :sidekiq, :clean_gitlab_redis_shared_state do
let(:worker) { described_class.new }
+ describe '.minimum_interval' do
+ it 'returns 2 minutes' do
+ expect(described_class.minimum_interval).to eq(2.minutes.to_i)
+ end
+ end
+
describe '.perform' do
it 'performs a background migration' do
expect(Gitlab::BackgroundMigration)
@@ -28,5 +34,51 @@ describe BackgroundMigrationWorker, :sidekiq, :clean_gitlab_redis_shared_state d
worker.perform('Foo', [10, 20])
end
+
+ it 'reschedules a migration if the database is not healthy' do
+ allow(worker)
+ .to receive(:always_perform?)
+ .and_return(false)
+
+ allow(worker)
+ .to receive(:healthy_database?)
+ .and_return(false)
+
+ expect(described_class)
+ .to receive(:perform_in)
+ .with(a_kind_of(Numeric), 'Foo', [10, 20])
+
+ worker.perform('Foo', [10, 20])
+ end
+ end
+
+ describe '#healthy_database?' do
+ context 'using MySQL', :mysql do
+ it 'returns true' do
+ expect(worker.healthy_database?).to eq(true)
+ end
+ end
+
+ context 'using PostgreSQL', :postgresql do
+ context 'when replication lag is too great' do
+ it 'returns false' do
+ allow(Postgresql::ReplicationSlot)
+ .to receive(:lag_too_great?)
+ .and_return(true)
+
+ expect(worker.healthy_database?).to eq(false)
+ end
+ end
+
+ context 'when replication lag is small enough' do
+ it 'returns true' do
+ allow(Postgresql::ReplicationSlot)
+ .to receive(:lag_too_great?)
+ .and_return(false)
+
+ expect(worker.healthy_database?).to eq(true)
+ end
+ end
+ end
end
end
diff --git a/spec/workers/ci/archive_traces_cron_worker_spec.rb b/spec/workers/ci/archive_traces_cron_worker_spec.rb
index 9af51b7d4d8..23f5dda298a 100644
--- a/spec/workers/ci/archive_traces_cron_worker_spec.rb
+++ b/spec/workers/ci/archive_traces_cron_worker_spec.rb
@@ -25,24 +25,36 @@ describe Ci::ArchiveTracesCronWorker do
end
end
- context 'when a job was succeeded' do
+ context 'when a job succeeded' do
let!(:build) { create(:ci_build, :success, :trace_live) }
it_behaves_like 'archives trace'
- context 'when archive raised an exception' do
- let!(:build) { create(:ci_build, :success, :trace_artifact, :trace_live) }
+ context 'when a trace had already been archived' do
+ let!(:build) { create(:ci_build, :success, :trace_live, :trace_artifact) }
let!(:build2) { create(:ci_build, :success, :trace_live) }
- it 'archives valid targets' do
- expect(Rails.logger).to receive(:error).with("Failed to archive stale live trace. id: #{build.id} message: Already archived")
-
+ it 'continues to archive live traces' do
subject
build2.reload
expect(build2.job_artifacts_trace).to be_exist
end
end
+
+ context 'when an unexpected exception happened during archiving' do
+ let!(:build) { create(:ci_build, :success, :trace_live) }
+
+ before do
+ allow_any_instance_of(Gitlab::Ci::Trace).to receive(:archive!).and_raise('Unexpected error')
+ end
+
+ it 'puts a log' do
+ expect(Rails.logger).to receive(:error).with("Failed to archive stale live trace. id: #{build.id} message: Unexpected error")
+
+ subject
+ end
+ end
end
context 'when a job was cancelled' do
diff --git a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
index 615462380e0..9c187bead0a 100644
--- a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
@@ -51,7 +51,6 @@ describe Gitlab::GithubImport::ObjectImporter do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, { 'number' => 10 })
diff --git a/spec/workers/concerns/waitable_worker_spec.rb b/spec/workers/concerns/waitable_worker_spec.rb
index 199825b5097..ce38cde9208 100644
--- a/spec/workers/concerns/waitable_worker_spec.rb
+++ b/spec/workers/concerns/waitable_worker_spec.rb
@@ -18,8 +18,8 @@ describe WaitableWorker do
def self.bulk_perform_inline(args_list)
end
- def perform(i = 0)
- self.class.counter += i
+ def perform(count = 0)
+ self.class.counter += count
end
end
end
diff --git a/spec/workers/create_gpg_signature_worker_spec.rb b/spec/workers/create_gpg_signature_worker_spec.rb
index aa6c347d738..f5479e57260 100644
--- a/spec/workers/create_gpg_signature_worker_spec.rb
+++ b/spec/workers/create_gpg_signature_worker_spec.rb
@@ -2,21 +2,46 @@ require 'spec_helper'
describe CreateGpgSignatureWorker do
let(:project) { create(:project, :repository) }
+ let(:commits) { project.repository.commits('HEAD', limit: 3).commits }
+ let(:commit_shas) { commits.map(&:id) }
+ let(:gpg_commit) { instance_double(Gitlab::Gpg::Commit) }
context 'when GpgKey is found' do
- let(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' }
+ before do
+ allow(Project).to receive(:find_by).with(id: project.id).and_return(project)
+ allow(project).to receive(:commits_by).with(oids: commit_shas).and_return(commits)
+ end
+
+ subject { described_class.new.perform(commit_shas, project.id) }
it 'calls Gitlab::Gpg::Commit#signature' do
- commit = instance_double(Commit)
- gpg_commit = instance_double(Gitlab::Gpg::Commit)
+ commits.each do |commit|
+ expect(Gitlab::Gpg::Commit).to receive(:new).with(commit).and_return(gpg_commit).once
+ end
- allow(Project).to receive(:find_by).with(id: project.id).and_return(project)
- allow(project).to receive(:commit).with(commit_sha).and_return(commit)
+ expect(gpg_commit).to receive(:signature).exactly(commits.size).times
+
+ subject
+ end
+
+ it 'can recover from exception and continue the signature process' do
+ allow(gpg_commit).to receive(:signature)
+ allow(Gitlab::Gpg::Commit).to receive(:new).and_return(gpg_commit)
+ allow(Gitlab::Gpg::Commit).to receive(:new).with(commits.first).and_raise(StandardError)
+
+ expect(gpg_commit).to receive(:signature).exactly(2).times
+
+ subject
+ end
+ end
+
+ context 'handles when a string is passed in for the commit SHA' do
+ it 'creates a signature once' do
+ allow(Gitlab::Gpg::Commit).to receive(:new).with(commits.first).and_return(gpg_commit)
- expect(Gitlab::Gpg::Commit).to receive(:new).with(commit).and_return(gpg_commit)
- expect(gpg_commit).to receive(:signature)
+ expect(gpg_commit).to receive(:signature).once
- described_class.new.perform(commit_sha, project.id)
+ described_class.new.perform(commit_shas.first, project.id)
end
end
@@ -24,7 +49,7 @@ describe CreateGpgSignatureWorker do
let(:nonexisting_commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a34' }
it 'does not raise errors' do
- expect { described_class.new.perform(nonexisting_commit_sha, project.id) }.not_to raise_error
+ expect { described_class.new.perform([nonexisting_commit_sha], project.id) }.not_to raise_error
end
end
@@ -32,13 +57,13 @@ describe CreateGpgSignatureWorker do
let(:nonexisting_project_id) { -1 }
it 'does not raise errors' do
- expect { described_class.new.perform(anything, nonexisting_project_id) }.not_to raise_error
+ expect { described_class.new.perform(commit_shas, nonexisting_project_id) }.not_to raise_error
end
it 'does not call Gitlab::Gpg::Commit#signature' do
expect_any_instance_of(Gitlab::Gpg::Commit).not_to receive(:signature)
- described_class.new.perform(anything, nonexisting_project_id)
+ described_class.new.perform(commit_shas, nonexisting_project_id)
end
end
end
diff --git a/spec/workers/detect_repository_languages_worker_spec.rb b/spec/workers/detect_repository_languages_worker_spec.rb
new file mode 100644
index 00000000000..ff3878fbc8e
--- /dev/null
+++ b/spec/workers/detect_repository_languages_worker_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe DetectRepositoryLanguagesWorker do
+ set(:project) { create(:project) }
+ let(:user) { project.owner }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ it 'calls de DetectRepositoryLanguages service' do
+ service = double
+ allow(::Projects::DetectRepositoryLanguagesService).to receive(:new).and_return(service)
+ expect(service).to receive(:execute)
+
+ subject.perform(project.id, user.id)
+ end
+
+ context 'when invalid ids are used' do
+ it 'does not raise when the project could not be found' do
+ expect do
+ subject.perform(-1, user.id)
+ end.not_to raise_error
+ end
+
+ it 'does not raise when the user could not be found' do
+ expect do
+ subject.perform(project.id, -1)
+ end.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb
index 318aad4bc1e..f17c5ac6aac 100644
--- a/spec/workers/emails_on_push_worker_spec.rb
+++ b/spec/workers/emails_on_push_worker_spec.rb
@@ -100,10 +100,6 @@ describe EmailsOnPushWorker, :mailer do
end
context "when there are multiple recipients" do
- let(:recipients) do
- 1.upto(5).map { |i| user.email.sub('@', "+#{i}@") }.join("\n")
- end
-
before do
# This is a hack because we modify the mail object before sending, for efficency,
# but the TestMailer adapter just appends the objects to an array. To clone a mail
@@ -114,16 +110,57 @@ describe EmailsOnPushWorker, :mailer do
end
end
- it "sends the mail to each of the recipients" do
- perform
- expect(ActionMailer::Base.deliveries.count).to eq(5)
- expect(ActionMailer::Base.deliveries.map(&:to).flatten).to contain_exactly(*recipients.split)
+ context "when the recipient addresses are a list of email addresses" do
+ let(:recipients) do
+ 1.upto(5).map { |i| user.email.sub('@', "+#{i}@") }.join("\n")
+ end
+
+ it "sends the mail to each of the recipients" do
+ perform
+
+ expect(ActionMailer::Base.deliveries.count).to eq(5)
+ expect(email_recipients).to contain_exactly(*recipients.split)
+ end
+
+ it "only generates the mail once" do
+ expect(Notify).to receive(:repository_push_email).once.and_call_original
+ expect(Premailer::Rails::CustomizedPremailer).to receive(:new).once.and_call_original
+
+ perform
+ end
end
- it "only generates the mail once" do
- expect(Notify).to receive(:repository_push_email).once.and_call_original
- expect(Premailer::Rails::CustomizedPremailer).to receive(:new).once.and_call_original
- perform
+ context "when the recipient addresses contains angle brackets and are separated by spaces" do
+ let(:recipients) { "John Doe <johndoe@example.com> Jane Doe <janedoe@example.com>" }
+
+ it "accepts emails separated by whitespace" do
+ perform
+
+ expect(ActionMailer::Base.deliveries.count).to eq(2)
+ expect(email_recipients).to contain_exactly("johndoe@example.com", "janedoe@example.com")
+ end
+ end
+
+ context "when the recipient addresses contain a mix of emails with and without angle brackets" do
+ let(:recipients) { "johndoe@example.com Jane Doe <janedoe@example.com>" }
+
+ it "accepts both kind of emails" do
+ perform
+
+ expect(ActionMailer::Base.deliveries.count).to eq(2)
+ expect(email_recipients).to contain_exactly("johndoe@example.com", "janedoe@example.com")
+ end
+ end
+
+ context "when the recipient addresses contains angle brackets and are separated by newlines" do
+ let(:recipients) { "John Doe <johndoe@example.com>\nJane Doe <janedoe@example.com>" }
+
+ it "accepts emails separated by newlines" do
+ perform
+
+ expect(ActionMailer::Base.deliveries.count).to eq(2)
+ expect(email_recipients).to contain_exactly("johndoe@example.com", "janedoe@example.com")
+ end
end
end
end
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index e39dec556fc..30e67e67e0e 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -27,6 +27,12 @@ describe GitGarbageCollectWorker do
subject.perform(project.id, :gc, lease_key, lease_uuid)
end
+
+ it 'handles gRPC errors' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect).and_raise(GRPC::NotFound)
+
+ expect { subject.perform(project.id, :gc, lease_key, lease_uuid) }.to raise_exception(Gitlab::Git::Repository::NoRepository)
+ end
end
context 'with different lease than the active one' do
@@ -203,12 +209,7 @@ describe GitGarbageCollectWorker do
tree: old_commit.tree,
parents: [old_commit]
)
- Gitlab::Git::OperationService.new(nil, project.repository.raw_repository).send(
- :update_ref,
- "refs/heads/#{SecureRandom.hex(6)}",
- new_commit_sha,
- Gitlab::Git::BLANK_SHA
- )
+ rugged.references.create("refs/heads/#{SecureRandom.hex(6)}", new_commit_sha)
end
def packs(project)
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 48e7eaf32fc..5b1c6b6010a 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
@@ -33,7 +33,6 @@ describe Gitlab::GithubImport::ImportDiffNoteWorker do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, 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 8cf6ac15919..ab070d6d081 100644
--- a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb
@@ -36,7 +36,6 @@ describe Gitlab::GithubImport::ImportIssueWorker do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, 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 677697c02df..3a30f06bb2d 100644
--- a/spec/workers/gitlab/github_import/import_note_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_note_worker_spec.rb
@@ -31,7 +31,6 @@ describe Gitlab::GithubImport::ImportNoteWorker do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, hash)
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 e287ddbe0d7..3cccd7cab21 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
@@ -42,7 +42,6 @@ describe Gitlab::GithubImport::ImportPullRequestWorker do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, hash)
diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb
index c861a56497e..b57c275c770 100644
--- a/spec/workers/merge_worker_spec.rb
+++ b/spec/workers/merge_worker_spec.rb
@@ -8,7 +8,7 @@ describe MergeWorker do
let!(:author) { merge_request.author }
before do
- source_project.add_master(author)
+ source_project.add_maintainer(author)
source_project.repository.expire_branches_cache
end
diff --git a/spec/workers/object_storage_upload_worker_spec.rb b/spec/workers/object_storage_upload_worker_spec.rb
deleted file mode 100644
index 32ddcbe9757..00000000000
--- a/spec/workers/object_storage_upload_worker_spec.rb
+++ /dev/null
@@ -1,108 +0,0 @@
-require 'spec_helper'
-
-describe ObjectStorageUploadWorker do
- let(:local) { ObjectStorage::Store::LOCAL }
- let(:remote) { ObjectStorage::Store::REMOTE }
-
- def perform
- described_class.perform_async(uploader_class.name, subject_class, file_field, subject_id)
- end
-
- context 'for LFS' do
- let!(:lfs_object) { create(:lfs_object, :with_file, file_store: local) }
- let(:uploader_class) { LfsObjectUploader }
- let(:subject_class) { LfsObject }
- let(:file_field) { :file }
- let(:subject_id) { lfs_object.id }
-
- context 'when object storage is enabled' do
- before do
- stub_lfs_object_storage(background_upload: true)
- end
-
- it 'uploads object to storage' do
- expect { perform }.to change { lfs_object.reload.file_store }.from(local).to(remote)
- end
-
- context 'when background upload is disabled' do
- before do
- allow(Gitlab.config.lfs.object_store).to receive(:background_upload) { false }
- end
-
- it 'is skipped' do
- expect { perform }.not_to change { lfs_object.reload.file_store }
- end
- end
- end
-
- context 'when object storage is disabled' do
- before do
- stub_lfs_object_storage(enabled: false)
- end
-
- it "doesn't migrate files" do
- perform
-
- expect(lfs_object.reload.file_store).to eq(local)
- end
- end
- end
-
- context 'for legacy artifacts' do
- let(:build) { create(:ci_build, :legacy_artifacts) }
- let(:uploader_class) { LegacyArtifactUploader }
- let(:subject_class) { Ci::Build }
- let(:file_field) { :artifacts_file }
- let(:subject_id) { build.id }
-
- context 'when local storage is used' do
- let(:store) { local }
-
- context 'and remote storage is defined' do
- before do
- stub_artifacts_object_storage(background_upload: true)
- end
-
- it "migrates file to remote storage" do
- perform
-
- expect(build.reload.artifacts_file_store).to eq(remote)
- end
-
- context 'for artifacts_metadata' do
- let(:file_field) { :artifacts_metadata }
-
- it 'migrates metadata to remote storage' do
- perform
-
- expect(build.reload.artifacts_metadata_store).to eq(remote)
- end
- end
- end
- end
- end
-
- context 'for job artifacts' do
- let(:artifact) { create(:ci_job_artifact, :archive) }
- let(:uploader_class) { JobArtifactUploader }
- let(:subject_class) { Ci::JobArtifact }
- let(:file_field) { :file }
- let(:subject_id) { artifact.id }
-
- context 'when local storage is used' do
- let(:store) { local }
-
- context 'and remote storage is defined' do
- before do
- stub_artifacts_object_storage(background_upload: true)
- end
-
- it "migrates file to remote storage" do
- perform
-
- expect(artifact.reload.file_store).to eq(remote)
- end
- end
- end
- end
-end
diff --git a/spec/workers/pipeline_schedule_worker_spec.rb b/spec/workers/pipeline_schedule_worker_spec.rb
index e7a4ac0f3d6..a2fe4734d47 100644
--- a/spec/workers/pipeline_schedule_worker_spec.rb
+++ b/spec/workers/pipeline_schedule_worker_spec.rb
@@ -18,7 +18,7 @@ describe PipelineScheduleWorker do
context 'when the schedule is runnable by the user' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when there is a scheduled pipeline within next_run_at' do
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index ac79d9c0ac1..2d071c181c2 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe ProcessCommitWorker do
+ include ProjectForksHelper
+
let(:worker) { described_class.new }
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
@@ -32,15 +34,41 @@ describe ProcessCommitWorker do
worker.perform(project.id, user.id, commit.to_hash)
end
- context 'when commit already exists in upstream project' do
- let(:forked) { create(:project, :public, :repository) }
+ context 'when the project is forked' do
+ context 'when commit already exists in the upstream project' do
+ it 'does not process the commit message' do
+ forked = fork_project(project, user, repository: true)
+
+ expect(worker).not_to receive(:process_commit_message)
+
+ worker.perform(forked.id, user.id, forked.commit.to_hash)
+ end
+ end
+
+ context 'when the commit does not exist in the upstream project' do
+ it 'processes the commit message' do
+ empty_project = create(:project, :public)
+ forked = fork_project(empty_project, user, repository: true)
+
+ TestEnv.copy_repo(forked,
+ bare_repo: TestEnv.factory_repo_path_bare,
+ refs: TestEnv::BRANCH_SHA)
+
+ expect(worker).to receive(:process_commit_message)
+
+ worker.perform(forked.id, user.id, forked.commit.to_hash)
+ end
+ end
- it 'does not process commit message' do
- create(:forked_project_link, forked_to_project: forked, forked_from_project: project)
+ context 'when the upstream project no longer exists' do
+ it 'processes the commit message' do
+ forked = fork_project(project, user, repository: true)
+ project.destroy!
- expect(worker).not_to receive(:process_commit_message)
+ expect(worker).to receive(:process_commit_message)
- worker.perform(forked.id, user.id, forked.commit.to_hash)
+ worker.perform(forked.id, user.id, forked.commit.to_hash)
+ end
end
end
end
diff --git a/spec/workers/project_migrate_hashed_storage_worker_spec.rb b/spec/workers/project_migrate_hashed_storage_worker_spec.rb
index 9551e358af1..3703320418b 100644
--- a/spec/workers/project_migrate_hashed_storage_worker_spec.rb
+++ b/spec/workers/project_migrate_hashed_storage_worker_spec.rb
@@ -28,7 +28,7 @@ describe ProjectMigrateHashedStorageWorker, :clean_gitlab_redis_shared_state do
migration_service = spy
allow(::Projects::HashedStorageMigrationService)
- .to receive(:new).with(project, subject.logger)
+ .to receive(:new).with(project, project.full_path, logger: subject.logger)
.and_return(migration_service)
subject.perform(project.id)
diff --git a/spec/workers/prune_web_hook_logs_worker_spec.rb b/spec/workers/prune_web_hook_logs_worker_spec.rb
new file mode 100644
index 00000000000..d7d64a1f641
--- /dev/null
+++ b/spec/workers/prune_web_hook_logs_worker_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe PruneWebHookLogsWorker do
+ describe '#perform' do
+ before do
+ hook = create(:project_hook)
+
+ 5.times do
+ create(:web_hook_log, web_hook: hook, created_at: 5.months.ago)
+ end
+
+ create(:web_hook_log, web_hook: hook, response_status: '404')
+ end
+
+ it 'removes all web hook logs older than one month' do
+ described_class.new.perform
+
+ expect(WebHookLog.count).to eq(1)
+ expect(WebHookLog.first.response_status).to eq('404')
+ end
+ end
+end
diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb
index 6bc551be9ad..ede271b2cdd 100644
--- a/spec/workers/repository_check/batch_worker_spec.rb
+++ b/spec/workers/repository_check/batch_worker_spec.rb
@@ -62,4 +62,12 @@ describe RepositoryCheck::BatchWorker do
expect(subject.perform(shard_name)).to eq([])
end
+
+ it 'does not run if the exclusive lease is taken' do
+ allow(subject).to receive(:try_obtain_lease).and_return(false)
+
+ expect(subject).not_to receive(:perform_repository_checks)
+
+ subject.perform(shard_name)
+ end
end
diff --git a/spec/workers/repository_check/dispatch_worker_spec.rb b/spec/workers/repository_check/dispatch_worker_spec.rb
index 20a4f1f5344..7877429aa8f 100644
--- a/spec/workers/repository_check/dispatch_worker_spec.rb
+++ b/spec/workers/repository_check/dispatch_worker_spec.rb
@@ -11,6 +11,14 @@ describe RepositoryCheck::DispatchWorker do
subject.perform
end
+ it 'does nothing if the exclusive lease is taken' do
+ allow(subject).to receive(:try_obtain_lease).and_return(false)
+
+ expect(RepositoryCheck::BatchWorker).not_to receive(:perform_async)
+
+ subject.perform
+ end
+
it 'dispatches work to RepositoryCheck::BatchWorker' do
expect(RepositoryCheck::BatchWorker).to receive(:perform_async).at_least(:once)
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index f0884ad0aff..d07e40377d4 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -51,7 +51,7 @@ describe RepositoryImportWorker do
it 'hide the credentials that were used in the import URL' do
error = %q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found }
- project.update_attributes(import_jid: '123')
+ project.update(import_jid: '123')
expect_any_instance_of(Projects::ImportService).to receive(:execute).and_return({ status: :error, message: error })
expect do
@@ -63,7 +63,7 @@ describe RepositoryImportWorker do
it 'updates the error on Import/Export' do
error = %q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found }
- project.update_attributes(import_jid: '123', import_type: 'gitlab_project')
+ project.update(import_jid: '123', import_type: 'gitlab_project')
expect_any_instance_of(Projects::ImportService).to receive(:execute).and_return({ status: :error, message: error })
expect do
diff --git a/spec/workers/repository_update_remote_mirror_worker_spec.rb b/spec/workers/repository_update_remote_mirror_worker_spec.rb
index 152ba2509b9..4f1ad2474f5 100644
--- a/spec/workers/repository_update_remote_mirror_worker_spec.rb
+++ b/spec/workers/repository_update_remote_mirror_worker_spec.rb
@@ -13,7 +13,7 @@ describe RepositoryUpdateRemoteMirrorWorker do
describe '#perform' do
context 'with status none' do
before do
- remote_mirror.update_attributes(update_status: 'none')
+ remote_mirror.update(update_status: 'none')
end
it 'sets status as finished when update remote mirror service executes successfully' do
@@ -34,7 +34,7 @@ describe RepositoryUpdateRemoteMirrorWorker do
end
it 'does nothing if last_update_started_at is higher than the time the job was scheduled in' do
- remote_mirror.update_attributes(last_update_started_at: Time.now)
+ remote_mirror.update(last_update_started_at: Time.now)
expect_any_instance_of(RemoteMirror).to receive(:updated_since?).with(scheduled_time).and_return(true)
expect_any_instance_of(Projects::UpdateRemoteMirrorService).not_to receive(:execute).with(remote_mirror)
@@ -56,7 +56,7 @@ describe RepositoryUpdateRemoteMirrorWorker do
context 'with another worker already running' do
before do
- remote_mirror.update_attributes(update_status: 'started')
+ remote_mirror.update(update_status: 'started')
end
it 'raises RemoteMirrorUpdateAlreadyInProgressError' do
@@ -68,11 +68,11 @@ describe RepositoryUpdateRemoteMirrorWorker do
context 'with status failed' do
before do
- remote_mirror.update_attributes(update_status: 'failed')
+ remote_mirror.update(update_status: 'failed')
end
it 'sets status as finished if last_update_started_at is higher than the time the job was scheduled in' do
- remote_mirror.update_attributes(last_update_started_at: Time.now)
+ remote_mirror.update(last_update_started_at: Time.now)
expect_any_instance_of(RemoteMirror).to receive(:updated_since?).with(scheduled_time).and_return(false)
expect_any_instance_of(Projects::UpdateRemoteMirrorService).to receive(:execute).with(remote_mirror).and_return(status: :success)
diff --git a/spec/workers/schedule_update_user_activity_worker_spec.rb b/spec/workers/schedule_update_user_activity_worker_spec.rb
deleted file mode 100644
index 32c59381b01..00000000000
--- a/spec/workers/schedule_update_user_activity_worker_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require 'spec_helper'
-
-describe ScheduleUpdateUserActivityWorker, :clean_gitlab_redis_shared_state do
- let(:now) { Time.now }
-
- before do
- Gitlab::UserActivities.record('1', now)
- Gitlab::UserActivities.record('2', now)
- end
-
- it 'schedules UpdateUserActivityWorker once' do
- expect(UpdateUserActivityWorker).to receive(:perform_async).with({ '1' => now.to_i.to_s, '2' => now.to_i.to_s })
-
- subject.perform
- end
-
- context 'when specifying a batch size' do
- it 'schedules UpdateUserActivityWorker twice' do
- expect(UpdateUserActivityWorker).to receive(:perform_async).with({ '1' => now.to_i.to_s })
- expect(UpdateUserActivityWorker).to receive(:perform_async).with({ '2' => now.to_i.to_s })
-
- subject.perform(1)
- end
- end
-end
diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb
index 815432aacce..808084c8f7c 100644
--- a/spec/workers/storage_migrator_worker_spec.rb
+++ b/spec/workers/storage_migrator_worker_spec.rb
@@ -13,7 +13,7 @@ describe StorageMigratorWorker do
end
it 'migrates projects in the specified range' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
worker.perform(ids.min, ids.max)
end
diff --git a/spec/workers/stuck_import_jobs_worker_spec.rb b/spec/workers/stuck_import_jobs_worker_spec.rb
index af7675c8cab..2169c14218b 100644
--- a/spec/workers/stuck_import_jobs_worker_spec.rb
+++ b/spec/workers/stuck_import_jobs_worker_spec.rb
@@ -51,7 +51,7 @@ describe StuckImportJobsWorker do
let(:project) { create(:project, :import_scheduled) }
before do
- project.import_state.update_attributes(jid: '123')
+ project.import_state.update(jid: '123')
end
end
end
@@ -61,7 +61,7 @@ describe StuckImportJobsWorker do
let(:project) { create(:project, :import_started) }
before do
- project.import_state.update_attributes(jid: '123')
+ project.import_state.update(jid: '123')
end
end
end
diff --git a/spec/workers/todos_destroyer/confidential_issue_worker_spec.rb b/spec/workers/todos_destroyer/confidential_issue_worker_spec.rb
new file mode 100644
index 00000000000..9d7c0b8f560
--- /dev/null
+++ b/spec/workers/todos_destroyer/confidential_issue_worker_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe TodosDestroyer::ConfidentialIssueWorker do
+ it "calls the Todos::Destroy::ConfidentialIssueService with the params it was given" do
+ service = double
+
+ expect(::Todos::Destroy::ConfidentialIssueService).to receive(:new).with(100).and_return(service)
+ expect(service).to receive(:execute)
+
+ described_class.new.perform(100)
+ end
+end
diff --git a/spec/workers/todos_destroyer/entity_leave_worker_spec.rb b/spec/workers/todos_destroyer/entity_leave_worker_spec.rb
new file mode 100644
index 00000000000..955447906aa
--- /dev/null
+++ b/spec/workers/todos_destroyer/entity_leave_worker_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe TodosDestroyer::EntityLeaveWorker do
+ it "calls the Todos::Destroy::EntityLeaveService with the params it was given" do
+ service = double
+
+ expect(::Todos::Destroy::EntityLeaveService).to receive(:new).with(100, 5, 'Group').and_return(service)
+ expect(service).to receive(:execute)
+
+ described_class.new.perform(100, 5, 'Group')
+ end
+end
diff --git a/spec/workers/todos_destroyer/group_private_worker_spec.rb b/spec/workers/todos_destroyer/group_private_worker_spec.rb
new file mode 100644
index 00000000000..fcc38989ced
--- /dev/null
+++ b/spec/workers/todos_destroyer/group_private_worker_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe TodosDestroyer::GroupPrivateWorker do
+ it "calls the Todos::Destroy::GroupPrivateService with the params it was given" do
+ service = double
+
+ expect(::Todos::Destroy::GroupPrivateService).to receive(:new).with(100).and_return(service)
+ expect(service).to receive(:execute)
+
+ described_class.new.perform(100)
+ end
+end
diff --git a/spec/workers/todos_destroyer/private_features_worker_spec.rb b/spec/workers/todos_destroyer/private_features_worker_spec.rb
new file mode 100644
index 00000000000..9599f5ee071
--- /dev/null
+++ b/spec/workers/todos_destroyer/private_features_worker_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe TodosDestroyer::PrivateFeaturesWorker do
+ it "calls the Todos::Destroy::PrivateFeaturesService with the params it was given" do
+ service = double
+
+ expect(::Todos::Destroy::PrivateFeaturesService).to receive(:new).with(100, nil).and_return(service)
+ expect(service).to receive(:execute)
+
+ described_class.new.perform(100)
+ end
+end
diff --git a/spec/workers/todos_destroyer/project_private_worker_spec.rb b/spec/workers/todos_destroyer/project_private_worker_spec.rb
new file mode 100644
index 00000000000..15d926fa9d5
--- /dev/null
+++ b/spec/workers/todos_destroyer/project_private_worker_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe TodosDestroyer::ProjectPrivateWorker do
+ it "calls the Todos::Destroy::ProjectPrivateService with the params it was given" do
+ service = double
+
+ expect(::Todos::Destroy::ProjectPrivateService).to receive(:new).with(100).and_return(service)
+ expect(service).to receive(:execute)
+
+ described_class.new.perform(100)
+ end
+end
diff --git a/spec/workers/update_user_activity_worker_spec.rb b/spec/workers/update_user_activity_worker_spec.rb
deleted file mode 100644
index 268ca1d81f2..00000000000
--- a/spec/workers/update_user_activity_worker_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'spec_helper'
-
-describe UpdateUserActivityWorker, :clean_gitlab_redis_shared_state do
- let(:user_active_2_days_ago) { create(:user, current_sign_in_at: 10.months.ago) }
- let(:user_active_yesterday_1) { create(:user) }
- let(:user_active_yesterday_2) { create(:user) }
- let(:user_active_today) { create(:user) }
- let(:data) do
- {
- user_active_2_days_ago.id.to_s => 2.days.ago.at_midday.to_i.to_s,
- user_active_yesterday_1.id.to_s => 1.day.ago.at_midday.to_i.to_s,
- user_active_yesterday_2.id.to_s => 1.day.ago.at_midday.to_i.to_s,
- user_active_today.id.to_s => Time.now.to_i.to_s
- }
- end
-
- it 'updates users.last_activity_on' do
- subject.perform(data)
-
- aggregate_failures do
- expect(user_active_2_days_ago.reload.last_activity_on).to eq(2.days.ago.to_date)
- expect(user_active_yesterday_1.reload.last_activity_on).to eq(1.day.ago.to_date)
- expect(user_active_yesterday_2.reload.last_activity_on).to eq(1.day.ago.to_date)
- expect(user_active_today.reload.reload.last_activity_on).to eq(Date.today)
- end
- end
-
- it 'deletes the pairs from SharedState' do
- data.each { |id, time| Gitlab::UserActivities.record(id, time) }
-
- subject.perform(data)
-
- expect(Gitlab::UserActivities.new.to_a).to be_empty
- end
-end
diff --git a/vendor/Dockerfile/Node-alpine.Dockerfile b/vendor/Dockerfile/Node-alpine.Dockerfile
index 9776b1336b5..5b9b495644a 100644
--- a/vendor/Dockerfile/Node-alpine.Dockerfile
+++ b/vendor/Dockerfile/Node-alpine.Dockerfile
@@ -1,14 +1,15 @@
-FROM node:7.9-alpine
+FROM node:8.11-alpine
WORKDIR /usr/src/app
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
+
COPY package.json /usr/src/app/
-RUN npm install && npm cache clean
-COPY . /usr/src/app
+RUN npm install
-CMD [ "npm", "start" ]
+COPY . /usr/src/app
# replace this with your application's default port
EXPOSE 8888
+CMD [ "npm", "start" ]
diff --git a/vendor/Dockerfile/Node.Dockerfile b/vendor/Dockerfile/Node.Dockerfile
index 7e936d5e887..e8b64b3a6e4 100644
--- a/vendor/Dockerfile/Node.Dockerfile
+++ b/vendor/Dockerfile/Node.Dockerfile
@@ -1,14 +1,15 @@
-FROM node:7.9
+FROM node:8.11
WORKDIR /usr/src/app
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
+
COPY package.json /usr/src/app/
-RUN npm install && npm cache clean
-COPY . /usr/src/app
+RUN npm install
-CMD [ "npm", "start" ]
+COPY . /usr/src/app
# replace this with your application's default port
EXPOSE 8888
+CMD [ "npm", "start" ] \ No newline at end of file
diff --git a/vendor/Dockerfile/Ruby-alpine.Dockerfile b/vendor/Dockerfile/Ruby-alpine.Dockerfile
index 9db4e2130f2..dffe9a65116 100644
--- a/vendor/Dockerfile/Ruby-alpine.Dockerfile
+++ b/vendor/Dockerfile/Ruby-alpine.Dockerfile
@@ -1,8 +1,8 @@
-FROM ruby:2.4-alpine
+FROM ruby:2.5-alpine
# Edit with nodejs, mysql-client, postgresql-client, sqlite3, etc. for your needs.
# Or delete entirely if not needed.
-RUN apk --no-cache add nodejs postgresql-client
+RUN apk --no-cache add nodejs postgresql-client tzdata
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
@@ -11,7 +11,10 @@ RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY Gemfile Gemfile.lock /usr/src/app/
-RUN bundle install
+# Install build dependencies - required for gems with native dependencies
+RUN apk add --no-cache --virtual build-deps build-base postgresql-dev && \
+ bundle install && \
+ apk del build-deps
COPY . /usr/src/app
@@ -21,4 +24,4 @@ COPY . /usr/src/app
# For Rails
EXPOSE 3000
-CMD ["rails", "server"]
+CMD ["bundle", "exec", "rails", "server"]
diff --git a/vendor/Dockerfile/Ruby.Dockerfile b/vendor/Dockerfile/Ruby.Dockerfile
index feb880ee4b2..289ed57bfa2 100644
--- a/vendor/Dockerfile/Ruby.Dockerfile
+++ b/vendor/Dockerfile/Ruby.Dockerfile
@@ -1,4 +1,4 @@
-FROM ruby:2.4
+FROM ruby:2.5
# Edit with nodejs, mysql-client, postgresql-client, sqlite3, etc. for your needs.
# Or delete entirely if not needed.
@@ -24,4 +24,4 @@ COPY . /usr/src/app
# For Rails
EXPOSE 3000
-CMD ["rails", "server", "-b", "0.0.0.0"]
+CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
diff --git a/vendor/assets/javascripts/date.format.js b/vendor/assets/javascripts/date.format.js
deleted file mode 100644
index 2c9b4825443..00000000000
--- a/vendor/assets/javascripts/date.format.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Date Format 1.2.3
- * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
- * MIT license
- *
- * Includes enhancements by Scott Trenda <scott.trenda.net>
- * and Kris Kowal <cixar.com/~kris.kowal/>
- *
- * Accepts a date, a mask, or a date and a mask.
- * Returns a formatted version of the given date.
- * The date defaults to the current date/time.
- * The mask defaults to dateFormat.masks.default.
- */
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global.dateFormat = factory());
- }(this, (function () { 'use strict';
- var dateFormat = function () {
- var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
- timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
- timezoneClip = /[^-+\dA-Z]/g,
- pad = function (val, len) {
- val = String(val);
- len = len || 2;
- while (val.length < len) val = "0" + val;
- return val;
- };
-
- // Regexes and supporting functions are cached through closure
- return function (date, mask, utc) {
- var dF = dateFormat;
-
- // You can't provide utc if you skip other args (use the "UTC:" mask prefix)
- if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
- mask = date;
- date = undefined;
- }
-
- // Passing date through Date applies Date.parse, if necessary
- date = date ? new Date(date) : new Date;
- if (isNaN(date)) throw SyntaxError("invalid date");
-
- mask = String(dF.masks[mask] || mask || dF.masks["default"]);
-
- // Allow setting the utc argument via the mask
- if (mask.slice(0, 4) == "UTC:") {
- mask = mask.slice(4);
- utc = true;
- }
-
- var _ = utc ? "getUTC" : "get",
- d = date[_ + "Date"](),
- D = date[_ + "Day"](),
- m = date[_ + "Month"](),
- y = date[_ + "FullYear"](),
- H = date[_ + "Hours"](),
- M = date[_ + "Minutes"](),
- s = date[_ + "Seconds"](),
- L = date[_ + "Milliseconds"](),
- o = utc ? 0 : date.getTimezoneOffset(),
- flags = {
- d: d,
- dd: pad(d),
- ddd: dF.i18n.dayNames[D],
- dddd: dF.i18n.dayNames[D + 7],
- m: m + 1,
- mm: pad(m + 1),
- mmm: dF.i18n.monthNames[m],
- mmmm: dF.i18n.monthNames[m + 12],
- yy: String(y).slice(2),
- yyyy: y,
- h: H % 12 || 12,
- hh: pad(H % 12 || 12),
- H: H,
- HH: pad(H),
- M: M,
- MM: pad(M),
- s: s,
- ss: pad(s),
- l: pad(L, 3),
- L: pad(L > 99 ? Math.round(L / 10) : L),
- t: H < 12 ? "a" : "p",
- tt: H < 12 ? "am" : "pm",
- T: H < 12 ? "A" : "P",
- TT: H < 12 ? "AM" : "PM",
- Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
- o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
- S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
- };
-
- return mask.replace(token, function ($0) {
- return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
- });
- };
- }();
-
- // Some common format strings
- dateFormat.masks = {
- "default": "ddd mmm dd yyyy HH:MM:ss",
- shortDate: "m/d/yy",
- mediumDate: "mmm d, yyyy",
- longDate: "mmmm d, yyyy",
- fullDate: "dddd, mmmm d, yyyy",
- shortTime: "h:MM TT",
- mediumTime: "h:MM:ss TT",
- longTime: "h:MM:ss TT Z",
- isoDate: "yyyy-mm-dd",
- isoTime: "HH:MM:ss",
- isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
- isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
- };
-
- // Internationalization strings
- dateFormat.i18n = {
- dayNames: [
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
- "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
- ],
- monthNames: [
- "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
- "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
- ]
- };
-
- // For convenience...
- Date.prototype.format = function (mask, utc) {
- return dateFormat(this, mask, utc);
- };
-
- return dateFormat;
-})));
diff --git a/vendor/assets/javascripts/xterm/encoding-indexes.js b/vendor/assets/javascripts/xterm/encoding-indexes.js
deleted file mode 100644
index 5fd98f5577e..00000000000
--- a/vendor/assets/javascripts/xterm/encoding-indexes.js
+++ /dev/null
@@ -1,39 +0,0 @@
-(function(global) {
- 'use strict';
- global["encoding-indexes"] =
-{
- "big5":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,17392,19506,17923,17830,17784,160359,19831,17843,162993,19682,163013,15253,18230,18244,19527,19520,148159,144919,160594,159371,159954,19543,172881,18255,17882,19589,162924,19719,19108,18081,158499,29221,154196,137827,146950,147297,26189,22267,null,32149,22813,166841,15860,38708,162799,23515,138590,23204,13861,171696,23249,23479,23804,26478,34195,170309,29793,29853,14453,138579,145054,155681,16108,153822,15093,31484,40855,147809,166157,143850,133770,143966,17162,33924,40854,37935,18736,34323,22678,38730,37400,31184,31282,26208,27177,34973,29772,31685,26498,31276,21071,36934,13542,29636,155065,29894,40903,22451,18735,21580,16689,145038,22552,31346,162661,35727,18094,159368,16769,155033,31662,140476,40904,140481,140489,140492,40905,34052,144827,16564,40906,17633,175615,25281,28782,40907,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,12736,12737,12738,12739,12740,131340,12741,131281,131277,12742,12743,131275,139240,12744,131274,12745,12746,12747,12748,131342,12749,12750,256,193,461,192,274,201,282,200,332,211,465,210,null,7870,null,7872,202,257,225,462,224,593,275,233,283,232,299,237,464,236,333,243,466,242,363,250,468,249,470,472,474,476,252,null,7871,null,7873,234,609,9178,9179,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,172969,135493,null,25866,null,null,20029,28381,40270,37343,null,null,161589,25745,20250,20264,20392,20822,20852,20892,20964,21153,21160,21307,21326,21457,21464,22242,22768,22788,22791,22834,22836,23398,23454,23455,23706,24198,24635,25993,26622,26628,26725,27982,28860,30005,32420,32428,32442,32455,32463,32479,32518,32567,33402,33487,33647,35270,35774,35810,36710,36711,36718,29713,31996,32205,26950,31433,21031,null,null,null,null,37260,30904,37214,32956,null,36107,33014,133607,null,null,32927,40647,19661,40393,40460,19518,171510,159758,40458,172339,13761,null,28314,33342,29977,null,18705,39532,39567,40857,31111,164972,138698,132560,142054,20004,20097,20096,20103,20159,20203,20279,13388,20413,15944,20483,20616,13437,13459,13477,20870,22789,20955,20988,20997,20105,21113,21136,21287,13767,21417,13649,21424,13651,21442,21539,13677,13682,13953,21651,21667,21684,21689,21712,21743,21784,21795,21800,13720,21823,13733,13759,21975,13765,163204,21797,null,134210,134421,151851,21904,142534,14828,131905,36422,150968,169189,16467,164030,30586,142392,14900,18389,164189,158194,151018,25821,134524,135092,134357,135412,25741,36478,134806,134155,135012,142505,164438,148691,null,134470,170573,164073,18420,151207,142530,39602,14951,169460,16365,13574,152263,169940,161992,142660,40302,38933,null,17369,155813,25780,21731,142668,142282,135287,14843,135279,157402,157462,162208,25834,151634,134211,36456,139681,166732,132913,null,18443,131497,16378,22643,142733,null,148936,132348,155799,134988,134550,21881,16571,17338,null,19124,141926,135325,33194,39157,134556,25465,14846,141173,36288,22177,25724,15939,null,173569,134665,142031,142537,null,135368,145858,14738,14854,164507,13688,155209,139463,22098,134961,142514,169760,13500,27709,151099,null,null,161140,142987,139784,173659,167117,134778,134196,157724,32659,135375,141315,141625,13819,152035,134796,135053,134826,16275,134960,134471,135503,134732,null,134827,134057,134472,135360,135485,16377,140950,25650,135085,144372,161337,142286,134526,134527,142417,142421,14872,134808,135367,134958,173618,158544,167122,167321,167114,38314,21708,33476,21945,null,171715,39974,39606,161630,142830,28992,33133,33004,23580,157042,33076,14231,21343,164029,37302,134906,134671,134775,134907,13789,151019,13833,134358,22191,141237,135369,134672,134776,135288,135496,164359,136277,134777,151120,142756,23124,135197,135198,135413,135414,22428,134673,161428,164557,135093,134779,151934,14083,135094,135552,152280,172733,149978,137274,147831,164476,22681,21096,13850,153405,31666,23400,18432,19244,40743,18919,39967,39821,154484,143677,22011,13810,22153,20008,22786,138177,194680,38737,131206,20059,20155,13630,23587,24401,24516,14586,25164,25909,27514,27701,27706,28780,29227,20012,29357,149737,32594,31035,31993,32595,156266,13505,null,156491,32770,32896,157202,158033,21341,34916,35265,161970,35744,36125,38021,38264,38271,38376,167439,38886,39029,39118,39134,39267,170000,40060,40479,40644,27503,63751,20023,131207,38429,25143,38050,null,20539,28158,171123,40870,15817,34959,147790,28791,23797,19232,152013,13657,154928,24866,166450,36775,37366,29073,26393,29626,144001,172295,15499,137600,19216,30948,29698,20910,165647,16393,27235,172730,16931,34319,133743,31274,170311,166634,38741,28749,21284,139390,37876,30425,166371,40871,30685,20131,20464,20668,20015,20247,40872,21556,32139,22674,22736,138678,24210,24217,24514,141074,25995,144377,26905,27203,146531,27903,null,29184,148741,29580,16091,150035,23317,29881,35715,154788,153237,31379,31724,31939,32364,33528,34199,40873,34960,40874,36537,40875,36815,34143,39392,37409,40876,167353,136255,16497,17058,23066,null,null,null,39016,26475,17014,22333,null,34262,149883,33471,160013,19585,159092,23931,158485,159678,40877,40878,23446,40879,26343,32347,28247,31178,15752,17603,143958,141206,17306,17718,null,23765,146202,35577,23672,15634,144721,23928,40882,29015,17752,147692,138787,19575,14712,13386,131492,158785,35532,20404,131641,22975,33132,38998,170234,24379,134047,null,139713,166253,16642,18107,168057,16135,40883,172469,16632,14294,18167,158790,16764,165554,160767,17773,14548,152730,17761,17691,19849,19579,19830,17898,16328,150287,13921,17630,17597,16877,23870,23880,23894,15868,14351,23972,23993,14368,14392,24130,24253,24357,24451,14600,14612,14655,14669,24791,24893,23781,14729,25015,25017,25039,14776,25132,25232,25317,25368,14840,22193,14851,25570,25595,25607,25690,14923,25792,23829,22049,40863,14999,25990,15037,26111,26195,15090,26258,15138,26390,15170,26532,26624,15192,26698,26756,15218,15217,15227,26889,26947,29276,26980,27039,27013,15292,27094,15325,27237,27252,27249,27266,15340,27289,15346,27307,27317,27348,27382,27521,27585,27626,27765,27818,15563,27906,27910,27942,28033,15599,28068,28081,28181,28184,28201,28294,166336,28347,28386,28378,40831,28392,28393,28452,28468,15686,147265,28545,28606,15722,15733,29111,23705,15754,28716,15761,28752,28756,28783,28799,28809,131877,17345,13809,134872,147159,22462,159443,28990,153568,13902,27042,166889,23412,31305,153825,169177,31333,31357,154028,31419,31408,31426,31427,29137,156813,16842,31450,31453,31466,16879,21682,154625,31499,31573,31529,152334,154878,31650,31599,33692,154548,158847,31696,33825,31634,31672,154912,15789,154725,33938,31738,31750,31797,154817,31812,31875,149634,31910,26237,148856,31945,31943,31974,31860,31987,31989,31950,32359,17693,159300,32093,159446,29837,32137,32171,28981,32179,32210,147543,155689,32228,15635,32245,137209,32229,164717,32285,155937,155994,32366,32402,17195,37996,32295,32576,32577,32583,31030,156368,39393,32663,156497,32675,136801,131176,17756,145254,17667,164666,32762,156809,32773,32776,32797,32808,32815,172167,158915,32827,32828,32865,141076,18825,157222,146915,157416,26405,32935,166472,33031,33050,22704,141046,27775,156824,151480,25831,136330,33304,137310,27219,150117,150165,17530,33321,133901,158290,146814,20473,136445,34018,33634,158474,149927,144688,137075,146936,33450,26907,194964,16859,34123,33488,33562,134678,137140,14017,143741,144730,33403,33506,33560,147083,159139,158469,158615,144846,15807,33565,21996,33669,17675,159141,33708,33729,33747,13438,159444,27223,34138,13462,159298,143087,33880,154596,33905,15827,17636,27303,33866,146613,31064,33960,158614,159351,159299,34014,33807,33681,17568,33939,34020,154769,16960,154816,17731,34100,23282,159385,17703,34163,17686,26559,34326,165413,165435,34241,159880,34306,136578,159949,194994,17770,34344,13896,137378,21495,160666,34430,34673,172280,34798,142375,34737,34778,34831,22113,34412,26710,17935,34885,34886,161248,146873,161252,34910,34972,18011,34996,34997,25537,35013,30583,161551,35207,35210,35238,35241,35239,35260,166437,35303,162084,162493,35484,30611,37374,35472,162393,31465,162618,147343,18195,162616,29052,35596,35615,152624,152933,35647,35660,35661,35497,150138,35728,35739,35503,136927,17941,34895,35995,163156,163215,195028,14117,163155,36054,163224,163261,36114,36099,137488,36059,28764,36113,150729,16080,36215,36265,163842,135188,149898,15228,164284,160012,31463,36525,36534,36547,37588,36633,36653,164709,164882,36773,37635,172703,133712,36787,18730,166366,165181,146875,24312,143970,36857,172052,165564,165121,140069,14720,159447,36919,165180,162494,36961,165228,165387,37032,165651,37060,165606,37038,37117,37223,15088,37289,37316,31916,166195,138889,37390,27807,37441,37474,153017,37561,166598,146587,166668,153051,134449,37676,37739,166625,166891,28815,23235,166626,166629,18789,37444,166892,166969,166911,37747,37979,36540,38277,38310,37926,38304,28662,17081,140922,165592,135804,146990,18911,27676,38523,38550,16748,38563,159445,25050,38582,30965,166624,38589,21452,18849,158904,131700,156688,168111,168165,150225,137493,144138,38705,34370,38710,18959,17725,17797,150249,28789,23361,38683,38748,168405,38743,23370,168427,38751,37925,20688,143543,143548,38793,38815,38833,38846,38848,38866,38880,152684,38894,29724,169011,38911,38901,168989,162170,19153,38964,38963,38987,39014,15118,160117,15697,132656,147804,153350,39114,39095,39112,39111,19199,159015,136915,21936,39137,39142,39148,37752,39225,150057,19314,170071,170245,39413,39436,39483,39440,39512,153381,14020,168113,170965,39648,39650,170757,39668,19470,39700,39725,165376,20532,39732,158120,14531,143485,39760,39744,171326,23109,137315,39822,148043,39938,39935,39948,171624,40404,171959,172434,172459,172257,172323,172511,40318,40323,172340,40462,26760,40388,139611,172435,172576,137531,172595,40249,172217,172724,40592,40597,40606,40610,19764,40618,40623,148324,40641,15200,14821,15645,20274,14270,166955,40706,40712,19350,37924,159138,40727,40726,40761,22175,22154,40773,39352,168075,38898,33919,40802,40809,31452,40846,29206,19390,149877,149947,29047,150008,148296,150097,29598,166874,137466,31135,166270,167478,37737,37875,166468,37612,37761,37835,166252,148665,29207,16107,30578,31299,28880,148595,148472,29054,137199,28835,137406,144793,16071,137349,152623,137208,14114,136955,137273,14049,137076,137425,155467,14115,136896,22363,150053,136190,135848,136134,136374,34051,145062,34051,33877,149908,160101,146993,152924,147195,159826,17652,145134,170397,159526,26617,14131,15381,15847,22636,137506,26640,16471,145215,147681,147595,147727,158753,21707,22174,157361,22162,135135,134056,134669,37830,166675,37788,20216,20779,14361,148534,20156,132197,131967,20299,20362,153169,23144,131499,132043,14745,131850,132116,13365,20265,131776,167603,131701,35546,131596,20120,20685,20749,20386,20227,150030,147082,20290,20526,20588,20609,20428,20453,20568,20732,20825,20827,20829,20830,28278,144789,147001,147135,28018,137348,147081,20904,20931,132576,17629,132259,132242,132241,36218,166556,132878,21081,21156,133235,21217,37742,18042,29068,148364,134176,149932,135396,27089,134685,29817,16094,29849,29716,29782,29592,19342,150204,147597,21456,13700,29199,147657,21940,131909,21709,134086,22301,37469,38644,37734,22493,22413,22399,13886,22731,23193,166470,136954,137071,136976,23084,22968,37519,23166,23247,23058,153926,137715,137313,148117,14069,27909,29763,23073,155267,23169,166871,132115,37856,29836,135939,28933,18802,37896,166395,37821,14240,23582,23710,24158,24136,137622,137596,146158,24269,23375,137475,137476,14081,137376,14045,136958,14035,33066,166471,138682,144498,166312,24332,24334,137511,137131,23147,137019,23364,34324,161277,34912,24702,141408,140843,24539,16056,140719,140734,168072,159603,25024,131134,131142,140827,24985,24984,24693,142491,142599,149204,168269,25713,149093,142186,14889,142114,144464,170218,142968,25399,173147,25782,25393,25553,149987,142695,25252,142497,25659,25963,26994,15348,143502,144045,149897,144043,21773,144096,137433,169023,26318,144009,143795,15072,16784,152964,166690,152975,136956,152923,152613,30958,143619,137258,143924,13412,143887,143746,148169,26254,159012,26219,19347,26160,161904,138731,26211,144082,144097,26142,153714,14545,145466,145340,15257,145314,144382,29904,15254,26511,149034,26806,26654,15300,27326,14435,145365,148615,27187,27218,27337,27397,137490,25873,26776,27212,15319,27258,27479,147392,146586,37792,37618,166890,166603,37513,163870,166364,37991,28069,28427,149996,28007,147327,15759,28164,147516,23101,28170,22599,27940,30786,28987,148250,148086,28913,29264,29319,29332,149391,149285,20857,150180,132587,29818,147192,144991,150090,149783,155617,16134,16049,150239,166947,147253,24743,16115,29900,29756,37767,29751,17567,159210,17745,30083,16227,150745,150790,16216,30037,30323,173510,15129,29800,166604,149931,149902,15099,15821,150094,16127,149957,149747,37370,22322,37698,166627,137316,20703,152097,152039,30584,143922,30478,30479,30587,149143,145281,14942,149744,29752,29851,16063,150202,150215,16584,150166,156078,37639,152961,30750,30861,30856,30930,29648,31065,161601,153315,16654,31131,33942,31141,27181,147194,31290,31220,16750,136934,16690,37429,31217,134476,149900,131737,146874,137070,13719,21867,13680,13994,131540,134157,31458,23129,141045,154287,154268,23053,131675,30960,23082,154566,31486,16889,31837,31853,16913,154547,155324,155302,31949,150009,137136,31886,31868,31918,27314,32220,32263,32211,32590,156257,155996,162632,32151,155266,17002,158581,133398,26582,131150,144847,22468,156690,156664,149858,32733,31527,133164,154345,154947,31500,155150,39398,34373,39523,27164,144447,14818,150007,157101,39455,157088,33920,160039,158929,17642,33079,17410,32966,33033,33090,157620,39107,158274,33378,33381,158289,33875,159143,34320,160283,23174,16767,137280,23339,137377,23268,137432,34464,195004,146831,34861,160802,23042,34926,20293,34951,35007,35046,35173,35149,153219,35156,161669,161668,166901,166873,166812,166393,16045,33955,18165,18127,14322,35389,35356,169032,24397,37419,148100,26068,28969,28868,137285,40301,35999,36073,163292,22938,30659,23024,17262,14036,36394,36519,150537,36656,36682,17140,27736,28603,140065,18587,28537,28299,137178,39913,14005,149807,37051,37015,21873,18694,37307,37892,166475,16482,166652,37927,166941,166971,34021,35371,38297,38311,38295,38294,167220,29765,16066,149759,150082,148458,16103,143909,38543,167655,167526,167525,16076,149997,150136,147438,29714,29803,16124,38721,168112,26695,18973,168083,153567,38749,37736,166281,166950,166703,156606,37562,23313,35689,18748,29689,147995,38811,38769,39224,134950,24001,166853,150194,38943,169178,37622,169431,37349,17600,166736,150119,166756,39132,166469,16128,37418,18725,33812,39227,39245,162566,15869,39323,19311,39338,39516,166757,153800,27279,39457,23294,39471,170225,19344,170312,39356,19389,19351,37757,22642,135938,22562,149944,136424,30788,141087,146872,26821,15741,37976,14631,24912,141185,141675,24839,40015,40019,40059,39989,39952,39807,39887,171565,39839,172533,172286,40225,19630,147716,40472,19632,40204,172468,172269,172275,170287,40357,33981,159250,159711,158594,34300,17715,159140,159364,159216,33824,34286,159232,145367,155748,31202,144796,144960,18733,149982,15714,37851,37566,37704,131775,30905,37495,37965,20452,13376,36964,152925,30781,30804,30902,30795,137047,143817,149825,13978,20338,28634,28633,28702,28702,21524,147893,22459,22771,22410,40214,22487,28980,13487,147884,29163,158784,151447,23336,137141,166473,24844,23246,23051,17084,148616,14124,19323,166396,37819,37816,137430,134941,33906,158912,136211,148218,142374,148417,22932,146871,157505,32168,155995,155812,149945,149899,166394,37605,29666,16105,29876,166755,137375,16097,150195,27352,29683,29691,16086,150078,150164,137177,150118,132007,136228,149989,29768,149782,28837,149878,37508,29670,37727,132350,37681,166606,166422,37766,166887,153045,18741,166530,29035,149827,134399,22180,132634,134123,134328,21762,31172,137210,32254,136898,150096,137298,17710,37889,14090,166592,149933,22960,137407,137347,160900,23201,14050,146779,14000,37471,23161,166529,137314,37748,15565,133812,19094,14730,20724,15721,15692,136092,29045,17147,164376,28175,168164,17643,27991,163407,28775,27823,15574,147437,146989,28162,28428,15727,132085,30033,14012,13512,18048,16090,18545,22980,37486,18750,36673,166940,158656,22546,22472,14038,136274,28926,148322,150129,143331,135856,140221,26809,26983,136088,144613,162804,145119,166531,145366,144378,150687,27162,145069,158903,33854,17631,17614,159014,159057,158850,159710,28439,160009,33597,137018,33773,158848,159827,137179,22921,23170,137139,23137,23153,137477,147964,14125,23023,137020,14023,29070,37776,26266,148133,23150,23083,148115,27179,147193,161590,148571,148170,28957,148057,166369,20400,159016,23746,148686,163405,148413,27148,148054,135940,28838,28979,148457,15781,27871,194597,150095,32357,23019,23855,15859,24412,150109,137183,32164,33830,21637,146170,144128,131604,22398,133333,132633,16357,139166,172726,28675,168283,23920,29583,31955,166489,168992,20424,32743,29389,29456,162548,29496,29497,153334,29505,29512,16041,162584,36972,29173,149746,29665,33270,16074,30476,16081,27810,22269,29721,29726,29727,16098,16112,16116,16122,29907,16142,16211,30018,30061,30066,30093,16252,30152,30172,16320,30285,16343,30324,16348,30330,151388,29064,22051,35200,22633,16413,30531,16441,26465,16453,13787,30616,16490,16495,23646,30654,30667,22770,30744,28857,30748,16552,30777,30791,30801,30822,33864,152885,31027,26627,31026,16643,16649,31121,31129,36795,31238,36796,16743,31377,16818,31420,33401,16836,31439,31451,16847,20001,31586,31596,31611,31762,31771,16992,17018,31867,31900,17036,31928,17044,31981,36755,28864,134351,32207,32212,32208,32253,32686,32692,29343,17303,32800,32805,31545,32814,32817,32852,15820,22452,28832,32951,33001,17389,33036,29482,33038,33042,30048,33044,17409,15161,33110,33113,33114,17427,22586,33148,33156,17445,33171,17453,33189,22511,33217,33252,33364,17551,33446,33398,33482,33496,33535,17584,33623,38505,27018,33797,28917,33892,24803,33928,17668,33982,34017,34040,34064,34104,34130,17723,34159,34160,34272,17783,34418,34450,34482,34543,38469,34699,17926,17943,34990,35071,35108,35143,35217,162151,35369,35384,35476,35508,35921,36052,36082,36124,18328,22623,36291,18413,20206,36410,21976,22356,36465,22005,36528,18487,36558,36578,36580,36589,36594,36791,36801,36810,36812,36915,39364,18605,39136,37395,18718,37416,37464,37483,37553,37550,37567,37603,37611,37619,37620,37629,37699,37764,37805,18757,18769,40639,37911,21249,37917,37933,37950,18794,37972,38009,38189,38306,18855,38388,38451,18917,26528,18980,38720,18997,38834,38850,22100,19172,24808,39097,19225,39153,22596,39182,39193,20916,39196,39223,39234,39261,39266,19312,39365,19357,39484,39695,31363,39785,39809,39901,39921,39924,19565,39968,14191,138178,40265,39994,40702,22096,40339,40381,40384,40444,38134,36790,40571,40620,40625,40637,40646,38108,40674,40689,40696,31432,40772,131220,131767,132000,26906,38083,22956,132311,22592,38081,14265,132565,132629,132726,136890,22359,29043,133826,133837,134079,21610,194619,134091,21662,134139,134203,134227,134245,134268,24807,134285,22138,134325,134365,134381,134511,134578,134600,26965,39983,34725,134660,134670,134871,135056,134957,134771,23584,135100,24075,135260,135247,135286,26398,135291,135304,135318,13895,135359,135379,135471,135483,21348,33965,135907,136053,135990,35713,136567,136729,137155,137159,20088,28859,137261,137578,137773,137797,138282,138352,138412,138952,25283,138965,139029,29080,26709,139333,27113,14024,139900,140247,140282,141098,141425,141647,33533,141671,141715,142037,35237,142056,36768,142094,38840,142143,38983,39613,142412,null,142472,142519,154600,142600,142610,142775,142741,142914,143220,143308,143411,143462,144159,144350,24497,26184,26303,162425,144743,144883,29185,149946,30679,144922,145174,32391,131910,22709,26382,26904,146087,161367,155618,146961,147129,161278,139418,18640,19128,147737,166554,148206,148237,147515,148276,148374,150085,132554,20946,132625,22943,138920,15294,146687,148484,148694,22408,149108,14747,149295,165352,170441,14178,139715,35678,166734,39382,149522,149755,150037,29193,150208,134264,22885,151205,151430,132985,36570,151596,21135,22335,29041,152217,152601,147274,150183,21948,152646,152686,158546,37332,13427,152895,161330,152926,18200,152930,152934,153543,149823,153693,20582,13563,144332,24798,153859,18300,166216,154286,154505,154630,138640,22433,29009,28598,155906,162834,36950,156082,151450,35682,156674,156746,23899,158711,36662,156804,137500,35562,150006,156808,147439,156946,19392,157119,157365,141083,37989,153569,24981,23079,194765,20411,22201,148769,157436,20074,149812,38486,28047,158909,13848,35191,157593,157806,156689,157790,29151,157895,31554,168128,133649,157990,37124,158009,31301,40432,158202,39462,158253,13919,156777,131105,31107,158260,158555,23852,144665,33743,158621,18128,158884,30011,34917,159150,22710,14108,140685,159819,160205,15444,160384,160389,37505,139642,160395,37680,160486,149968,27705,38047,160848,134904,34855,35061,141606,164979,137137,28344,150058,137248,14756,14009,23568,31203,17727,26294,171181,170148,35139,161740,161880,22230,16607,136714,14753,145199,164072,136133,29101,33638,162269,168360,23143,19639,159919,166315,162301,162314,162571,163174,147834,31555,31102,163849,28597,172767,27139,164632,21410,159239,37823,26678,38749,164207,163875,158133,136173,143919,163912,23941,166960,163971,22293,38947,166217,23979,149896,26046,27093,21458,150181,147329,15377,26422,163984,164084,164142,139169,164175,164233,164271,164378,164614,164655,164746,13770,164968,165546,18682,25574,166230,30728,37461,166328,17394,166375,17375,166376,166726,166868,23032,166921,36619,167877,168172,31569,168208,168252,15863,168286,150218,36816,29327,22155,169191,169449,169392,169400,169778,170193,170313,170346,170435,170536,170766,171354,171419,32415,171768,171811,19620,38215,172691,29090,172799,19857,36882,173515,19868,134300,36798,21953,36794,140464,36793,150163,17673,32383,28502,27313,20202,13540,166700,161949,14138,36480,137205,163876,166764,166809,162366,157359,15851,161365,146615,153141,153942,20122,155265,156248,22207,134765,36366,23405,147080,150686,25566,25296,137206,137339,25904,22061,154698,21530,152337,15814,171416,19581,22050,22046,32585,155352,22901,146752,34672,19996,135146,134473,145082,33047,40286,36120,30267,40005,30286,30649,37701,21554,33096,33527,22053,33074,33816,32957,21994,31074,22083,21526,134813,13774,22021,22001,26353,164578,13869,30004,22000,21946,21655,21874,134209,134294,24272,151880,134774,142434,134818,40619,32090,21982,135285,25245,38765,21652,36045,29174,37238,25596,25529,25598,21865,142147,40050,143027,20890,13535,134567,20903,21581,21790,21779,30310,36397,157834,30129,32950,34820,34694,35015,33206,33820,135361,17644,29444,149254,23440,33547,157843,22139,141044,163119,147875,163187,159440,160438,37232,135641,37384,146684,173737,134828,134905,29286,138402,18254,151490,163833,135147,16634,40029,25887,142752,18675,149472,171388,135148,134666,24674,161187,135149,null,155720,135559,29091,32398,40272,19994,19972,13687,23309,27826,21351,13996,14812,21373,13989,149016,22682,150382,33325,21579,22442,154261,133497,null,14930,140389,29556,171692,19721,39917,146686,171824,19547,151465,169374,171998,33884,146870,160434,157619,145184,25390,32037,147191,146988,14890,36872,21196,15988,13946,17897,132238,30272,23280,134838,30842,163630,22695,16575,22140,39819,23924,30292,173108,40581,19681,30201,14331,24857,143578,148466,null,22109,135849,22439,149859,171526,21044,159918,13741,27722,40316,31830,39737,22494,137068,23635,25811,169168,156469,160100,34477,134440,159010,150242,134513,null,20990,139023,23950,38659,138705,40577,36940,31519,39682,23761,31651,25192,25397,39679,31695,39722,31870,39726,31810,31878,39957,31740,39689,40727,39963,149822,40794,21875,23491,20477,40600,20466,21088,15878,21201,22375,20566,22967,24082,38856,40363,36700,21609,38836,39232,38842,21292,24880,26924,21466,39946,40194,19515,38465,27008,20646,30022,137069,39386,21107,null,37209,38529,37212,null,37201,167575,25471,159011,27338,22033,37262,30074,25221,132092,29519,31856,154657,146685,null,149785,30422,39837,20010,134356,33726,34882,null,23626,27072,20717,22394,21023,24053,20174,27697,131570,20281,21660,21722,21146,36226,13822,24332,13811,null,27474,37244,40869,39831,38958,39092,39610,40616,40580,29050,31508,null,27642,34840,32632,null,22048,173642,36471,40787,null,36308,36431,40476,36353,25218,164733,36392,36469,31443,150135,31294,30936,27882,35431,30215,166490,40742,27854,34774,30147,172722,30803,194624,36108,29410,29553,35629,29442,29937,36075,150203,34351,24506,34976,17591,null,137275,159237,null,35454,140571,null,24829,30311,39639,40260,37742,39823,34805,null,34831,36087,29484,38689,39856,13782,29362,19463,31825,39242,155993,24921,19460,40598,24957,null,22367,24943,25254,25145,25294,14940,25058,21418,144373,25444,26626,13778,23895,166850,36826,167481,null,20697,138566,30982,21298,38456,134971,16485,null,30718,null,31938,155418,31962,31277,32870,32867,32077,29957,29938,35220,33306,26380,32866,160902,32859,29936,33027,30500,35209,157644,30035,159441,34729,34766,33224,34700,35401,36013,35651,30507,29944,34010,13877,27058,36262,null,35241,29800,28089,34753,147473,29927,15835,29046,24740,24988,15569,29026,24695,null,32625,166701,29264,24809,19326,21024,15384,146631,155351,161366,152881,137540,135934,170243,159196,159917,23745,156077,166415,145015,131310,157766,151310,17762,23327,156492,40784,40614,156267,12288,65292,12289,12290,65294,8231,65307,65306,65311,65281,65072,8230,8229,65104,65105,65106,183,65108,65109,65110,65111,65372,8211,65073,8212,65075,9588,65076,65103,65288,65289,65077,65078,65371,65373,65079,65080,12308,12309,65081,65082,12304,12305,65083,65084,12298,12299,65085,65086,12296,12297,65087,65088,12300,12301,65089,65090,12302,12303,65091,65092,65113,65114,65115,65116,65117,65118,8216,8217,8220,8221,12317,12318,8245,8242,65283,65286,65290,8251,167,12291,9675,9679,9651,9650,9678,9734,9733,9671,9670,9633,9632,9661,9660,12963,8453,175,65507,65343,717,65097,65098,65101,65102,65099,65100,65119,65120,65121,65291,65293,215,247,177,8730,65308,65310,65309,8806,8807,8800,8734,8786,8801,65122,65123,65124,65125,65126,65374,8745,8746,8869,8736,8735,8895,13266,13265,8747,8750,8757,8756,9792,9794,8853,8857,8593,8595,8592,8594,8598,8599,8601,8600,8741,8739,65295,65340,8725,65128,65284,65509,12306,65504,65505,65285,65312,8451,8457,65129,65130,65131,13269,13212,13213,13214,13262,13217,13198,13199,13252,176,20825,20827,20830,20829,20833,20835,21991,29929,31950,9601,9602,9603,9604,9605,9606,9607,9608,9615,9614,9613,9612,9611,9610,9609,9532,9524,9516,9508,9500,9620,9472,9474,9621,9484,9488,9492,9496,9581,9582,9584,9583,9552,9566,9578,9569,9698,9699,9701,9700,9585,9586,9587,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,12321,12322,12323,12324,12325,12326,12327,12328,12329,21313,21316,21317,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,12549,12550,12551,12552,12553,12554,12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570,12571,12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583,12584,12585,729,713,714,711,715,9216,9217,9218,9219,9220,9221,9222,9223,9224,9225,9226,9227,9228,9229,9230,9231,9232,9233,9234,9235,9236,9237,9238,9239,9240,9241,9242,9243,9244,9245,9246,9247,9249,8364,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,19968,20057,19969,19971,20035,20061,20102,20108,20154,20799,20837,20843,20960,20992,20993,21147,21269,21313,21340,21448,19977,19979,19976,19978,20011,20024,20961,20037,20040,20063,20062,20110,20129,20800,20995,21242,21315,21449,21475,22303,22763,22805,22823,22899,23376,23377,23379,23544,23567,23586,23608,23665,24029,24037,24049,24050,24051,24062,24178,24318,24331,24339,25165,19985,19984,19981,20013,20016,20025,20043,23609,20104,20113,20117,20114,20116,20130,20161,20160,20163,20166,20167,20173,20170,20171,20164,20803,20801,20839,20845,20846,20844,20887,20982,20998,20999,21000,21243,21246,21247,21270,21305,21320,21319,21317,21342,21380,21451,21450,21453,22764,22825,22827,22826,22829,23380,23569,23588,23610,23663,24052,24187,24319,24340,24341,24515,25096,25142,25163,25166,25903,25991,26007,26020,26041,26085,26352,26376,26408,27424,27490,27513,27595,27604,27611,27663,27700,28779,29226,29238,29243,29255,29273,29275,29356,29579,19993,19990,19989,19988,19992,20027,20045,20047,20046,20197,20184,20180,20181,20182,20183,20195,20196,20185,20190,20805,20804,20873,20874,20908,20985,20986,20984,21002,21152,21151,21253,21254,21271,21277,20191,21322,21321,21345,21344,21359,21358,21435,21487,21476,21491,21484,21486,21481,21480,21500,21496,21493,21483,21478,21482,21490,21489,21488,21477,21485,21499,22235,22234,22806,22830,22833,22900,22902,23381,23427,23612,24040,24039,24038,24066,24067,24179,24188,24321,24344,24343,24517,25098,25171,25172,25170,25169,26021,26086,26414,26412,26410,26411,26413,27491,27597,27665,27664,27704,27713,27712,27710,29359,29572,29577,29916,29926,29976,29983,29992,29993,30000,30001,30002,30003,30091,30333,30382,30399,30446,30683,30690,30707,31034,31166,31348,31435,19998,19999,20050,20051,20073,20121,20132,20134,20133,20223,20233,20249,20234,20245,20237,20240,20241,20239,20210,20214,20219,20208,20211,20221,20225,20235,20809,20807,20806,20808,20840,20849,20877,20912,21015,21009,21010,21006,21014,21155,21256,21281,21280,21360,21361,21513,21519,21516,21514,21520,21505,21515,21508,21521,21517,21512,21507,21518,21510,21522,22240,22238,22237,22323,22320,22312,22317,22316,22319,22313,22809,22810,22839,22840,22916,22904,22915,22909,22905,22914,22913,23383,23384,23431,23432,23429,23433,23546,23574,23673,24030,24070,24182,24180,24335,24347,24537,24534,25102,25100,25101,25104,25187,25179,25176,25910,26089,26088,26092,26093,26354,26355,26377,26429,26420,26417,26421,27425,27492,27515,27670,27741,27735,27737,27743,27744,27728,27733,27745,27739,27725,27726,28784,29279,29277,30334,31481,31859,31992,32566,32650,32701,32769,32771,32780,32786,32819,32895,32905,32907,32908,33251,33258,33267,33276,33292,33307,33311,33390,33394,33406,34411,34880,34892,34915,35199,38433,20018,20136,20301,20303,20295,20311,20318,20276,20315,20309,20272,20304,20305,20285,20282,20280,20291,20308,20284,20294,20323,20316,20320,20271,20302,20278,20313,20317,20296,20314,20812,20811,20813,20853,20918,20919,21029,21028,21033,21034,21032,21163,21161,21162,21164,21283,21363,21365,21533,21549,21534,21566,21542,21582,21543,21574,21571,21555,21576,21570,21531,21545,21578,21561,21563,21560,21550,21557,21558,21536,21564,21568,21553,21547,21535,21548,22250,22256,22244,22251,22346,22353,22336,22349,22343,22350,22334,22352,22351,22331,22767,22846,22941,22930,22952,22942,22947,22937,22934,22925,22948,22931,22922,22949,23389,23388,23386,23387,23436,23435,23439,23596,23616,23617,23615,23614,23696,23697,23700,23692,24043,24076,24207,24199,24202,24311,24324,24351,24420,24418,24439,24441,24536,24524,24535,24525,24561,24555,24568,24554,25106,25105,25220,25239,25238,25216,25206,25225,25197,25226,25212,25214,25209,25203,25234,25199,25240,25198,25237,25235,25233,25222,25913,25915,25912,26097,26356,26463,26446,26447,26448,26449,26460,26454,26462,26441,26438,26464,26451,26455,27493,27599,27714,27742,27801,27777,27784,27785,27781,27803,27754,27770,27792,27760,27788,27752,27798,27794,27773,27779,27762,27774,27764,27782,27766,27789,27796,27800,27778,28790,28796,28797,28792,29282,29281,29280,29380,29378,29590,29996,29995,30007,30008,30338,30447,30691,31169,31168,31167,31350,31995,32597,32918,32915,32925,32920,32923,32922,32946,33391,33426,33419,33421,35211,35282,35328,35895,35910,35925,35997,36196,36208,36275,36523,36554,36763,36784,36802,36806,36805,36804,24033,37009,37026,37034,37030,37027,37193,37318,37324,38450,38446,38449,38442,38444,20006,20054,20083,20107,20123,20126,20139,20140,20335,20381,20365,20339,20351,20332,20379,20363,20358,20355,20336,20341,20360,20329,20347,20374,20350,20367,20369,20346,20820,20818,20821,20841,20855,20854,20856,20925,20989,21051,21048,21047,21050,21040,21038,21046,21057,21182,21179,21330,21332,21331,21329,21350,21367,21368,21369,21462,21460,21463,21619,21621,21654,21624,21653,21632,21627,21623,21636,21650,21638,21628,21648,21617,21622,21644,21658,21602,21608,21643,21629,21646,22266,22403,22391,22378,22377,22369,22374,22372,22396,22812,22857,22855,22856,22852,22868,22974,22971,22996,22969,22958,22993,22982,22992,22989,22987,22995,22986,22959,22963,22994,22981,23391,23396,23395,23447,23450,23448,23452,23449,23451,23578,23624,23621,23622,23735,23713,23736,23721,23723,23729,23731,24088,24090,24086,24085,24091,24081,24184,24218,24215,24220,24213,24214,24310,24358,24359,24361,24448,24449,24447,24444,24541,24544,24573,24565,24575,24591,24596,24623,24629,24598,24618,24597,24609,24615,24617,24619,24603,25110,25109,25151,25150,25152,25215,25289,25292,25284,25279,25282,25273,25298,25307,25259,25299,25300,25291,25288,25256,25277,25276,25296,25305,25287,25293,25269,25306,25265,25304,25302,25303,25286,25260,25294,25918,26023,26044,26106,26132,26131,26124,26118,26114,26126,26112,26127,26133,26122,26119,26381,26379,26477,26507,26517,26481,26524,26483,26487,26503,26525,26519,26479,26480,26495,26505,26494,26512,26485,26522,26515,26492,26474,26482,27427,27494,27495,27519,27667,27675,27875,27880,27891,27825,27852,27877,27827,27837,27838,27836,27874,27819,27861,27859,27832,27844,27833,27841,27822,27863,27845,27889,27839,27835,27873,27867,27850,27820,27887,27868,27862,27872,28821,28814,28818,28810,28825,29228,29229,29240,29256,29287,29289,29376,29390,29401,29399,29392,29609,29608,29599,29611,29605,30013,30109,30105,30106,30340,30402,30450,30452,30693,30717,31038,31040,31041,31177,31176,31354,31353,31482,31998,32596,32652,32651,32773,32954,32933,32930,32945,32929,32939,32937,32948,32938,32943,33253,33278,33293,33459,33437,33433,33453,33469,33439,33465,33457,33452,33445,33455,33464,33443,33456,33470,33463,34382,34417,21021,34920,36555,36814,36820,36817,37045,37048,37041,37046,37319,37329,38263,38272,38428,38464,38463,38459,38468,38466,38585,38632,38738,38750,20127,20141,20142,20449,20405,20399,20415,20448,20433,20431,20445,20419,20406,20440,20447,20426,20439,20398,20432,20420,20418,20442,20430,20446,20407,20823,20882,20881,20896,21070,21059,21066,21069,21068,21067,21063,21191,21193,21187,21185,21261,21335,21371,21402,21467,21676,21696,21672,21710,21705,21688,21670,21683,21703,21698,21693,21674,21697,21700,21704,21679,21675,21681,21691,21673,21671,21695,22271,22402,22411,22432,22435,22434,22478,22446,22419,22869,22865,22863,22862,22864,23004,23000,23039,23011,23016,23043,23013,23018,23002,23014,23041,23035,23401,23459,23462,23460,23458,23461,23553,23630,23631,23629,23627,23769,23762,24055,24093,24101,24095,24189,24224,24230,24314,24328,24365,24421,24456,24453,24458,24459,24455,24460,24457,24594,24605,24608,24613,24590,24616,24653,24688,24680,24674,24646,24643,24684,24683,24682,24676,25153,25308,25366,25353,25340,25325,25345,25326,25341,25351,25329,25335,25327,25324,25342,25332,25361,25346,25919,25925,26027,26045,26082,26149,26157,26144,26151,26159,26143,26152,26161,26148,26359,26623,26579,26609,26580,26576,26604,26550,26543,26613,26601,26607,26564,26577,26548,26586,26597,26552,26575,26590,26611,26544,26585,26594,26589,26578,27498,27523,27526,27573,27602,27607,27679,27849,27915,27954,27946,27969,27941,27916,27953,27934,27927,27963,27965,27966,27958,27931,27893,27961,27943,27960,27945,27950,27957,27918,27947,28843,28858,28851,28844,28847,28845,28856,28846,28836,29232,29298,29295,29300,29417,29408,29409,29623,29642,29627,29618,29645,29632,29619,29978,29997,30031,30028,30030,30027,30123,30116,30117,30114,30115,30328,30342,30343,30344,30408,30406,30403,30405,30465,30457,30456,30473,30475,30462,30460,30471,30684,30722,30740,30732,30733,31046,31049,31048,31047,31161,31162,31185,31186,31179,31359,31361,31487,31485,31869,32002,32005,32000,32009,32007,32004,32006,32568,32654,32703,32772,32784,32781,32785,32822,32982,32997,32986,32963,32964,32972,32993,32987,32974,32990,32996,32989,33268,33314,33511,33539,33541,33507,33499,33510,33540,33509,33538,33545,33490,33495,33521,33537,33500,33492,33489,33502,33491,33503,33519,33542,34384,34425,34427,34426,34893,34923,35201,35284,35336,35330,35331,35998,36000,36212,36211,36276,36557,36556,36848,36838,36834,36842,36837,36845,36843,36836,36840,37066,37070,37057,37059,37195,37194,37325,38274,38480,38475,38476,38477,38754,38761,38859,38893,38899,38913,39080,39131,39135,39318,39321,20056,20147,20492,20493,20515,20463,20518,20517,20472,20521,20502,20486,20540,20511,20506,20498,20497,20474,20480,20500,20520,20465,20513,20491,20505,20504,20467,20462,20525,20522,20478,20523,20489,20860,20900,20901,20898,20941,20940,20934,20939,21078,21084,21076,21083,21085,21290,21375,21407,21405,21471,21736,21776,21761,21815,21756,21733,21746,21766,21754,21780,21737,21741,21729,21769,21742,21738,21734,21799,21767,21757,21775,22275,22276,22466,22484,22475,22467,22537,22799,22871,22872,22874,23057,23064,23068,23071,23067,23059,23020,23072,23075,23081,23077,23052,23049,23403,23640,23472,23475,23478,23476,23470,23477,23481,23480,23556,23633,23637,23632,23789,23805,23803,23786,23784,23792,23798,23809,23796,24046,24109,24107,24235,24237,24231,24369,24466,24465,24464,24665,24675,24677,24656,24661,24685,24681,24687,24708,24735,24730,24717,24724,24716,24709,24726,25159,25331,25352,25343,25422,25406,25391,25429,25410,25414,25423,25417,25402,25424,25405,25386,25387,25384,25421,25420,25928,25929,26009,26049,26053,26178,26185,26191,26179,26194,26188,26181,26177,26360,26388,26389,26391,26657,26680,26696,26694,26707,26681,26690,26708,26665,26803,26647,26700,26705,26685,26612,26704,26688,26684,26691,26666,26693,26643,26648,26689,27530,27529,27575,27683,27687,27688,27686,27684,27888,28010,28053,28040,28039,28006,28024,28023,27993,28051,28012,28041,28014,27994,28020,28009,28044,28042,28025,28037,28005,28052,28874,28888,28900,28889,28872,28879,29241,29305,29436,29433,29437,29432,29431,29574,29677,29705,29678,29664,29674,29662,30036,30045,30044,30042,30041,30142,30149,30151,30130,30131,30141,30140,30137,30146,30136,30347,30384,30410,30413,30414,30505,30495,30496,30504,30697,30768,30759,30776,30749,30772,30775,30757,30765,30752,30751,30770,31061,31056,31072,31071,31062,31070,31069,31063,31066,31204,31203,31207,31199,31206,31209,31192,31364,31368,31449,31494,31505,31881,32033,32023,32011,32010,32032,32034,32020,32016,32021,32026,32028,32013,32025,32027,32570,32607,32660,32709,32705,32774,32792,32789,32793,32791,32829,32831,33009,33026,33008,33029,33005,33012,33030,33016,33011,33032,33021,33034,33020,33007,33261,33260,33280,33296,33322,33323,33320,33324,33467,33579,33618,33620,33610,33592,33616,33609,33589,33588,33615,33586,33593,33590,33559,33600,33585,33576,33603,34388,34442,34474,34451,34468,34473,34444,34467,34460,34928,34935,34945,34946,34941,34937,35352,35344,35342,35340,35349,35338,35351,35347,35350,35343,35345,35912,35962,35961,36001,36002,36215,36524,36562,36564,36559,36785,36865,36870,36855,36864,36858,36852,36867,36861,36869,36856,37013,37089,37085,37090,37202,37197,37196,37336,37341,37335,37340,37337,38275,38498,38499,38497,38491,38493,38500,38488,38494,38587,39138,39340,39592,39640,39717,39730,39740,20094,20602,20605,20572,20551,20547,20556,20570,20553,20581,20598,20558,20565,20597,20596,20599,20559,20495,20591,20589,20828,20885,20976,21098,21103,21202,21209,21208,21205,21264,21263,21273,21311,21312,21310,21443,26364,21830,21866,21862,21828,21854,21857,21827,21834,21809,21846,21839,21845,21807,21860,21816,21806,21852,21804,21859,21811,21825,21847,22280,22283,22281,22495,22533,22538,22534,22496,22500,22522,22530,22581,22519,22521,22816,22882,23094,23105,23113,23142,23146,23104,23100,23138,23130,23110,23114,23408,23495,23493,23492,23490,23487,23494,23561,23560,23559,23648,23644,23645,23815,23814,23822,23835,23830,23842,23825,23849,23828,23833,23844,23847,23831,24034,24120,24118,24115,24119,24247,24248,24246,24245,24254,24373,24375,24407,24428,24425,24427,24471,24473,24478,24472,24481,24480,24476,24703,24739,24713,24736,24744,24779,24756,24806,24765,24773,24763,24757,24796,24764,24792,24789,24774,24799,24760,24794,24775,25114,25115,25160,25504,25511,25458,25494,25506,25509,25463,25447,25496,25514,25457,25513,25481,25475,25499,25451,25512,25476,25480,25497,25505,25516,25490,25487,25472,25467,25449,25448,25466,25949,25942,25937,25945,25943,21855,25935,25944,25941,25940,26012,26011,26028,26063,26059,26060,26062,26205,26202,26212,26216,26214,26206,26361,21207,26395,26753,26799,26786,26771,26805,26751,26742,26801,26791,26775,26800,26755,26820,26797,26758,26757,26772,26781,26792,26783,26785,26754,27442,27578,27627,27628,27691,28046,28092,28147,28121,28082,28129,28108,28132,28155,28154,28165,28103,28107,28079,28113,28078,28126,28153,28088,28151,28149,28101,28114,28186,28085,28122,28139,28120,28138,28145,28142,28136,28102,28100,28074,28140,28095,28134,28921,28937,28938,28925,28911,29245,29309,29313,29468,29467,29462,29459,29465,29575,29701,29706,29699,29702,29694,29709,29920,29942,29943,29980,29986,30053,30054,30050,30064,30095,30164,30165,30133,30154,30157,30350,30420,30418,30427,30519,30526,30524,30518,30520,30522,30827,30787,30798,31077,31080,31085,31227,31378,31381,31520,31528,31515,31532,31526,31513,31518,31534,31890,31895,31893,32070,32067,32113,32046,32057,32060,32064,32048,32051,32068,32047,32066,32050,32049,32573,32670,32666,32716,32718,32722,32796,32842,32838,33071,33046,33059,33067,33065,33072,33060,33282,33333,33335,33334,33337,33678,33694,33688,33656,33698,33686,33725,33707,33682,33674,33683,33673,33696,33655,33659,33660,33670,33703,34389,24426,34503,34496,34486,34500,34485,34502,34507,34481,34479,34505,34899,34974,34952,34987,34962,34966,34957,34955,35219,35215,35370,35357,35363,35365,35377,35373,35359,35355,35362,35913,35930,36009,36012,36011,36008,36010,36007,36199,36198,36286,36282,36571,36575,36889,36877,36890,36887,36899,36895,36893,36880,36885,36894,36896,36879,36898,36886,36891,36884,37096,37101,37117,37207,37326,37365,37350,37347,37351,37357,37353,38281,38506,38517,38515,38520,38512,38516,38518,38519,38508,38592,38634,38633,31456,31455,38914,38915,39770,40165,40565,40575,40613,40635,20642,20621,20613,20633,20625,20608,20630,20632,20634,26368,20977,21106,21108,21109,21097,21214,21213,21211,21338,21413,21883,21888,21927,21884,21898,21917,21912,21890,21916,21930,21908,21895,21899,21891,21939,21934,21919,21822,21938,21914,21947,21932,21937,21886,21897,21931,21913,22285,22575,22570,22580,22564,22576,22577,22561,22557,22560,22777,22778,22880,23159,23194,23167,23186,23195,23207,23411,23409,23506,23500,23507,23504,23562,23563,23601,23884,23888,23860,23879,24061,24133,24125,24128,24131,24190,24266,24257,24258,24260,24380,24429,24489,24490,24488,24785,24801,24754,24758,24800,24860,24867,24826,24853,24816,24827,24820,24936,24817,24846,24822,24841,24832,24850,25119,25161,25507,25484,25551,25536,25577,25545,25542,25549,25554,25571,25552,25569,25558,25581,25582,25462,25588,25578,25563,25682,25562,25593,25950,25958,25954,25955,26001,26000,26031,26222,26224,26228,26230,26223,26257,26234,26238,26231,26366,26367,26399,26397,26874,26837,26848,26840,26839,26885,26847,26869,26862,26855,26873,26834,26866,26851,26827,26829,26893,26898,26894,26825,26842,26990,26875,27454,27450,27453,27544,27542,27580,27631,27694,27695,27692,28207,28216,28244,28193,28210,28263,28234,28192,28197,28195,28187,28251,28248,28196,28246,28270,28205,28198,28271,28212,28237,28218,28204,28227,28189,28222,28363,28297,28185,28238,28259,28228,28274,28265,28255,28953,28954,28966,28976,28961,28982,29038,28956,29260,29316,29312,29494,29477,29492,29481,29754,29738,29747,29730,29733,29749,29750,29748,29743,29723,29734,29736,29989,29990,30059,30058,30178,30171,30179,30169,30168,30174,30176,30331,30332,30358,30355,30388,30428,30543,30701,30813,30828,30831,31245,31240,31243,31237,31232,31384,31383,31382,31461,31459,31561,31574,31558,31568,31570,31572,31565,31563,31567,31569,31903,31909,32094,32080,32104,32085,32043,32110,32114,32097,32102,32098,32112,32115,21892,32724,32725,32779,32850,32901,33109,33108,33099,33105,33102,33081,33094,33086,33100,33107,33140,33298,33308,33769,33795,33784,33805,33760,33733,33803,33729,33775,33777,33780,33879,33802,33776,33804,33740,33789,33778,33738,33848,33806,33796,33756,33799,33748,33759,34395,34527,34521,34541,34516,34523,34532,34512,34526,34903,35009,35010,34993,35203,35222,35387,35424,35413,35422,35388,35393,35412,35419,35408,35398,35380,35386,35382,35414,35937,35970,36015,36028,36019,36029,36033,36027,36032,36020,36023,36022,36031,36024,36234,36229,36225,36302,36317,36299,36314,36305,36300,36315,36294,36603,36600,36604,36764,36910,36917,36913,36920,36914,36918,37122,37109,37129,37118,37219,37221,37327,37396,37397,37411,37385,37406,37389,37392,37383,37393,38292,38287,38283,38289,38291,38290,38286,38538,38542,38539,38525,38533,38534,38541,38514,38532,38593,38597,38596,38598,38599,38639,38642,38860,38917,38918,38920,39143,39146,39151,39145,39154,39149,39342,39341,40643,40653,40657,20098,20653,20661,20658,20659,20677,20670,20652,20663,20667,20655,20679,21119,21111,21117,21215,21222,21220,21218,21219,21295,21983,21992,21971,21990,21966,21980,21959,21969,21987,21988,21999,21978,21985,21957,21958,21989,21961,22290,22291,22622,22609,22616,22615,22618,22612,22635,22604,22637,22602,22626,22610,22603,22887,23233,23241,23244,23230,23229,23228,23219,23234,23218,23913,23919,24140,24185,24265,24264,24338,24409,24492,24494,24858,24847,24904,24863,24819,24859,24825,24833,24840,24910,24908,24900,24909,24894,24884,24871,24845,24838,24887,25121,25122,25619,25662,25630,25642,25645,25661,25644,25615,25628,25620,25613,25654,25622,25623,25606,25964,26015,26032,26263,26249,26247,26248,26262,26244,26264,26253,26371,27028,26989,26970,26999,26976,26964,26997,26928,27010,26954,26984,26987,26974,26963,27001,27014,26973,26979,26971,27463,27506,27584,27583,27603,27645,28322,28335,28371,28342,28354,28304,28317,28359,28357,28325,28312,28348,28346,28331,28369,28310,28316,28356,28372,28330,28327,28340,29006,29017,29033,29028,29001,29031,29020,29036,29030,29004,29029,29022,28998,29032,29014,29242,29266,29495,29509,29503,29502,29807,29786,29781,29791,29790,29761,29759,29785,29787,29788,30070,30072,30208,30192,30209,30194,30193,30202,30207,30196,30195,30430,30431,30555,30571,30566,30558,30563,30585,30570,30572,30556,30565,30568,30562,30702,30862,30896,30871,30872,30860,30857,30844,30865,30867,30847,31098,31103,31105,33836,31165,31260,31258,31264,31252,31263,31262,31391,31392,31607,31680,31584,31598,31591,31921,31923,31925,32147,32121,32145,32129,32143,32091,32622,32617,32618,32626,32681,32680,32676,32854,32856,32902,32900,33137,33136,33144,33125,33134,33139,33131,33145,33146,33126,33285,33351,33922,33911,33853,33841,33909,33894,33899,33865,33900,33883,33852,33845,33889,33891,33897,33901,33862,34398,34396,34399,34553,34579,34568,34567,34560,34558,34555,34562,34563,34566,34570,34905,35039,35028,35033,35036,35032,35037,35041,35018,35029,35026,35228,35299,35435,35442,35443,35430,35433,35440,35463,35452,35427,35488,35441,35461,35437,35426,35438,35436,35449,35451,35390,35432,35938,35978,35977,36042,36039,36040,36036,36018,36035,36034,36037,36321,36319,36328,36335,36339,36346,36330,36324,36326,36530,36611,36617,36606,36618,36767,36786,36939,36938,36947,36930,36948,36924,36949,36944,36935,36943,36942,36941,36945,36926,36929,37138,37143,37228,37226,37225,37321,37431,37463,37432,37437,37440,37438,37467,37451,37476,37457,37428,37449,37453,37445,37433,37439,37466,38296,38552,38548,38549,38605,38603,38601,38602,38647,38651,38649,38646,38742,38772,38774,38928,38929,38931,38922,38930,38924,39164,39156,39165,39166,39347,39345,39348,39649,40169,40578,40718,40723,40736,20711,20718,20709,20694,20717,20698,20693,20687,20689,20721,20686,20713,20834,20979,21123,21122,21297,21421,22014,22016,22043,22039,22013,22036,22022,22025,22029,22030,22007,22038,22047,22024,22032,22006,22296,22294,22645,22654,22659,22675,22666,22649,22661,22653,22781,22821,22818,22820,22890,22889,23265,23270,23273,23255,23254,23256,23267,23413,23518,23527,23521,23525,23526,23528,23522,23524,23519,23565,23650,23940,23943,24155,24163,24149,24151,24148,24275,24278,24330,24390,24432,24505,24903,24895,24907,24951,24930,24931,24927,24922,24920,24949,25130,25735,25688,25684,25764,25720,25695,25722,25681,25703,25652,25709,25723,25970,26017,26071,26070,26274,26280,26269,27036,27048,27029,27073,27054,27091,27083,27035,27063,27067,27051,27060,27088,27085,27053,27084,27046,27075,27043,27465,27468,27699,28467,28436,28414,28435,28404,28457,28478,28448,28460,28431,28418,28450,28415,28399,28422,28465,28472,28466,28451,28437,28459,28463,28552,28458,28396,28417,28402,28364,28407,29076,29081,29053,29066,29060,29074,29246,29330,29334,29508,29520,29796,29795,29802,29808,29805,29956,30097,30247,30221,30219,30217,30227,30433,30435,30596,30589,30591,30561,30913,30879,30887,30899,30889,30883,31118,31119,31117,31278,31281,31402,31401,31469,31471,31649,31637,31627,31605,31639,31645,31636,31631,31672,31623,31620,31929,31933,31934,32187,32176,32156,32189,32190,32160,32202,32180,32178,32177,32186,32162,32191,32181,32184,32173,32210,32199,32172,32624,32736,32737,32735,32862,32858,32903,33104,33152,33167,33160,33162,33151,33154,33255,33274,33287,33300,33310,33355,33993,33983,33990,33988,33945,33950,33970,33948,33995,33976,33984,34003,33936,33980,34001,33994,34623,34588,34619,34594,34597,34612,34584,34645,34615,34601,35059,35074,35060,35065,35064,35069,35048,35098,35055,35494,35468,35486,35491,35469,35489,35475,35492,35498,35493,35496,35480,35473,35482,35495,35946,35981,35980,36051,36049,36050,36203,36249,36245,36348,36628,36626,36629,36627,36771,36960,36952,36956,36963,36953,36958,36962,36957,36955,37145,37144,37150,37237,37240,37239,37236,37496,37504,37509,37528,37526,37499,37523,37532,37544,37500,37521,38305,38312,38313,38307,38309,38308,38553,38556,38555,38604,38610,38656,38780,38789,38902,38935,38936,39087,39089,39171,39173,39180,39177,39361,39599,39600,39654,39745,39746,40180,40182,40179,40636,40763,40778,20740,20736,20731,20725,20729,20738,20744,20745,20741,20956,21127,21128,21129,21133,21130,21232,21426,22062,22075,22073,22066,22079,22068,22057,22099,22094,22103,22132,22070,22063,22064,22656,22687,22686,22707,22684,22702,22697,22694,22893,23305,23291,23307,23285,23308,23304,23534,23532,23529,23531,23652,23653,23965,23956,24162,24159,24161,24290,24282,24287,24285,24291,24288,24392,24433,24503,24501,24950,24935,24942,24925,24917,24962,24956,24944,24939,24958,24999,24976,25003,24974,25004,24986,24996,24980,25006,25134,25705,25711,25721,25758,25778,25736,25744,25776,25765,25747,25749,25769,25746,25774,25773,25771,25754,25772,25753,25762,25779,25973,25975,25976,26286,26283,26292,26289,27171,27167,27112,27137,27166,27161,27133,27169,27155,27146,27123,27138,27141,27117,27153,27472,27470,27556,27589,27590,28479,28540,28548,28497,28518,28500,28550,28525,28507,28536,28526,28558,28538,28528,28516,28567,28504,28373,28527,28512,28511,29087,29100,29105,29096,29270,29339,29518,29527,29801,29835,29827,29822,29824,30079,30240,30249,30239,30244,30246,30241,30242,30362,30394,30436,30606,30599,30604,30609,30603,30923,30917,30906,30922,30910,30933,30908,30928,31295,31292,31296,31293,31287,31291,31407,31406,31661,31665,31684,31668,31686,31687,31681,31648,31692,31946,32224,32244,32239,32251,32216,32236,32221,32232,32227,32218,32222,32233,32158,32217,32242,32249,32629,32631,32687,32745,32806,33179,33180,33181,33184,33178,33176,34071,34109,34074,34030,34092,34093,34067,34065,34083,34081,34068,34028,34085,34047,34054,34690,34676,34678,34656,34662,34680,34664,34649,34647,34636,34643,34907,34909,35088,35079,35090,35091,35093,35082,35516,35538,35527,35524,35477,35531,35576,35506,35529,35522,35519,35504,35542,35533,35510,35513,35547,35916,35918,35948,36064,36062,36070,36068,36076,36077,36066,36067,36060,36074,36065,36205,36255,36259,36395,36368,36381,36386,36367,36393,36383,36385,36382,36538,36637,36635,36639,36649,36646,36650,36636,36638,36645,36969,36974,36968,36973,36983,37168,37165,37159,37169,37255,37257,37259,37251,37573,37563,37559,37610,37548,37604,37569,37555,37564,37586,37575,37616,37554,38317,38321,38660,38662,38663,38665,38752,38797,38795,38799,38945,38955,38940,39091,39178,39187,39186,39192,39389,39376,39391,39387,39377,39381,39378,39385,39607,39662,39663,39719,39749,39748,39799,39791,40198,40201,40195,40617,40638,40654,22696,40786,20754,20760,20756,20752,20757,20864,20906,20957,21137,21139,21235,22105,22123,22137,22121,22116,22136,22122,22120,22117,22129,22127,22124,22114,22134,22721,22718,22727,22725,22894,23325,23348,23416,23536,23566,24394,25010,24977,25001,24970,25037,25014,25022,25034,25032,25136,25797,25793,25803,25787,25788,25818,25796,25799,25794,25805,25791,25810,25812,25790,25972,26310,26313,26297,26308,26311,26296,27197,27192,27194,27225,27243,27224,27193,27204,27234,27233,27211,27207,27189,27231,27208,27481,27511,27653,28610,28593,28577,28611,28580,28609,28583,28595,28608,28601,28598,28582,28576,28596,29118,29129,29136,29138,29128,29141,29113,29134,29145,29148,29123,29124,29544,29852,29859,29848,29855,29854,29922,29964,29965,30260,30264,30266,30439,30437,30624,30622,30623,30629,30952,30938,30956,30951,31142,31309,31310,31302,31308,31307,31418,31705,31761,31689,31716,31707,31713,31721,31718,31957,31958,32266,32273,32264,32283,32291,32286,32285,32265,32272,32633,32690,32752,32753,32750,32808,33203,33193,33192,33275,33288,33368,33369,34122,34137,34120,34152,34153,34115,34121,34157,34154,34142,34691,34719,34718,34722,34701,34913,35114,35122,35109,35115,35105,35242,35238,35558,35578,35563,35569,35584,35548,35559,35566,35582,35585,35586,35575,35565,35571,35574,35580,35947,35949,35987,36084,36420,36401,36404,36418,36409,36405,36667,36655,36664,36659,36776,36774,36981,36980,36984,36978,36988,36986,37172,37266,37664,37686,37624,37683,37679,37666,37628,37675,37636,37658,37648,37670,37665,37653,37678,37657,38331,38567,38568,38570,38613,38670,38673,38678,38669,38675,38671,38747,38748,38758,38808,38960,38968,38971,38967,38957,38969,38948,39184,39208,39198,39195,39201,39194,39405,39394,39409,39608,39612,39675,39661,39720,39825,40213,40227,40230,40232,40210,40219,40664,40660,40845,40860,20778,20767,20769,20786,21237,22158,22144,22160,22149,22151,22159,22741,22739,22737,22734,23344,23338,23332,23418,23607,23656,23996,23994,23997,23992,24171,24396,24509,25033,25026,25031,25062,25035,25138,25140,25806,25802,25816,25824,25840,25830,25836,25841,25826,25837,25986,25987,26329,26326,27264,27284,27268,27298,27292,27355,27299,27262,27287,27280,27296,27484,27566,27610,27656,28632,28657,28639,28640,28635,28644,28651,28655,28544,28652,28641,28649,28629,28654,28656,29159,29151,29166,29158,29157,29165,29164,29172,29152,29237,29254,29552,29554,29865,29872,29862,29864,30278,30274,30284,30442,30643,30634,30640,30636,30631,30637,30703,30967,30970,30964,30959,30977,31143,31146,31319,31423,31751,31757,31742,31735,31756,31712,31968,31964,31966,31970,31967,31961,31965,32302,32318,32326,32311,32306,32323,32299,32317,32305,32325,32321,32308,32313,32328,32309,32319,32303,32580,32755,32764,32881,32882,32880,32879,32883,33222,33219,33210,33218,33216,33215,33213,33225,33214,33256,33289,33393,34218,34180,34174,34204,34193,34196,34223,34203,34183,34216,34186,34407,34752,34769,34739,34770,34758,34731,34747,34746,34760,34763,35131,35126,35140,35128,35133,35244,35598,35607,35609,35611,35594,35616,35613,35588,35600,35905,35903,35955,36090,36093,36092,36088,36091,36264,36425,36427,36424,36426,36676,36670,36674,36677,36671,36991,36989,36996,36993,36994,36992,37177,37283,37278,37276,37709,37762,37672,37749,37706,37733,37707,37656,37758,37740,37723,37744,37722,37716,38346,38347,38348,38344,38342,38577,38584,38614,38684,38686,38816,38867,38982,39094,39221,39425,39423,39854,39851,39850,39853,40251,40255,40587,40655,40670,40668,40669,40667,40766,40779,21474,22165,22190,22745,22744,23352,24413,25059,25139,25844,25842,25854,25862,25850,25851,25847,26039,26332,26406,27315,27308,27331,27323,27320,27330,27310,27311,27487,27512,27567,28681,28683,28670,28678,28666,28689,28687,29179,29180,29182,29176,29559,29557,29863,29887,29973,30294,30296,30290,30653,30655,30651,30652,30990,31150,31329,31330,31328,31428,31429,31787,31783,31786,31774,31779,31777,31975,32340,32341,32350,32346,32353,32338,32345,32584,32761,32763,32887,32886,33229,33231,33290,34255,34217,34253,34256,34249,34224,34234,34233,34214,34799,34796,34802,34784,35206,35250,35316,35624,35641,35628,35627,35920,36101,36441,36451,36454,36452,36447,36437,36544,36681,36685,36999,36995,37000,37291,37292,37328,37780,37770,37782,37794,37811,37806,37804,37808,37784,37786,37783,38356,38358,38352,38357,38626,38620,38617,38619,38622,38692,38819,38822,38829,38905,38989,38991,38988,38990,38995,39098,39230,39231,39229,39214,39333,39438,39617,39683,39686,39759,39758,39757,39882,39881,39933,39880,39872,40273,40285,40288,40672,40725,40748,20787,22181,22750,22751,22754,23541,40848,24300,25074,25079,25078,25077,25856,25871,26336,26333,27365,27357,27354,27347,28699,28703,28712,28698,28701,28693,28696,29190,29197,29272,29346,29560,29562,29885,29898,29923,30087,30086,30303,30305,30663,31001,31153,31339,31337,31806,31807,31800,31805,31799,31808,32363,32365,32377,32361,32362,32645,32371,32694,32697,32696,33240,34281,34269,34282,34261,34276,34277,34295,34811,34821,34829,34809,34814,35168,35167,35158,35166,35649,35676,35672,35657,35674,35662,35663,35654,35673,36104,36106,36476,36466,36487,36470,36460,36474,36468,36692,36686,36781,37002,37003,37297,37294,37857,37841,37855,37827,37832,37852,37853,37846,37858,37837,37848,37860,37847,37864,38364,38580,38627,38698,38695,38753,38876,38907,39006,39000,39003,39100,39237,39241,39446,39449,39693,39912,39911,39894,39899,40329,40289,40306,40298,40300,40594,40599,40595,40628,21240,22184,22199,22198,22196,22204,22756,23360,23363,23421,23542,24009,25080,25082,25880,25876,25881,26342,26407,27372,28734,28720,28722,29200,29563,29903,30306,30309,31014,31018,31020,31019,31431,31478,31820,31811,31821,31983,31984,36782,32381,32380,32386,32588,32768,33242,33382,34299,34297,34321,34298,34310,34315,34311,34314,34836,34837,35172,35258,35320,35696,35692,35686,35695,35679,35691,36111,36109,36489,36481,36485,36482,37300,37323,37912,37891,37885,38369,38704,39108,39250,39249,39336,39467,39472,39479,39477,39955,39949,40569,40629,40680,40751,40799,40803,40801,20791,20792,22209,22208,22210,22804,23660,24013,25084,25086,25885,25884,26005,26345,27387,27396,27386,27570,28748,29211,29351,29910,29908,30313,30675,31824,32399,32396,32700,34327,34349,34330,34851,34850,34849,34847,35178,35180,35261,35700,35703,35709,36115,36490,36493,36491,36703,36783,37306,37934,37939,37941,37946,37944,37938,37931,38370,38712,38713,38706,38911,39015,39013,39255,39493,39491,39488,39486,39631,39764,39761,39981,39973,40367,40372,40386,40376,40605,40687,40729,40796,40806,40807,20796,20795,22216,22218,22217,23423,24020,24018,24398,25087,25892,27402,27489,28753,28760,29568,29924,30090,30318,30316,31155,31840,31839,32894,32893,33247,35186,35183,35324,35712,36118,36119,36497,36499,36705,37192,37956,37969,37970,38717,38718,38851,38849,39019,39253,39509,39501,39634,39706,40009,39985,39998,39995,40403,40407,40756,40812,40810,40852,22220,24022,25088,25891,25899,25898,26348,27408,29914,31434,31844,31843,31845,32403,32406,32404,33250,34360,34367,34865,35722,37008,37007,37987,37984,37988,38760,39023,39260,39514,39515,39511,39635,39636,39633,40020,40023,40022,40421,40607,40692,22225,22761,25900,28766,30321,30322,30679,32592,32648,34870,34873,34914,35731,35730,35734,33399,36123,37312,37994,38722,38728,38724,38854,39024,39519,39714,39768,40031,40441,40442,40572,40573,40711,40823,40818,24307,27414,28771,31852,31854,34875,35264,36513,37313,38002,38000,39025,39262,39638,39715,40652,28772,30682,35738,38007,38857,39522,39525,32412,35740,36522,37317,38013,38014,38012,40055,40056,40695,35924,38015,40474,29224,39530,39729,40475,40478,31858,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,20022,20031,20101,20128,20866,20886,20907,21241,21304,21353,21430,22794,23424,24027,12083,24191,24308,24400,24417,25908,26080,30098,30326,36789,38582,168,710,12541,12542,12445,12446,12291,20189,12293,12294,12295,12540,65339,65341,10045,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,8679,8632,8633,12751,131276,20058,131210,20994,17553,40880,20872,40881,161287,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,65506,65508,65287,65282,12849,8470,8481,12443,12444,11904,11908,11910,11911,11912,11914,11916,11917,11925,11932,11933,11941,11943,11946,11948,11950,11958,11964,11966,11974,11978,11980,11981,11983,11990,11991,11998,12003,null,null,null,643,592,603,596,629,339,248,331,650,618,20034,20060,20981,21274,21378,19975,19980,20039,20109,22231,64012,23662,24435,19983,20871,19982,20014,20115,20162,20169,20168,20888,21244,21356,21433,22304,22787,22828,23568,24063,26081,27571,27596,27668,29247,20017,20028,20200,20188,20201,20193,20189,20186,21004,21276,21324,22306,22307,22807,22831,23425,23428,23570,23611,23668,23667,24068,24192,24194,24521,25097,25168,27669,27702,27715,27711,27707,29358,29360,29578,31160,32906,38430,20238,20248,20268,20213,20244,20209,20224,20215,20232,20253,20226,20229,20258,20243,20228,20212,20242,20913,21011,21001,21008,21158,21282,21279,21325,21386,21511,22241,22239,22318,22314,22324,22844,22912,22908,22917,22907,22910,22903,22911,23382,23573,23589,23676,23674,23675,23678,24031,24181,24196,24322,24346,24436,24533,24532,24527,25180,25182,25188,25185,25190,25186,25177,25184,25178,25189,26095,26094,26430,26425,26424,26427,26426,26431,26428,26419,27672,27718,27730,27740,27727,27722,27732,27723,27724,28785,29278,29364,29365,29582,29994,30335,31349,32593,33400,33404,33408,33405,33407,34381,35198,37017,37015,37016,37019,37012,38434,38436,38432,38435,20310,20283,20322,20297,20307,20324,20286,20327,20306,20319,20289,20312,20269,20275,20287,20321,20879,20921,21020,21022,21025,21165,21166,21257,21347,21362,21390,21391,21552,21559,21546,21588,21573,21529,21532,21541,21528,21565,21583,21569,21544,21540,21575,22254,22247,22245,22337,22341,22348,22345,22347,22354,22790,22848,22950,22936,22944,22935,22926,22946,22928,22927,22951,22945,23438,23442,23592,23594,23693,23695,23688,23691,23689,23698,23690,23686,23699,23701,24032,24074,24078,24203,24201,24204,24200,24205,24325,24349,24440,24438,24530,24529,24528,24557,24552,24558,24563,24545,24548,24547,24570,24559,24567,24571,24576,24564,25146,25219,25228,25230,25231,25236,25223,25201,25211,25210,25200,25217,25224,25207,25213,25202,25204,25911,26096,26100,26099,26098,26101,26437,26439,26457,26453,26444,26440,26461,26445,26458,26443,27600,27673,27674,27768,27751,27755,27780,27787,27791,27761,27759,27753,27802,27757,27783,27797,27804,27750,27763,27749,27771,27790,28788,28794,29283,29375,29373,29379,29382,29377,29370,29381,29589,29591,29587,29588,29586,30010,30009,30100,30101,30337,31037,32820,32917,32921,32912,32914,32924,33424,33423,33413,33422,33425,33427,33418,33411,33412,35960,36809,36799,37023,37025,37029,37022,37031,37024,38448,38440,38447,38445,20019,20376,20348,20357,20349,20352,20359,20342,20340,20361,20356,20343,20300,20375,20330,20378,20345,20353,20344,20368,20380,20372,20382,20370,20354,20373,20331,20334,20894,20924,20926,21045,21042,21043,21062,21041,21180,21258,21259,21308,21394,21396,21639,21631,21633,21649,21634,21640,21611,21626,21630,21605,21612,21620,21606,21645,21615,21601,21600,21656,21603,21607,21604,22263,22265,22383,22386,22381,22379,22385,22384,22390,22400,22389,22395,22387,22388,22370,22376,22397,22796,22853,22965,22970,22991,22990,22962,22988,22977,22966,22972,22979,22998,22961,22973,22976,22984,22964,22983,23394,23397,23443,23445,23620,23623,23726,23716,23712,23733,23727,23720,23724,23711,23715,23725,23714,23722,23719,23709,23717,23734,23728,23718,24087,24084,24089,24360,24354,24355,24356,24404,24450,24446,24445,24542,24549,24621,24614,24601,24626,24587,24628,24586,24599,24627,24602,24606,24620,24610,24589,24592,24622,24595,24593,24588,24585,24604,25108,25149,25261,25268,25297,25278,25258,25270,25290,25262,25267,25263,25275,25257,25264,25272,25917,26024,26043,26121,26108,26116,26130,26120,26107,26115,26123,26125,26117,26109,26129,26128,26358,26378,26501,26476,26510,26514,26486,26491,26520,26502,26500,26484,26509,26508,26490,26527,26513,26521,26499,26493,26497,26488,26489,26516,27429,27520,27518,27614,27677,27795,27884,27883,27886,27865,27830,27860,27821,27879,27831,27856,27842,27834,27843,27846,27885,27890,27858,27869,27828,27786,27805,27776,27870,27840,27952,27853,27847,27824,27897,27855,27881,27857,28820,28824,28805,28819,28806,28804,28817,28822,28802,28826,28803,29290,29398,29387,29400,29385,29404,29394,29396,29402,29388,29393,29604,29601,29613,29606,29602,29600,29612,29597,29917,29928,30015,30016,30014,30092,30104,30383,30451,30449,30448,30453,30712,30716,30713,30715,30714,30711,31042,31039,31173,31352,31355,31483,31861,31997,32821,32911,32942,32931,32952,32949,32941,33312,33440,33472,33451,33434,33432,33435,33461,33447,33454,33468,33438,33466,33460,33448,33441,33449,33474,33444,33475,33462,33442,34416,34415,34413,34414,35926,36818,36811,36819,36813,36822,36821,36823,37042,37044,37039,37043,37040,38457,38461,38460,38458,38467,20429,20421,20435,20402,20425,20427,20417,20436,20444,20441,20411,20403,20443,20423,20438,20410,20416,20409,20460,21060,21065,21184,21186,21309,21372,21399,21398,21401,21400,21690,21665,21677,21669,21711,21699,33549,21687,21678,21718,21686,21701,21702,21664,21616,21692,21666,21694,21618,21726,21680,22453,22430,22431,22436,22412,22423,22429,22427,22420,22424,22415,22425,22437,22426,22421,22772,22797,22867,23009,23006,23022,23040,23025,23005,23034,23037,23036,23030,23012,23026,23031,23003,23017,23027,23029,23008,23038,23028,23021,23464,23628,23760,23768,23756,23767,23755,23771,23774,23770,23753,23751,23754,23766,23763,23764,23759,23752,23750,23758,23775,23800,24057,24097,24098,24099,24096,24100,24240,24228,24226,24219,24227,24229,24327,24366,24406,24454,24631,24633,24660,24690,24670,24645,24659,24647,24649,24667,24652,24640,24642,24671,24612,24644,24664,24678,24686,25154,25155,25295,25357,25355,25333,25358,25347,25323,25337,25359,25356,25336,25334,25344,25363,25364,25338,25365,25339,25328,25921,25923,26026,26047,26166,26145,26162,26165,26140,26150,26146,26163,26155,26170,26141,26164,26169,26158,26383,26384,26561,26610,26568,26554,26588,26555,26616,26584,26560,26551,26565,26603,26596,26591,26549,26573,26547,26615,26614,26606,26595,26562,26553,26574,26599,26608,26546,26620,26566,26605,26572,26542,26598,26587,26618,26569,26570,26563,26602,26571,27432,27522,27524,27574,27606,27608,27616,27680,27681,27944,27956,27949,27935,27964,27967,27922,27914,27866,27955,27908,27929,27962,27930,27921,27904,27933,27970,27905,27928,27959,27907,27919,27968,27911,27936,27948,27912,27938,27913,27920,28855,28831,28862,28849,28848,28833,28852,28853,28841,29249,29257,29258,29292,29296,29299,29294,29386,29412,29416,29419,29407,29418,29414,29411,29573,29644,29634,29640,29637,29625,29622,29621,29620,29675,29631,29639,29630,29635,29638,29624,29643,29932,29934,29998,30023,30024,30119,30122,30329,30404,30472,30467,30468,30469,30474,30455,30459,30458,30695,30696,30726,30737,30738,30725,30736,30735,30734,30729,30723,30739,31050,31052,31051,31045,31044,31189,31181,31183,31190,31182,31360,31358,31441,31488,31489,31866,31864,31865,31871,31872,31873,32003,32008,32001,32600,32657,32653,32702,32775,32782,32783,32788,32823,32984,32967,32992,32977,32968,32962,32976,32965,32995,32985,32988,32970,32981,32969,32975,32983,32998,32973,33279,33313,33428,33497,33534,33529,33543,33512,33536,33493,33594,33515,33494,33524,33516,33505,33522,33525,33548,33531,33526,33520,33514,33508,33504,33530,33523,33517,34423,34420,34428,34419,34881,34894,34919,34922,34921,35283,35332,35335,36210,36835,36833,36846,36832,37105,37053,37055,37077,37061,37054,37063,37067,37064,37332,37331,38484,38479,38481,38483,38474,38478,20510,20485,20487,20499,20514,20528,20507,20469,20468,20531,20535,20524,20470,20471,20503,20508,20512,20519,20533,20527,20529,20494,20826,20884,20883,20938,20932,20933,20936,20942,21089,21082,21074,21086,21087,21077,21090,21197,21262,21406,21798,21730,21783,21778,21735,21747,21732,21786,21759,21764,21768,21739,21777,21765,21745,21770,21755,21751,21752,21728,21774,21763,21771,22273,22274,22476,22578,22485,22482,22458,22470,22461,22460,22456,22454,22463,22471,22480,22457,22465,22798,22858,23065,23062,23085,23086,23061,23055,23063,23050,23070,23091,23404,23463,23469,23468,23555,23638,23636,23788,23807,23790,23793,23799,23808,23801,24105,24104,24232,24238,24234,24236,24371,24368,24423,24669,24666,24679,24641,24738,24712,24704,24722,24705,24733,24707,24725,24731,24727,24711,24732,24718,25113,25158,25330,25360,25430,25388,25412,25413,25398,25411,25572,25401,25419,25418,25404,25385,25409,25396,25432,25428,25433,25389,25415,25395,25434,25425,25400,25431,25408,25416,25930,25926,26054,26051,26052,26050,26186,26207,26183,26193,26386,26387,26655,26650,26697,26674,26675,26683,26699,26703,26646,26673,26652,26677,26667,26669,26671,26702,26692,26676,26653,26642,26644,26662,26664,26670,26701,26682,26661,26656,27436,27439,27437,27441,27444,27501,32898,27528,27622,27620,27624,27619,27618,27623,27685,28026,28003,28004,28022,27917,28001,28050,27992,28002,28013,28015,28049,28045,28143,28031,28038,27998,28007,28000,28055,28016,28028,27999,28034,28056,27951,28008,28043,28030,28032,28036,27926,28035,28027,28029,28021,28048,28892,28883,28881,28893,28875,32569,28898,28887,28882,28894,28896,28884,28877,28869,28870,28871,28890,28878,28897,29250,29304,29303,29302,29440,29434,29428,29438,29430,29427,29435,29441,29651,29657,29669,29654,29628,29671,29667,29673,29660,29650,29659,29652,29661,29658,29655,29656,29672,29918,29919,29940,29941,29985,30043,30047,30128,30145,30139,30148,30144,30143,30134,30138,30346,30409,30493,30491,30480,30483,30482,30499,30481,30485,30489,30490,30498,30503,30755,30764,30754,30773,30767,30760,30766,30763,30753,30761,30771,30762,30769,31060,31067,31055,31068,31059,31058,31057,31211,31212,31200,31214,31213,31210,31196,31198,31197,31366,31369,31365,31371,31372,31370,31367,31448,31504,31492,31507,31493,31503,31496,31498,31502,31497,31506,31876,31889,31882,31884,31880,31885,31877,32030,32029,32017,32014,32024,32022,32019,32031,32018,32015,32012,32604,32609,32606,32608,32605,32603,32662,32658,32707,32706,32704,32790,32830,32825,33018,33010,33017,33013,33025,33019,33024,33281,33327,33317,33587,33581,33604,33561,33617,33573,33622,33599,33601,33574,33564,33570,33602,33614,33563,33578,33544,33596,33613,33558,33572,33568,33591,33583,33577,33607,33605,33612,33619,33566,33580,33611,33575,33608,34387,34386,34466,34472,34454,34445,34449,34462,34439,34455,34438,34443,34458,34437,34469,34457,34465,34471,34453,34456,34446,34461,34448,34452,34883,34884,34925,34933,34934,34930,34944,34929,34943,34927,34947,34942,34932,34940,35346,35911,35927,35963,36004,36003,36214,36216,36277,36279,36278,36561,36563,36862,36853,36866,36863,36859,36868,36860,36854,37078,37088,37081,37082,37091,37087,37093,37080,37083,37079,37084,37092,37200,37198,37199,37333,37346,37338,38492,38495,38588,39139,39647,39727,20095,20592,20586,20577,20574,20576,20563,20555,20573,20594,20552,20557,20545,20571,20554,20578,20501,20549,20575,20585,20587,20579,20580,20550,20544,20590,20595,20567,20561,20944,21099,21101,21100,21102,21206,21203,21293,21404,21877,21878,21820,21837,21840,21812,21802,21841,21858,21814,21813,21808,21842,21829,21772,21810,21861,21838,21817,21832,21805,21819,21824,21835,22282,22279,22523,22548,22498,22518,22492,22516,22528,22509,22525,22536,22520,22539,22515,22479,22535,22510,22499,22514,22501,22508,22497,22542,22524,22544,22503,22529,22540,22513,22505,22512,22541,22532,22876,23136,23128,23125,23143,23134,23096,23093,23149,23120,23135,23141,23148,23123,23140,23127,23107,23133,23122,23108,23131,23112,23182,23102,23117,23097,23116,23152,23145,23111,23121,23126,23106,23132,23410,23406,23489,23488,23641,23838,23819,23837,23834,23840,23820,23848,23821,23846,23845,23823,23856,23826,23843,23839,23854,24126,24116,24241,24244,24249,24242,24243,24374,24376,24475,24470,24479,24714,24720,24710,24766,24752,24762,24787,24788,24783,24804,24793,24797,24776,24753,24795,24759,24778,24767,24771,24781,24768,25394,25445,25482,25474,25469,25533,25502,25517,25501,25495,25515,25486,25455,25479,25488,25454,25519,25461,25500,25453,25518,25468,25508,25403,25503,25464,25477,25473,25489,25485,25456,25939,26061,26213,26209,26203,26201,26204,26210,26392,26745,26759,26768,26780,26733,26734,26798,26795,26966,26735,26787,26796,26793,26741,26740,26802,26767,26743,26770,26748,26731,26738,26794,26752,26737,26750,26779,26774,26763,26784,26761,26788,26744,26747,26769,26764,26762,26749,27446,27443,27447,27448,27537,27535,27533,27534,27532,27690,28096,28075,28084,28083,28276,28076,28137,28130,28087,28150,28116,28160,28104,28128,28127,28118,28094,28133,28124,28125,28123,28148,28106,28093,28141,28144,28090,28117,28098,28111,28105,28112,28146,28115,28157,28119,28109,28131,28091,28922,28941,28919,28951,28916,28940,28912,28932,28915,28944,28924,28927,28934,28947,28928,28920,28918,28939,28930,28942,29310,29307,29308,29311,29469,29463,29447,29457,29464,29450,29448,29439,29455,29470,29576,29686,29688,29685,29700,29697,29693,29703,29696,29690,29692,29695,29708,29707,29684,29704,30052,30051,30158,30162,30159,30155,30156,30161,30160,30351,30345,30419,30521,30511,30509,30513,30514,30516,30515,30525,30501,30523,30517,30792,30802,30793,30797,30794,30796,30758,30789,30800,31076,31079,31081,31082,31075,31083,31073,31163,31226,31224,31222,31223,31375,31380,31376,31541,31559,31540,31525,31536,31522,31524,31539,31512,31530,31517,31537,31531,31533,31535,31538,31544,31514,31523,31892,31896,31894,31907,32053,32061,32056,32054,32058,32069,32044,32041,32065,32071,32062,32063,32074,32059,32040,32611,32661,32668,32669,32667,32714,32715,32717,32720,32721,32711,32719,32713,32799,32798,32795,32839,32835,32840,33048,33061,33049,33051,33069,33055,33068,33054,33057,33045,33063,33053,33058,33297,33336,33331,33338,33332,33330,33396,33680,33699,33704,33677,33658,33651,33700,33652,33679,33665,33685,33689,33653,33684,33705,33661,33667,33676,33693,33691,33706,33675,33662,33701,33711,33672,33687,33712,33663,33702,33671,33710,33654,33690,34393,34390,34495,34487,34498,34497,34501,34490,34480,34504,34489,34483,34488,34508,34484,34491,34492,34499,34493,34494,34898,34953,34965,34984,34978,34986,34970,34961,34977,34975,34968,34983,34969,34971,34967,34980,34988,34956,34963,34958,35202,35286,35289,35285,35376,35367,35372,35358,35897,35899,35932,35933,35965,36005,36221,36219,36217,36284,36290,36281,36287,36289,36568,36574,36573,36572,36567,36576,36577,36900,36875,36881,36892,36876,36897,37103,37098,37104,37108,37106,37107,37076,37099,37100,37097,37206,37208,37210,37203,37205,37356,37364,37361,37363,37368,37348,37369,37354,37355,37367,37352,37358,38266,38278,38280,38524,38509,38507,38513,38511,38591,38762,38916,39141,39319,20635,20629,20628,20638,20619,20643,20611,20620,20622,20637,20584,20636,20626,20610,20615,20831,20948,21266,21265,21412,21415,21905,21928,21925,21933,21879,22085,21922,21907,21896,21903,21941,21889,21923,21906,21924,21885,21900,21926,21887,21909,21921,21902,22284,22569,22583,22553,22558,22567,22563,22568,22517,22600,22565,22556,22555,22579,22591,22582,22574,22585,22584,22573,22572,22587,22881,23215,23188,23199,23162,23202,23198,23160,23206,23164,23205,23212,23189,23214,23095,23172,23178,23191,23171,23179,23209,23163,23165,23180,23196,23183,23187,23197,23530,23501,23499,23508,23505,23498,23502,23564,23600,23863,23875,23915,23873,23883,23871,23861,23889,23886,23893,23859,23866,23890,23869,23857,23897,23874,23865,23881,23864,23868,23858,23862,23872,23877,24132,24129,24408,24486,24485,24491,24777,24761,24780,24802,24782,24772,24852,24818,24842,24854,24837,24821,24851,24824,24828,24830,24769,24835,24856,24861,24848,24831,24836,24843,25162,25492,25521,25520,25550,25573,25576,25583,25539,25757,25587,25546,25568,25590,25557,25586,25589,25697,25567,25534,25565,25564,25540,25560,25555,25538,25543,25548,25547,25544,25584,25559,25561,25906,25959,25962,25956,25948,25960,25957,25996,26013,26014,26030,26064,26066,26236,26220,26235,26240,26225,26233,26218,26226,26369,26892,26835,26884,26844,26922,26860,26858,26865,26895,26838,26871,26859,26852,26870,26899,26896,26867,26849,26887,26828,26888,26992,26804,26897,26863,26822,26900,26872,26832,26877,26876,26856,26891,26890,26903,26830,26824,26845,26846,26854,26868,26833,26886,26836,26857,26901,26917,26823,27449,27451,27455,27452,27540,27543,27545,27541,27581,27632,27634,27635,27696,28156,28230,28231,28191,28233,28296,28220,28221,28229,28258,28203,28223,28225,28253,28275,28188,28211,28235,28224,28241,28219,28163,28206,28254,28264,28252,28257,28209,28200,28256,28273,28267,28217,28194,28208,28243,28261,28199,28280,28260,28279,28245,28281,28242,28262,28213,28214,28250,28960,28958,28975,28923,28974,28977,28963,28965,28962,28978,28959,28968,28986,28955,29259,29274,29320,29321,29318,29317,29323,29458,29451,29488,29474,29489,29491,29479,29490,29485,29478,29475,29493,29452,29742,29740,29744,29739,29718,29722,29729,29741,29745,29732,29731,29725,29737,29728,29746,29947,29999,30063,30060,30183,30170,30177,30182,30173,30175,30180,30167,30357,30354,30426,30534,30535,30532,30541,30533,30538,30542,30539,30540,30686,30700,30816,30820,30821,30812,30829,30833,30826,30830,30832,30825,30824,30814,30818,31092,31091,31090,31088,31234,31242,31235,31244,31236,31385,31462,31460,31562,31547,31556,31560,31564,31566,31552,31576,31557,31906,31902,31912,31905,32088,32111,32099,32083,32086,32103,32106,32079,32109,32092,32107,32082,32084,32105,32081,32095,32078,32574,32575,32613,32614,32674,32672,32673,32727,32849,32847,32848,33022,32980,33091,33098,33106,33103,33095,33085,33101,33082,33254,33262,33271,33272,33273,33284,33340,33341,33343,33397,33595,33743,33785,33827,33728,33768,33810,33767,33764,33788,33782,33808,33734,33736,33771,33763,33727,33793,33757,33765,33752,33791,33761,33739,33742,33750,33781,33737,33801,33807,33758,33809,33798,33730,33779,33749,33786,33735,33745,33770,33811,33731,33772,33774,33732,33787,33751,33762,33819,33755,33790,34520,34530,34534,34515,34531,34522,34538,34525,34539,34524,34540,34537,34519,34536,34513,34888,34902,34901,35002,35031,35001,35000,35008,35006,34998,35004,34999,35005,34994,35073,35017,35221,35224,35223,35293,35290,35291,35406,35405,35385,35417,35392,35415,35416,35396,35397,35410,35400,35409,35402,35404,35407,35935,35969,35968,36026,36030,36016,36025,36021,36228,36224,36233,36312,36307,36301,36295,36310,36316,36303,36309,36313,36296,36311,36293,36591,36599,36602,36601,36582,36590,36581,36597,36583,36584,36598,36587,36593,36588,36596,36585,36909,36916,36911,37126,37164,37124,37119,37116,37128,37113,37115,37121,37120,37127,37125,37123,37217,37220,37215,37218,37216,37377,37386,37413,37379,37402,37414,37391,37388,37376,37394,37375,37373,37382,37380,37415,37378,37404,37412,37401,37399,37381,37398,38267,38285,38284,38288,38535,38526,38536,38537,38531,38528,38594,38600,38595,38641,38640,38764,38768,38766,38919,39081,39147,40166,40697,20099,20100,20150,20669,20671,20678,20654,20676,20682,20660,20680,20674,20656,20673,20666,20657,20683,20681,20662,20664,20951,21114,21112,21115,21116,21955,21979,21964,21968,21963,21962,21981,21952,21972,21956,21993,21951,21970,21901,21967,21973,21986,21974,21960,22002,21965,21977,21954,22292,22611,22632,22628,22607,22605,22601,22639,22613,22606,22621,22617,22629,22619,22589,22627,22641,22780,23239,23236,23243,23226,23224,23217,23221,23216,23231,23240,23227,23238,23223,23232,23242,23220,23222,23245,23225,23184,23510,23512,23513,23583,23603,23921,23907,23882,23909,23922,23916,23902,23912,23911,23906,24048,24143,24142,24138,24141,24139,24261,24268,24262,24267,24263,24384,24495,24493,24823,24905,24906,24875,24901,24886,24882,24878,24902,24879,24911,24873,24896,25120,37224,25123,25125,25124,25541,25585,25579,25616,25618,25609,25632,25636,25651,25667,25631,25621,25624,25657,25655,25634,25635,25612,25638,25648,25640,25665,25653,25647,25610,25626,25664,25637,25639,25611,25575,25627,25646,25633,25614,25967,26002,26067,26246,26252,26261,26256,26251,26250,26265,26260,26232,26400,26982,26975,26936,26958,26978,26993,26943,26949,26986,26937,26946,26967,26969,27002,26952,26953,26933,26988,26931,26941,26981,26864,27000,26932,26985,26944,26991,26948,26998,26968,26945,26996,26956,26939,26955,26935,26972,26959,26961,26930,26962,26927,27003,26940,27462,27461,27459,27458,27464,27457,27547,64013,27643,27644,27641,27639,27640,28315,28374,28360,28303,28352,28319,28307,28308,28320,28337,28345,28358,28370,28349,28353,28318,28361,28343,28336,28365,28326,28367,28338,28350,28355,28380,28376,28313,28306,28302,28301,28324,28321,28351,28339,28368,28362,28311,28334,28323,28999,29012,29010,29027,29024,28993,29021,29026,29042,29048,29034,29025,28994,29016,28995,29003,29040,29023,29008,29011,28996,29005,29018,29263,29325,29324,29329,29328,29326,29500,29506,29499,29498,29504,29514,29513,29764,29770,29771,29778,29777,29783,29760,29775,29776,29774,29762,29766,29773,29780,29921,29951,29950,29949,29981,30073,30071,27011,30191,30223,30211,30199,30206,30204,30201,30200,30224,30203,30198,30189,30197,30205,30361,30389,30429,30549,30559,30560,30546,30550,30554,30569,30567,30548,30553,30573,30688,30855,30874,30868,30863,30852,30869,30853,30854,30881,30851,30841,30873,30848,30870,30843,31100,31106,31101,31097,31249,31256,31257,31250,31255,31253,31266,31251,31259,31248,31395,31394,31390,31467,31590,31588,31597,31604,31593,31602,31589,31603,31601,31600,31585,31608,31606,31587,31922,31924,31919,32136,32134,32128,32141,32127,32133,32122,32142,32123,32131,32124,32140,32148,32132,32125,32146,32621,32619,32615,32616,32620,32678,32677,32679,32731,32732,32801,33124,33120,33143,33116,33129,33115,33122,33138,26401,33118,33142,33127,33135,33092,33121,33309,33353,33348,33344,33346,33349,34033,33855,33878,33910,33913,33935,33933,33893,33873,33856,33926,33895,33840,33869,33917,33882,33881,33908,33907,33885,34055,33886,33847,33850,33844,33914,33859,33912,33842,33861,33833,33753,33867,33839,33858,33837,33887,33904,33849,33870,33868,33874,33903,33989,33934,33851,33863,33846,33843,33896,33918,33860,33835,33888,33876,33902,33872,34571,34564,34551,34572,34554,34518,34549,34637,34552,34574,34569,34561,34550,34573,34565,35030,35019,35021,35022,35038,35035,35034,35020,35024,35205,35227,35295,35301,35300,35297,35296,35298,35292,35302,35446,35462,35455,35425,35391,35447,35458,35460,35445,35459,35457,35444,35450,35900,35915,35914,35941,35940,35942,35974,35972,35973,36044,36200,36201,36241,36236,36238,36239,36237,36243,36244,36240,36242,36336,36320,36332,36337,36334,36304,36329,36323,36322,36327,36338,36331,36340,36614,36607,36609,36608,36613,36615,36616,36610,36619,36946,36927,36932,36937,36925,37136,37133,37135,37137,37142,37140,37131,37134,37230,37231,37448,37458,37424,37434,37478,37427,37477,37470,37507,37422,37450,37446,37485,37484,37455,37472,37479,37487,37430,37473,37488,37425,37460,37475,37456,37490,37454,37459,37452,37462,37426,38303,38300,38302,38299,38546,38547,38545,38551,38606,38650,38653,38648,38645,38771,38775,38776,38770,38927,38925,38926,39084,39158,39161,39343,39346,39344,39349,39597,39595,39771,40170,40173,40167,40576,40701,20710,20692,20695,20712,20723,20699,20714,20701,20708,20691,20716,20720,20719,20707,20704,20952,21120,21121,21225,21227,21296,21420,22055,22037,22028,22034,22012,22031,22044,22017,22035,22018,22010,22045,22020,22015,22009,22665,22652,22672,22680,22662,22657,22655,22644,22667,22650,22663,22673,22670,22646,22658,22664,22651,22676,22671,22782,22891,23260,23278,23269,23253,23274,23258,23277,23275,23283,23266,23264,23259,23276,23262,23261,23257,23272,23263,23415,23520,23523,23651,23938,23936,23933,23942,23930,23937,23927,23946,23945,23944,23934,23932,23949,23929,23935,24152,24153,24147,24280,24273,24279,24270,24284,24277,24281,24274,24276,24388,24387,24431,24502,24876,24872,24897,24926,24945,24947,24914,24915,24946,24940,24960,24948,24916,24954,24923,24933,24891,24938,24929,24918,25129,25127,25131,25643,25677,25691,25693,25716,25718,25714,25715,25725,25717,25702,25766,25678,25730,25694,25692,25675,25683,25696,25680,25727,25663,25708,25707,25689,25701,25719,25971,26016,26273,26272,26271,26373,26372,26402,27057,27062,27081,27040,27086,27030,27056,27052,27068,27025,27033,27022,27047,27021,27049,27070,27055,27071,27076,27069,27044,27092,27065,27082,27034,27087,27059,27027,27050,27041,27038,27097,27031,27024,27074,27061,27045,27078,27466,27469,27467,27550,27551,27552,27587,27588,27646,28366,28405,28401,28419,28453,28408,28471,28411,28462,28425,28494,28441,28442,28455,28440,28475,28434,28397,28426,28470,28531,28409,28398,28461,28480,28464,28476,28469,28395,28423,28430,28483,28421,28413,28406,28473,28444,28412,28474,28447,28429,28446,28424,28449,29063,29072,29065,29056,29061,29058,29071,29051,29062,29057,29079,29252,29267,29335,29333,29331,29507,29517,29521,29516,29794,29811,29809,29813,29810,29799,29806,29952,29954,29955,30077,30096,30230,30216,30220,30229,30225,30218,30228,30392,30593,30588,30597,30594,30574,30592,30575,30590,30595,30898,30890,30900,30893,30888,30846,30891,30878,30885,30880,30892,30882,30884,31128,31114,31115,31126,31125,31124,31123,31127,31112,31122,31120,31275,31306,31280,31279,31272,31270,31400,31403,31404,31470,31624,31644,31626,31633,31632,31638,31629,31628,31643,31630,31621,31640,21124,31641,31652,31618,31931,31935,31932,31930,32167,32183,32194,32163,32170,32193,32192,32197,32157,32206,32196,32198,32203,32204,32175,32185,32150,32188,32159,32166,32174,32169,32161,32201,32627,32738,32739,32741,32734,32804,32861,32860,33161,33158,33155,33159,33165,33164,33163,33301,33943,33956,33953,33951,33978,33998,33986,33964,33966,33963,33977,33972,33985,33997,33962,33946,33969,34000,33949,33959,33979,33954,33940,33991,33996,33947,33961,33967,33960,34006,33944,33974,33999,33952,34007,34004,34002,34011,33968,33937,34401,34611,34595,34600,34667,34624,34606,34590,34593,34585,34587,34627,34604,34625,34622,34630,34592,34610,34602,34605,34620,34578,34618,34609,34613,34626,34598,34599,34616,34596,34586,34608,34577,35063,35047,35057,35058,35066,35070,35054,35068,35062,35067,35056,35052,35051,35229,35233,35231,35230,35305,35307,35304,35499,35481,35467,35474,35471,35478,35901,35944,35945,36053,36047,36055,36246,36361,36354,36351,36365,36349,36362,36355,36359,36358,36357,36350,36352,36356,36624,36625,36622,36621,37155,37148,37152,37154,37151,37149,37146,37156,37153,37147,37242,37234,37241,37235,37541,37540,37494,37531,37498,37536,37524,37546,37517,37542,37530,37547,37497,37527,37503,37539,37614,37518,37506,37525,37538,37501,37512,37537,37514,37510,37516,37529,37543,37502,37511,37545,37533,37515,37421,38558,38561,38655,38744,38781,38778,38782,38787,38784,38786,38779,38788,38785,38783,38862,38861,38934,39085,39086,39170,39168,39175,39325,39324,39363,39353,39355,39354,39362,39357,39367,39601,39651,39655,39742,39743,39776,39777,39775,40177,40178,40181,40615,20735,20739,20784,20728,20742,20743,20726,20734,20747,20748,20733,20746,21131,21132,21233,21231,22088,22082,22092,22069,22081,22090,22089,22086,22104,22106,22080,22067,22077,22060,22078,22072,22058,22074,22298,22699,22685,22705,22688,22691,22703,22700,22693,22689,22783,23295,23284,23293,23287,23286,23299,23288,23298,23289,23297,23303,23301,23311,23655,23961,23959,23967,23954,23970,23955,23957,23968,23964,23969,23962,23966,24169,24157,24160,24156,32243,24283,24286,24289,24393,24498,24971,24963,24953,25009,25008,24994,24969,24987,24979,25007,25005,24991,24978,25002,24993,24973,24934,25011,25133,25710,25712,25750,25760,25733,25751,25756,25743,25739,25738,25740,25763,25759,25704,25777,25752,25974,25978,25977,25979,26034,26035,26293,26288,26281,26290,26295,26282,26287,27136,27142,27159,27109,27128,27157,27121,27108,27168,27135,27116,27106,27163,27165,27134,27175,27122,27118,27156,27127,27111,27200,27144,27110,27131,27149,27132,27115,27145,27140,27160,27173,27151,27126,27174,27143,27124,27158,27473,27557,27555,27554,27558,27649,27648,27647,27650,28481,28454,28542,28551,28614,28562,28557,28553,28556,28514,28495,28549,28506,28566,28534,28524,28546,28501,28530,28498,28496,28503,28564,28563,28509,28416,28513,28523,28541,28519,28560,28499,28555,28521,28543,28565,28515,28535,28522,28539,29106,29103,29083,29104,29088,29082,29097,29109,29085,29093,29086,29092,29089,29098,29084,29095,29107,29336,29338,29528,29522,29534,29535,29536,29533,29531,29537,29530,29529,29538,29831,29833,29834,29830,29825,29821,29829,29832,29820,29817,29960,29959,30078,30245,30238,30233,30237,30236,30243,30234,30248,30235,30364,30365,30366,30363,30605,30607,30601,30600,30925,30907,30927,30924,30929,30926,30932,30920,30915,30916,30921,31130,31137,31136,31132,31138,31131,27510,31289,31410,31412,31411,31671,31691,31678,31660,31694,31663,31673,31690,31669,31941,31944,31948,31947,32247,32219,32234,32231,32215,32225,32259,32250,32230,32246,32241,32240,32238,32223,32630,32684,32688,32685,32749,32747,32746,32748,32742,32744,32868,32871,33187,33183,33182,33173,33186,33177,33175,33302,33359,33363,33362,33360,33358,33361,34084,34107,34063,34048,34089,34062,34057,34061,34079,34058,34087,34076,34043,34091,34042,34056,34060,34036,34090,34034,34069,34039,34027,34035,34044,34066,34026,34025,34070,34046,34088,34077,34094,34050,34045,34078,34038,34097,34086,34023,34024,34032,34031,34041,34072,34080,34096,34059,34073,34095,34402,34646,34659,34660,34679,34785,34675,34648,34644,34651,34642,34657,34650,34641,34654,34669,34666,34640,34638,34655,34653,34671,34668,34682,34670,34652,34661,34639,34683,34677,34658,34663,34665,34906,35077,35084,35092,35083,35095,35096,35097,35078,35094,35089,35086,35081,35234,35236,35235,35309,35312,35308,35535,35526,35512,35539,35537,35540,35541,35515,35543,35518,35520,35525,35544,35523,35514,35517,35545,35902,35917,35983,36069,36063,36057,36072,36058,36061,36071,36256,36252,36257,36251,36384,36387,36389,36388,36398,36373,36379,36374,36369,36377,36390,36391,36372,36370,36376,36371,36380,36375,36378,36652,36644,36632,36634,36640,36643,36630,36631,36979,36976,36975,36967,36971,37167,37163,37161,37162,37170,37158,37166,37253,37254,37258,37249,37250,37252,37248,37584,37571,37572,37568,37593,37558,37583,37617,37599,37592,37609,37591,37597,37580,37615,37570,37608,37578,37576,37582,37606,37581,37589,37577,37600,37598,37607,37585,37587,37557,37601,37574,37556,38268,38316,38315,38318,38320,38564,38562,38611,38661,38664,38658,38746,38794,38798,38792,38864,38863,38942,38941,38950,38953,38952,38944,38939,38951,39090,39176,39162,39185,39188,39190,39191,39189,39388,39373,39375,39379,39380,39374,39369,39382,39384,39371,39383,39372,39603,39660,39659,39667,39666,39665,39750,39747,39783,39796,39793,39782,39798,39797,39792,39784,39780,39788,40188,40186,40189,40191,40183,40199,40192,40185,40187,40200,40197,40196,40579,40659,40719,40720,20764,20755,20759,20762,20753,20958,21300,21473,22128,22112,22126,22131,22118,22115,22125,22130,22110,22135,22300,22299,22728,22717,22729,22719,22714,22722,22716,22726,23319,23321,23323,23329,23316,23315,23312,23318,23336,23322,23328,23326,23535,23980,23985,23977,23975,23989,23984,23982,23978,23976,23986,23981,23983,23988,24167,24168,24166,24175,24297,24295,24294,24296,24293,24395,24508,24989,25000,24982,25029,25012,25030,25025,25036,25018,25023,25016,24972,25815,25814,25808,25807,25801,25789,25737,25795,25819,25843,25817,25907,25983,25980,26018,26312,26302,26304,26314,26315,26319,26301,26299,26298,26316,26403,27188,27238,27209,27239,27186,27240,27198,27229,27245,27254,27227,27217,27176,27226,27195,27199,27201,27242,27236,27216,27215,27220,27247,27241,27232,27196,27230,27222,27221,27213,27214,27206,27477,27476,27478,27559,27562,27563,27592,27591,27652,27651,27654,28589,28619,28579,28615,28604,28622,28616,28510,28612,28605,28574,28618,28584,28676,28581,28590,28602,28588,28586,28623,28607,28600,28578,28617,28587,28621,28591,28594,28592,29125,29122,29119,29112,29142,29120,29121,29131,29140,29130,29127,29135,29117,29144,29116,29126,29146,29147,29341,29342,29545,29542,29543,29548,29541,29547,29546,29823,29850,29856,29844,29842,29845,29857,29963,30080,30255,30253,30257,30269,30259,30268,30261,30258,30256,30395,30438,30618,30621,30625,30620,30619,30626,30627,30613,30617,30615,30941,30953,30949,30954,30942,30947,30939,30945,30946,30957,30943,30944,31140,31300,31304,31303,31414,31416,31413,31409,31415,31710,31715,31719,31709,31701,31717,31706,31720,31737,31700,31722,31714,31708,31723,31704,31711,31954,31956,31959,31952,31953,32274,32289,32279,32268,32287,32288,32275,32270,32284,32277,32282,32290,32267,32271,32278,32269,32276,32293,32292,32579,32635,32636,32634,32689,32751,32810,32809,32876,33201,33190,33198,33209,33205,33195,33200,33196,33204,33202,33207,33191,33266,33365,33366,33367,34134,34117,34155,34125,34131,34145,34136,34112,34118,34148,34113,34146,34116,34129,34119,34147,34110,34139,34161,34126,34158,34165,34133,34151,34144,34188,34150,34141,34132,34149,34156,34403,34405,34404,34715,34703,34711,34707,34706,34696,34689,34710,34712,34681,34695,34723,34693,34704,34705,34717,34692,34708,34716,34714,34697,35102,35110,35120,35117,35118,35111,35121,35106,35113,35107,35119,35116,35103,35313,35552,35554,35570,35572,35573,35549,35604,35556,35551,35568,35528,35550,35553,35560,35583,35567,35579,35985,35986,35984,36085,36078,36081,36080,36083,36204,36206,36261,36263,36403,36414,36408,36416,36421,36406,36412,36413,36417,36400,36415,36541,36662,36654,36661,36658,36665,36663,36660,36982,36985,36987,36998,37114,37171,37173,37174,37267,37264,37265,37261,37263,37671,37662,37640,37663,37638,37647,37754,37688,37692,37659,37667,37650,37633,37702,37677,37646,37645,37579,37661,37626,37669,37651,37625,37623,37684,37634,37668,37631,37673,37689,37685,37674,37652,37644,37643,37630,37641,37632,37627,37654,38332,38349,38334,38329,38330,38326,38335,38325,38333,38569,38612,38667,38674,38672,38809,38807,38804,38896,38904,38965,38959,38962,39204,39199,39207,39209,39326,39406,39404,39397,39396,39408,39395,39402,39401,39399,39609,39615,39604,39611,39670,39674,39673,39671,39731,39808,39813,39815,39804,39806,39803,39810,39827,39826,39824,39802,39829,39805,39816,40229,40215,40224,40222,40212,40233,40221,40216,40226,40208,40217,40223,40584,40582,40583,40622,40621,40661,40662,40698,40722,40765,20774,20773,20770,20772,20768,20777,21236,22163,22156,22157,22150,22148,22147,22142,22146,22143,22145,22742,22740,22735,22738,23341,23333,23346,23331,23340,23335,23334,23343,23342,23419,23537,23538,23991,24172,24170,24510,24507,25027,25013,25020,25063,25056,25061,25060,25064,25054,25839,25833,25827,25835,25828,25832,25985,25984,26038,26074,26322,27277,27286,27265,27301,27273,27295,27291,27297,27294,27271,27283,27278,27285,27267,27304,27300,27281,27263,27302,27290,27269,27276,27282,27483,27565,27657,28620,28585,28660,28628,28643,28636,28653,28647,28646,28638,28658,28637,28642,28648,29153,29169,29160,29170,29156,29168,29154,29555,29550,29551,29847,29874,29867,29840,29866,29869,29873,29861,29871,29968,29969,29970,29967,30084,30275,30280,30281,30279,30372,30441,30645,30635,30642,30647,30646,30644,30641,30632,30704,30963,30973,30978,30971,30972,30962,30981,30969,30974,30980,31147,31144,31324,31323,31318,31320,31316,31322,31422,31424,31425,31749,31759,31730,31744,31743,31739,31758,31732,31755,31731,31746,31753,31747,31745,31736,31741,31750,31728,31729,31760,31754,31976,32301,32316,32322,32307,38984,32312,32298,32329,32320,32327,32297,32332,32304,32315,32310,32324,32314,32581,32639,32638,32637,32756,32754,32812,33211,33220,33228,33226,33221,33223,33212,33257,33371,33370,33372,34179,34176,34191,34215,34197,34208,34187,34211,34171,34212,34202,34206,34167,34172,34185,34209,34170,34168,34135,34190,34198,34182,34189,34201,34205,34177,34210,34178,34184,34181,34169,34166,34200,34192,34207,34408,34750,34730,34733,34757,34736,34732,34745,34741,34748,34734,34761,34755,34754,34764,34743,34735,34756,34762,34740,34742,34751,34744,34749,34782,34738,35125,35123,35132,35134,35137,35154,35127,35138,35245,35247,35246,35314,35315,35614,35608,35606,35601,35589,35595,35618,35599,35602,35605,35591,35597,35592,35590,35612,35603,35610,35919,35952,35954,35953,35951,35989,35988,36089,36207,36430,36429,36435,36432,36428,36423,36675,36672,36997,36990,37176,37274,37282,37275,37273,37279,37281,37277,37280,37793,37763,37807,37732,37718,37703,37756,37720,37724,37750,37705,37712,37713,37728,37741,37775,37708,37738,37753,37719,37717,37714,37711,37745,37751,37755,37729,37726,37731,37735,37760,37710,37721,38343,38336,38345,38339,38341,38327,38574,38576,38572,38688,38687,38680,38685,38681,38810,38817,38812,38814,38813,38869,38868,38897,38977,38980,38986,38985,38981,38979,39205,39211,39212,39210,39219,39218,39215,39213,39217,39216,39320,39331,39329,39426,39418,39412,39415,39417,39416,39414,39419,39421,39422,39420,39427,39614,39678,39677,39681,39676,39752,39834,39848,39838,39835,39846,39841,39845,39844,39814,39842,39840,39855,40243,40257,40295,40246,40238,40239,40241,40248,40240,40261,40258,40259,40254,40247,40256,40253,32757,40237,40586,40585,40589,40624,40648,40666,40699,40703,40740,40739,40738,40788,40864,20785,20781,20782,22168,22172,22167,22170,22173,22169,22896,23356,23657,23658,24000,24173,24174,25048,25055,25069,25070,25073,25066,25072,25067,25046,25065,25855,25860,25853,25848,25857,25859,25852,26004,26075,26330,26331,26328,27333,27321,27325,27361,27334,27322,27318,27319,27335,27316,27309,27486,27593,27659,28679,28684,28685,28673,28677,28692,28686,28671,28672,28667,28710,28668,28663,28682,29185,29183,29177,29187,29181,29558,29880,29888,29877,29889,29886,29878,29883,29890,29972,29971,30300,30308,30297,30288,30291,30295,30298,30374,30397,30444,30658,30650,30975,30988,30995,30996,30985,30992,30994,30993,31149,31148,31327,31772,31785,31769,31776,31775,31789,31773,31782,31784,31778,31781,31792,32348,32336,32342,32355,32344,32354,32351,32337,32352,32343,32339,32693,32691,32759,32760,32885,33233,33234,33232,33375,33374,34228,34246,34240,34243,34242,34227,34229,34237,34247,34244,34239,34251,34254,34248,34245,34225,34230,34258,34340,34232,34231,34238,34409,34791,34790,34786,34779,34795,34794,34789,34783,34803,34788,34772,34780,34771,34797,34776,34787,34724,34775,34777,34817,34804,34792,34781,35155,35147,35151,35148,35142,35152,35153,35145,35626,35623,35619,35635,35632,35637,35655,35631,35644,35646,35633,35621,35639,35622,35638,35630,35620,35643,35645,35642,35906,35957,35993,35992,35991,36094,36100,36098,36096,36444,36450,36448,36439,36438,36446,36453,36455,36443,36442,36449,36445,36457,36436,36678,36679,36680,36683,37160,37178,37179,37182,37288,37285,37287,37295,37290,37813,37772,37778,37815,37787,37789,37769,37799,37774,37802,37790,37798,37781,37768,37785,37791,37773,37809,37777,37810,37796,37800,37812,37795,37797,38354,38355,38353,38579,38615,38618,24002,38623,38616,38621,38691,38690,38693,38828,38830,38824,38827,38820,38826,38818,38821,38871,38873,38870,38872,38906,38992,38993,38994,39096,39233,39228,39226,39439,39435,39433,39437,39428,39441,39434,39429,39431,39430,39616,39644,39688,39684,39685,39721,39733,39754,39756,39755,39879,39878,39875,39871,39873,39861,39864,39891,39862,39876,39865,39869,40284,40275,40271,40266,40283,40267,40281,40278,40268,40279,40274,40276,40287,40280,40282,40590,40588,40671,40705,40704,40726,40741,40747,40746,40745,40744,40780,40789,20788,20789,21142,21239,21428,22187,22189,22182,22183,22186,22188,22746,22749,22747,22802,23357,23358,23359,24003,24176,24511,25083,25863,25872,25869,25865,25868,25870,25988,26078,26077,26334,27367,27360,27340,27345,27353,27339,27359,27356,27344,27371,27343,27341,27358,27488,27568,27660,28697,28711,28704,28694,28715,28705,28706,28707,28713,28695,28708,28700,28714,29196,29194,29191,29186,29189,29349,29350,29348,29347,29345,29899,29893,29879,29891,29974,30304,30665,30666,30660,30705,31005,31003,31009,31004,30999,31006,31152,31335,31336,31795,31804,31801,31788,31803,31980,31978,32374,32373,32376,32368,32375,32367,32378,32370,32372,32360,32587,32586,32643,32646,32695,32765,32766,32888,33239,33237,33380,33377,33379,34283,34289,34285,34265,34273,34280,34266,34263,34284,34290,34296,34264,34271,34275,34268,34257,34288,34278,34287,34270,34274,34816,34810,34819,34806,34807,34825,34828,34827,34822,34812,34824,34815,34826,34818,35170,35162,35163,35159,35169,35164,35160,35165,35161,35208,35255,35254,35318,35664,35656,35658,35648,35667,35670,35668,35659,35669,35665,35650,35666,35671,35907,35959,35958,35994,36102,36103,36105,36268,36266,36269,36267,36461,36472,36467,36458,36463,36475,36546,36690,36689,36687,36688,36691,36788,37184,37183,37296,37293,37854,37831,37839,37826,37850,37840,37881,37868,37836,37849,37801,37862,37834,37844,37870,37859,37845,37828,37838,37824,37842,37863,38269,38362,38363,38625,38697,38699,38700,38696,38694,38835,38839,38838,38877,38878,38879,39004,39001,39005,38999,39103,39101,39099,39102,39240,39239,39235,39334,39335,39450,39445,39461,39453,39460,39451,39458,39456,39463,39459,39454,39452,39444,39618,39691,39690,39694,39692,39735,39914,39915,39904,39902,39908,39910,39906,39920,39892,39895,39916,39900,39897,39909,39893,39905,39898,40311,40321,40330,40324,40328,40305,40320,40312,40326,40331,40332,40317,40299,40308,40309,40304,40297,40325,40307,40315,40322,40303,40313,40319,40327,40296,40596,40593,40640,40700,40749,40768,40769,40781,40790,40791,40792,21303,22194,22197,22195,22755,23365,24006,24007,24302,24303,24512,24513,25081,25879,25878,25877,25875,26079,26344,26339,26340,27379,27376,27370,27368,27385,27377,27374,27375,28732,28725,28719,28727,28724,28721,28738,28728,28735,28730,28729,28736,28731,28723,28737,29203,29204,29352,29565,29564,29882,30379,30378,30398,30445,30668,30670,30671,30669,30706,31013,31011,31015,31016,31012,31017,31154,31342,31340,31341,31479,31817,31816,31818,31815,31813,31982,32379,32382,32385,32384,32698,32767,32889,33243,33241,33291,33384,33385,34338,34303,34305,34302,34331,34304,34294,34308,34313,34309,34316,34301,34841,34832,34833,34839,34835,34838,35171,35174,35257,35319,35680,35690,35677,35688,35683,35685,35687,35693,36270,36486,36488,36484,36697,36694,36695,36693,36696,36698,37005,37187,37185,37303,37301,37298,37299,37899,37907,37883,37920,37903,37908,37886,37909,37904,37928,37913,37901,37877,37888,37879,37895,37902,37910,37906,37882,37897,37880,37898,37887,37884,37900,37878,37905,37894,38366,38368,38367,38702,38703,38841,38843,38909,38910,39008,39010,39011,39007,39105,39106,39248,39246,39257,39244,39243,39251,39474,39476,39473,39468,39466,39478,39465,39470,39480,39469,39623,39626,39622,39696,39698,39697,39947,39944,39927,39941,39954,39928,40000,39943,39950,39942,39959,39956,39945,40351,40345,40356,40349,40338,40344,40336,40347,40352,40340,40348,40362,40343,40353,40346,40354,40360,40350,40355,40383,40361,40342,40358,40359,40601,40603,40602,40677,40676,40679,40678,40752,40750,40795,40800,40798,40797,40793,40849,20794,20793,21144,21143,22211,22205,22206,23368,23367,24011,24015,24305,25085,25883,27394,27388,27395,27384,27392,28739,28740,28746,28744,28745,28741,28742,29213,29210,29209,29566,29975,30314,30672,31021,31025,31023,31828,31827,31986,32394,32391,32392,32395,32390,32397,32589,32699,32816,33245,34328,34346,34342,34335,34339,34332,34329,34343,34350,34337,34336,34345,34334,34341,34857,34845,34843,34848,34852,34844,34859,34890,35181,35177,35182,35179,35322,35705,35704,35653,35706,35707,36112,36116,36271,36494,36492,36702,36699,36701,37190,37188,37189,37305,37951,37947,37942,37929,37949,37948,37936,37945,37930,37943,37932,37952,37937,38373,38372,38371,38709,38714,38847,38881,39012,39113,39110,39104,39256,39254,39481,39485,39494,39492,39490,39489,39482,39487,39629,39701,39703,39704,39702,39738,39762,39979,39965,39964,39980,39971,39976,39977,39972,39969,40375,40374,40380,40385,40391,40394,40399,40382,40389,40387,40379,40373,40398,40377,40378,40364,40392,40369,40365,40396,40371,40397,40370,40570,40604,40683,40686,40685,40731,40728,40730,40753,40782,40805,40804,40850,20153,22214,22213,22219,22897,23371,23372,24021,24017,24306,25889,25888,25894,25890,27403,27400,27401,27661,28757,28758,28759,28754,29214,29215,29353,29567,29912,29909,29913,29911,30317,30381,31029,31156,31344,31345,31831,31836,31833,31835,31834,31988,31985,32401,32591,32647,33246,33387,34356,34357,34355,34348,34354,34358,34860,34856,34854,34858,34853,35185,35263,35262,35323,35710,35716,35714,35718,35717,35711,36117,36501,36500,36506,36498,36496,36502,36503,36704,36706,37191,37964,37968,37962,37963,37967,37959,37957,37960,37961,37958,38719,38883,39018,39017,39115,39252,39259,39502,39507,39508,39500,39503,39496,39498,39497,39506,39504,39632,39705,39723,39739,39766,39765,40006,40008,39999,40004,39993,39987,40001,39996,39991,39988,39986,39997,39990,40411,40402,40414,40410,40395,40400,40412,40401,40415,40425,40409,40408,40406,40437,40405,40413,40630,40688,40757,40755,40754,40770,40811,40853,40866,20797,21145,22760,22759,22898,23373,24024,34863,24399,25089,25091,25092,25897,25893,26006,26347,27409,27410,27407,27594,28763,28762,29218,29570,29569,29571,30320,30676,31847,31846,32405,33388,34362,34368,34361,34364,34353,34363,34366,34864,34866,34862,34867,35190,35188,35187,35326,35724,35726,35723,35720,35909,36121,36504,36708,36707,37308,37986,37973,37981,37975,37982,38852,38853,38912,39510,39513,39710,39711,39712,40018,40024,40016,40010,40013,40011,40021,40025,40012,40014,40443,40439,40431,40419,40427,40440,40420,40438,40417,40430,40422,40434,40432,40418,40428,40436,40435,40424,40429,40642,40656,40690,40691,40710,40732,40760,40759,40758,40771,40783,40817,40816,40814,40815,22227,22221,23374,23661,25901,26349,26350,27411,28767,28769,28765,28768,29219,29915,29925,30677,31032,31159,31158,31850,32407,32649,33389,34371,34872,34871,34869,34891,35732,35733,36510,36511,36512,36509,37310,37309,37314,37995,37992,37993,38629,38726,38723,38727,38855,38885,39518,39637,39769,40035,40039,40038,40034,40030,40032,40450,40446,40455,40451,40454,40453,40448,40449,40457,40447,40445,40452,40608,40734,40774,40820,40821,40822,22228,25902,26040,27416,27417,27415,27418,28770,29222,29354,30680,30681,31033,31849,31851,31990,32410,32408,32411,32409,33248,33249,34374,34375,34376,35193,35194,35196,35195,35327,35736,35737,36517,36516,36515,37998,37997,37999,38001,38003,38729,39026,39263,40040,40046,40045,40459,40461,40464,40463,40466,40465,40609,40693,40713,40775,40824,40827,40826,40825,22302,28774,31855,34876,36274,36518,37315,38004,38008,38006,38005,39520,40052,40051,40049,40053,40468,40467,40694,40714,40868,28776,28773,31991,34410,34878,34877,34879,35742,35996,36521,36553,38731,39027,39028,39116,39265,39339,39524,39526,39527,39716,40469,40471,40776,25095,27422,29223,34380,36520,38018,38016,38017,39529,39528,39726,40473,29225,34379,35743,38019,40057,40631,30325,39531,40058,40477,28777,28778,40612,40830,40777,40856,30849,37561,35023,22715,24658,31911,23290,9556,9574,9559,9568,9580,9571,9562,9577,9565,9554,9572,9557,9566,9578,9569,9560,9575,9563,9555,9573,9558,9567,9579,9570,9561,9576,9564,9553,9552,9581,9582,9584,9583,65517,132423,37595,132575,147397,34124,17077,29679,20917,13897,149826,166372,37700,137691,33518,146632,30780,26436,25311,149811,166314,131744,158643,135941,20395,140525,20488,159017,162436,144896,150193,140563,20521,131966,24484,131968,131911,28379,132127,20605,20737,13434,20750,39020,14147,33814,149924,132231,20832,144308,20842,134143,139516,131813,140592,132494,143923,137603,23426,34685,132531,146585,20914,20920,40244,20937,20943,20945,15580,20947,150182,20915,20962,21314,20973,33741,26942,145197,24443,21003,21030,21052,21173,21079,21140,21177,21189,31765,34114,21216,34317,158483,21253,166622,21833,28377,147328,133460,147436,21299,21316,134114,27851,136998,26651,29653,24650,16042,14540,136936,29149,17570,21357,21364,165547,21374,21375,136598,136723,30694,21395,166555,21408,21419,21422,29607,153458,16217,29596,21441,21445,27721,20041,22526,21465,15019,134031,21472,147435,142755,21494,134263,21523,28793,21803,26199,27995,21613,158547,134516,21853,21647,21668,18342,136973,134877,15796,134477,166332,140952,21831,19693,21551,29719,21894,21929,22021,137431,147514,17746,148533,26291,135348,22071,26317,144010,26276,26285,22093,22095,30961,22257,38791,21502,22272,22255,22253,166758,13859,135759,22342,147877,27758,28811,22338,14001,158846,22502,136214,22531,136276,148323,22566,150517,22620,22698,13665,22752,22748,135740,22779,23551,22339,172368,148088,37843,13729,22815,26790,14019,28249,136766,23076,21843,136850,34053,22985,134478,158849,159018,137180,23001,137211,137138,159142,28017,137256,136917,23033,159301,23211,23139,14054,149929,23159,14088,23190,29797,23251,159649,140628,15749,137489,14130,136888,24195,21200,23414,25992,23420,162318,16388,18525,131588,23509,24928,137780,154060,132517,23539,23453,19728,23557,138052,23571,29646,23572,138405,158504,23625,18653,23685,23785,23791,23947,138745,138807,23824,23832,23878,138916,23738,24023,33532,14381,149761,139337,139635,33415,14390,15298,24110,27274,24181,24186,148668,134355,21414,20151,24272,21416,137073,24073,24308,164994,24313,24315,14496,24316,26686,37915,24333,131521,194708,15070,18606,135994,24378,157832,140240,24408,140401,24419,38845,159342,24434,37696,166454,24487,23990,15711,152144,139114,159992,140904,37334,131742,166441,24625,26245,137335,14691,15815,13881,22416,141236,31089,15936,24734,24740,24755,149890,149903,162387,29860,20705,23200,24932,33828,24898,194726,159442,24961,20980,132694,24967,23466,147383,141407,25043,166813,170333,25040,14642,141696,141505,24611,24924,25886,25483,131352,25285,137072,25301,142861,25452,149983,14871,25656,25592,136078,137212,25744,28554,142902,38932,147596,153373,25825,25829,38011,14950,25658,14935,25933,28438,150056,150051,25989,25965,25951,143486,26037,149824,19255,26065,16600,137257,26080,26083,24543,144384,26136,143863,143864,26180,143780,143781,26187,134773,26215,152038,26227,26228,138813,143921,165364,143816,152339,30661,141559,39332,26370,148380,150049,15147,27130,145346,26462,26471,26466,147917,168173,26583,17641,26658,28240,37436,26625,144358,159136,26717,144495,27105,27147,166623,26995,26819,144845,26881,26880,15666,14849,144956,15232,26540,26977,166474,17148,26934,27032,15265,132041,33635,20624,27129,144985,139562,27205,145155,27293,15347,26545,27336,168348,15373,27421,133411,24798,27445,27508,141261,28341,146139,132021,137560,14144,21537,146266,27617,147196,27612,27703,140427,149745,158545,27738,33318,27769,146876,17605,146877,147876,149772,149760,146633,14053,15595,134450,39811,143865,140433,32655,26679,159013,159137,159211,28054,27996,28284,28420,149887,147589,159346,34099,159604,20935,27804,28189,33838,166689,28207,146991,29779,147330,31180,28239,23185,143435,28664,14093,28573,146992,28410,136343,147517,17749,37872,28484,28508,15694,28532,168304,15675,28575,147780,28627,147601,147797,147513,147440,147380,147775,20959,147798,147799,147776,156125,28747,28798,28839,28801,28876,28885,28886,28895,16644,15848,29108,29078,148087,28971,28997,23176,29002,29038,23708,148325,29007,37730,148161,28972,148570,150055,150050,29114,166888,28861,29198,37954,29205,22801,37955,29220,37697,153093,29230,29248,149876,26813,29269,29271,15957,143428,26637,28477,29314,29482,29483,149539,165931,18669,165892,29480,29486,29647,29610,134202,158254,29641,29769,147938,136935,150052,26147,14021,149943,149901,150011,29687,29717,26883,150054,29753,132547,16087,29788,141485,29792,167602,29767,29668,29814,33721,29804,14128,29812,37873,27180,29826,18771,150156,147807,150137,166799,23366,166915,137374,29896,137608,29966,29929,29982,167641,137803,23511,167596,37765,30029,30026,30055,30062,151426,16132,150803,30094,29789,30110,30132,30210,30252,30289,30287,30319,30326,156661,30352,33263,14328,157969,157966,30369,30373,30391,30412,159647,33890,151709,151933,138780,30494,30502,30528,25775,152096,30552,144044,30639,166244,166248,136897,30708,30729,136054,150034,26826,30895,30919,30931,38565,31022,153056,30935,31028,30897,161292,36792,34948,166699,155779,140828,31110,35072,26882,31104,153687,31133,162617,31036,31145,28202,160038,16040,31174,168205,31188],
- "euc-kr":[44034,44035,44037,44038,44043,44044,44045,44046,44047,44056,44062,44063,44065,44066,44067,44069,44070,44071,44072,44073,44074,44075,44078,44082,44083,44084,null,null,null,null,null,null,44085,44086,44087,44090,44091,44093,44094,44095,44097,44098,44099,44100,44101,44102,44103,44104,44105,44106,44108,44110,44111,44112,44113,44114,44115,44117,null,null,null,null,null,null,44118,44119,44121,44122,44123,44125,44126,44127,44128,44129,44130,44131,44132,44133,44134,44135,44136,44137,44138,44139,44140,44141,44142,44143,44146,44147,44149,44150,44153,44155,44156,44157,44158,44159,44162,44167,44168,44173,44174,44175,44177,44178,44179,44181,44182,44183,44184,44185,44186,44187,44190,44194,44195,44196,44197,44198,44199,44203,44205,44206,44209,44210,44211,44212,44213,44214,44215,44218,44222,44223,44224,44226,44227,44229,44230,44231,44233,44234,44235,44237,44238,44239,44240,44241,44242,44243,44244,44246,44248,44249,44250,44251,44252,44253,44254,44255,44258,44259,44261,44262,44265,44267,44269,44270,44274,44276,44279,44280,44281,44282,44283,44286,44287,44289,44290,44291,44293,44295,44296,44297,44298,44299,44302,44304,44306,44307,44308,44309,44310,44311,44313,44314,44315,44317,44318,44319,44321,44322,44323,44324,44325,44326,44327,44328,44330,44331,44334,44335,44336,44337,44338,44339,null,null,null,null,null,null,44342,44343,44345,44346,44347,44349,44350,44351,44352,44353,44354,44355,44358,44360,44362,44363,44364,44365,44366,44367,44369,44370,44371,44373,44374,44375,null,null,null,null,null,null,44377,44378,44379,44380,44381,44382,44383,44384,44386,44388,44389,44390,44391,44392,44393,44394,44395,44398,44399,44401,44402,44407,44408,44409,44410,44414,44416,44419,44420,44421,44422,44423,44426,44427,44429,44430,44431,44433,44434,44435,44436,44437,44438,44439,44440,44441,44442,44443,44446,44447,44448,44449,44450,44451,44453,44454,44455,44456,44457,44458,44459,44460,44461,44462,44463,44464,44465,44466,44467,44468,44469,44470,44472,44473,44474,44475,44476,44477,44478,44479,44482,44483,44485,44486,44487,44489,44490,44491,44492,44493,44494,44495,44498,44500,44501,44502,44503,44504,44505,44506,44507,44509,44510,44511,44513,44514,44515,44517,44518,44519,44520,44521,44522,44523,44524,44525,44526,44527,44528,44529,44530,44531,44532,44533,44534,44535,44538,44539,44541,44542,44546,44547,44548,44549,44550,44551,44554,44556,44558,44559,44560,44561,44562,44563,44565,44566,44567,44568,44569,44570,44571,44572,null,null,null,null,null,null,44573,44574,44575,44576,44577,44578,44579,44580,44581,44582,44583,44584,44585,44586,44587,44588,44589,44590,44591,44594,44595,44597,44598,44601,44603,44604,null,null,null,null,null,null,44605,44606,44607,44610,44612,44615,44616,44617,44619,44623,44625,44626,44627,44629,44631,44632,44633,44634,44635,44638,44642,44643,44644,44646,44647,44650,44651,44653,44654,44655,44657,44658,44659,44660,44661,44662,44663,44666,44670,44671,44672,44673,44674,44675,44678,44679,44680,44681,44682,44683,44685,44686,44687,44688,44689,44690,44691,44692,44693,44694,44695,44696,44697,44698,44699,44700,44701,44702,44703,44704,44705,44706,44707,44708,44709,44710,44711,44712,44713,44714,44715,44716,44717,44718,44719,44720,44721,44722,44723,44724,44725,44726,44727,44728,44729,44730,44731,44735,44737,44738,44739,44741,44742,44743,44744,44745,44746,44747,44750,44754,44755,44756,44757,44758,44759,44762,44763,44765,44766,44767,44768,44769,44770,44771,44772,44773,44774,44775,44777,44778,44780,44782,44783,44784,44785,44786,44787,44789,44790,44791,44793,44794,44795,44797,44798,44799,44800,44801,44802,44803,44804,44805,null,null,null,null,null,null,44806,44809,44810,44811,44812,44814,44815,44817,44818,44819,44820,44821,44822,44823,44824,44825,44826,44827,44828,44829,44830,44831,44832,44833,44834,44835,null,null,null,null,null,null,44836,44837,44838,44839,44840,44841,44842,44843,44846,44847,44849,44851,44853,44854,44855,44856,44857,44858,44859,44862,44864,44868,44869,44870,44871,44874,44875,44876,44877,44878,44879,44881,44882,44883,44884,44885,44886,44887,44888,44889,44890,44891,44894,44895,44896,44897,44898,44899,44902,44903,44904,44905,44906,44907,44908,44909,44910,44911,44912,44913,44914,44915,44916,44917,44918,44919,44920,44922,44923,44924,44925,44926,44927,44929,44930,44931,44933,44934,44935,44937,44938,44939,44940,44941,44942,44943,44946,44947,44948,44950,44951,44952,44953,44954,44955,44957,44958,44959,44960,44961,44962,44963,44964,44965,44966,44967,44968,44969,44970,44971,44972,44973,44974,44975,44976,44977,44978,44979,44980,44981,44982,44983,44986,44987,44989,44990,44991,44993,44994,44995,44996,44997,44998,45002,45004,45007,45008,45009,45010,45011,45013,45014,45015,45016,45017,45018,45019,45021,45022,45023,45024,45025,null,null,null,null,null,null,45026,45027,45028,45029,45030,45031,45034,45035,45036,45037,45038,45039,45042,45043,45045,45046,45047,45049,45050,45051,45052,45053,45054,45055,45058,45059,null,null,null,null,null,null,45061,45062,45063,45064,45065,45066,45067,45069,45070,45071,45073,45074,45075,45077,45078,45079,45080,45081,45082,45083,45086,45087,45088,45089,45090,45091,45092,45093,45094,45095,45097,45098,45099,45100,45101,45102,45103,45104,45105,45106,45107,45108,45109,45110,45111,45112,45113,45114,45115,45116,45117,45118,45119,45120,45121,45122,45123,45126,45127,45129,45131,45133,45135,45136,45137,45138,45142,45144,45146,45147,45148,45150,45151,45152,45153,45154,45155,45156,45157,45158,45159,45160,45161,45162,45163,45164,45165,45166,45167,45168,45169,45170,45171,45172,45173,45174,45175,45176,45177,45178,45179,45182,45183,45185,45186,45187,45189,45190,45191,45192,45193,45194,45195,45198,45200,45202,45203,45204,45205,45206,45207,45211,45213,45214,45219,45220,45221,45222,45223,45226,45232,45234,45238,45239,45241,45242,45243,45245,45246,45247,45248,45249,45250,45251,45254,45258,45259,45260,45261,45262,45263,45266,null,null,null,null,null,null,45267,45269,45270,45271,45273,45274,45275,45276,45277,45278,45279,45281,45282,45283,45284,45286,45287,45288,45289,45290,45291,45292,45293,45294,45295,45296,null,null,null,null,null,null,45297,45298,45299,45300,45301,45302,45303,45304,45305,45306,45307,45308,45309,45310,45311,45312,45313,45314,45315,45316,45317,45318,45319,45322,45325,45326,45327,45329,45332,45333,45334,45335,45338,45342,45343,45344,45345,45346,45350,45351,45353,45354,45355,45357,45358,45359,45360,45361,45362,45363,45366,45370,45371,45372,45373,45374,45375,45378,45379,45381,45382,45383,45385,45386,45387,45388,45389,45390,45391,45394,45395,45398,45399,45401,45402,45403,45405,45406,45407,45409,45410,45411,45412,45413,45414,45415,45416,45417,45418,45419,45420,45421,45422,45423,45424,45425,45426,45427,45428,45429,45430,45431,45434,45435,45437,45438,45439,45441,45443,45444,45445,45446,45447,45450,45452,45454,45455,45456,45457,45461,45462,45463,45465,45466,45467,45469,45470,45471,45472,45473,45474,45475,45476,45477,45478,45479,45481,45482,45483,45484,45485,45486,45487,45488,45489,45490,45491,45492,45493,45494,45495,45496,null,null,null,null,null,null,45497,45498,45499,45500,45501,45502,45503,45504,45505,45506,45507,45508,45509,45510,45511,45512,45513,45514,45515,45517,45518,45519,45521,45522,45523,45525,null,null,null,null,null,null,45526,45527,45528,45529,45530,45531,45534,45536,45537,45538,45539,45540,45541,45542,45543,45546,45547,45549,45550,45551,45553,45554,45555,45556,45557,45558,45559,45560,45562,45564,45566,45567,45568,45569,45570,45571,45574,45575,45577,45578,45581,45582,45583,45584,45585,45586,45587,45590,45592,45594,45595,45596,45597,45598,45599,45601,45602,45603,45604,45605,45606,45607,45608,45609,45610,45611,45612,45613,45614,45615,45616,45617,45618,45619,45621,45622,45623,45624,45625,45626,45627,45629,45630,45631,45632,45633,45634,45635,45636,45637,45638,45639,45640,45641,45642,45643,45644,45645,45646,45647,45648,45649,45650,45651,45652,45653,45654,45655,45657,45658,45659,45661,45662,45663,45665,45666,45667,45668,45669,45670,45671,45674,45675,45676,45677,45678,45679,45680,45681,45682,45683,45686,45687,45688,45689,45690,45691,45693,45694,45695,45696,45697,45698,45699,45702,45703,45704,45706,45707,45708,45709,45710,null,null,null,null,null,null,45711,45714,45715,45717,45718,45719,45723,45724,45725,45726,45727,45730,45732,45735,45736,45737,45739,45741,45742,45743,45745,45746,45747,45749,45750,45751,null,null,null,null,null,null,45752,45753,45754,45755,45756,45757,45758,45759,45760,45761,45762,45763,45764,45765,45766,45767,45770,45771,45773,45774,45775,45777,45779,45780,45781,45782,45783,45786,45788,45790,45791,45792,45793,45795,45799,45801,45802,45808,45809,45810,45814,45820,45821,45822,45826,45827,45829,45830,45831,45833,45834,45835,45836,45837,45838,45839,45842,45846,45847,45848,45849,45850,45851,45853,45854,45855,45856,45857,45858,45859,45860,45861,45862,45863,45864,45865,45866,45867,45868,45869,45870,45871,45872,45873,45874,45875,45876,45877,45878,45879,45880,45881,45882,45883,45884,45885,45886,45887,45888,45889,45890,45891,45892,45893,45894,45895,45896,45897,45898,45899,45900,45901,45902,45903,45904,45905,45906,45907,45911,45913,45914,45917,45920,45921,45922,45923,45926,45928,45930,45932,45933,45935,45938,45939,45941,45942,45943,45945,45946,45947,45948,45949,45950,45951,45954,45958,45959,45960,45961,45962,45963,45965,null,null,null,null,null,null,45966,45967,45969,45970,45971,45973,45974,45975,45976,45977,45978,45979,45980,45981,45982,45983,45986,45987,45988,45989,45990,45991,45993,45994,45995,45997,null,null,null,null,null,null,45998,45999,46000,46001,46002,46003,46004,46005,46006,46007,46008,46009,46010,46011,46012,46013,46014,46015,46016,46017,46018,46019,46022,46023,46025,46026,46029,46031,46033,46034,46035,46038,46040,46042,46044,46046,46047,46049,46050,46051,46053,46054,46055,46057,46058,46059,46060,46061,46062,46063,46064,46065,46066,46067,46068,46069,46070,46071,46072,46073,46074,46075,46077,46078,46079,46080,46081,46082,46083,46084,46085,46086,46087,46088,46089,46090,46091,46092,46093,46094,46095,46097,46098,46099,46100,46101,46102,46103,46105,46106,46107,46109,46110,46111,46113,46114,46115,46116,46117,46118,46119,46122,46124,46125,46126,46127,46128,46129,46130,46131,46133,46134,46135,46136,46137,46138,46139,46140,46141,46142,46143,46144,46145,46146,46147,46148,46149,46150,46151,46152,46153,46154,46155,46156,46157,46158,46159,46162,46163,46165,46166,46167,46169,46170,46171,46172,46173,46174,46175,46178,46180,46182,null,null,null,null,null,null,46183,46184,46185,46186,46187,46189,46190,46191,46192,46193,46194,46195,46196,46197,46198,46199,46200,46201,46202,46203,46204,46205,46206,46207,46209,46210,null,null,null,null,null,null,46211,46212,46213,46214,46215,46217,46218,46219,46220,46221,46222,46223,46224,46225,46226,46227,46228,46229,46230,46231,46232,46233,46234,46235,46236,46238,46239,46240,46241,46242,46243,46245,46246,46247,46249,46250,46251,46253,46254,46255,46256,46257,46258,46259,46260,46262,46264,46266,46267,46268,46269,46270,46271,46273,46274,46275,46277,46278,46279,46281,46282,46283,46284,46285,46286,46287,46289,46290,46291,46292,46294,46295,46296,46297,46298,46299,46302,46303,46305,46306,46309,46311,46312,46313,46314,46315,46318,46320,46322,46323,46324,46325,46326,46327,46329,46330,46331,46332,46333,46334,46335,46336,46337,46338,46339,46340,46341,46342,46343,46344,46345,46346,46347,46348,46349,46350,46351,46352,46353,46354,46355,46358,46359,46361,46362,46365,46366,46367,46368,46369,46370,46371,46374,46379,46380,46381,46382,46383,46386,46387,46389,46390,46391,46393,46394,46395,46396,46397,46398,46399,46402,46406,null,null,null,null,null,null,46407,46408,46409,46410,46414,46415,46417,46418,46419,46421,46422,46423,46424,46425,46426,46427,46430,46434,46435,46436,46437,46438,46439,46440,46441,46442,null,null,null,null,null,null,46443,46444,46445,46446,46447,46448,46449,46450,46451,46452,46453,46454,46455,46456,46457,46458,46459,46460,46461,46462,46463,46464,46465,46466,46467,46468,46469,46470,46471,46472,46473,46474,46475,46476,46477,46478,46479,46480,46481,46482,46483,46484,46485,46486,46487,46488,46489,46490,46491,46492,46493,46494,46495,46498,46499,46501,46502,46503,46505,46508,46509,46510,46511,46514,46518,46519,46520,46521,46522,46526,46527,46529,46530,46531,46533,46534,46535,46536,46537,46538,46539,46542,46546,46547,46548,46549,46550,46551,46553,46554,46555,46556,46557,46558,46559,46560,46561,46562,46563,46564,46565,46566,46567,46568,46569,46570,46571,46573,46574,46575,46576,46577,46578,46579,46580,46581,46582,46583,46584,46585,46586,46587,46588,46589,46590,46591,46592,46593,46594,46595,46596,46597,46598,46599,46600,46601,46602,46603,46604,46605,46606,46607,46610,46611,46613,46614,46615,46617,46618,46619,46620,46621,null,null,null,null,null,null,46622,46623,46624,46625,46626,46627,46628,46630,46631,46632,46633,46634,46635,46637,46638,46639,46640,46641,46642,46643,46645,46646,46647,46648,46649,46650,null,null,null,null,null,null,46651,46652,46653,46654,46655,46656,46657,46658,46659,46660,46661,46662,46663,46665,46666,46667,46668,46669,46670,46671,46672,46673,46674,46675,46676,46677,46678,46679,46680,46681,46682,46683,46684,46685,46686,46687,46688,46689,46690,46691,46693,46694,46695,46697,46698,46699,46700,46701,46702,46703,46704,46705,46706,46707,46708,46709,46710,46711,46712,46713,46714,46715,46716,46717,46718,46719,46720,46721,46722,46723,46724,46725,46726,46727,46728,46729,46730,46731,46732,46733,46734,46735,46736,46737,46738,46739,46740,46741,46742,46743,46744,46745,46746,46747,46750,46751,46753,46754,46755,46757,46758,46759,46760,46761,46762,46765,46766,46767,46768,46770,46771,46772,46773,46774,46775,46776,46777,46778,46779,46780,46781,46782,46783,46784,46785,46786,46787,46788,46789,46790,46791,46792,46793,46794,46795,46796,46797,46798,46799,46800,46801,46802,46803,46805,46806,46807,46808,46809,46810,46811,46812,46813,null,null,null,null,null,null,46814,46815,46816,46817,46818,46819,46820,46821,46822,46823,46824,46825,46826,46827,46828,46829,46830,46831,46833,46834,46835,46837,46838,46839,46841,46842,null,null,null,null,null,null,46843,46844,46845,46846,46847,46850,46851,46852,46854,46855,46856,46857,46858,46859,46860,46861,46862,46863,46864,46865,46866,46867,46868,46869,46870,46871,46872,46873,46874,46875,46876,46877,46878,46879,46880,46881,46882,46883,46884,46885,46886,46887,46890,46891,46893,46894,46897,46898,46899,46900,46901,46902,46903,46906,46908,46909,46910,46911,46912,46913,46914,46915,46917,46918,46919,46921,46922,46923,46925,46926,46927,46928,46929,46930,46931,46934,46935,46936,46937,46938,46939,46940,46941,46942,46943,46945,46946,46947,46949,46950,46951,46953,46954,46955,46956,46957,46958,46959,46962,46964,46966,46967,46968,46969,46970,46971,46974,46975,46977,46978,46979,46981,46982,46983,46984,46985,46986,46987,46990,46995,46996,46997,47002,47003,47005,47006,47007,47009,47010,47011,47012,47013,47014,47015,47018,47022,47023,47024,47025,47026,47027,47030,47031,47033,47034,47035,47036,47037,47038,47039,47040,47041,null,null,null,null,null,null,47042,47043,47044,47045,47046,47048,47050,47051,47052,47053,47054,47055,47056,47057,47058,47059,47060,47061,47062,47063,47064,47065,47066,47067,47068,47069,null,null,null,null,null,null,47070,47071,47072,47073,47074,47075,47076,47077,47078,47079,47080,47081,47082,47083,47086,47087,47089,47090,47091,47093,47094,47095,47096,47097,47098,47099,47102,47106,47107,47108,47109,47110,47114,47115,47117,47118,47119,47121,47122,47123,47124,47125,47126,47127,47130,47132,47134,47135,47136,47137,47138,47139,47142,47143,47145,47146,47147,47149,47150,47151,47152,47153,47154,47155,47158,47162,47163,47164,47165,47166,47167,47169,47170,47171,47173,47174,47175,47176,47177,47178,47179,47180,47181,47182,47183,47184,47186,47188,47189,47190,47191,47192,47193,47194,47195,47198,47199,47201,47202,47203,47205,47206,47207,47208,47209,47210,47211,47214,47216,47218,47219,47220,47221,47222,47223,47225,47226,47227,47229,47230,47231,47232,47233,47234,47235,47236,47237,47238,47239,47240,47241,47242,47243,47244,47246,47247,47248,47249,47250,47251,47252,47253,47254,47255,47256,47257,47258,47259,47260,47261,47262,47263,null,null,null,null,null,null,47264,47265,47266,47267,47268,47269,47270,47271,47273,47274,47275,47276,47277,47278,47279,47281,47282,47283,47285,47286,47287,47289,47290,47291,47292,47293,null,null,null,null,null,null,47294,47295,47298,47300,47302,47303,47304,47305,47306,47307,47309,47310,47311,47313,47314,47315,47317,47318,47319,47320,47321,47322,47323,47324,47326,47328,47330,47331,47332,47333,47334,47335,47338,47339,47341,47342,47343,47345,47346,47347,47348,47349,47350,47351,47354,47356,47358,47359,47360,47361,47362,47363,47365,47366,47367,47368,47369,47370,47371,47372,47373,47374,47375,47376,47377,47378,47379,47380,47381,47382,47383,47385,47386,47387,47388,47389,47390,47391,47393,47394,47395,47396,47397,47398,47399,47400,47401,47402,47403,47404,47405,47406,47407,47408,47409,47410,47411,47412,47413,47414,47415,47416,47417,47418,47419,47422,47423,47425,47426,47427,47429,47430,47431,47432,47433,47434,47435,47437,47438,47440,47442,47443,47444,47445,47446,47447,47450,47451,47453,47454,47455,47457,47458,47459,47460,47461,47462,47463,47466,47468,47470,47471,47472,47473,47474,47475,47478,47479,47481,47482,47483,47485,null,null,null,null,null,null,47486,47487,47488,47489,47490,47491,47494,47496,47499,47500,47503,47504,47505,47506,47507,47508,47509,47510,47511,47512,47513,47514,47515,47516,47517,47518,null,null,null,null,null,null,47519,47520,47521,47522,47523,47524,47525,47526,47527,47528,47529,47530,47531,47534,47535,47537,47538,47539,47541,47542,47543,47544,47545,47546,47547,47550,47552,47554,47555,47556,47557,47558,47559,47562,47563,47565,47571,47572,47573,47574,47575,47578,47580,47583,47584,47586,47590,47591,47593,47594,47595,47597,47598,47599,47600,47601,47602,47603,47606,47611,47612,47613,47614,47615,47618,47619,47620,47621,47622,47623,47625,47626,47627,47628,47629,47630,47631,47632,47633,47634,47635,47636,47638,47639,47640,47641,47642,47643,47644,47645,47646,47647,47648,47649,47650,47651,47652,47653,47654,47655,47656,47657,47658,47659,47660,47661,47662,47663,47664,47665,47666,47667,47668,47669,47670,47671,47674,47675,47677,47678,47679,47681,47683,47684,47685,47686,47687,47690,47692,47695,47696,47697,47698,47702,47703,47705,47706,47707,47709,47710,47711,47712,47713,47714,47715,47718,47722,47723,47724,47725,47726,47727,null,null,null,null,null,null,47730,47731,47733,47734,47735,47737,47738,47739,47740,47741,47742,47743,47744,47745,47746,47750,47752,47753,47754,47755,47757,47758,47759,47760,47761,47762,null,null,null,null,null,null,47763,47764,47765,47766,47767,47768,47769,47770,47771,47772,47773,47774,47775,47776,47777,47778,47779,47780,47781,47782,47783,47786,47789,47790,47791,47793,47795,47796,47797,47798,47799,47802,47804,47806,47807,47808,47809,47810,47811,47813,47814,47815,47817,47818,47819,47820,47821,47822,47823,47824,47825,47826,47827,47828,47829,47830,47831,47834,47835,47836,47837,47838,47839,47840,47841,47842,47843,47844,47845,47846,47847,47848,47849,47850,47851,47852,47853,47854,47855,47856,47857,47858,47859,47860,47861,47862,47863,47864,47865,47866,47867,47869,47870,47871,47873,47874,47875,47877,47878,47879,47880,47881,47882,47883,47884,47886,47888,47890,47891,47892,47893,47894,47895,47897,47898,47899,47901,47902,47903,47905,47906,47907,47908,47909,47910,47911,47912,47914,47916,47917,47918,47919,47920,47921,47922,47923,47927,47929,47930,47935,47936,47937,47938,47939,47942,47944,47946,47947,47948,47950,47953,47954,null,null,null,null,null,null,47955,47957,47958,47959,47961,47962,47963,47964,47965,47966,47967,47968,47970,47972,47973,47974,47975,47976,47977,47978,47979,47981,47982,47983,47984,47985,null,null,null,null,null,null,47986,47987,47988,47989,47990,47991,47992,47993,47994,47995,47996,47997,47998,47999,48000,48001,48002,48003,48004,48005,48006,48007,48009,48010,48011,48013,48014,48015,48017,48018,48019,48020,48021,48022,48023,48024,48025,48026,48027,48028,48029,48030,48031,48032,48033,48034,48035,48037,48038,48039,48041,48042,48043,48045,48046,48047,48048,48049,48050,48051,48053,48054,48056,48057,48058,48059,48060,48061,48062,48063,48065,48066,48067,48069,48070,48071,48073,48074,48075,48076,48077,48078,48079,48081,48082,48084,48085,48086,48087,48088,48089,48090,48091,48092,48093,48094,48095,48096,48097,48098,48099,48100,48101,48102,48103,48104,48105,48106,48107,48108,48109,48110,48111,48112,48113,48114,48115,48116,48117,48118,48119,48122,48123,48125,48126,48129,48131,48132,48133,48134,48135,48138,48142,48144,48146,48147,48153,48154,48160,48161,48162,48163,48166,48168,48170,48171,48172,48174,48175,48178,48179,48181,null,null,null,null,null,null,48182,48183,48185,48186,48187,48188,48189,48190,48191,48194,48198,48199,48200,48202,48203,48206,48207,48209,48210,48211,48212,48213,48214,48215,48216,48217,null,null,null,null,null,null,48218,48219,48220,48222,48223,48224,48225,48226,48227,48228,48229,48230,48231,48232,48233,48234,48235,48236,48237,48238,48239,48240,48241,48242,48243,48244,48245,48246,48247,48248,48249,48250,48251,48252,48253,48254,48255,48256,48257,48258,48259,48262,48263,48265,48266,48269,48271,48272,48273,48274,48275,48278,48280,48283,48284,48285,48286,48287,48290,48291,48293,48294,48297,48298,48299,48300,48301,48302,48303,48306,48310,48311,48312,48313,48314,48315,48318,48319,48321,48322,48323,48325,48326,48327,48328,48329,48330,48331,48332,48334,48338,48339,48340,48342,48343,48345,48346,48347,48349,48350,48351,48352,48353,48354,48355,48356,48357,48358,48359,48360,48361,48362,48363,48364,48365,48366,48367,48368,48369,48370,48371,48375,48377,48378,48379,48381,48382,48383,48384,48385,48386,48387,48390,48392,48394,48395,48396,48397,48398,48399,48401,48402,48403,48405,48406,48407,48408,48409,48410,48411,48412,48413,null,null,null,null,null,null,48414,48415,48416,48417,48418,48419,48421,48422,48423,48424,48425,48426,48427,48429,48430,48431,48432,48433,48434,48435,48436,48437,48438,48439,48440,48441,null,null,null,null,null,null,48442,48443,48444,48445,48446,48447,48449,48450,48451,48452,48453,48454,48455,48458,48459,48461,48462,48463,48465,48466,48467,48468,48469,48470,48471,48474,48475,48476,48477,48478,48479,48480,48481,48482,48483,48485,48486,48487,48489,48490,48491,48492,48493,48494,48495,48496,48497,48498,48499,48500,48501,48502,48503,48504,48505,48506,48507,48508,48509,48510,48511,48514,48515,48517,48518,48523,48524,48525,48526,48527,48530,48532,48534,48535,48536,48539,48541,48542,48543,48544,48545,48546,48547,48549,48550,48551,48552,48553,48554,48555,48556,48557,48558,48559,48561,48562,48563,48564,48565,48566,48567,48569,48570,48571,48572,48573,48574,48575,48576,48577,48578,48579,48580,48581,48582,48583,48584,48585,48586,48587,48588,48589,48590,48591,48592,48593,48594,48595,48598,48599,48601,48602,48603,48605,48606,48607,48608,48609,48610,48611,48612,48613,48614,48615,48616,48618,48619,48620,48621,48622,48623,48625,null,null,null,null,null,null,48626,48627,48629,48630,48631,48633,48634,48635,48636,48637,48638,48639,48641,48642,48644,48646,48647,48648,48649,48650,48651,48654,48655,48657,48658,48659,null,null,null,null,null,null,48661,48662,48663,48664,48665,48666,48667,48670,48672,48673,48674,48675,48676,48677,48678,48679,48680,48681,48682,48683,48684,48685,48686,48687,48688,48689,48690,48691,48692,48693,48694,48695,48696,48697,48698,48699,48700,48701,48702,48703,48704,48705,48706,48707,48710,48711,48713,48714,48715,48717,48719,48720,48721,48722,48723,48726,48728,48732,48733,48734,48735,48738,48739,48741,48742,48743,48745,48747,48748,48749,48750,48751,48754,48758,48759,48760,48761,48762,48766,48767,48769,48770,48771,48773,48774,48775,48776,48777,48778,48779,48782,48786,48787,48788,48789,48790,48791,48794,48795,48796,48797,48798,48799,48800,48801,48802,48803,48804,48805,48806,48807,48809,48810,48811,48812,48813,48814,48815,48816,48817,48818,48819,48820,48821,48822,48823,48824,48825,48826,48827,48828,48829,48830,48831,48832,48833,48834,48835,48836,48837,48838,48839,48840,48841,48842,48843,48844,48845,48846,48847,48850,48851,null,null,null,null,null,null,48853,48854,48857,48858,48859,48860,48861,48862,48863,48865,48866,48870,48871,48872,48873,48874,48875,48877,48878,48879,48880,48881,48882,48883,48884,48885,null,null,null,null,null,null,48886,48887,48888,48889,48890,48891,48892,48893,48894,48895,48896,48898,48899,48900,48901,48902,48903,48906,48907,48908,48909,48910,48911,48912,48913,48914,48915,48916,48917,48918,48919,48922,48926,48927,48928,48929,48930,48931,48932,48933,48934,48935,48936,48937,48938,48939,48940,48941,48942,48943,48944,48945,48946,48947,48948,48949,48950,48951,48952,48953,48954,48955,48956,48957,48958,48959,48962,48963,48965,48966,48967,48969,48970,48971,48972,48973,48974,48975,48978,48979,48980,48982,48983,48984,48985,48986,48987,48988,48989,48990,48991,48992,48993,48994,48995,48996,48997,48998,48999,49000,49001,49002,49003,49004,49005,49006,49007,49008,49009,49010,49011,49012,49013,49014,49015,49016,49017,49018,49019,49020,49021,49022,49023,49024,49025,49026,49027,49028,49029,49030,49031,49032,49033,49034,49035,49036,49037,49038,49039,49040,49041,49042,49043,49045,49046,49047,49048,49049,49050,49051,49052,49053,null,null,null,null,null,null,49054,49055,49056,49057,49058,49059,49060,49061,49062,49063,49064,49065,49066,49067,49068,49069,49070,49071,49073,49074,49075,49076,49077,49078,49079,49080,null,null,null,null,null,null,49081,49082,49083,49084,49085,49086,49087,49088,49089,49090,49091,49092,49094,49095,49096,49097,49098,49099,49102,49103,49105,49106,49107,49109,49110,49111,49112,49113,49114,49115,49117,49118,49120,49122,49123,49124,49125,49126,49127,49128,49129,49130,49131,49132,49133,49134,49135,49136,49137,49138,49139,49140,49141,49142,49143,49144,49145,49146,49147,49148,49149,49150,49151,49152,49153,49154,49155,49156,49157,49158,49159,49160,49161,49162,49163,49164,49165,49166,49167,49168,49169,49170,49171,49172,49173,49174,49175,49176,49177,49178,49179,49180,49181,49182,49183,49184,49185,49186,49187,49188,49189,49190,49191,49192,49193,49194,49195,49196,49197,49198,49199,49200,49201,49202,49203,49204,49205,49206,49207,49208,49209,49210,49211,49213,49214,49215,49216,49217,49218,49219,49220,49221,49222,49223,49224,49225,49226,49227,49228,49229,49230,49231,49232,49234,49235,49236,49237,49238,49239,49241,49242,49243,null,null,null,null,null,null,49245,49246,49247,49249,49250,49251,49252,49253,49254,49255,49258,49259,49260,49261,49262,49263,49264,49265,49266,49267,49268,49269,49270,49271,49272,49273,null,null,null,null,null,null,49274,49275,49276,49277,49278,49279,49280,49281,49282,49283,49284,49285,49286,49287,49288,49289,49290,49291,49292,49293,49294,49295,49298,49299,49301,49302,49303,49305,49306,49307,49308,49309,49310,49311,49314,49316,49318,49319,49320,49321,49322,49323,49326,49329,49330,49335,49336,49337,49338,49339,49342,49346,49347,49348,49350,49351,49354,49355,49357,49358,49359,49361,49362,49363,49364,49365,49366,49367,49370,49374,49375,49376,49377,49378,49379,49382,49383,49385,49386,49387,49389,49390,49391,49392,49393,49394,49395,49398,49400,49402,49403,49404,49405,49406,49407,49409,49410,49411,49413,49414,49415,49417,49418,49419,49420,49421,49422,49423,49425,49426,49427,49428,49430,49431,49432,49433,49434,49435,49441,49442,49445,49448,49449,49450,49451,49454,49458,49459,49460,49461,49463,49466,49467,49469,49470,49471,49473,49474,49475,49476,49477,49478,49479,49482,49486,49487,49488,49489,49490,49491,49494,49495,null,null,null,null,null,null,49497,49498,49499,49501,49502,49503,49504,49505,49506,49507,49510,49514,49515,49516,49517,49518,49519,49521,49522,49523,49525,49526,49527,49529,49530,49531,null,null,null,null,null,null,49532,49533,49534,49535,49536,49537,49538,49539,49540,49542,49543,49544,49545,49546,49547,49551,49553,49554,49555,49557,49559,49560,49561,49562,49563,49566,49568,49570,49571,49572,49574,49575,49578,49579,49581,49582,49583,49585,49586,49587,49588,49589,49590,49591,49592,49593,49594,49595,49596,49598,49599,49600,49601,49602,49603,49605,49606,49607,49609,49610,49611,49613,49614,49615,49616,49617,49618,49619,49621,49622,49625,49626,49627,49628,49629,49630,49631,49633,49634,49635,49637,49638,49639,49641,49642,49643,49644,49645,49646,49647,49650,49652,49653,49654,49655,49656,49657,49658,49659,49662,49663,49665,49666,49667,49669,49670,49671,49672,49673,49674,49675,49678,49680,49682,49683,49684,49685,49686,49687,49690,49691,49693,49694,49697,49698,49699,49700,49701,49702,49703,49706,49708,49710,49712,49715,49717,49718,49719,49720,49721,49722,49723,49724,49725,49726,49727,49728,49729,49730,49731,49732,49733,null,null,null,null,null,null,49734,49735,49737,49738,49739,49740,49741,49742,49743,49746,49747,49749,49750,49751,49753,49754,49755,49756,49757,49758,49759,49761,49762,49763,49764,49766,null,null,null,null,null,null,49767,49768,49769,49770,49771,49774,49775,49777,49778,49779,49781,49782,49783,49784,49785,49786,49787,49790,49792,49794,49795,49796,49797,49798,49799,49802,49803,49804,49805,49806,49807,49809,49810,49811,49812,49813,49814,49815,49817,49818,49820,49822,49823,49824,49825,49826,49827,49830,49831,49833,49834,49835,49838,49839,49840,49841,49842,49843,49846,49848,49850,49851,49852,49853,49854,49855,49856,49857,49858,49859,49860,49861,49862,49863,49864,49865,49866,49867,49868,49869,49870,49871,49872,49873,49874,49875,49876,49877,49878,49879,49880,49881,49882,49883,49886,49887,49889,49890,49893,49894,49895,49896,49897,49898,49902,49904,49906,49907,49908,49909,49911,49914,49917,49918,49919,49921,49922,49923,49924,49925,49926,49927,49930,49931,49934,49935,49936,49937,49938,49942,49943,49945,49946,49947,49949,49950,49951,49952,49953,49954,49955,49958,49959,49962,49963,49964,49965,49966,49967,49968,49969,49970,null,null,null,null,null,null,49971,49972,49973,49974,49975,49976,49977,49978,49979,49980,49981,49982,49983,49984,49985,49986,49987,49988,49990,49991,49992,49993,49994,49995,49996,49997,null,null,null,null,null,null,49998,49999,50000,50001,50002,50003,50004,50005,50006,50007,50008,50009,50010,50011,50012,50013,50014,50015,50016,50017,50018,50019,50020,50021,50022,50023,50026,50027,50029,50030,50031,50033,50035,50036,50037,50038,50039,50042,50043,50046,50047,50048,50049,50050,50051,50053,50054,50055,50057,50058,50059,50061,50062,50063,50064,50065,50066,50067,50068,50069,50070,50071,50072,50073,50074,50075,50076,50077,50078,50079,50080,50081,50082,50083,50084,50085,50086,50087,50088,50089,50090,50091,50092,50093,50094,50095,50096,50097,50098,50099,50100,50101,50102,50103,50104,50105,50106,50107,50108,50109,50110,50111,50113,50114,50115,50116,50117,50118,50119,50120,50121,50122,50123,50124,50125,50126,50127,50128,50129,50130,50131,50132,50133,50134,50135,50138,50139,50141,50142,50145,50147,50148,50149,50150,50151,50154,50155,50156,50158,50159,50160,50161,50162,50163,50166,50167,50169,50170,50171,50172,50173,50174,null,null,null,null,null,null,50175,50176,50177,50178,50179,50180,50181,50182,50183,50185,50186,50187,50188,50189,50190,50191,50193,50194,50195,50196,50197,50198,50199,50200,50201,50202,null,null,null,null,null,null,50203,50204,50205,50206,50207,50208,50209,50210,50211,50213,50214,50215,50216,50217,50218,50219,50221,50222,50223,50225,50226,50227,50229,50230,50231,50232,50233,50234,50235,50238,50239,50240,50241,50242,50243,50244,50245,50246,50247,50249,50250,50251,50252,50253,50254,50255,50256,50257,50258,50259,50260,50261,50262,50263,50264,50265,50266,50267,50268,50269,50270,50271,50272,50273,50274,50275,50278,50279,50281,50282,50283,50285,50286,50287,50288,50289,50290,50291,50294,50295,50296,50298,50299,50300,50301,50302,50303,50305,50306,50307,50308,50309,50310,50311,50312,50313,50314,50315,50316,50317,50318,50319,50320,50321,50322,50323,50325,50326,50327,50328,50329,50330,50331,50333,50334,50335,50336,50337,50338,50339,50340,50341,50342,50343,50344,50345,50346,50347,50348,50349,50350,50351,50352,50353,50354,50355,50356,50357,50358,50359,50361,50362,50363,50365,50366,50367,50368,50369,50370,50371,50372,50373,null,null,null,null,null,null,50374,50375,50376,50377,50378,50379,50380,50381,50382,50383,50384,50385,50386,50387,50388,50389,50390,50391,50392,50393,50394,50395,50396,50397,50398,50399,null,null,null,null,null,null,50400,50401,50402,50403,50404,50405,50406,50407,50408,50410,50411,50412,50413,50414,50415,50418,50419,50421,50422,50423,50425,50427,50428,50429,50430,50434,50435,50436,50437,50438,50439,50440,50441,50442,50443,50445,50446,50447,50449,50450,50451,50453,50454,50455,50456,50457,50458,50459,50461,50462,50463,50464,50465,50466,50467,50468,50469,50470,50471,50474,50475,50477,50478,50479,50481,50482,50483,50484,50485,50486,50487,50490,50492,50494,50495,50496,50497,50498,50499,50502,50503,50507,50511,50512,50513,50514,50518,50522,50523,50524,50527,50530,50531,50533,50534,50535,50537,50538,50539,50540,50541,50542,50543,50546,50550,50551,50552,50553,50554,50555,50558,50559,50561,50562,50563,50565,50566,50568,50569,50570,50571,50574,50576,50578,50579,50580,50582,50585,50586,50587,50589,50590,50591,50593,50594,50595,50596,50597,50598,50599,50600,50602,50603,50604,50605,50606,50607,50608,50609,50610,50611,50614,null,null,null,null,null,null,50615,50618,50623,50624,50625,50626,50627,50635,50637,50639,50642,50643,50645,50646,50647,50649,50650,50651,50652,50653,50654,50655,50658,50660,50662,50663,null,null,null,null,null,null,50664,50665,50666,50667,50671,50673,50674,50675,50677,50680,50681,50682,50683,50690,50691,50692,50697,50698,50699,50701,50702,50703,50705,50706,50707,50708,50709,50710,50711,50714,50717,50718,50719,50720,50721,50722,50723,50726,50727,50729,50730,50731,50735,50737,50738,50742,50744,50746,50748,50749,50750,50751,50754,50755,50757,50758,50759,50761,50762,50763,50764,50765,50766,50767,50770,50774,50775,50776,50777,50778,50779,50782,50783,50785,50786,50787,50788,50789,50790,50791,50792,50793,50794,50795,50797,50798,50800,50802,50803,50804,50805,50806,50807,50810,50811,50813,50814,50815,50817,50818,50819,50820,50821,50822,50823,50826,50828,50830,50831,50832,50833,50834,50835,50838,50839,50841,50842,50843,50845,50846,50847,50848,50849,50850,50851,50854,50856,50858,50859,50860,50861,50862,50863,50866,50867,50869,50870,50871,50875,50876,50877,50878,50879,50882,50884,50886,50887,50888,50889,50890,50891,50894,null,null,null,null,null,null,50895,50897,50898,50899,50901,50902,50903,50904,50905,50906,50907,50910,50911,50914,50915,50916,50917,50918,50919,50922,50923,50925,50926,50927,50929,50930,null,null,null,null,null,null,50931,50932,50933,50934,50935,50938,50939,50940,50942,50943,50944,50945,50946,50947,50950,50951,50953,50954,50955,50957,50958,50959,50960,50961,50962,50963,50966,50968,50970,50971,50972,50973,50974,50975,50978,50979,50981,50982,50983,50985,50986,50987,50988,50989,50990,50991,50994,50996,50998,51000,51001,51002,51003,51006,51007,51009,51010,51011,51013,51014,51015,51016,51017,51019,51022,51024,51033,51034,51035,51037,51038,51039,51041,51042,51043,51044,51045,51046,51047,51049,51050,51052,51053,51054,51055,51056,51057,51058,51059,51062,51063,51065,51066,51067,51071,51072,51073,51074,51078,51083,51084,51085,51087,51090,51091,51093,51097,51099,51100,51101,51102,51103,51106,51111,51112,51113,51114,51115,51118,51119,51121,51122,51123,51125,51126,51127,51128,51129,51130,51131,51134,51138,51139,51140,51141,51142,51143,51146,51147,51149,51151,51153,51154,51155,51156,51157,51158,51159,51161,51162,51163,51164,null,null,null,null,null,null,51166,51167,51168,51169,51170,51171,51173,51174,51175,51177,51178,51179,51181,51182,51183,51184,51185,51186,51187,51188,51189,51190,51191,51192,51193,51194,null,null,null,null,null,null,51195,51196,51197,51198,51199,51202,51203,51205,51206,51207,51209,51211,51212,51213,51214,51215,51218,51220,51223,51224,51225,51226,51227,51230,51231,51233,51234,51235,51237,51238,51239,51240,51241,51242,51243,51246,51248,51250,51251,51252,51253,51254,51255,51257,51258,51259,51261,51262,51263,51265,51266,51267,51268,51269,51270,51271,51274,51275,51278,51279,51280,51281,51282,51283,51285,51286,51287,51288,51289,51290,51291,51292,51293,51294,51295,51296,51297,51298,51299,51300,51301,51302,51303,51304,51305,51306,51307,51308,51309,51310,51311,51314,51315,51317,51318,51319,51321,51323,51324,51325,51326,51327,51330,51332,51336,51337,51338,51342,51343,51344,51345,51346,51347,51349,51350,51351,51352,51353,51354,51355,51356,51358,51360,51362,51363,51364,51365,51366,51367,51369,51370,51371,51372,51373,51374,51375,51376,51377,51378,51379,51380,51381,51382,51383,51384,51385,51386,51387,51390,51391,51392,51393,null,null,null,null,null,null,51394,51395,51397,51398,51399,51401,51402,51403,51405,51406,51407,51408,51409,51410,51411,51414,51416,51418,51419,51420,51421,51422,51423,51426,51427,51429,null,null,null,null,null,null,51430,51431,51432,51433,51434,51435,51436,51437,51438,51439,51440,51441,51442,51443,51444,51446,51447,51448,51449,51450,51451,51454,51455,51457,51458,51459,51463,51464,51465,51466,51467,51470,12288,12289,12290,183,8229,8230,168,12291,173,8213,8741,65340,8764,8216,8217,8220,8221,12308,12309,12296,12297,12298,12299,12300,12301,12302,12303,12304,12305,177,215,247,8800,8804,8805,8734,8756,176,8242,8243,8451,8491,65504,65505,65509,9794,9792,8736,8869,8978,8706,8711,8801,8786,167,8251,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,9661,9660,8594,8592,8593,8595,8596,12307,8810,8811,8730,8765,8733,8757,8747,8748,8712,8715,8838,8839,8834,8835,8746,8745,8743,8744,65506,51472,51474,51475,51476,51477,51478,51479,51481,51482,51483,51484,51485,51486,51487,51488,51489,51490,51491,51492,51493,51494,51495,51496,51497,51498,51499,null,null,null,null,null,null,51501,51502,51503,51504,51505,51506,51507,51509,51510,51511,51512,51513,51514,51515,51516,51517,51518,51519,51520,51521,51522,51523,51524,51525,51526,51527,null,null,null,null,null,null,51528,51529,51530,51531,51532,51533,51534,51535,51538,51539,51541,51542,51543,51545,51546,51547,51548,51549,51550,51551,51554,51556,51557,51558,51559,51560,51561,51562,51563,51565,51566,51567,8658,8660,8704,8707,180,65374,711,728,733,730,729,184,731,161,191,720,8750,8721,8719,164,8457,8240,9665,9664,9655,9654,9828,9824,9825,9829,9831,9827,8857,9672,9635,9680,9681,9618,9636,9637,9640,9639,9638,9641,9832,9743,9742,9756,9758,182,8224,8225,8597,8599,8601,8598,8600,9837,9833,9834,9836,12927,12828,8470,13255,8482,13250,13272,8481,8364,174,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,51569,51570,51571,51573,51574,51575,51576,51577,51578,51579,51581,51582,51583,51584,51585,51586,51587,51588,51589,51590,51591,51594,51595,51597,51598,51599,null,null,null,null,null,null,51601,51602,51603,51604,51605,51606,51607,51610,51612,51614,51615,51616,51617,51618,51619,51620,51621,51622,51623,51624,51625,51626,51627,51628,51629,51630,null,null,null,null,null,null,51631,51632,51633,51634,51635,51636,51637,51638,51639,51640,51641,51642,51643,51644,51645,51646,51647,51650,51651,51653,51654,51657,51659,51660,51661,51662,51663,51666,51668,51671,51672,51675,65281,65282,65283,65284,65285,65286,65287,65288,65289,65290,65291,65292,65293,65294,65295,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,65306,65307,65308,65309,65310,65311,65312,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65339,65510,65341,65342,65343,65344,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,65371,65372,65373,65507,51678,51679,51681,51683,51685,51686,51688,51689,51690,51691,51694,51698,51699,51700,51701,51702,51703,51706,51707,51709,51710,51711,51713,51714,51715,51716,null,null,null,null,null,null,51717,51718,51719,51722,51726,51727,51728,51729,51730,51731,51733,51734,51735,51737,51738,51739,51740,51741,51742,51743,51744,51745,51746,51747,51748,51749,null,null,null,null,null,null,51750,51751,51752,51754,51755,51756,51757,51758,51759,51760,51761,51762,51763,51764,51765,51766,51767,51768,51769,51770,51771,51772,51773,51774,51775,51776,51777,51778,51779,51780,51781,51782,12593,12594,12595,12596,12597,12598,12599,12600,12601,12602,12603,12604,12605,12606,12607,12608,12609,12610,12611,12612,12613,12614,12615,12616,12617,12618,12619,12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631,12632,12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643,12644,12645,12646,12647,12648,12649,12650,12651,12652,12653,12654,12655,12656,12657,12658,12659,12660,12661,12662,12663,12664,12665,12666,12667,12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679,12680,12681,12682,12683,12684,12685,12686,51783,51784,51785,51786,51787,51790,51791,51793,51794,51795,51797,51798,51799,51800,51801,51802,51803,51806,51810,51811,51812,51813,51814,51815,51817,51818,null,null,null,null,null,null,51819,51820,51821,51822,51823,51824,51825,51826,51827,51828,51829,51830,51831,51832,51833,51834,51835,51836,51838,51839,51840,51841,51842,51843,51845,51846,null,null,null,null,null,null,51847,51848,51849,51850,51851,51852,51853,51854,51855,51856,51857,51858,51859,51860,51861,51862,51863,51865,51866,51867,51868,51869,51870,51871,51872,51873,51874,51875,51876,51877,51878,51879,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,null,null,null,null,null,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,null,null,null,null,null,null,null,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,null,null,null,null,null,null,null,null,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,null,null,null,null,null,null,51880,51881,51882,51883,51884,51885,51886,51887,51888,51889,51890,51891,51892,51893,51894,51895,51896,51897,51898,51899,51902,51903,51905,51906,51907,51909,null,null,null,null,null,null,51910,51911,51912,51913,51914,51915,51918,51920,51922,51924,51925,51926,51927,51930,51931,51932,51933,51934,51935,51937,51938,51939,51940,51941,51942,51943,null,null,null,null,null,null,51944,51945,51946,51947,51949,51950,51951,51952,51953,51954,51955,51957,51958,51959,51960,51961,51962,51963,51964,51965,51966,51967,51968,51969,51970,51971,51972,51973,51974,51975,51977,51978,9472,9474,9484,9488,9496,9492,9500,9516,9508,9524,9532,9473,9475,9487,9491,9499,9495,9507,9523,9515,9531,9547,9504,9519,9512,9527,9535,9501,9520,9509,9528,9538,9490,9489,9498,9497,9494,9493,9486,9485,9502,9503,9505,9506,9510,9511,9513,9514,9517,9518,9521,9522,9525,9526,9529,9530,9533,9534,9536,9537,9539,9540,9541,9542,9543,9544,9545,9546,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,51979,51980,51981,51982,51983,51985,51986,51987,51989,51990,51991,51993,51994,51995,51996,51997,51998,51999,52002,52003,52004,52005,52006,52007,52008,52009,null,null,null,null,null,null,52010,52011,52012,52013,52014,52015,52016,52017,52018,52019,52020,52021,52022,52023,52024,52025,52026,52027,52028,52029,52030,52031,52032,52034,52035,52036,null,null,null,null,null,null,52037,52038,52039,52042,52043,52045,52046,52047,52049,52050,52051,52052,52053,52054,52055,52058,52059,52060,52062,52063,52064,52065,52066,52067,52069,52070,52071,52072,52073,52074,52075,52076,13205,13206,13207,8467,13208,13252,13219,13220,13221,13222,13209,13210,13211,13212,13213,13214,13215,13216,13217,13218,13258,13197,13198,13199,13263,13192,13193,13256,13223,13224,13232,13233,13234,13235,13236,13237,13238,13239,13240,13241,13184,13185,13186,13187,13188,13242,13243,13244,13245,13246,13247,13200,13201,13202,13203,13204,8486,13248,13249,13194,13195,13196,13270,13253,13229,13230,13231,13275,13225,13226,13227,13228,13277,13264,13267,13251,13257,13276,13254,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52077,52078,52079,52080,52081,52082,52083,52084,52085,52086,52087,52090,52091,52092,52093,52094,52095,52096,52097,52098,52099,52100,52101,52102,52103,52104,null,null,null,null,null,null,52105,52106,52107,52108,52109,52110,52111,52112,52113,52114,52115,52116,52117,52118,52119,52120,52121,52122,52123,52125,52126,52127,52128,52129,52130,52131,null,null,null,null,null,null,52132,52133,52134,52135,52136,52137,52138,52139,52140,52141,52142,52143,52144,52145,52146,52147,52148,52149,52150,52151,52153,52154,52155,52156,52157,52158,52159,52160,52161,52162,52163,52164,198,208,170,294,null,306,null,319,321,216,338,186,222,358,330,null,12896,12897,12898,12899,12900,12901,12902,12903,12904,12905,12906,12907,12908,12909,12910,12911,12912,12913,12914,12915,12916,12917,12918,12919,12920,12921,12922,12923,9424,9425,9426,9427,9428,9429,9430,9431,9432,9433,9434,9435,9436,9437,9438,9439,9440,9441,9442,9443,9444,9445,9446,9447,9448,9449,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9322,9323,9324,9325,9326,189,8531,8532,188,190,8539,8540,8541,8542,52165,52166,52167,52168,52169,52170,52171,52172,52173,52174,52175,52176,52177,52178,52179,52181,52182,52183,52184,52185,52186,52187,52188,52189,52190,52191,null,null,null,null,null,null,52192,52193,52194,52195,52197,52198,52200,52202,52203,52204,52205,52206,52207,52208,52209,52210,52211,52212,52213,52214,52215,52216,52217,52218,52219,52220,null,null,null,null,null,null,52221,52222,52223,52224,52225,52226,52227,52228,52229,52230,52231,52232,52233,52234,52235,52238,52239,52241,52242,52243,52245,52246,52247,52248,52249,52250,52251,52254,52255,52256,52259,52260,230,273,240,295,305,307,312,320,322,248,339,223,254,359,331,329,12800,12801,12802,12803,12804,12805,12806,12807,12808,12809,12810,12811,12812,12813,12814,12815,12816,12817,12818,12819,12820,12821,12822,12823,12824,12825,12826,12827,9372,9373,9374,9375,9376,9377,9378,9379,9380,9381,9382,9383,9384,9385,9386,9387,9388,9389,9390,9391,9392,9393,9394,9395,9396,9397,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345,9346,185,178,179,8308,8319,8321,8322,8323,8324,52261,52262,52266,52267,52269,52271,52273,52274,52275,52276,52277,52278,52279,52282,52287,52288,52289,52290,52291,52294,52295,52297,52298,52299,52301,52302,null,null,null,null,null,null,52303,52304,52305,52306,52307,52310,52314,52315,52316,52317,52318,52319,52321,52322,52323,52325,52327,52329,52330,52331,52332,52333,52334,52335,52337,52338,null,null,null,null,null,null,52339,52340,52342,52343,52344,52345,52346,52347,52348,52349,52350,52351,52352,52353,52354,52355,52356,52357,52358,52359,52360,52361,52362,52363,52364,52365,52366,52367,52368,52369,52370,52371,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,null,null,null,null,null,null,null,null,null,null,null,52372,52373,52374,52375,52378,52379,52381,52382,52383,52385,52386,52387,52388,52389,52390,52391,52394,52398,52399,52400,52401,52402,52403,52406,52407,52409,null,null,null,null,null,null,52410,52411,52413,52414,52415,52416,52417,52418,52419,52422,52424,52426,52427,52428,52429,52430,52431,52433,52434,52435,52437,52438,52439,52440,52441,52442,null,null,null,null,null,null,52443,52444,52445,52446,52447,52448,52449,52450,52451,52453,52454,52455,52456,52457,52458,52459,52461,52462,52463,52465,52466,52467,52468,52469,52470,52471,52472,52473,52474,52475,52476,52477,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,null,null,null,null,null,null,null,null,52478,52479,52480,52482,52483,52484,52485,52486,52487,52490,52491,52493,52494,52495,52497,52498,52499,52500,52501,52502,52503,52506,52508,52510,52511,52512,null,null,null,null,null,null,52513,52514,52515,52517,52518,52519,52521,52522,52523,52525,52526,52527,52528,52529,52530,52531,52532,52533,52534,52535,52536,52538,52539,52540,52541,52542,null,null,null,null,null,null,52543,52544,52545,52546,52547,52548,52549,52550,52551,52552,52553,52554,52555,52556,52557,52558,52559,52560,52561,52562,52563,52564,52565,52566,52567,52568,52569,52570,52571,52573,52574,52575,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,null,null,null,null,null,null,null,null,null,null,null,null,null,52577,52578,52579,52581,52582,52583,52584,52585,52586,52587,52590,52592,52594,52595,52596,52597,52598,52599,52601,52602,52603,52604,52605,52606,52607,52608,null,null,null,null,null,null,52609,52610,52611,52612,52613,52614,52615,52617,52618,52619,52620,52621,52622,52623,52624,52625,52626,52627,52630,52631,52633,52634,52635,52637,52638,52639,null,null,null,null,null,null,52640,52641,52642,52643,52646,52648,52650,52651,52652,52653,52654,52655,52657,52658,52659,52660,52661,52662,52663,52664,52665,52666,52667,52668,52669,52670,52671,52672,52673,52674,52675,52677,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52678,52679,52680,52681,52682,52683,52685,52686,52687,52689,52690,52691,52692,52693,52694,52695,52696,52697,52698,52699,52700,52701,52702,52703,52704,52705,null,null,null,null,null,null,52706,52707,52708,52709,52710,52711,52713,52714,52715,52717,52718,52719,52721,52722,52723,52724,52725,52726,52727,52730,52732,52734,52735,52736,52737,52738,null,null,null,null,null,null,52739,52741,52742,52743,52745,52746,52747,52749,52750,52751,52752,52753,52754,52755,52757,52758,52759,52760,52762,52763,52764,52765,52766,52767,52770,52771,52773,52774,52775,52777,52778,52779,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52780,52781,52782,52783,52786,52788,52790,52791,52792,52793,52794,52795,52796,52797,52798,52799,52800,52801,52802,52803,52804,52805,52806,52807,52808,52809,null,null,null,null,null,null,52810,52811,52812,52813,52814,52815,52816,52817,52818,52819,52820,52821,52822,52823,52826,52827,52829,52830,52834,52835,52836,52837,52838,52839,52842,52844,null,null,null,null,null,null,52846,52847,52848,52849,52850,52851,52854,52855,52857,52858,52859,52861,52862,52863,52864,52865,52866,52867,52870,52872,52874,52875,52876,52877,52878,52879,52882,52883,52885,52886,52887,52889,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52890,52891,52892,52893,52894,52895,52898,52902,52903,52904,52905,52906,52907,52910,52911,52912,52913,52914,52915,52916,52917,52918,52919,52920,52921,52922,null,null,null,null,null,null,52923,52924,52925,52926,52927,52928,52930,52931,52932,52933,52934,52935,52936,52937,52938,52939,52940,52941,52942,52943,52944,52945,52946,52947,52948,52949,null,null,null,null,null,null,52950,52951,52952,52953,52954,52955,52956,52957,52958,52959,52960,52961,52962,52963,52966,52967,52969,52970,52973,52974,52975,52976,52977,52978,52979,52982,52986,52987,52988,52989,52990,52991,44032,44033,44036,44039,44040,44041,44042,44048,44049,44050,44051,44052,44053,44054,44055,44057,44058,44059,44060,44061,44064,44068,44076,44077,44079,44080,44081,44088,44089,44092,44096,44107,44109,44116,44120,44124,44144,44145,44148,44151,44152,44154,44160,44161,44163,44164,44165,44166,44169,44170,44171,44172,44176,44180,44188,44189,44191,44192,44193,44200,44201,44202,44204,44207,44208,44216,44217,44219,44220,44221,44225,44228,44232,44236,44245,44247,44256,44257,44260,44263,44264,44266,44268,44271,44272,44273,44275,44277,44278,44284,44285,44288,44292,44294,52994,52995,52997,52998,52999,53001,53002,53003,53004,53005,53006,53007,53010,53012,53014,53015,53016,53017,53018,53019,53021,53022,53023,53025,53026,53027,null,null,null,null,null,null,53029,53030,53031,53032,53033,53034,53035,53038,53042,53043,53044,53045,53046,53047,53049,53050,53051,53052,53053,53054,53055,53056,53057,53058,53059,53060,null,null,null,null,null,null,53061,53062,53063,53064,53065,53066,53067,53068,53069,53070,53071,53072,53073,53074,53075,53078,53079,53081,53082,53083,53085,53086,53087,53088,53089,53090,53091,53094,53096,53098,53099,53100,44300,44301,44303,44305,44312,44316,44320,44329,44332,44333,44340,44341,44344,44348,44356,44357,44359,44361,44368,44372,44376,44385,44387,44396,44397,44400,44403,44404,44405,44406,44411,44412,44413,44415,44417,44418,44424,44425,44428,44432,44444,44445,44452,44471,44480,44481,44484,44488,44496,44497,44499,44508,44512,44516,44536,44537,44540,44543,44544,44545,44552,44553,44555,44557,44564,44592,44593,44596,44599,44600,44602,44608,44609,44611,44613,44614,44618,44620,44621,44622,44624,44628,44630,44636,44637,44639,44640,44641,44645,44648,44649,44652,44656,44664,53101,53102,53103,53106,53107,53109,53110,53111,53113,53114,53115,53116,53117,53118,53119,53121,53122,53123,53124,53126,53127,53128,53129,53130,53131,53133,null,null,null,null,null,null,53134,53135,53136,53137,53138,53139,53140,53141,53142,53143,53144,53145,53146,53147,53148,53149,53150,53151,53152,53154,53155,53156,53157,53158,53159,53161,null,null,null,null,null,null,53162,53163,53164,53165,53166,53167,53169,53170,53171,53172,53173,53174,53175,53176,53177,53178,53179,53180,53181,53182,53183,53184,53185,53186,53187,53189,53190,53191,53192,53193,53194,53195,44665,44667,44668,44669,44676,44677,44684,44732,44733,44734,44736,44740,44748,44749,44751,44752,44753,44760,44761,44764,44776,44779,44781,44788,44792,44796,44807,44808,44813,44816,44844,44845,44848,44850,44852,44860,44861,44863,44865,44866,44867,44872,44873,44880,44892,44893,44900,44901,44921,44928,44932,44936,44944,44945,44949,44956,44984,44985,44988,44992,44999,45000,45001,45003,45005,45006,45012,45020,45032,45033,45040,45041,45044,45048,45056,45057,45060,45068,45072,45076,45084,45085,45096,45124,45125,45128,45130,45132,45134,45139,45140,45141,45143,45145,53196,53197,53198,53199,53200,53201,53202,53203,53204,53205,53206,53207,53208,53209,53210,53211,53212,53213,53214,53215,53218,53219,53221,53222,53223,53225,null,null,null,null,null,null,53226,53227,53228,53229,53230,53231,53234,53236,53238,53239,53240,53241,53242,53243,53245,53246,53247,53249,53250,53251,53253,53254,53255,53256,53257,53258,null,null,null,null,null,null,53259,53260,53261,53262,53263,53264,53266,53267,53268,53269,53270,53271,53273,53274,53275,53276,53277,53278,53279,53280,53281,53282,53283,53284,53285,53286,53287,53288,53289,53290,53291,53292,45149,45180,45181,45184,45188,45196,45197,45199,45201,45208,45209,45210,45212,45215,45216,45217,45218,45224,45225,45227,45228,45229,45230,45231,45233,45235,45236,45237,45240,45244,45252,45253,45255,45256,45257,45264,45265,45268,45272,45280,45285,45320,45321,45323,45324,45328,45330,45331,45336,45337,45339,45340,45341,45347,45348,45349,45352,45356,45364,45365,45367,45368,45369,45376,45377,45380,45384,45392,45393,45396,45397,45400,45404,45408,45432,45433,45436,45440,45442,45448,45449,45451,45453,45458,45459,45460,45464,45468,45480,45516,45520,45524,45532,45533,53294,53295,53296,53297,53298,53299,53302,53303,53305,53306,53307,53309,53310,53311,53312,53313,53314,53315,53318,53320,53322,53323,53324,53325,53326,53327,null,null,null,null,null,null,53329,53330,53331,53333,53334,53335,53337,53338,53339,53340,53341,53342,53343,53345,53346,53347,53348,53349,53350,53351,53352,53353,53354,53355,53358,53359,null,null,null,null,null,null,53361,53362,53363,53365,53366,53367,53368,53369,53370,53371,53374,53375,53376,53378,53379,53380,53381,53382,53383,53384,53385,53386,53387,53388,53389,53390,53391,53392,53393,53394,53395,53396,45535,45544,45545,45548,45552,45561,45563,45565,45572,45573,45576,45579,45580,45588,45589,45591,45593,45600,45620,45628,45656,45660,45664,45672,45673,45684,45685,45692,45700,45701,45705,45712,45713,45716,45720,45721,45722,45728,45729,45731,45733,45734,45738,45740,45744,45748,45768,45769,45772,45776,45778,45784,45785,45787,45789,45794,45796,45797,45798,45800,45803,45804,45805,45806,45807,45811,45812,45813,45815,45816,45817,45818,45819,45823,45824,45825,45828,45832,45840,45841,45843,45844,45845,45852,45908,45909,45910,45912,45915,45916,45918,45919,45924,45925,53397,53398,53399,53400,53401,53402,53403,53404,53405,53406,53407,53408,53409,53410,53411,53414,53415,53417,53418,53419,53421,53422,53423,53424,53425,53426,null,null,null,null,null,null,53427,53430,53432,53434,53435,53436,53437,53438,53439,53442,53443,53445,53446,53447,53450,53451,53452,53453,53454,53455,53458,53462,53463,53464,53465,53466,null,null,null,null,null,null,53467,53470,53471,53473,53474,53475,53477,53478,53479,53480,53481,53482,53483,53486,53490,53491,53492,53493,53494,53495,53497,53498,53499,53500,53501,53502,53503,53504,53505,53506,53507,53508,45927,45929,45931,45934,45936,45937,45940,45944,45952,45953,45955,45956,45957,45964,45968,45972,45984,45985,45992,45996,46020,46021,46024,46027,46028,46030,46032,46036,46037,46039,46041,46043,46045,46048,46052,46056,46076,46096,46104,46108,46112,46120,46121,46123,46132,46160,46161,46164,46168,46176,46177,46179,46181,46188,46208,46216,46237,46244,46248,46252,46261,46263,46265,46272,46276,46280,46288,46293,46300,46301,46304,46307,46308,46310,46316,46317,46319,46321,46328,46356,46357,46360,46363,46364,46372,46373,46375,46376,46377,46378,46384,46385,46388,46392,53509,53510,53511,53512,53513,53514,53515,53516,53518,53519,53520,53521,53522,53523,53524,53525,53526,53527,53528,53529,53530,53531,53532,53533,53534,53535,null,null,null,null,null,null,53536,53537,53538,53539,53540,53541,53542,53543,53544,53545,53546,53547,53548,53549,53550,53551,53554,53555,53557,53558,53559,53561,53563,53564,53565,53566,null,null,null,null,null,null,53567,53570,53574,53575,53576,53577,53578,53579,53582,53583,53585,53586,53587,53589,53590,53591,53592,53593,53594,53595,53598,53600,53602,53603,53604,53605,53606,53607,53609,53610,53611,53613,46400,46401,46403,46404,46405,46411,46412,46413,46416,46420,46428,46429,46431,46432,46433,46496,46497,46500,46504,46506,46507,46512,46513,46515,46516,46517,46523,46524,46525,46528,46532,46540,46541,46543,46544,46545,46552,46572,46608,46609,46612,46616,46629,46636,46644,46664,46692,46696,46748,46749,46752,46756,46763,46764,46769,46804,46832,46836,46840,46848,46849,46853,46888,46889,46892,46895,46896,46904,46905,46907,46916,46920,46924,46932,46933,46944,46948,46952,46960,46961,46963,46965,46972,46973,46976,46980,46988,46989,46991,46992,46993,46994,46998,46999,53614,53615,53616,53617,53618,53619,53620,53621,53622,53623,53624,53625,53626,53627,53629,53630,53631,53632,53633,53634,53635,53637,53638,53639,53641,53642,null,null,null,null,null,null,53643,53644,53645,53646,53647,53648,53649,53650,53651,53652,53653,53654,53655,53656,53657,53658,53659,53660,53661,53662,53663,53666,53667,53669,53670,53671,null,null,null,null,null,null,53673,53674,53675,53676,53677,53678,53679,53682,53684,53686,53687,53688,53689,53691,53693,53694,53695,53697,53698,53699,53700,53701,53702,53703,53704,53705,53706,53707,53708,53709,53710,53711,47000,47001,47004,47008,47016,47017,47019,47020,47021,47028,47029,47032,47047,47049,47084,47085,47088,47092,47100,47101,47103,47104,47105,47111,47112,47113,47116,47120,47128,47129,47131,47133,47140,47141,47144,47148,47156,47157,47159,47160,47161,47168,47172,47185,47187,47196,47197,47200,47204,47212,47213,47215,47217,47224,47228,47245,47272,47280,47284,47288,47296,47297,47299,47301,47308,47312,47316,47325,47327,47329,47336,47337,47340,47344,47352,47353,47355,47357,47364,47384,47392,47420,47421,47424,47428,47436,47439,47441,47448,47449,47452,47456,47464,47465,53712,53713,53714,53715,53716,53717,53718,53719,53721,53722,53723,53724,53725,53726,53727,53728,53729,53730,53731,53732,53733,53734,53735,53736,53737,53738,null,null,null,null,null,null,53739,53740,53741,53742,53743,53744,53745,53746,53747,53749,53750,53751,53753,53754,53755,53756,53757,53758,53759,53760,53761,53762,53763,53764,53765,53766,null,null,null,null,null,null,53768,53770,53771,53772,53773,53774,53775,53777,53778,53779,53780,53781,53782,53783,53784,53785,53786,53787,53788,53789,53790,53791,53792,53793,53794,53795,53796,53797,53798,53799,53800,53801,47467,47469,47476,47477,47480,47484,47492,47493,47495,47497,47498,47501,47502,47532,47533,47536,47540,47548,47549,47551,47553,47560,47561,47564,47566,47567,47568,47569,47570,47576,47577,47579,47581,47582,47585,47587,47588,47589,47592,47596,47604,47605,47607,47608,47609,47610,47616,47617,47624,47637,47672,47673,47676,47680,47682,47688,47689,47691,47693,47694,47699,47700,47701,47704,47708,47716,47717,47719,47720,47721,47728,47729,47732,47736,47747,47748,47749,47751,47756,47784,47785,47787,47788,47792,47794,47800,47801,47803,47805,47812,47816,47832,47833,47868,53802,53803,53806,53807,53809,53810,53811,53813,53814,53815,53816,53817,53818,53819,53822,53824,53826,53827,53828,53829,53830,53831,53833,53834,53835,53836,null,null,null,null,null,null,53837,53838,53839,53840,53841,53842,53843,53844,53845,53846,53847,53848,53849,53850,53851,53853,53854,53855,53856,53857,53858,53859,53861,53862,53863,53864,null,null,null,null,null,null,53865,53866,53867,53868,53869,53870,53871,53872,53873,53874,53875,53876,53877,53878,53879,53880,53881,53882,53883,53884,53885,53886,53887,53890,53891,53893,53894,53895,53897,53898,53899,53900,47872,47876,47885,47887,47889,47896,47900,47904,47913,47915,47924,47925,47926,47928,47931,47932,47933,47934,47940,47941,47943,47945,47949,47951,47952,47956,47960,47969,47971,47980,48008,48012,48016,48036,48040,48044,48052,48055,48064,48068,48072,48080,48083,48120,48121,48124,48127,48128,48130,48136,48137,48139,48140,48141,48143,48145,48148,48149,48150,48151,48152,48155,48156,48157,48158,48159,48164,48165,48167,48169,48173,48176,48177,48180,48184,48192,48193,48195,48196,48197,48201,48204,48205,48208,48221,48260,48261,48264,48267,48268,48270,48276,48277,48279,53901,53902,53903,53906,53907,53908,53910,53911,53912,53913,53914,53915,53917,53918,53919,53921,53922,53923,53925,53926,53927,53928,53929,53930,53931,53933,null,null,null,null,null,null,53934,53935,53936,53938,53939,53940,53941,53942,53943,53946,53947,53949,53950,53953,53955,53956,53957,53958,53959,53962,53964,53965,53966,53967,53968,53969,null,null,null,null,null,null,53970,53971,53973,53974,53975,53977,53978,53979,53981,53982,53983,53984,53985,53986,53987,53990,53991,53992,53993,53994,53995,53996,53997,53998,53999,54002,54003,54005,54006,54007,54009,54010,48281,48282,48288,48289,48292,48295,48296,48304,48305,48307,48308,48309,48316,48317,48320,48324,48333,48335,48336,48337,48341,48344,48348,48372,48373,48374,48376,48380,48388,48389,48391,48393,48400,48404,48420,48428,48448,48456,48457,48460,48464,48472,48473,48484,48488,48512,48513,48516,48519,48520,48521,48522,48528,48529,48531,48533,48537,48538,48540,48548,48560,48568,48596,48597,48600,48604,48617,48624,48628,48632,48640,48643,48645,48652,48653,48656,48660,48668,48669,48671,48708,48709,48712,48716,48718,48724,48725,48727,48729,48730,48731,48736,48737,48740,54011,54012,54013,54014,54015,54018,54020,54022,54023,54024,54025,54026,54027,54031,54033,54034,54035,54037,54039,54040,54041,54042,54043,54046,54050,54051,null,null,null,null,null,null,54052,54054,54055,54058,54059,54061,54062,54063,54065,54066,54067,54068,54069,54070,54071,54074,54078,54079,54080,54081,54082,54083,54086,54087,54088,54089,null,null,null,null,null,null,54090,54091,54092,54093,54094,54095,54096,54097,54098,54099,54100,54101,54102,54103,54104,54105,54106,54107,54108,54109,54110,54111,54112,54113,54114,54115,54116,54117,54118,54119,54120,54121,48744,48746,48752,48753,48755,48756,48757,48763,48764,48765,48768,48772,48780,48781,48783,48784,48785,48792,48793,48808,48848,48849,48852,48855,48856,48864,48867,48868,48869,48876,48897,48904,48905,48920,48921,48923,48924,48925,48960,48961,48964,48968,48976,48977,48981,49044,49072,49093,49100,49101,49104,49108,49116,49119,49121,49212,49233,49240,49244,49248,49256,49257,49296,49297,49300,49304,49312,49313,49315,49317,49324,49325,49327,49328,49331,49332,49333,49334,49340,49341,49343,49344,49345,49349,49352,49353,49356,49360,49368,49369,49371,49372,49373,49380,54122,54123,54124,54125,54126,54127,54128,54129,54130,54131,54132,54133,54134,54135,54136,54137,54138,54139,54142,54143,54145,54146,54147,54149,54150,54151,null,null,null,null,null,null,54152,54153,54154,54155,54158,54162,54163,54164,54165,54166,54167,54170,54171,54173,54174,54175,54177,54178,54179,54180,54181,54182,54183,54186,54188,54190,null,null,null,null,null,null,54191,54192,54193,54194,54195,54197,54198,54199,54201,54202,54203,54205,54206,54207,54208,54209,54210,54211,54214,54215,54218,54219,54220,54221,54222,54223,54225,54226,54227,54228,54229,54230,49381,49384,49388,49396,49397,49399,49401,49408,49412,49416,49424,49429,49436,49437,49438,49439,49440,49443,49444,49446,49447,49452,49453,49455,49456,49457,49462,49464,49465,49468,49472,49480,49481,49483,49484,49485,49492,49493,49496,49500,49508,49509,49511,49512,49513,49520,49524,49528,49541,49548,49549,49550,49552,49556,49558,49564,49565,49567,49569,49573,49576,49577,49580,49584,49597,49604,49608,49612,49620,49623,49624,49632,49636,49640,49648,49649,49651,49660,49661,49664,49668,49676,49677,49679,49681,49688,49689,49692,49695,49696,49704,49705,49707,49709,54231,54233,54234,54235,54236,54237,54238,54239,54240,54242,54244,54245,54246,54247,54248,54249,54250,54251,54254,54255,54257,54258,54259,54261,54262,54263,null,null,null,null,null,null,54264,54265,54266,54267,54270,54272,54274,54275,54276,54277,54278,54279,54281,54282,54283,54284,54285,54286,54287,54288,54289,54290,54291,54292,54293,54294,null,null,null,null,null,null,54295,54296,54297,54298,54299,54300,54302,54303,54304,54305,54306,54307,54308,54309,54310,54311,54312,54313,54314,54315,54316,54317,54318,54319,54320,54321,54322,54323,54324,54325,54326,54327,49711,49713,49714,49716,49736,49744,49745,49748,49752,49760,49765,49772,49773,49776,49780,49788,49789,49791,49793,49800,49801,49808,49816,49819,49821,49828,49829,49832,49836,49837,49844,49845,49847,49849,49884,49885,49888,49891,49892,49899,49900,49901,49903,49905,49910,49912,49913,49915,49916,49920,49928,49929,49932,49933,49939,49940,49941,49944,49948,49956,49957,49960,49961,49989,50024,50025,50028,50032,50034,50040,50041,50044,50045,50052,50056,50060,50112,50136,50137,50140,50143,50144,50146,50152,50153,50157,50164,50165,50168,50184,50192,50212,50220,50224,54328,54329,54330,54331,54332,54333,54334,54335,54337,54338,54339,54341,54342,54343,54344,54345,54346,54347,54348,54349,54350,54351,54352,54353,54354,54355,null,null,null,null,null,null,54356,54357,54358,54359,54360,54361,54362,54363,54365,54366,54367,54369,54370,54371,54373,54374,54375,54376,54377,54378,54379,54380,54382,54384,54385,54386,null,null,null,null,null,null,54387,54388,54389,54390,54391,54394,54395,54397,54398,54401,54403,54404,54405,54406,54407,54410,54412,54414,54415,54416,54417,54418,54419,54421,54422,54423,54424,54425,54426,54427,54428,54429,50228,50236,50237,50248,50276,50277,50280,50284,50292,50293,50297,50304,50324,50332,50360,50364,50409,50416,50417,50420,50424,50426,50431,50432,50433,50444,50448,50452,50460,50472,50473,50476,50480,50488,50489,50491,50493,50500,50501,50504,50505,50506,50508,50509,50510,50515,50516,50517,50519,50520,50521,50525,50526,50528,50529,50532,50536,50544,50545,50547,50548,50549,50556,50557,50560,50564,50567,50572,50573,50575,50577,50581,50583,50584,50588,50592,50601,50612,50613,50616,50617,50619,50620,50621,50622,50628,50629,50630,50631,50632,50633,50634,50636,50638,54430,54431,54432,54433,54434,54435,54436,54437,54438,54439,54440,54442,54443,54444,54445,54446,54447,54448,54449,54450,54451,54452,54453,54454,54455,54456,null,null,null,null,null,null,54457,54458,54459,54460,54461,54462,54463,54464,54465,54466,54467,54468,54469,54470,54471,54472,54473,54474,54475,54477,54478,54479,54481,54482,54483,54485,null,null,null,null,null,null,54486,54487,54488,54489,54490,54491,54493,54494,54496,54497,54498,54499,54500,54501,54502,54503,54505,54506,54507,54509,54510,54511,54513,54514,54515,54516,54517,54518,54519,54521,54522,54524,50640,50641,50644,50648,50656,50657,50659,50661,50668,50669,50670,50672,50676,50678,50679,50684,50685,50686,50687,50688,50689,50693,50694,50695,50696,50700,50704,50712,50713,50715,50716,50724,50725,50728,50732,50733,50734,50736,50739,50740,50741,50743,50745,50747,50752,50753,50756,50760,50768,50769,50771,50772,50773,50780,50781,50784,50796,50799,50801,50808,50809,50812,50816,50824,50825,50827,50829,50836,50837,50840,50844,50852,50853,50855,50857,50864,50865,50868,50872,50873,50874,50880,50881,50883,50885,50892,50893,50896,50900,50908,50909,50912,50913,50920,54526,54527,54528,54529,54530,54531,54533,54534,54535,54537,54538,54539,54541,54542,54543,54544,54545,54546,54547,54550,54552,54553,54554,54555,54556,54557,null,null,null,null,null,null,54558,54559,54560,54561,54562,54563,54564,54565,54566,54567,54568,54569,54570,54571,54572,54573,54574,54575,54576,54577,54578,54579,54580,54581,54582,54583,null,null,null,null,null,null,54584,54585,54586,54587,54590,54591,54593,54594,54595,54597,54598,54599,54600,54601,54602,54603,54606,54608,54610,54611,54612,54613,54614,54615,54618,54619,54621,54622,54623,54625,54626,54627,50921,50924,50928,50936,50937,50941,50948,50949,50952,50956,50964,50965,50967,50969,50976,50977,50980,50984,50992,50993,50995,50997,50999,51004,51005,51008,51012,51018,51020,51021,51023,51025,51026,51027,51028,51029,51030,51031,51032,51036,51040,51048,51051,51060,51061,51064,51068,51069,51070,51075,51076,51077,51079,51080,51081,51082,51086,51088,51089,51092,51094,51095,51096,51098,51104,51105,51107,51108,51109,51110,51116,51117,51120,51124,51132,51133,51135,51136,51137,51144,51145,51148,51150,51152,51160,51165,51172,51176,51180,51200,51201,51204,51208,51210,54628,54630,54631,54634,54636,54638,54639,54640,54641,54642,54643,54646,54647,54649,54650,54651,54653,54654,54655,54656,54657,54658,54659,54662,54666,54667,null,null,null,null,null,null,54668,54669,54670,54671,54673,54674,54675,54676,54677,54678,54679,54680,54681,54682,54683,54684,54685,54686,54687,54688,54689,54690,54691,54692,54694,54695,null,null,null,null,null,null,54696,54697,54698,54699,54700,54701,54702,54703,54704,54705,54706,54707,54708,54709,54710,54711,54712,54713,54714,54715,54716,54717,54718,54719,54720,54721,54722,54723,54724,54725,54726,54727,51216,51217,51219,51221,51222,51228,51229,51232,51236,51244,51245,51247,51249,51256,51260,51264,51272,51273,51276,51277,51284,51312,51313,51316,51320,51322,51328,51329,51331,51333,51334,51335,51339,51340,51341,51348,51357,51359,51361,51368,51388,51389,51396,51400,51404,51412,51413,51415,51417,51424,51425,51428,51445,51452,51453,51456,51460,51461,51462,51468,51469,51471,51473,51480,51500,51508,51536,51537,51540,51544,51552,51553,51555,51564,51568,51572,51580,51592,51593,51596,51600,51608,51609,51611,51613,51648,51649,51652,51655,51656,51658,51664,51665,51667,54730,54731,54733,54734,54735,54737,54739,54740,54741,54742,54743,54746,54748,54750,54751,54752,54753,54754,54755,54758,54759,54761,54762,54763,54765,54766,null,null,null,null,null,null,54767,54768,54769,54770,54771,54774,54776,54778,54779,54780,54781,54782,54783,54786,54787,54789,54790,54791,54793,54794,54795,54796,54797,54798,54799,54802,null,null,null,null,null,null,54806,54807,54808,54809,54810,54811,54813,54814,54815,54817,54818,54819,54821,54822,54823,54824,54825,54826,54827,54828,54830,54831,54832,54833,54834,54835,54836,54837,54838,54839,54842,54843,51669,51670,51673,51674,51676,51677,51680,51682,51684,51687,51692,51693,51695,51696,51697,51704,51705,51708,51712,51720,51721,51723,51724,51725,51732,51736,51753,51788,51789,51792,51796,51804,51805,51807,51808,51809,51816,51837,51844,51864,51900,51901,51904,51908,51916,51917,51919,51921,51923,51928,51929,51936,51948,51956,51976,51984,51988,51992,52000,52001,52033,52040,52041,52044,52048,52056,52057,52061,52068,52088,52089,52124,52152,52180,52196,52199,52201,52236,52237,52240,52244,52252,52253,52257,52258,52263,52264,52265,52268,52270,52272,52280,52281,52283,54845,54846,54847,54849,54850,54851,54852,54854,54855,54858,54860,54862,54863,54864,54866,54867,54870,54871,54873,54874,54875,54877,54878,54879,54880,54881,null,null,null,null,null,null,54882,54883,54884,54885,54886,54888,54890,54891,54892,54893,54894,54895,54898,54899,54901,54902,54903,54904,54905,54906,54907,54908,54909,54910,54911,54912,null,null,null,null,null,null,54913,54914,54916,54918,54919,54920,54921,54922,54923,54926,54927,54929,54930,54931,54933,54934,54935,54936,54937,54938,54939,54940,54942,54944,54946,54947,54948,54949,54950,54951,54953,54954,52284,52285,52286,52292,52293,52296,52300,52308,52309,52311,52312,52313,52320,52324,52326,52328,52336,52341,52376,52377,52380,52384,52392,52393,52395,52396,52397,52404,52405,52408,52412,52420,52421,52423,52425,52432,52436,52452,52460,52464,52481,52488,52489,52492,52496,52504,52505,52507,52509,52516,52520,52524,52537,52572,52576,52580,52588,52589,52591,52593,52600,52616,52628,52629,52632,52636,52644,52645,52647,52649,52656,52676,52684,52688,52712,52716,52720,52728,52729,52731,52733,52740,52744,52748,52756,52761,52768,52769,52772,52776,52784,52785,52787,52789,54955,54957,54958,54959,54961,54962,54963,54964,54965,54966,54967,54968,54970,54972,54973,54974,54975,54976,54977,54978,54979,54982,54983,54985,54986,54987,null,null,null,null,null,null,54989,54990,54991,54992,54994,54995,54997,54998,55000,55002,55003,55004,55005,55006,55007,55009,55010,55011,55013,55014,55015,55017,55018,55019,55020,55021,null,null,null,null,null,null,55022,55023,55025,55026,55027,55028,55030,55031,55032,55033,55034,55035,55038,55039,55041,55042,55043,55045,55046,55047,55048,55049,55050,55051,55052,55053,55054,55055,55056,55058,55059,55060,52824,52825,52828,52831,52832,52833,52840,52841,52843,52845,52852,52853,52856,52860,52868,52869,52871,52873,52880,52881,52884,52888,52896,52897,52899,52900,52901,52908,52909,52929,52964,52965,52968,52971,52972,52980,52981,52983,52984,52985,52992,52993,52996,53000,53008,53009,53011,53013,53020,53024,53028,53036,53037,53039,53040,53041,53048,53076,53077,53080,53084,53092,53093,53095,53097,53104,53105,53108,53112,53120,53125,53132,53153,53160,53168,53188,53216,53217,53220,53224,53232,53233,53235,53237,53244,53248,53252,53265,53272,53293,53300,53301,53304,53308,55061,55062,55063,55066,55067,55069,55070,55071,55073,55074,55075,55076,55077,55078,55079,55082,55084,55086,55087,55088,55089,55090,55091,55094,55095,55097,null,null,null,null,null,null,55098,55099,55101,55102,55103,55104,55105,55106,55107,55109,55110,55112,55114,55115,55116,55117,55118,55119,55122,55123,55125,55130,55131,55132,55133,55134,null,null,null,null,null,null,55135,55138,55140,55142,55143,55144,55146,55147,55149,55150,55151,55153,55154,55155,55157,55158,55159,55160,55161,55162,55163,55166,55167,55168,55170,55171,55172,55173,55174,55175,55178,55179,53316,53317,53319,53321,53328,53332,53336,53344,53356,53357,53360,53364,53372,53373,53377,53412,53413,53416,53420,53428,53429,53431,53433,53440,53441,53444,53448,53449,53456,53457,53459,53460,53461,53468,53469,53472,53476,53484,53485,53487,53488,53489,53496,53517,53552,53553,53556,53560,53562,53568,53569,53571,53572,53573,53580,53581,53584,53588,53596,53597,53599,53601,53608,53612,53628,53636,53640,53664,53665,53668,53672,53680,53681,53683,53685,53690,53692,53696,53720,53748,53752,53767,53769,53776,53804,53805,53808,53812,53820,53821,53823,53825,53832,53852,55181,55182,55183,55185,55186,55187,55188,55189,55190,55191,55194,55196,55198,55199,55200,55201,55202,55203,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,53860,53888,53889,53892,53896,53904,53905,53909,53916,53920,53924,53932,53937,53944,53945,53948,53951,53952,53954,53960,53961,53963,53972,53976,53980,53988,53989,54000,54001,54004,54008,54016,54017,54019,54021,54028,54029,54030,54032,54036,54038,54044,54045,54047,54048,54049,54053,54056,54057,54060,54064,54072,54073,54075,54076,54077,54084,54085,54140,54141,54144,54148,54156,54157,54159,54160,54161,54168,54169,54172,54176,54184,54185,54187,54189,54196,54200,54204,54212,54213,54216,54217,54224,54232,54241,54243,54252,54253,54256,54260,54268,54269,54271,54273,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,54280,54301,54336,54340,54364,54368,54372,54381,54383,54392,54393,54396,54399,54400,54402,54408,54409,54411,54413,54420,54441,54476,54480,54484,54492,54495,54504,54508,54512,54520,54523,54525,54532,54536,54540,54548,54549,54551,54588,54589,54592,54596,54604,54605,54607,54609,54616,54617,54620,54624,54629,54632,54633,54635,54637,54644,54645,54648,54652,54660,54661,54663,54664,54665,54672,54693,54728,54729,54732,54736,54738,54744,54745,54747,54749,54756,54757,54760,54764,54772,54773,54775,54777,54784,54785,54788,54792,54800,54801,54803,54804,54805,54812,54816,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,54820,54829,54840,54841,54844,54848,54853,54856,54857,54859,54861,54865,54868,54869,54872,54876,54887,54889,54896,54897,54900,54915,54917,54924,54925,54928,54932,54941,54943,54945,54952,54956,54960,54969,54971,54980,54981,54984,54988,54993,54996,54999,55001,55008,55012,55016,55024,55029,55036,55037,55040,55044,55057,55064,55065,55068,55072,55080,55081,55083,55085,55092,55093,55096,55100,55108,55111,55113,55120,55121,55124,55126,55127,55128,55129,55136,55137,55139,55141,55145,55148,55152,55156,55164,55165,55169,55176,55177,55180,55184,55192,55193,55195,55197,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20285,20339,20551,20729,21152,21487,21621,21733,22025,23233,23478,26247,26550,26551,26607,27468,29634,30146,31292,33499,33540,34903,34952,35382,36040,36303,36603,36838,39381,21051,21364,21508,24682,24932,27580,29647,33050,35258,35282,38307,20355,21002,22718,22904,23014,24178,24185,25031,25536,26438,26604,26751,28567,30286,30475,30965,31240,31487,31777,32925,33390,33393,35563,38291,20075,21917,26359,28212,30883,31469,33883,35088,34638,38824,21208,22350,22570,23884,24863,25022,25121,25954,26577,27204,28187,29976,30131,30435,30640,32058,37039,37969,37970,40853,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21283,23724,30002,32987,37440,38296,21083,22536,23004,23713,23831,24247,24378,24394,24951,27743,30074,30086,31968,32115,32177,32652,33108,33313,34193,35137,35611,37628,38477,40007,20171,20215,20491,20977,22607,24887,24894,24936,25913,27114,28433,30117,30342,30422,31623,33445,33995,63744,37799,38283,21888,23458,22353,63745,31923,32697,37301,20520,21435,23621,24040,25298,25454,25818,25831,28192,28844,31067,36317,36382,63746,36989,37445,37624,20094,20214,20581,24062,24314,24838,26967,33137,34388,36423,37749,39467,20062,20625,26480,26688,20745,21133,21138,27298,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,30652,37392,40660,21163,24623,36850,20552,25001,25581,25802,26684,27268,28608,33160,35233,38548,22533,29309,29356,29956,32121,32365,32937,35211,35700,36963,40273,25225,27770,28500,32080,32570,35363,20860,24906,31645,35609,37463,37772,20140,20435,20510,20670,20742,21185,21197,21375,22384,22659,24218,24465,24950,25004,25806,25964,26223,26299,26356,26775,28039,28805,28913,29855,29861,29898,30169,30828,30956,31455,31478,32069,32147,32789,32831,33051,33686,35686,36629,36885,37857,38915,38968,39514,39912,20418,21843,22586,22865,23395,23622,24760,25106,26690,26800,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26856,28330,30028,30328,30926,31293,31995,32363,32380,35336,35489,35903,38542,40388,21476,21481,21578,21617,22266,22993,23396,23611,24235,25335,25911,25925,25970,26272,26543,27073,27837,30204,30352,30590,31295,32660,32771,32929,33167,33510,33533,33776,34241,34865,34996,35493,63747,36764,37678,38599,39015,39640,40723,21741,26011,26354,26767,31296,35895,40288,22256,22372,23825,26118,26801,26829,28414,29736,34974,39908,27752,63748,39592,20379,20844,20849,21151,23380,24037,24656,24685,25329,25511,25915,29657,31354,34467,36002,38799,20018,23521,25096,26524,29916,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31185,33747,35463,35506,36328,36942,37707,38982,24275,27112,34303,37101,63749,20896,23448,23532,24931,26874,27454,28748,29743,29912,31649,32592,33733,35264,36011,38364,39208,21038,24669,25324,36866,20362,20809,21281,22745,24291,26336,27960,28826,29378,29654,31568,33009,37979,21350,25499,32619,20054,20608,22602,22750,24618,24871,25296,27088,39745,23439,32024,32945,36703,20132,20689,21676,21932,23308,23968,24039,25898,25934,26657,27211,29409,30350,30703,32094,32761,33184,34126,34527,36611,36686,37066,39171,39509,39851,19992,20037,20061,20167,20465,20855,21246,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21312,21475,21477,21646,22036,22389,22434,23495,23943,24272,25084,25304,25937,26552,26601,27083,27472,27590,27628,27714,28317,28792,29399,29590,29699,30655,30697,31350,32127,32777,33276,33285,33290,33503,34914,35635,36092,36544,36881,37041,37476,37558,39378,39493,40169,40407,40860,22283,23616,33738,38816,38827,40628,21531,31384,32676,35033,36557,37089,22528,23624,25496,31391,23470,24339,31353,31406,33422,36524,20518,21048,21240,21367,22280,25331,25458,27402,28099,30519,21413,29527,34152,36470,38357,26426,27331,28528,35437,36556,39243,63750,26231,27512,36020,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,39740,63751,21483,22317,22862,25542,27131,29674,30789,31418,31429,31998,33909,35215,36211,36917,38312,21243,22343,30023,31584,33740,37406,63752,27224,20811,21067,21127,25119,26840,26997,38553,20677,21156,21220,25027,26020,26681,27135,29822,31563,33465,33771,35250,35641,36817,39241,63753,20170,22935,25810,26129,27278,29748,31105,31165,33449,34942,34943,35167,63754,37670,20235,21450,24613,25201,27762,32026,32102,20120,20834,30684,32943,20225,20238,20854,20864,21980,22120,22331,22522,22524,22804,22855,22931,23492,23696,23822,24049,24190,24524,25216,26071,26083,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26398,26399,26462,26827,26820,27231,27450,27683,27773,27778,28103,29592,29734,29738,29826,29859,30072,30079,30849,30959,31041,31047,31048,31098,31637,32000,32186,32648,32774,32813,32908,35352,35663,35912,36215,37665,37668,39138,39249,39438,39439,39525,40594,32202,20342,21513,25326,26708,37329,21931,20794,63755,63756,23068,25062,63757,25295,25343,63758,63759,63760,63761,63762,63763,37027,63764,63765,63766,63767,63768,35582,63769,63770,63771,63772,26262,63773,29014,63774,63775,38627,63776,25423,25466,21335,63777,26511,26976,28275,63778,30007,63779,63780,63781,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32013,63782,63783,34930,22218,23064,63784,63785,63786,63787,63788,20035,63789,20839,22856,26608,32784,63790,22899,24180,25754,31178,24565,24684,25288,25467,23527,23511,21162,63791,22900,24361,24594,63792,63793,63794,29785,63795,63796,63797,63798,63799,63800,39377,63801,63802,63803,63804,63805,63806,63807,63808,63809,63810,63811,28611,63812,63813,33215,36786,24817,63814,63815,33126,63816,63817,23615,63818,63819,63820,63821,63822,63823,63824,63825,23273,35365,26491,32016,63826,63827,63828,63829,63830,63831,33021,63832,63833,23612,27877,21311,28346,22810,33590,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20025,20150,20294,21934,22296,22727,24406,26039,26086,27264,27573,28237,30701,31471,31774,32222,34507,34962,37170,37723,25787,28606,29562,30136,36948,21846,22349,25018,25812,26311,28129,28251,28525,28601,30192,32835,33213,34113,35203,35527,35674,37663,27795,30035,31572,36367,36957,21776,22530,22616,24162,25095,25758,26848,30070,31958,34739,40680,20195,22408,22382,22823,23565,23729,24118,24453,25140,25825,29619,33274,34955,36024,38538,40667,23429,24503,24755,20498,20992,21040,22294,22581,22615,23566,23648,23798,23947,24230,24466,24764,25361,25481,25623,26691,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26873,27330,28120,28193,28372,28644,29182,30428,30585,31153,31291,33796,35241,36077,36339,36424,36867,36884,36947,37117,37709,38518,38876,27602,28678,29272,29346,29544,30563,31167,31716,32411,35712,22697,24775,25958,26109,26302,27788,28958,29129,35930,38931,20077,31361,20189,20908,20941,21205,21516,24999,26481,26704,26847,27934,28540,30140,30643,31461,33012,33891,37509,20828,26007,26460,26515,30168,31431,33651,63834,35910,36887,38957,23663,33216,33434,36929,36975,37389,24471,23965,27225,29128,30331,31561,34276,35588,37159,39472,21895,25078,63835,30313,32645,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,34367,34746,35064,37007,63836,27931,28889,29662,32097,33853,63837,37226,39409,63838,20098,21365,27396,27410,28734,29211,34349,40478,21068,36771,23888,25829,25900,27414,28651,31811,32412,34253,35172,35261,25289,33240,34847,24266,26391,28010,29436,29701,29807,34690,37086,20358,23821,24480,33802,20919,25504,30053,20142,20486,20841,20937,26753,27153,31918,31921,31975,33391,35538,36635,37327,20406,20791,21237,21570,24300,24942,25150,26053,27354,28670,31018,34268,34851,38317,39522,39530,40599,40654,21147,26310,27511,28701,31019,36706,38722,24976,25088,25891,28451,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,29001,29833,32244,32879,34030,36646,36899,37706,20925,21015,21155,27916,28872,35010,24265,25986,27566,28610,31806,29557,20196,20278,22265,63839,23738,23994,24604,29618,31533,32666,32718,32838,36894,37428,38646,38728,38936,40801,20363,28583,31150,37300,38583,21214,63840,25736,25796,27347,28510,28696,29200,30439,32769,34310,34396,36335,36613,38706,39791,40442,40565,30860,31103,32160,33737,37636,40575,40595,35542,22751,24324,26407,28711,29903,31840,32894,20769,28712,29282,30922,36034,36058,36084,38647,20102,20698,23534,24278,26009,29134,30274,30637,32842,34044,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36988,39719,40845,22744,23105,23650,27155,28122,28431,30267,32047,32311,34078,35128,37860,38475,21129,26066,26611,27060,27969,28316,28687,29705,29792,30041,30244,30827,35628,39006,20845,25134,38520,20374,20523,23833,28138,32184,36650,24459,24900,26647,63841,38534,21202,32907,20956,20940,26974,31260,32190,33777,38517,20442,21033,21400,21519,21774,23653,24743,26446,26792,28012,29313,29432,29702,29827,63842,30178,31852,32633,32696,33673,35023,35041,37324,37328,38626,39881,21533,28542,29136,29848,34298,36522,38563,40023,40607,26519,28107,29747,33256,38678,30764,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31435,31520,31890,25705,29802,30194,30908,30952,39340,39764,40635,23518,24149,28448,33180,33707,37000,19975,21325,23081,24018,24398,24930,25405,26217,26364,28415,28459,28771,30622,33836,34067,34875,36627,39237,39995,21788,25273,26411,27819,33545,35178,38778,20129,22916,24536,24537,26395,32178,32596,33426,33579,33725,36638,37017,22475,22969,23186,23504,26151,26522,26757,27599,29028,32629,36023,36067,36993,39749,33032,35978,38476,39488,40613,23391,27667,29467,30450,30431,33804,20906,35219,20813,20885,21193,26825,27796,30468,30496,32191,32236,38754,40629,28357,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,34065,20901,21517,21629,26126,26269,26919,28319,30399,30609,33559,33986,34719,37225,37528,40180,34946,20398,20882,21215,22982,24125,24917,25720,25721,26286,26576,27169,27597,27611,29279,29281,29761,30520,30683,32791,33468,33541,35584,35624,35980,26408,27792,29287,30446,30566,31302,40361,27519,27794,22818,26406,33945,21359,22675,22937,24287,25551,26164,26483,28218,29483,31447,33495,37672,21209,24043,25006,25035,25098,25287,25771,26080,26969,27494,27595,28961,29687,30045,32326,33310,33538,34154,35491,36031,38695,40289,22696,40664,20497,21006,21563,21839,25991,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,27766,32010,32011,32862,34442,38272,38639,21247,27797,29289,21619,23194,23614,23883,24396,24494,26410,26806,26979,28220,28228,30473,31859,32654,34183,35598,36855,38753,40692,23735,24758,24845,25003,25935,26107,26108,27665,27887,29599,29641,32225,38292,23494,34588,35600,21085,21338,25293,25615,25778,26420,27192,27850,29632,29854,31636,31893,32283,33162,33334,34180,36843,38649,39361,20276,21322,21453,21467,25292,25644,25856,26001,27075,27886,28504,29677,30036,30242,30436,30460,30928,30971,31020,32070,33324,34784,36820,38930,39151,21187,25300,25765,28196,28497,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,30332,36299,37297,37474,39662,39747,20515,20621,22346,22952,23592,24135,24439,25151,25918,26041,26049,26121,26507,27036,28354,30917,32033,32938,33152,33323,33459,33953,34444,35370,35607,37030,38450,40848,20493,20467,63843,22521,24472,25308,25490,26479,28227,28953,30403,32972,32986,35060,35061,35097,36064,36649,37197,38506,20271,20336,24091,26575,26658,30333,30334,39748,24161,27146,29033,29140,30058,63844,32321,34115,34281,39132,20240,31567,32624,38309,20961,24070,26805,27710,27726,27867,29359,31684,33539,27861,29754,20731,21128,22721,25816,27287,29863,30294,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,30887,34327,38370,38713,63845,21342,24321,35722,36776,36783,37002,21029,30629,40009,40712,19993,20482,20853,23643,24183,26142,26170,26564,26821,28851,29953,30149,31177,31453,36647,39200,39432,20445,22561,22577,23542,26222,27493,27921,28282,28541,29668,29995,33769,35036,35091,35676,36628,20239,20693,21264,21340,23443,24489,26381,31119,33145,33583,34068,35079,35206,36665,36667,39333,39954,26412,20086,20472,22857,23553,23791,23792,25447,26834,28925,29090,29739,32299,34028,34562,36898,37586,40179,19981,20184,20463,20613,21078,21103,21542,21648,22496,22827,23142,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,23386,23413,23500,24220,63846,25206,25975,26023,28014,28325,29238,31526,31807,32566,33104,33105,33178,33344,33433,33705,35331,36000,36070,36091,36212,36282,37096,37340,38428,38468,39385,40167,21271,20998,21545,22132,22707,22868,22894,24575,24996,25198,26128,27774,28954,30406,31881,31966,32027,33452,36033,38640,63847,20315,24343,24447,25282,23849,26379,26842,30844,32323,40300,19989,20633,21269,21290,21329,22915,23138,24199,24754,24970,25161,25209,26000,26503,27047,27604,27606,27607,27608,27832,63848,29749,30202,30738,30865,31189,31192,31875,32203,32737,32933,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,33086,33218,33778,34586,35048,35513,35692,36027,37145,38750,39131,40763,22188,23338,24428,25996,27315,27567,27996,28657,28693,29277,29613,36007,36051,38971,24977,27703,32856,39425,20045,20107,20123,20181,20282,20284,20351,20447,20735,21490,21496,21766,21987,22235,22763,22882,23057,23531,23546,23556,24051,24107,24473,24605,25448,26012,26031,26614,26619,26797,27515,27801,27863,28195,28681,29509,30722,31038,31040,31072,31169,31721,32023,32114,32902,33293,33678,34001,34503,35039,35408,35422,35613,36060,36198,36781,37034,39164,39391,40605,21066,63849,26388,63850,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20632,21034,23665,25955,27733,29642,29987,30109,31639,33948,37240,38704,20087,25746,27578,29022,34217,19977,63851,26441,26862,28183,33439,34072,34923,25591,28545,37394,39087,19978,20663,20687,20767,21830,21930,22039,23360,23577,23776,24120,24202,24224,24258,24819,26705,27233,28248,29245,29248,29376,30456,31077,31665,32724,35059,35316,35443,35937,36062,38684,22622,29885,36093,21959,63852,31329,32034,33394,29298,29983,29989,63853,31513,22661,22779,23996,24207,24246,24464,24661,25234,25471,25933,26257,26329,26360,26646,26866,29312,29790,31598,32110,32214,32626,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32997,33298,34223,35199,35475,36893,37604,40653,40736,22805,22893,24109,24796,26132,26227,26512,27728,28101,28511,30707,30889,33990,37323,37675,20185,20682,20808,21892,23307,23459,25159,25982,26059,28210,29053,29697,29764,29831,29887,30316,31146,32218,32341,32680,33146,33203,33337,34330,34796,35445,36323,36984,37521,37925,39245,39854,21352,23633,26964,27844,27945,28203,33292,34203,35131,35373,35498,38634,40807,21089,26297,27570,32406,34814,36109,38275,38493,25885,28041,29166,63854,22478,22995,23468,24615,24826,25104,26143,26207,29481,29689,30427,30465,31596,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32854,32882,33125,35488,37266,19990,21218,27506,27927,31237,31545,32048,63855,36016,21484,22063,22609,23477,23567,23569,24034,25152,25475,25620,26157,26803,27836,28040,28335,28703,28836,29138,29990,30095,30094,30233,31505,31712,31787,32032,32057,34092,34157,34311,35380,36877,36961,37045,37559,38902,39479,20439,23660,26463,28049,31903,32396,35606,36118,36895,23403,24061,25613,33984,36956,39137,29575,23435,24730,26494,28126,35359,35494,36865,38924,21047,63856,28753,30862,37782,34928,37335,20462,21463,22013,22234,22402,22781,23234,23432,23723,23744,24101,24833,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,25101,25163,25480,25628,25910,25976,27193,27530,27700,27929,28465,29159,29417,29560,29703,29874,30246,30561,31168,31319,31466,31929,32143,32172,32353,32670,33065,33585,33936,34010,34282,34966,35504,35728,36664,36930,36995,37228,37526,37561,38539,38567,38568,38614,38656,38920,39318,39635,39706,21460,22654,22809,23408,23487,28113,28506,29087,29729,29881,32901,33789,24033,24455,24490,24642,26092,26642,26991,27219,27529,27957,28147,29667,30462,30636,31565,32020,33059,33308,33600,34036,34147,35426,35524,37255,37662,38918,39348,25100,34899,36848,37477,23815,23847,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,23913,29791,33181,34664,28629,25342,32722,35126,35186,19998,20056,20711,21213,21319,25215,26119,32361,34821,38494,20365,21273,22070,22987,23204,23608,23630,23629,24066,24337,24643,26045,26159,26178,26558,26612,29468,30690,31034,32709,33940,33997,35222,35430,35433,35553,35925,35962,22516,23508,24335,24687,25325,26893,27542,28252,29060,31698,34645,35672,36606,39135,39166,20280,20353,20449,21627,23072,23480,24892,26032,26216,29180,30003,31070,32051,33102,33251,33688,34218,34254,34563,35338,36523,36763,63857,36805,22833,23460,23526,24713,23529,23563,24515,27777,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63858,28145,28683,29978,33455,35574,20160,21313,63859,38617,27663,20126,20420,20818,21854,23077,23784,25105,29273,33469,33706,34558,34905,35357,38463,38597,39187,40201,40285,22538,23731,23997,24132,24801,24853,25569,27138,28197,37122,37716,38990,39952,40823,23433,23736,25353,26191,26696,30524,38593,38797,38996,39839,26017,35585,36555,38332,21813,23721,24022,24245,26263,30284,33780,38343,22739,25276,29390,40232,20208,22830,24591,26171,27523,31207,40230,21395,21696,22467,23830,24859,26326,28079,30861,33406,38552,38724,21380,25212,25494,28082,32266,33099,38989,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,27387,32588,40367,40474,20063,20539,20918,22812,24825,25590,26928,29242,32822,63860,37326,24369,63861,63862,32004,33509,33903,33979,34277,36493,63863,20335,63864,63865,22756,23363,24665,25562,25880,25965,26264,63866,26954,27171,27915,28673,29036,30162,30221,31155,31344,63867,32650,63868,35140,63869,35731,37312,38525,63870,39178,22276,24481,26044,28417,30208,31142,35486,39341,39770,40812,20740,25014,25233,27277,33222,20547,22576,24422,28937,35328,35578,23420,34326,20474,20796,22196,22852,25513,28153,23978,26989,20870,20104,20313,63871,63872,63873,22914,63874,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63875,27487,27741,63876,29877,30998,63877,33287,33349,33593,36671,36701,63878,39192,63879,63880,63881,20134,63882,22495,24441,26131,63883,63884,30123,32377,35695,63885,36870,39515,22181,22567,23032,23071,23476,63886,24310,63887,63888,25424,25403,63889,26941,27783,27839,28046,28051,28149,28436,63890,28895,28982,29017,63891,29123,29141,63892,30799,30831,63893,31605,32227,63894,32303,63895,34893,36575,63896,63897,63898,37467,63899,40182,63900,63901,63902,24709,28037,63903,29105,63904,63905,38321,21421,63906,63907,63908,26579,63909,28814,28976,29744,33398,33490,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63910,38331,39653,40573,26308,63911,29121,33865,63912,63913,22603,63914,63915,23992,24433,63916,26144,26254,27001,27054,27704,27891,28214,28481,28634,28699,28719,29008,29151,29552,63917,29787,63918,29908,30408,31310,32403,63919,63920,33521,35424,36814,63921,37704,63922,38681,63923,63924,20034,20522,63925,21000,21473,26355,27757,28618,29450,30591,31330,33454,34269,34306,63926,35028,35427,35709,35947,63927,37555,63928,38675,38928,20116,20237,20425,20658,21320,21566,21555,21978,22626,22714,22887,23067,23524,24735,63929,25034,25942,26111,26212,26791,27738,28595,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,28879,29100,29522,31613,34568,35492,39986,40711,23627,27779,29508,29577,37434,28331,29797,30239,31337,32277,34314,20800,22725,25793,29934,29973,30320,32705,37013,38605,39252,28198,29926,31401,31402,33253,34521,34680,35355,23113,23436,23451,26785,26880,28003,29609,29715,29740,30871,32233,32747,33048,33109,33694,35916,38446,38929,26352,24448,26106,26505,27754,29579,20525,23043,27498,30702,22806,23916,24013,29477,30031,63930,63931,20709,20985,22575,22829,22934,23002,23525,63932,63933,23970,25303,25622,25747,25854,63934,26332,63935,27208,63936,29183,29796,63937,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31368,31407,32327,32350,32768,33136,63938,34799,35201,35616,36953,63939,36992,39250,24958,27442,28020,32287,35109,36785,20433,20653,20887,21191,22471,22665,23481,24248,24898,27029,28044,28263,28342,29076,29794,29992,29996,32883,33592,33993,36362,37780,37854,63940,20110,20305,20598,20778,21448,21451,21491,23431,23507,23588,24858,24962,26100,29275,29591,29760,30402,31056,31121,31161,32006,32701,33419,34261,34398,36802,36935,37109,37354,38533,38632,38633,21206,24423,26093,26161,26671,29020,31286,37057,38922,20113,63941,27218,27550,28560,29065,32792,33464,34131,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36939,38549,38642,38907,34074,39729,20112,29066,38596,20803,21407,21729,22291,22290,22435,23195,23236,23491,24616,24895,25588,27781,27961,28274,28304,29232,29503,29783,33489,34945,36677,36960,63942,38498,39000,40219,26376,36234,37470,20301,20553,20702,21361,22285,22996,23041,23561,24944,26256,28205,29234,29771,32239,32963,33806,33894,34111,34655,34907,35096,35586,36949,38859,39759,20083,20369,20754,20842,63943,21807,21929,23418,23461,24188,24189,24254,24736,24799,24840,24841,25540,25912,26377,63944,26580,26586,63945,26977,26978,27833,27943,63946,28216,63947,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,28641,29494,29495,63948,29788,30001,63949,30290,63950,63951,32173,33278,33848,35029,35480,35547,35565,36400,36418,36938,36926,36986,37193,37321,37742,63952,63953,22537,63954,27603,32905,32946,63955,63956,20801,22891,23609,63957,63958,28516,29607,32996,36103,63959,37399,38287,63960,63961,63962,63963,32895,25102,28700,32104,34701,63964,22432,24681,24903,27575,35518,37504,38577,20057,21535,28139,34093,38512,38899,39150,25558,27875,37009,20957,25033,33210,40441,20381,20506,20736,23452,24847,25087,25836,26885,27589,30097,30691,32681,33380,34191,34811,34915,35516,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,35696,37291,20108,20197,20234,63965,63966,22839,23016,63967,24050,24347,24411,24609,63968,63969,63970,63971,29246,29669,63972,30064,30157,63973,31227,63974,32780,32819,32900,33505,33617,63975,63976,36029,36019,36999,63977,63978,39156,39180,63979,63980,28727,30410,32714,32716,32764,35610,20154,20161,20995,21360,63981,21693,22240,23035,23493,24341,24525,28270,63982,63983,32106,33589,63984,34451,35469,63985,38765,38775,63986,63987,19968,20314,20350,22777,26085,28322,36920,37808,39353,20219,22764,22922,23001,24641,63988,63989,31252,63990,33615,36035,20837,21316,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63991,63992,63993,20173,21097,23381,33471,20180,21050,21672,22985,23039,23376,23383,23388,24675,24904,28363,28825,29038,29574,29943,30133,30913,32043,32773,33258,33576,34071,34249,35566,36039,38604,20316,21242,22204,26027,26152,28796,28856,29237,32189,33421,37196,38592,40306,23409,26855,27544,28538,30430,23697,26283,28507,31668,31786,34870,38620,19976,20183,21280,22580,22715,22767,22892,23559,24115,24196,24373,25484,26290,26454,27167,27299,27404,28479,29254,63994,29520,29835,31456,31911,33144,33247,33255,33674,33900,34083,34196,34255,35037,36115,37292,38263,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38556,20877,21705,22312,23472,25165,26448,26685,26771,28221,28371,28797,32289,35009,36001,36617,40779,40782,29229,31631,35533,37658,20295,20302,20786,21632,22992,24213,25269,26485,26990,27159,27822,28186,29401,29482,30141,31672,32053,33511,33785,33879,34295,35419,36015,36487,36889,37048,38606,40799,21219,21514,23265,23490,25688,25973,28404,29380,63995,30340,31309,31515,31821,32318,32735,33659,35627,36042,36196,36321,36447,36842,36857,36969,37841,20291,20346,20659,20840,20856,21069,21098,22625,22652,22880,23560,23637,24283,24731,25136,26643,27583,27656,28593,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,29006,29728,30000,30008,30033,30322,31564,31627,31661,31686,32399,35438,36670,36681,37439,37523,37666,37931,38651,39002,39019,39198,20999,25130,25240,27993,30308,31434,31680,32118,21344,23742,24215,28472,28857,31896,38673,39822,40670,25509,25722,34678,19969,20117,20141,20572,20597,21576,22979,23450,24128,24237,24311,24449,24773,25402,25919,25972,26060,26230,26232,26622,26984,27273,27491,27712,28096,28136,28191,28254,28702,28833,29582,29693,30010,30555,30855,31118,31243,31357,31934,32142,33351,35330,35562,35998,37165,37194,37336,37478,37580,37664,38662,38742,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38748,38914,40718,21046,21137,21884,22564,24093,24351,24716,25552,26799,28639,31085,31532,33229,34234,35069,35576,36420,37261,38500,38555,38717,38988,40778,20430,20806,20939,21161,22066,24340,24427,25514,25805,26089,26177,26362,26361,26397,26781,26839,27133,28437,28526,29031,29157,29226,29866,30522,31062,31066,31199,31264,31381,31895,31967,32068,32368,32903,34299,34468,35412,35519,36249,36481,36896,36973,37347,38459,38613,40165,26063,31751,36275,37827,23384,23562,21330,25305,29469,20519,23447,24478,24752,24939,26837,28121,29742,31278,32066,32156,32305,33131,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36394,36405,37758,37912,20304,22352,24038,24231,25387,32618,20027,20303,20367,20570,23005,32964,21610,21608,22014,22863,23449,24030,24282,26205,26417,26609,26666,27880,27954,28234,28557,28855,29664,30087,31820,32002,32044,32162,33311,34523,35387,35461,36208,36490,36659,36913,37198,37202,37956,39376,31481,31909,20426,20737,20934,22472,23535,23803,26201,27197,27994,28310,28652,28940,30063,31459,34850,36897,36981,38603,39423,33537,20013,20210,34886,37325,21373,27355,26987,27713,33914,22686,24974,26366,25327,28893,29969,30151,32338,33976,35657,36104,20043,21482,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21675,22320,22336,24535,25345,25351,25711,25903,26088,26234,26525,26547,27490,27744,27802,28460,30693,30757,31049,31063,32025,32930,33026,33267,33437,33463,34584,35468,63996,36100,36286,36978,30452,31257,31287,32340,32887,21767,21972,22645,25391,25634,26185,26187,26733,27035,27524,27941,28337,29645,29800,29857,30043,30137,30433,30494,30603,31206,32265,32285,33275,34095,34967,35386,36049,36587,36784,36914,37805,38499,38515,38663,20356,21489,23018,23241,24089,26702,29894,30142,31209,31378,33187,34541,36074,36300,36845,26015,26389,63997,22519,28503,32221,36655,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,37878,38598,24501,25074,28548,19988,20376,20511,21449,21983,23919,24046,27425,27492,30923,31642,63998,36425,36554,36974,25417,25662,30528,31364,37679,38015,40810,25776,28591,29158,29864,29914,31428,31762,32386,31922,32408,35738,36106,38013,39184,39244,21049,23519,25830,26413,32046,20717,21443,22649,24920,24921,25082,26028,31449,35730,35734,20489,20513,21109,21809,23100,24288,24432,24884,25950,26124,26166,26274,27085,28356,28466,29462,30241,31379,33081,33369,33750,33980,20661,22512,23488,23528,24425,25505,30758,32181,33756,34081,37319,37365,20874,26613,31574,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36012,20932,22971,24765,34389,20508,63999,21076,23610,24957,25114,25299,25842,26021,28364,30240,33034,36448,38495,38587,20191,21315,21912,22825,24029,25797,27849,28154,29588,31359,33307,34214,36068,36368,36983,37351,38369,38433,38854,20984,21746,21894,24505,25764,28552,32180,36639,36685,37941,20681,23574,27838,28155,29979,30651,31805,31844,35449,35522,22558,22974,24086,25463,29266,30090,30571,35548,36028,36626,24307,26228,28152,32893,33729,35531,38737,39894,64000,21059,26367,28053,28399,32224,35558,36910,36958,39636,21021,21119,21736,24980,25220,25307,26786,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26898,26970,27189,28818,28966,30813,30977,30990,31186,31245,32918,33400,33493,33609,34121,35970,36229,37218,37259,37294,20419,22225,29165,30679,34560,35320,23544,24534,26449,37032,21474,22618,23541,24740,24961,25696,32317,32880,34085,37507,25774,20652,23828,26368,22684,25277,25512,26894,27000,27166,28267,30394,31179,33467,33833,35535,36264,36861,37138,37195,37276,37648,37656,37786,38619,39478,39949,19985,30044,31069,31482,31569,31689,32302,33988,36441,36468,36600,36880,26149,26943,29763,20986,26414,40668,20805,24544,27798,34802,34909,34935,24756,33205,33795,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36101,21462,21561,22068,23094,23601,28810,32736,32858,33030,33261,36259,37257,39519,40434,20596,20164,21408,24827,28204,23652,20360,20516,21988,23769,24159,24677,26772,27835,28100,29118,30164,30196,30305,31258,31305,32199,32251,32622,33268,34473,36636,38601,39347,40786,21063,21189,39149,35242,19971,26578,28422,20405,23522,26517,27784,28024,29723,30759,37341,37756,34756,31204,31281,24555,20182,21668,21822,22702,22949,24816,25171,25302,26422,26965,33333,38464,39345,39389,20524,21331,21828,22396,64001,25176,64002,25826,26219,26589,28609,28655,29730,29752,35351,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,37944,21585,22022,22374,24392,24986,27470,28760,28845,32187,35477,22890,33067,25506,30472,32829,36010,22612,25645,27067,23445,24081,28271,64003,34153,20812,21488,22826,24608,24907,27526,27760,27888,31518,32974,33492,36294,37040,39089,64004,25799,28580,25745,25860,20814,21520,22303,35342,24927,26742,64005,30171,31570,32113,36890,22534,27084,33151,35114,36864,38969,20600,22871,22956,25237,36879,39722,24925,29305,38358,22369,23110,24052,25226,25773,25850,26487,27874,27966,29228,29750,30772,32631,33453,36315,38935,21028,22338,26495,29256,29923,36009,36774,37393,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38442,20843,21485,25420,20329,21764,24726,25943,27803,28031,29260,29437,31255,35207,35997,24429,28558,28921,33192,24846,20415,20559,25153,29255,31687,32232,32745,36941,38829,39449,36022,22378,24179,26544,33805,35413,21536,23318,24163,24290,24330,25987,32954,34109,38281,38491,20296,21253,21261,21263,21638,21754,22275,24067,24598,25243,25265,25429,64006,27873,28006,30129,30770,32990,33071,33502,33889,33970,34957,35090,36875,37610,39165,39825,24133,26292,26333,28689,29190,64007,20469,21117,24426,24915,26451,27161,28418,29922,31080,34920,35961,39111,39108,39491,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21697,31263,26963,35575,35914,39080,39342,24444,25259,30130,30382,34987,36991,38466,21305,24380,24517,27852,29644,30050,30091,31558,33534,39325,20047,36924,19979,20309,21414,22799,24264,26160,27827,29781,33655,34662,36032,36944,38686,39957,22737,23416,34384,35604,40372,23506,24680,24717,26097,27735,28450,28579,28698,32597,32752,38289,38290,38480,38867,21106,36676,20989,21547,21688,21859,21898,27323,28085,32216,33382,37532,38519,40569,21512,21704,30418,34532,38308,38356,38492,20130,20233,23022,23270,24055,24658,25239,26477,26689,27782,28207,32568,32923,33322,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,64008,64009,38917,20133,20565,21683,22419,22874,23401,23475,25032,26999,28023,28707,34809,35299,35442,35559,36994,39405,39608,21182,26680,20502,24184,26447,33607,34892,20139,21521,22190,29670,37141,38911,39177,39255,39321,22099,22687,34395,35377,25010,27382,29563,36562,27463,38570,39511,22869,29184,36203,38761,20436,23796,24358,25080,26203,27883,28843,29572,29625,29694,30505,30541,32067,32098,32291,33335,34898,64010,36066,37449,39023,23377,31348,34880,38913,23244,20448,21332,22846,23805,25406,28025,29433,33029,33031,33698,37583,38960,20136,20804,21009,22411,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,24418,27842,28366,28677,28752,28847,29074,29673,29801,33610,34722,34913,36872,37026,37795,39336,20846,24407,24800,24935,26291,34137,36426,37295,38795,20046,20114,21628,22741,22778,22909,23733,24359,25142,25160,26122,26215,27627,28009,28111,28246,28408,28564,28640,28649,28765,29392,29733,29786,29920,30355,31068,31946,32286,32993,33446,33899,33983,34382,34399,34676,35703,35946,37804,38912,39013,24785,25110,37239,23130,26127,28151,28222,29759,39746,24573,24794,31503,21700,24344,27742,27859,27946,28888,32005,34425,35340,40251,21270,21644,23301,27194,28779,30069,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31117,31166,33457,33775,35441,35649,36008,38772,64011,25844,25899,30906,30907,31339,20024,21914,22864,23462,24187,24739,25563,27489,26213,26707,28185,29029,29872,32008,36996,39529,39973,27963,28369,29502,35905,38346,20976,24140,24488,24653,24822,24880,24908,26179,26180,27045,27841,28255,28361,28514,29004,29852,30343,31681,31783,33618,34647,36945,38541,40643,21295,22238,24315,24458,24674,24724,25079,26214,26371,27292,28142,28590,28784,29546,32362,33214,33588,34516,35496,36036,21123,29554,23446,27243,37892,21742,22150,23389,25928,25989,26313,26783,28045,28102,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,29243,32948,37237,39501,20399,20505,21402,21518,21564,21897,21957,24127,24460,26429,29030,29661,36869,21211,21235,22628,22734,28932,29071,29179,34224,35347,26248,34216,21927,26244,29002,33841,21321,21913,27585,24409,24509,25582,26249,28999,35569,36637,40638,20241,25658,28875,30054,34407,24676,35662,40440,20807,20982,21256,27958,33016,40657,26133,27427,28824,30165,21507,23673,32007,35350,27424,27453,27462,21560,24688,27965,32725,33288,20694,20958,21916,22123,22221,23020,23305,24076,24985,24984,25137,26206,26342,29081,29113,29114,29351,31143,31232,32690,35440,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
- "gb18030":[19970,19972,19973,19974,19983,19986,19991,19999,20000,20001,20003,20006,20009,20014,20015,20017,20019,20021,20023,20028,20032,20033,20034,20036,20038,20042,20049,20053,20055,20058,20059,20066,20067,20068,20069,20071,20072,20074,20075,20076,20077,20078,20079,20082,20084,20085,20086,20087,20088,20089,20090,20091,20092,20093,20095,20096,20097,20098,20099,20100,20101,20103,20106,20112,20118,20119,20121,20124,20125,20126,20131,20138,20143,20144,20145,20148,20150,20151,20152,20153,20156,20157,20158,20168,20172,20175,20176,20178,20186,20187,20188,20192,20194,20198,20199,20201,20205,20206,20207,20209,20212,20216,20217,20218,20220,20222,20224,20226,20227,20228,20229,20230,20231,20232,20235,20236,20242,20243,20244,20245,20246,20252,20253,20257,20259,20264,20265,20268,20269,20270,20273,20275,20277,20279,20281,20283,20286,20287,20288,20289,20290,20292,20293,20295,20296,20297,20298,20299,20300,20306,20308,20310,20321,20322,20326,20328,20330,20331,20333,20334,20337,20338,20341,20343,20344,20345,20346,20349,20352,20353,20354,20357,20358,20359,20362,20364,20366,20368,20370,20371,20373,20374,20376,20377,20378,20380,20382,20383,20385,20386,20388,20395,20397,20400,20401,20402,20403,20404,20406,20407,20408,20409,20410,20411,20412,20413,20414,20416,20417,20418,20422,20423,20424,20425,20427,20428,20429,20434,20435,20436,20437,20438,20441,20443,20448,20450,20452,20453,20455,20459,20460,20464,20466,20468,20469,20470,20471,20473,20475,20476,20477,20479,20480,20481,20482,20483,20484,20485,20486,20487,20488,20489,20490,20491,20494,20496,20497,20499,20501,20502,20503,20507,20509,20510,20512,20514,20515,20516,20519,20523,20527,20528,20529,20530,20531,20532,20533,20534,20535,20536,20537,20539,20541,20543,20544,20545,20546,20548,20549,20550,20553,20554,20555,20557,20560,20561,20562,20563,20564,20566,20567,20568,20569,20571,20573,20574,20575,20576,20577,20578,20579,20580,20582,20583,20584,20585,20586,20587,20589,20590,20591,20592,20593,20594,20595,20596,20597,20600,20601,20602,20604,20605,20609,20610,20611,20612,20614,20615,20617,20618,20619,20620,20622,20623,20624,20625,20626,20627,20628,20629,20630,20631,20632,20633,20634,20635,20636,20637,20638,20639,20640,20641,20642,20644,20646,20650,20651,20653,20654,20655,20656,20657,20659,20660,20661,20662,20663,20664,20665,20668,20669,20670,20671,20672,20673,20674,20675,20676,20677,20678,20679,20680,20681,20682,20683,20684,20685,20686,20688,20689,20690,20691,20692,20693,20695,20696,20697,20699,20700,20701,20702,20703,20704,20705,20706,20707,20708,20709,20712,20713,20714,20715,20719,20720,20721,20722,20724,20726,20727,20728,20729,20730,20732,20733,20734,20735,20736,20737,20738,20739,20740,20741,20744,20745,20746,20748,20749,20750,20751,20752,20753,20755,20756,20757,20758,20759,20760,20761,20762,20763,20764,20765,20766,20767,20768,20770,20771,20772,20773,20774,20775,20776,20777,20778,20779,20780,20781,20782,20783,20784,20785,20786,20787,20788,20789,20790,20791,20792,20793,20794,20795,20796,20797,20798,20802,20807,20810,20812,20814,20815,20816,20818,20819,20823,20824,20825,20827,20829,20830,20831,20832,20833,20835,20836,20838,20839,20841,20842,20847,20850,20858,20862,20863,20867,20868,20870,20871,20874,20875,20878,20879,20880,20881,20883,20884,20888,20890,20893,20894,20895,20897,20899,20902,20903,20904,20905,20906,20909,20910,20916,20920,20921,20922,20926,20927,20929,20930,20931,20933,20936,20938,20941,20942,20944,20946,20947,20948,20949,20950,20951,20952,20953,20954,20956,20958,20959,20962,20963,20965,20966,20967,20968,20969,20970,20972,20974,20977,20978,20980,20983,20990,20996,20997,21001,21003,21004,21007,21008,21011,21012,21013,21020,21022,21023,21025,21026,21027,21029,21030,21031,21034,21036,21039,21041,21042,21044,21045,21052,21054,21060,21061,21062,21063,21064,21065,21067,21070,21071,21074,21075,21077,21079,21080,21081,21082,21083,21085,21087,21088,21090,21091,21092,21094,21096,21099,21100,21101,21102,21104,21105,21107,21108,21109,21110,21111,21112,21113,21114,21115,21116,21118,21120,21123,21124,21125,21126,21127,21129,21130,21131,21132,21133,21134,21135,21137,21138,21140,21141,21142,21143,21144,21145,21146,21148,21156,21157,21158,21159,21166,21167,21168,21172,21173,21174,21175,21176,21177,21178,21179,21180,21181,21184,21185,21186,21188,21189,21190,21192,21194,21196,21197,21198,21199,21201,21203,21204,21205,21207,21209,21210,21211,21212,21213,21214,21216,21217,21218,21219,21221,21222,21223,21224,21225,21226,21227,21228,21229,21230,21231,21233,21234,21235,21236,21237,21238,21239,21240,21243,21244,21245,21249,21250,21251,21252,21255,21257,21258,21259,21260,21262,21265,21266,21267,21268,21272,21275,21276,21278,21279,21282,21284,21285,21287,21288,21289,21291,21292,21293,21295,21296,21297,21298,21299,21300,21301,21302,21303,21304,21308,21309,21312,21314,21316,21318,21323,21324,21325,21328,21332,21336,21337,21339,21341,21349,21352,21354,21356,21357,21362,21366,21369,21371,21372,21373,21374,21376,21377,21379,21383,21384,21386,21390,21391,21392,21393,21394,21395,21396,21398,21399,21401,21403,21404,21406,21408,21409,21412,21415,21418,21419,21420,21421,21423,21424,21425,21426,21427,21428,21429,21431,21432,21433,21434,21436,21437,21438,21440,21443,21444,21445,21446,21447,21454,21455,21456,21458,21459,21461,21466,21468,21469,21470,21473,21474,21479,21492,21498,21502,21503,21504,21506,21509,21511,21515,21524,21528,21529,21530,21532,21538,21540,21541,21546,21552,21555,21558,21559,21562,21565,21567,21569,21570,21572,21573,21575,21577,21580,21581,21582,21583,21585,21594,21597,21598,21599,21600,21601,21603,21605,21607,21609,21610,21611,21612,21613,21614,21615,21616,21620,21625,21626,21630,21631,21633,21635,21637,21639,21640,21641,21642,21645,21649,21651,21655,21656,21660,21662,21663,21664,21665,21666,21669,21678,21680,21682,21685,21686,21687,21689,21690,21692,21694,21699,21701,21706,21707,21718,21720,21723,21728,21729,21730,21731,21732,21739,21740,21743,21744,21745,21748,21749,21750,21751,21752,21753,21755,21758,21760,21762,21763,21764,21765,21768,21770,21771,21772,21773,21774,21778,21779,21781,21782,21783,21784,21785,21786,21788,21789,21790,21791,21793,21797,21798,21800,21801,21803,21805,21810,21812,21813,21814,21816,21817,21818,21819,21821,21824,21826,21829,21831,21832,21835,21836,21837,21838,21839,21841,21842,21843,21844,21847,21848,21849,21850,21851,21853,21854,21855,21856,21858,21859,21864,21865,21867,21871,21872,21873,21874,21875,21876,21881,21882,21885,21887,21893,21894,21900,21901,21902,21904,21906,21907,21909,21910,21911,21914,21915,21918,21920,21921,21922,21923,21924,21925,21926,21928,21929,21930,21931,21932,21933,21934,21935,21936,21938,21940,21942,21944,21946,21948,21951,21952,21953,21954,21955,21958,21959,21960,21962,21963,21966,21967,21968,21973,21975,21976,21977,21978,21979,21982,21984,21986,21991,21993,21997,21998,22000,22001,22004,22006,22008,22009,22010,22011,22012,22015,22018,22019,22020,22021,22022,22023,22026,22027,22029,22032,22033,22034,22035,22036,22037,22038,22039,22041,22042,22044,22045,22048,22049,22050,22053,22054,22056,22057,22058,22059,22062,22063,22064,22067,22069,22071,22072,22074,22076,22077,22078,22080,22081,22082,22083,22084,22085,22086,22087,22088,22089,22090,22091,22095,22096,22097,22098,22099,22101,22102,22106,22107,22109,22110,22111,22112,22113,22115,22117,22118,22119,22125,22126,22127,22128,22130,22131,22132,22133,22135,22136,22137,22138,22141,22142,22143,22144,22145,22146,22147,22148,22151,22152,22153,22154,22155,22156,22157,22160,22161,22162,22164,22165,22166,22167,22168,22169,22170,22171,22172,22173,22174,22175,22176,22177,22178,22180,22181,22182,22183,22184,22185,22186,22187,22188,22189,22190,22192,22193,22194,22195,22196,22197,22198,22200,22201,22202,22203,22205,22206,22207,22208,22209,22210,22211,22212,22213,22214,22215,22216,22217,22219,22220,22221,22222,22223,22224,22225,22226,22227,22229,22230,22232,22233,22236,22243,22245,22246,22247,22248,22249,22250,22252,22254,22255,22258,22259,22262,22263,22264,22267,22268,22272,22273,22274,22277,22279,22283,22284,22285,22286,22287,22288,22289,22290,22291,22292,22293,22294,22295,22296,22297,22298,22299,22301,22302,22304,22305,22306,22308,22309,22310,22311,22315,22321,22322,22324,22325,22326,22327,22328,22332,22333,22335,22337,22339,22340,22341,22342,22344,22345,22347,22354,22355,22356,22357,22358,22360,22361,22370,22371,22373,22375,22380,22382,22384,22385,22386,22388,22389,22392,22393,22394,22397,22398,22399,22400,22401,22407,22408,22409,22410,22413,22414,22415,22416,22417,22420,22421,22422,22423,22424,22425,22426,22428,22429,22430,22431,22437,22440,22442,22444,22447,22448,22449,22451,22453,22454,22455,22457,22458,22459,22460,22461,22462,22463,22464,22465,22468,22469,22470,22471,22472,22473,22474,22476,22477,22480,22481,22483,22486,22487,22491,22492,22494,22497,22498,22499,22501,22502,22503,22504,22505,22506,22507,22508,22510,22512,22513,22514,22515,22517,22518,22519,22523,22524,22526,22527,22529,22531,22532,22533,22536,22537,22538,22540,22542,22543,22544,22546,22547,22548,22550,22551,22552,22554,22555,22556,22557,22559,22562,22563,22565,22566,22567,22568,22569,22571,22572,22573,22574,22575,22577,22578,22579,22580,22582,22583,22584,22585,22586,22587,22588,22589,22590,22591,22592,22593,22594,22595,22597,22598,22599,22600,22601,22602,22603,22606,22607,22608,22610,22611,22613,22614,22615,22617,22618,22619,22620,22621,22623,22624,22625,22626,22627,22628,22630,22631,22632,22633,22634,22637,22638,22639,22640,22641,22642,22643,22644,22645,22646,22647,22648,22649,22650,22651,22652,22653,22655,22658,22660,22662,22663,22664,22666,22667,22668,22669,22670,22671,22672,22673,22676,22677,22678,22679,22680,22683,22684,22685,22688,22689,22690,22691,22692,22693,22694,22695,22698,22699,22700,22701,22702,22703,22704,22705,22706,22707,22708,22709,22710,22711,22712,22713,22714,22715,22717,22718,22719,22720,22722,22723,22724,22726,22727,22728,22729,22730,22731,22732,22733,22734,22735,22736,22738,22739,22740,22742,22743,22744,22745,22746,22747,22748,22749,22750,22751,22752,22753,22754,22755,22757,22758,22759,22760,22761,22762,22765,22767,22769,22770,22772,22773,22775,22776,22778,22779,22780,22781,22782,22783,22784,22785,22787,22789,22790,22792,22793,22794,22795,22796,22798,22800,22801,22802,22803,22807,22808,22811,22813,22814,22816,22817,22818,22819,22822,22824,22828,22832,22834,22835,22837,22838,22843,22845,22846,22847,22848,22851,22853,22854,22858,22860,22861,22864,22866,22867,22873,22875,22876,22877,22878,22879,22881,22883,22884,22886,22887,22888,22889,22890,22891,22892,22893,22894,22895,22896,22897,22898,22901,22903,22906,22907,22908,22910,22911,22912,22917,22921,22923,22924,22926,22927,22928,22929,22932,22933,22936,22938,22939,22940,22941,22943,22944,22945,22946,22950,22951,22956,22957,22960,22961,22963,22964,22965,22966,22967,22968,22970,22972,22973,22975,22976,22977,22978,22979,22980,22981,22983,22984,22985,22988,22989,22990,22991,22997,22998,23001,23003,23006,23007,23008,23009,23010,23012,23014,23015,23017,23018,23019,23021,23022,23023,23024,23025,23026,23027,23028,23029,23030,23031,23032,23034,23036,23037,23038,23040,23042,23050,23051,23053,23054,23055,23056,23058,23060,23061,23062,23063,23065,23066,23067,23069,23070,23073,23074,23076,23078,23079,23080,23082,23083,23084,23085,23086,23087,23088,23091,23093,23095,23096,23097,23098,23099,23101,23102,23103,23105,23106,23107,23108,23109,23111,23112,23115,23116,23117,23118,23119,23120,23121,23122,23123,23124,23126,23127,23128,23129,23131,23132,23133,23134,23135,23136,23137,23139,23140,23141,23142,23144,23145,23147,23148,23149,23150,23151,23152,23153,23154,23155,23160,23161,23163,23164,23165,23166,23168,23169,23170,23171,23172,23173,23174,23175,23176,23177,23178,23179,23180,23181,23182,23183,23184,23185,23187,23188,23189,23190,23191,23192,23193,23196,23197,23198,23199,23200,23201,23202,23203,23204,23205,23206,23207,23208,23209,23211,23212,23213,23214,23215,23216,23217,23220,23222,23223,23225,23226,23227,23228,23229,23231,23232,23235,23236,23237,23238,23239,23240,23242,23243,23245,23246,23247,23248,23249,23251,23253,23255,23257,23258,23259,23261,23262,23263,23266,23268,23269,23271,23272,23274,23276,23277,23278,23279,23280,23282,23283,23284,23285,23286,23287,23288,23289,23290,23291,23292,23293,23294,23295,23296,23297,23298,23299,23300,23301,23302,23303,23304,23306,23307,23308,23309,23310,23311,23312,23313,23314,23315,23316,23317,23320,23321,23322,23323,23324,23325,23326,23327,23328,23329,23330,23331,23332,23333,23334,23335,23336,23337,23338,23339,23340,23341,23342,23343,23344,23345,23347,23349,23350,23352,23353,23354,23355,23356,23357,23358,23359,23361,23362,23363,23364,23365,23366,23367,23368,23369,23370,23371,23372,23373,23374,23375,23378,23382,23390,23392,23393,23399,23400,23403,23405,23406,23407,23410,23412,23414,23415,23416,23417,23419,23420,23422,23423,23426,23430,23434,23437,23438,23440,23441,23442,23444,23446,23455,23463,23464,23465,23468,23469,23470,23471,23473,23474,23479,23482,23483,23484,23488,23489,23491,23496,23497,23498,23499,23501,23502,23503,23505,23508,23509,23510,23511,23512,23513,23514,23515,23516,23520,23522,23523,23526,23527,23529,23530,23531,23532,23533,23535,23537,23538,23539,23540,23541,23542,23543,23549,23550,23552,23554,23555,23557,23559,23560,23563,23564,23565,23566,23568,23570,23571,23575,23577,23579,23582,23583,23584,23585,23587,23590,23592,23593,23594,23595,23597,23598,23599,23600,23602,23603,23605,23606,23607,23619,23620,23622,23623,23628,23629,23634,23635,23636,23638,23639,23640,23642,23643,23644,23645,23647,23650,23652,23655,23656,23657,23658,23659,23660,23661,23664,23666,23667,23668,23669,23670,23671,23672,23675,23676,23677,23678,23680,23683,23684,23685,23686,23687,23689,23690,23691,23694,23695,23698,23699,23701,23709,23710,23711,23712,23713,23716,23717,23718,23719,23720,23722,23726,23727,23728,23730,23732,23734,23737,23738,23739,23740,23742,23744,23746,23747,23749,23750,23751,23752,23753,23754,23756,23757,23758,23759,23760,23761,23763,23764,23765,23766,23767,23768,23770,23771,23772,23773,23774,23775,23776,23778,23779,23783,23785,23787,23788,23790,23791,23793,23794,23795,23796,23797,23798,23799,23800,23801,23802,23804,23805,23806,23807,23808,23809,23812,23813,23816,23817,23818,23819,23820,23821,23823,23824,23825,23826,23827,23829,23831,23832,23833,23834,23836,23837,23839,23840,23841,23842,23843,23845,23848,23850,23851,23852,23855,23856,23857,23858,23859,23861,23862,23863,23864,23865,23866,23867,23868,23871,23872,23873,23874,23875,23876,23877,23878,23880,23881,23885,23886,23887,23888,23889,23890,23891,23892,23893,23894,23895,23897,23898,23900,23902,23903,23904,23905,23906,23907,23908,23909,23910,23911,23912,23914,23917,23918,23920,23921,23922,23923,23925,23926,23927,23928,23929,23930,23931,23932,23933,23934,23935,23936,23937,23939,23940,23941,23942,23943,23944,23945,23946,23947,23948,23949,23950,23951,23952,23953,23954,23955,23956,23957,23958,23959,23960,23962,23963,23964,23966,23967,23968,23969,23970,23971,23972,23973,23974,23975,23976,23977,23978,23979,23980,23981,23982,23983,23984,23985,23986,23987,23988,23989,23990,23992,23993,23994,23995,23996,23997,23998,23999,24000,24001,24002,24003,24004,24006,24007,24008,24009,24010,24011,24012,24014,24015,24016,24017,24018,24019,24020,24021,24022,24023,24024,24025,24026,24028,24031,24032,24035,24036,24042,24044,24045,24048,24053,24054,24056,24057,24058,24059,24060,24063,24064,24068,24071,24073,24074,24075,24077,24078,24082,24083,24087,24094,24095,24096,24097,24098,24099,24100,24101,24104,24105,24106,24107,24108,24111,24112,24114,24115,24116,24117,24118,24121,24122,24126,24127,24128,24129,24131,24134,24135,24136,24137,24138,24139,24141,24142,24143,24144,24145,24146,24147,24150,24151,24152,24153,24154,24156,24157,24159,24160,24163,24164,24165,24166,24167,24168,24169,24170,24171,24172,24173,24174,24175,24176,24177,24181,24183,24185,24190,24193,24194,24195,24197,24200,24201,24204,24205,24206,24210,24216,24219,24221,24225,24226,24227,24228,24232,24233,24234,24235,24236,24238,24239,24240,24241,24242,24244,24250,24251,24252,24253,24255,24256,24257,24258,24259,24260,24261,24262,24263,24264,24267,24268,24269,24270,24271,24272,24276,24277,24279,24280,24281,24282,24284,24285,24286,24287,24288,24289,24290,24291,24292,24293,24294,24295,24297,24299,24300,24301,24302,24303,24304,24305,24306,24307,24309,24312,24313,24315,24316,24317,24325,24326,24327,24329,24332,24333,24334,24336,24338,24340,24342,24345,24346,24348,24349,24350,24353,24354,24355,24356,24360,24363,24364,24366,24368,24370,24371,24372,24373,24374,24375,24376,24379,24381,24382,24383,24385,24386,24387,24388,24389,24390,24391,24392,24393,24394,24395,24396,24397,24398,24399,24401,24404,24409,24410,24411,24412,24414,24415,24416,24419,24421,24423,24424,24427,24430,24431,24434,24436,24437,24438,24440,24442,24445,24446,24447,24451,24454,24461,24462,24463,24465,24467,24468,24470,24474,24475,24477,24478,24479,24480,24482,24483,24484,24485,24486,24487,24489,24491,24492,24495,24496,24497,24498,24499,24500,24502,24504,24505,24506,24507,24510,24511,24512,24513,24514,24519,24520,24522,24523,24526,24531,24532,24533,24538,24539,24540,24542,24543,24546,24547,24549,24550,24552,24553,24556,24559,24560,24562,24563,24564,24566,24567,24569,24570,24572,24583,24584,24585,24587,24588,24592,24593,24595,24599,24600,24602,24606,24607,24610,24611,24612,24620,24621,24622,24624,24625,24626,24627,24628,24630,24631,24632,24633,24634,24637,24638,24640,24644,24645,24646,24647,24648,24649,24650,24652,24654,24655,24657,24659,24660,24662,24663,24664,24667,24668,24670,24671,24672,24673,24677,24678,24686,24689,24690,24692,24693,24695,24702,24704,24705,24706,24709,24710,24711,24712,24714,24715,24718,24719,24720,24721,24723,24725,24727,24728,24729,24732,24734,24737,24738,24740,24741,24743,24745,24746,24750,24752,24755,24757,24758,24759,24761,24762,24765,24766,24767,24768,24769,24770,24771,24772,24775,24776,24777,24780,24781,24782,24783,24784,24786,24787,24788,24790,24791,24793,24795,24798,24801,24802,24803,24804,24805,24810,24817,24818,24821,24823,24824,24827,24828,24829,24830,24831,24834,24835,24836,24837,24839,24842,24843,24844,24848,24849,24850,24851,24852,24854,24855,24856,24857,24859,24860,24861,24862,24865,24866,24869,24872,24873,24874,24876,24877,24878,24879,24880,24881,24882,24883,24884,24885,24886,24887,24888,24889,24890,24891,24892,24893,24894,24896,24897,24898,24899,24900,24901,24902,24903,24905,24907,24909,24911,24912,24914,24915,24916,24918,24919,24920,24921,24922,24923,24924,24926,24927,24928,24929,24931,24932,24933,24934,24937,24938,24939,24940,24941,24942,24943,24945,24946,24947,24948,24950,24952,24953,24954,24955,24956,24957,24958,24959,24960,24961,24962,24963,24964,24965,24966,24967,24968,24969,24970,24972,24973,24975,24976,24977,24978,24979,24981,24982,24983,24984,24985,24986,24987,24988,24990,24991,24992,24993,24994,24995,24996,24997,24998,25002,25003,25005,25006,25007,25008,25009,25010,25011,25012,25013,25014,25016,25017,25018,25019,25020,25021,25023,25024,25025,25027,25028,25029,25030,25031,25033,25036,25037,25038,25039,25040,25043,25045,25046,25047,25048,25049,25050,25051,25052,25053,25054,25055,25056,25057,25058,25059,25060,25061,25063,25064,25065,25066,25067,25068,25069,25070,25071,25072,25073,25074,25075,25076,25078,25079,25080,25081,25082,25083,25084,25085,25086,25088,25089,25090,25091,25092,25093,25095,25097,25107,25108,25113,25116,25117,25118,25120,25123,25126,25127,25128,25129,25131,25133,25135,25136,25137,25138,25141,25142,25144,25145,25146,25147,25148,25154,25156,25157,25158,25162,25167,25168,25173,25174,25175,25177,25178,25180,25181,25182,25183,25184,25185,25186,25188,25189,25192,25201,25202,25204,25205,25207,25208,25210,25211,25213,25217,25218,25219,25221,25222,25223,25224,25227,25228,25229,25230,25231,25232,25236,25241,25244,25245,25246,25251,25254,25255,25257,25258,25261,25262,25263,25264,25266,25267,25268,25270,25271,25272,25274,25278,25280,25281,25283,25291,25295,25297,25301,25309,25310,25312,25313,25316,25322,25323,25328,25330,25333,25336,25337,25338,25339,25344,25347,25348,25349,25350,25354,25355,25356,25357,25359,25360,25362,25363,25364,25365,25367,25368,25369,25372,25382,25383,25385,25388,25389,25390,25392,25393,25395,25396,25397,25398,25399,25400,25403,25404,25406,25407,25408,25409,25412,25415,25416,25418,25425,25426,25427,25428,25430,25431,25432,25433,25434,25435,25436,25437,25440,25444,25445,25446,25448,25450,25451,25452,25455,25456,25458,25459,25460,25461,25464,25465,25468,25469,25470,25471,25473,25475,25476,25477,25478,25483,25485,25489,25491,25492,25493,25495,25497,25498,25499,25500,25501,25502,25503,25505,25508,25510,25515,25519,25521,25522,25525,25526,25529,25531,25533,25535,25536,25537,25538,25539,25541,25543,25544,25546,25547,25548,25553,25555,25556,25557,25559,25560,25561,25562,25563,25564,25565,25567,25570,25572,25573,25574,25575,25576,25579,25580,25582,25583,25584,25585,25587,25589,25591,25593,25594,25595,25596,25598,25603,25604,25606,25607,25608,25609,25610,25613,25614,25617,25618,25621,25622,25623,25624,25625,25626,25629,25631,25634,25635,25636,25637,25639,25640,25641,25643,25646,25647,25648,25649,25650,25651,25653,25654,25655,25656,25657,25659,25660,25662,25664,25666,25667,25673,25675,25676,25677,25678,25679,25680,25681,25683,25685,25686,25687,25689,25690,25691,25692,25693,25695,25696,25697,25698,25699,25700,25701,25702,25704,25706,25707,25708,25710,25711,25712,25713,25714,25715,25716,25717,25718,25719,25723,25724,25725,25726,25727,25728,25729,25731,25734,25736,25737,25738,25739,25740,25741,25742,25743,25744,25747,25748,25751,25752,25754,25755,25756,25757,25759,25760,25761,25762,25763,25765,25766,25767,25768,25770,25771,25775,25777,25778,25779,25780,25782,25785,25787,25789,25790,25791,25793,25795,25796,25798,25799,25800,25801,25802,25803,25804,25807,25809,25811,25812,25813,25814,25817,25818,25819,25820,25821,25823,25824,25825,25827,25829,25831,25832,25833,25834,25835,25836,25837,25838,25839,25840,25841,25842,25843,25844,25845,25846,25847,25848,25849,25850,25851,25852,25853,25854,25855,25857,25858,25859,25860,25861,25862,25863,25864,25866,25867,25868,25869,25870,25871,25872,25873,25875,25876,25877,25878,25879,25881,25882,25883,25884,25885,25886,25887,25888,25889,25890,25891,25892,25894,25895,25896,25897,25898,25900,25901,25904,25905,25906,25907,25911,25914,25916,25917,25920,25921,25922,25923,25924,25926,25927,25930,25931,25933,25934,25936,25938,25939,25940,25943,25944,25946,25948,25951,25952,25953,25956,25957,25959,25960,25961,25962,25965,25966,25967,25969,25971,25973,25974,25976,25977,25978,25979,25980,25981,25982,25983,25984,25985,25986,25987,25988,25989,25990,25992,25993,25994,25997,25998,25999,26002,26004,26005,26006,26008,26010,26013,26014,26016,26018,26019,26022,26024,26026,26028,26030,26033,26034,26035,26036,26037,26038,26039,26040,26042,26043,26046,26047,26048,26050,26055,26056,26057,26058,26061,26064,26065,26067,26068,26069,26072,26073,26074,26075,26076,26077,26078,26079,26081,26083,26084,26090,26091,26098,26099,26100,26101,26104,26105,26107,26108,26109,26110,26111,26113,26116,26117,26119,26120,26121,26123,26125,26128,26129,26130,26134,26135,26136,26138,26139,26140,26142,26145,26146,26147,26148,26150,26153,26154,26155,26156,26158,26160,26162,26163,26167,26168,26169,26170,26171,26173,26175,26176,26178,26180,26181,26182,26183,26184,26185,26186,26189,26190,26192,26193,26200,26201,26203,26204,26205,26206,26208,26210,26211,26213,26215,26217,26218,26219,26220,26221,26225,26226,26227,26229,26232,26233,26235,26236,26237,26239,26240,26241,26243,26245,26246,26248,26249,26250,26251,26253,26254,26255,26256,26258,26259,26260,26261,26264,26265,26266,26267,26268,26270,26271,26272,26273,26274,26275,26276,26277,26278,26281,26282,26283,26284,26285,26287,26288,26289,26290,26291,26293,26294,26295,26296,26298,26299,26300,26301,26303,26304,26305,26306,26307,26308,26309,26310,26311,26312,26313,26314,26315,26316,26317,26318,26319,26320,26321,26322,26323,26324,26325,26326,26327,26328,26330,26334,26335,26336,26337,26338,26339,26340,26341,26343,26344,26346,26347,26348,26349,26350,26351,26353,26357,26358,26360,26362,26363,26365,26369,26370,26371,26372,26373,26374,26375,26380,26382,26383,26385,26386,26387,26390,26392,26393,26394,26396,26398,26400,26401,26402,26403,26404,26405,26407,26409,26414,26416,26418,26419,26422,26423,26424,26425,26427,26428,26430,26431,26433,26436,26437,26439,26442,26443,26445,26450,26452,26453,26455,26456,26457,26458,26459,26461,26466,26467,26468,26470,26471,26475,26476,26478,26481,26484,26486,26488,26489,26490,26491,26493,26496,26498,26499,26501,26502,26504,26506,26508,26509,26510,26511,26513,26514,26515,26516,26518,26521,26523,26527,26528,26529,26532,26534,26537,26540,26542,26545,26546,26548,26553,26554,26555,26556,26557,26558,26559,26560,26562,26565,26566,26567,26568,26569,26570,26571,26572,26573,26574,26581,26582,26583,26587,26591,26593,26595,26596,26598,26599,26600,26602,26603,26605,26606,26610,26613,26614,26615,26616,26617,26618,26619,26620,26622,26625,26626,26627,26628,26630,26637,26640,26642,26644,26645,26648,26649,26650,26651,26652,26654,26655,26656,26658,26659,26660,26661,26662,26663,26664,26667,26668,26669,26670,26671,26672,26673,26676,26677,26678,26682,26683,26687,26695,26699,26701,26703,26706,26710,26711,26712,26713,26714,26715,26716,26717,26718,26719,26730,26732,26733,26734,26735,26736,26737,26738,26739,26741,26744,26745,26746,26747,26748,26749,26750,26751,26752,26754,26756,26759,26760,26761,26762,26763,26764,26765,26766,26768,26769,26770,26772,26773,26774,26776,26777,26778,26779,26780,26781,26782,26783,26784,26785,26787,26788,26789,26793,26794,26795,26796,26798,26801,26802,26804,26806,26807,26808,26809,26810,26811,26812,26813,26814,26815,26817,26819,26820,26821,26822,26823,26824,26826,26828,26830,26831,26832,26833,26835,26836,26838,26839,26841,26843,26844,26845,26846,26847,26849,26850,26852,26853,26854,26855,26856,26857,26858,26859,26860,26861,26863,26866,26867,26868,26870,26871,26872,26875,26877,26878,26879,26880,26882,26883,26884,26886,26887,26888,26889,26890,26892,26895,26897,26899,26900,26901,26902,26903,26904,26905,26906,26907,26908,26909,26910,26913,26914,26915,26917,26918,26919,26920,26921,26922,26923,26924,26926,26927,26929,26930,26931,26933,26934,26935,26936,26938,26939,26940,26942,26944,26945,26947,26948,26949,26950,26951,26952,26953,26954,26955,26956,26957,26958,26959,26960,26961,26962,26963,26965,26966,26968,26969,26971,26972,26975,26977,26978,26980,26981,26983,26984,26985,26986,26988,26989,26991,26992,26994,26995,26996,26997,26998,27002,27003,27005,27006,27007,27009,27011,27013,27018,27019,27020,27022,27023,27024,27025,27026,27027,27030,27031,27033,27034,27037,27038,27039,27040,27041,27042,27043,27044,27045,27046,27049,27050,27052,27054,27055,27056,27058,27059,27061,27062,27064,27065,27066,27068,27069,27070,27071,27072,27074,27075,27076,27077,27078,27079,27080,27081,27083,27085,27087,27089,27090,27091,27093,27094,27095,27096,27097,27098,27100,27101,27102,27105,27106,27107,27108,27109,27110,27111,27112,27113,27114,27115,27116,27118,27119,27120,27121,27123,27124,27125,27126,27127,27128,27129,27130,27131,27132,27134,27136,27137,27138,27139,27140,27141,27142,27143,27144,27145,27147,27148,27149,27150,27151,27152,27153,27154,27155,27156,27157,27158,27161,27162,27163,27164,27165,27166,27168,27170,27171,27172,27173,27174,27175,27177,27179,27180,27181,27182,27184,27186,27187,27188,27190,27191,27192,27193,27194,27195,27196,27199,27200,27201,27202,27203,27205,27206,27208,27209,27210,27211,27212,27213,27214,27215,27217,27218,27219,27220,27221,27222,27223,27226,27228,27229,27230,27231,27232,27234,27235,27236,27238,27239,27240,27241,27242,27243,27244,27245,27246,27247,27248,27250,27251,27252,27253,27254,27255,27256,27258,27259,27261,27262,27263,27265,27266,27267,27269,27270,27271,27272,27273,27274,27275,27276,27277,27279,27282,27283,27284,27285,27286,27288,27289,27290,27291,27292,27293,27294,27295,27297,27298,27299,27300,27301,27302,27303,27304,27306,27309,27310,27311,27312,27313,27314,27315,27316,27317,27318,27319,27320,27321,27322,27323,27324,27325,27326,27327,27328,27329,27330,27331,27332,27333,27334,27335,27336,27337,27338,27339,27340,27341,27342,27343,27344,27345,27346,27347,27348,27349,27350,27351,27352,27353,27354,27355,27356,27357,27358,27359,27360,27361,27362,27363,27364,27365,27366,27367,27368,27369,27370,27371,27372,27373,27374,27375,27376,27377,27378,27379,27380,27381,27382,27383,27384,27385,27386,27387,27388,27389,27390,27391,27392,27393,27394,27395,27396,27397,27398,27399,27400,27401,27402,27403,27404,27405,27406,27407,27408,27409,27410,27411,27412,27413,27414,27415,27416,27417,27418,27419,27420,27421,27422,27423,27429,27430,27432,27433,27434,27435,27436,27437,27438,27439,27440,27441,27443,27444,27445,27446,27448,27451,27452,27453,27455,27456,27457,27458,27460,27461,27464,27466,27467,27469,27470,27471,27472,27473,27474,27475,27476,27477,27478,27479,27480,27482,27483,27484,27485,27486,27487,27488,27489,27496,27497,27499,27500,27501,27502,27503,27504,27505,27506,27507,27508,27509,27510,27511,27512,27514,27517,27518,27519,27520,27525,27528,27532,27534,27535,27536,27537,27540,27541,27543,27544,27545,27548,27549,27550,27551,27552,27554,27555,27556,27557,27558,27559,27560,27561,27563,27564,27565,27566,27567,27568,27569,27570,27574,27576,27577,27578,27579,27580,27581,27582,27584,27587,27588,27590,27591,27592,27593,27594,27596,27598,27600,27601,27608,27610,27612,27613,27614,27615,27616,27618,27619,27620,27621,27622,27623,27624,27625,27628,27629,27630,27632,27633,27634,27636,27638,27639,27640,27642,27643,27644,27646,27647,27648,27649,27650,27651,27652,27656,27657,27658,27659,27660,27662,27666,27671,27676,27677,27678,27680,27683,27685,27691,27692,27693,27697,27699,27702,27703,27705,27706,27707,27708,27710,27711,27715,27716,27717,27720,27723,27724,27725,27726,27727,27729,27730,27731,27734,27736,27737,27738,27746,27747,27749,27750,27751,27755,27756,27757,27758,27759,27761,27763,27765,27767,27768,27770,27771,27772,27775,27776,27780,27783,27786,27787,27789,27790,27793,27794,27797,27798,27799,27800,27802,27804,27805,27806,27808,27810,27816,27820,27823,27824,27828,27829,27830,27831,27834,27840,27841,27842,27843,27846,27847,27848,27851,27853,27854,27855,27857,27858,27864,27865,27866,27868,27869,27871,27876,27878,27879,27881,27884,27885,27890,27892,27897,27903,27904,27906,27907,27909,27910,27912,27913,27914,27917,27919,27920,27921,27923,27924,27925,27926,27928,27932,27933,27935,27936,27937,27938,27939,27940,27942,27944,27945,27948,27949,27951,27952,27956,27958,27959,27960,27962,27967,27968,27970,27972,27977,27980,27984,27989,27990,27991,27992,27995,27997,27999,28001,28002,28004,28005,28007,28008,28011,28012,28013,28016,28017,28018,28019,28021,28022,28025,28026,28027,28029,28030,28031,28032,28033,28035,28036,28038,28039,28042,28043,28045,28047,28048,28050,28054,28055,28056,28057,28058,28060,28066,28069,28076,28077,28080,28081,28083,28084,28086,28087,28089,28090,28091,28092,28093,28094,28097,28098,28099,28104,28105,28106,28109,28110,28111,28112,28114,28115,28116,28117,28119,28122,28123,28124,28127,28130,28131,28133,28135,28136,28137,28138,28141,28143,28144,28146,28148,28149,28150,28152,28154,28157,28158,28159,28160,28161,28162,28163,28164,28166,28167,28168,28169,28171,28175,28178,28179,28181,28184,28185,28187,28188,28190,28191,28194,28198,28199,28200,28202,28204,28206,28208,28209,28211,28213,28214,28215,28217,28219,28220,28221,28222,28223,28224,28225,28226,28229,28230,28231,28232,28233,28234,28235,28236,28239,28240,28241,28242,28245,28247,28249,28250,28252,28253,28254,28256,28257,28258,28259,28260,28261,28262,28263,28264,28265,28266,28268,28269,28271,28272,28273,28274,28275,28276,28277,28278,28279,28280,28281,28282,28283,28284,28285,28288,28289,28290,28292,28295,28296,28298,28299,28300,28301,28302,28305,28306,28307,28308,28309,28310,28311,28313,28314,28315,28317,28318,28320,28321,28323,28324,28326,28328,28329,28331,28332,28333,28334,28336,28339,28341,28344,28345,28348,28350,28351,28352,28355,28356,28357,28358,28360,28361,28362,28364,28365,28366,28368,28370,28374,28376,28377,28379,28380,28381,28387,28391,28394,28395,28396,28397,28398,28399,28400,28401,28402,28403,28405,28406,28407,28408,28410,28411,28412,28413,28414,28415,28416,28417,28419,28420,28421,28423,28424,28426,28427,28428,28429,28430,28432,28433,28434,28438,28439,28440,28441,28442,28443,28444,28445,28446,28447,28449,28450,28451,28453,28454,28455,28456,28460,28462,28464,28466,28468,28469,28471,28472,28473,28474,28475,28476,28477,28479,28480,28481,28482,28483,28484,28485,28488,28489,28490,28492,28494,28495,28496,28497,28498,28499,28500,28501,28502,28503,28505,28506,28507,28509,28511,28512,28513,28515,28516,28517,28519,28520,28521,28522,28523,28524,28527,28528,28529,28531,28533,28534,28535,28537,28539,28541,28542,28543,28544,28545,28546,28547,28549,28550,28551,28554,28555,28559,28560,28561,28562,28563,28564,28565,28566,28567,28568,28569,28570,28571,28573,28574,28575,28576,28578,28579,28580,28581,28582,28584,28585,28586,28587,28588,28589,28590,28591,28592,28593,28594,28596,28597,28599,28600,28602,28603,28604,28605,28606,28607,28609,28611,28612,28613,28614,28615,28616,28618,28619,28620,28621,28622,28623,28624,28627,28628,28629,28630,28631,28632,28633,28634,28635,28636,28637,28639,28642,28643,28644,28645,28646,28647,28648,28649,28650,28651,28652,28653,28656,28657,28658,28659,28660,28661,28662,28663,28664,28665,28666,28667,28668,28669,28670,28671,28672,28673,28674,28675,28676,28677,28678,28679,28680,28681,28682,28683,28684,28685,28686,28687,28688,28690,28691,28692,28693,28694,28695,28696,28697,28700,28701,28702,28703,28704,28705,28706,28708,28709,28710,28711,28712,28713,28714,28715,28716,28717,28718,28719,28720,28721,28722,28723,28724,28726,28727,28728,28730,28731,28732,28733,28734,28735,28736,28737,28738,28739,28740,28741,28742,28743,28744,28745,28746,28747,28749,28750,28752,28753,28754,28755,28756,28757,28758,28759,28760,28761,28762,28763,28764,28765,28767,28768,28769,28770,28771,28772,28773,28774,28775,28776,28777,28778,28782,28785,28786,28787,28788,28791,28793,28794,28795,28797,28801,28802,28803,28804,28806,28807,28808,28811,28812,28813,28815,28816,28817,28819,28823,28824,28826,28827,28830,28831,28832,28833,28834,28835,28836,28837,28838,28839,28840,28841,28842,28848,28850,28852,28853,28854,28858,28862,28863,28868,28869,28870,28871,28873,28875,28876,28877,28878,28879,28880,28881,28882,28883,28884,28885,28886,28887,28890,28892,28893,28894,28896,28897,28898,28899,28901,28906,28910,28912,28913,28914,28915,28916,28917,28918,28920,28922,28923,28924,28926,28927,28928,28929,28930,28931,28932,28933,28934,28935,28936,28939,28940,28941,28942,28943,28945,28946,28948,28951,28955,28956,28957,28958,28959,28960,28961,28962,28963,28964,28965,28967,28968,28969,28970,28971,28972,28973,28974,28978,28979,28980,28981,28983,28984,28985,28986,28987,28988,28989,28990,28991,28992,28993,28994,28995,28996,28998,28999,29000,29001,29003,29005,29007,29008,29009,29010,29011,29012,29013,29014,29015,29016,29017,29018,29019,29021,29023,29024,29025,29026,29027,29029,29033,29034,29035,29036,29037,29039,29040,29041,29044,29045,29046,29047,29049,29051,29052,29054,29055,29056,29057,29058,29059,29061,29062,29063,29064,29065,29067,29068,29069,29070,29072,29073,29074,29075,29077,29078,29079,29082,29083,29084,29085,29086,29089,29090,29091,29092,29093,29094,29095,29097,29098,29099,29101,29102,29103,29104,29105,29106,29108,29110,29111,29112,29114,29115,29116,29117,29118,29119,29120,29121,29122,29124,29125,29126,29127,29128,29129,29130,29131,29132,29133,29135,29136,29137,29138,29139,29142,29143,29144,29145,29146,29147,29148,29149,29150,29151,29153,29154,29155,29156,29158,29160,29161,29162,29163,29164,29165,29167,29168,29169,29170,29171,29172,29173,29174,29175,29176,29178,29179,29180,29181,29182,29183,29184,29185,29186,29187,29188,29189,29191,29192,29193,29194,29195,29196,29197,29198,29199,29200,29201,29202,29203,29204,29205,29206,29207,29208,29209,29210,29211,29212,29214,29215,29216,29217,29218,29219,29220,29221,29222,29223,29225,29227,29229,29230,29231,29234,29235,29236,29242,29244,29246,29248,29249,29250,29251,29252,29253,29254,29257,29258,29259,29262,29263,29264,29265,29267,29268,29269,29271,29272,29274,29276,29278,29280,29283,29284,29285,29288,29290,29291,29292,29293,29296,29297,29299,29300,29302,29303,29304,29307,29308,29309,29314,29315,29317,29318,29319,29320,29321,29324,29326,29328,29329,29331,29332,29333,29334,29335,29336,29337,29338,29339,29340,29341,29342,29344,29345,29346,29347,29348,29349,29350,29351,29352,29353,29354,29355,29358,29361,29362,29363,29365,29370,29371,29372,29373,29374,29375,29376,29381,29382,29383,29385,29386,29387,29388,29391,29393,29395,29396,29397,29398,29400,29402,29403,58566,58567,58568,58569,58570,58571,58572,58573,58574,58575,58576,58577,58578,58579,58580,58581,58582,58583,58584,58585,58586,58587,58588,58589,58590,58591,58592,58593,58594,58595,58596,58597,58598,58599,58600,58601,58602,58603,58604,58605,58606,58607,58608,58609,58610,58611,58612,58613,58614,58615,58616,58617,58618,58619,58620,58621,58622,58623,58624,58625,58626,58627,58628,58629,58630,58631,58632,58633,58634,58635,58636,58637,58638,58639,58640,58641,58642,58643,58644,58645,58646,58647,58648,58649,58650,58651,58652,58653,58654,58655,58656,58657,58658,58659,58660,58661,12288,12289,12290,183,713,711,168,12291,12293,8212,65374,8214,8230,8216,8217,8220,8221,12308,12309,12296,12297,12298,12299,12300,12301,12302,12303,12310,12311,12304,12305,177,215,247,8758,8743,8744,8721,8719,8746,8745,8712,8759,8730,8869,8741,8736,8978,8857,8747,8750,8801,8780,8776,8765,8733,8800,8814,8815,8804,8805,8734,8757,8756,9794,9792,176,8242,8243,8451,65284,164,65504,65505,8240,167,8470,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,8251,8594,8592,8593,8595,12307,58662,58663,58664,58665,58666,58667,58668,58669,58670,58671,58672,58673,58674,58675,58676,58677,58678,58679,58680,58681,58682,58683,58684,58685,58686,58687,58688,58689,58690,58691,58692,58693,58694,58695,58696,58697,58698,58699,58700,58701,58702,58703,58704,58705,58706,58707,58708,58709,58710,58711,58712,58713,58714,58715,58716,58717,58718,58719,58720,58721,58722,58723,58724,58725,58726,58727,58728,58729,58730,58731,58732,58733,58734,58735,58736,58737,58738,58739,58740,58741,58742,58743,58744,58745,58746,58747,58748,58749,58750,58751,58752,58753,58754,58755,58756,58757,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,59238,59239,59240,59241,59242,59243,9352,9353,9354,9355,9356,9357,9358,9359,9360,9361,9362,9363,9364,9365,9366,9367,9368,9369,9370,9371,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345,9346,9347,9348,9349,9350,9351,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,8364,59245,12832,12833,12834,12835,12836,12837,12838,12839,12840,12841,59246,59247,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554,8555,59248,59249,58758,58759,58760,58761,58762,58763,58764,58765,58766,58767,58768,58769,58770,58771,58772,58773,58774,58775,58776,58777,58778,58779,58780,58781,58782,58783,58784,58785,58786,58787,58788,58789,58790,58791,58792,58793,58794,58795,58796,58797,58798,58799,58800,58801,58802,58803,58804,58805,58806,58807,58808,58809,58810,58811,58812,58813,58814,58815,58816,58817,58818,58819,58820,58821,58822,58823,58824,58825,58826,58827,58828,58829,58830,58831,58832,58833,58834,58835,58836,58837,58838,58839,58840,58841,58842,58843,58844,58845,58846,58847,58848,58849,58850,58851,58852,12288,65281,65282,65283,65509,65285,65286,65287,65288,65289,65290,65291,65292,65293,65294,65295,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,65306,65307,65308,65309,65310,65311,65312,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65339,65340,65341,65342,65343,65344,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,65371,65372,65373,65507,58854,58855,58856,58857,58858,58859,58860,58861,58862,58863,58864,58865,58866,58867,58868,58869,58870,58871,58872,58873,58874,58875,58876,58877,58878,58879,58880,58881,58882,58883,58884,58885,58886,58887,58888,58889,58890,58891,58892,58893,58894,58895,58896,58897,58898,58899,58900,58901,58902,58903,58904,58905,58906,58907,58908,58909,58910,58911,58912,58913,58914,58915,58916,58917,58918,58919,58920,58921,58922,58923,58924,58925,58926,58927,58928,58929,58930,58931,58932,58933,58934,58935,58936,58937,58938,58939,58940,58941,58942,58943,58944,58945,58946,58947,58948,58949,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,59250,59251,59252,59253,59254,59255,59256,59257,59258,59259,59260,58950,58951,58952,58953,58954,58955,58956,58957,58958,58959,58960,58961,58962,58963,58964,58965,58966,58967,58968,58969,58970,58971,58972,58973,58974,58975,58976,58977,58978,58979,58980,58981,58982,58983,58984,58985,58986,58987,58988,58989,58990,58991,58992,58993,58994,58995,58996,58997,58998,58999,59000,59001,59002,59003,59004,59005,59006,59007,59008,59009,59010,59011,59012,59013,59014,59015,59016,59017,59018,59019,59020,59021,59022,59023,59024,59025,59026,59027,59028,59029,59030,59031,59032,59033,59034,59035,59036,59037,59038,59039,59040,59041,59042,59043,59044,59045,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,59261,59262,59263,59264,59265,59266,59267,59268,59046,59047,59048,59049,59050,59051,59052,59053,59054,59055,59056,59057,59058,59059,59060,59061,59062,59063,59064,59065,59066,59067,59068,59069,59070,59071,59072,59073,59074,59075,59076,59077,59078,59079,59080,59081,59082,59083,59084,59085,59086,59087,59088,59089,59090,59091,59092,59093,59094,59095,59096,59097,59098,59099,59100,59101,59102,59103,59104,59105,59106,59107,59108,59109,59110,59111,59112,59113,59114,59115,59116,59117,59118,59119,59120,59121,59122,59123,59124,59125,59126,59127,59128,59129,59130,59131,59132,59133,59134,59135,59136,59137,59138,59139,59140,59141,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,59269,59270,59271,59272,59273,59274,59275,59276,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,59277,59278,59279,59280,59281,59282,59283,65077,65078,65081,65082,65087,65088,65085,65086,65089,65090,65091,65092,59284,59285,65083,65084,65079,65080,65073,59286,65075,65076,59287,59288,59289,59290,59291,59292,59293,59294,59295,59142,59143,59144,59145,59146,59147,59148,59149,59150,59151,59152,59153,59154,59155,59156,59157,59158,59159,59160,59161,59162,59163,59164,59165,59166,59167,59168,59169,59170,59171,59172,59173,59174,59175,59176,59177,59178,59179,59180,59181,59182,59183,59184,59185,59186,59187,59188,59189,59190,59191,59192,59193,59194,59195,59196,59197,59198,59199,59200,59201,59202,59203,59204,59205,59206,59207,59208,59209,59210,59211,59212,59213,59214,59215,59216,59217,59218,59219,59220,59221,59222,59223,59224,59225,59226,59227,59228,59229,59230,59231,59232,59233,59234,59235,59236,59237,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,59296,59297,59298,59299,59300,59301,59302,59303,59304,59305,59306,59307,59308,59309,59310,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,59311,59312,59313,59314,59315,59316,59317,59318,59319,59320,59321,59322,59323,714,715,729,8211,8213,8229,8245,8453,8457,8598,8599,8600,8601,8725,8735,8739,8786,8806,8807,8895,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9581,9582,9583,9584,9585,9586,9587,9601,9602,9603,9604,9605,9606,9607,9608,9609,9610,9611,9612,9613,9614,9615,9619,9620,9621,9660,9661,9698,9699,9700,9701,9737,8853,12306,12317,12318,59324,59325,59326,59327,59328,59329,59330,59331,59332,59333,59334,257,225,462,224,275,233,283,232,299,237,464,236,333,243,466,242,363,250,468,249,470,472,474,476,252,234,593,7743,324,328,505,609,59337,59338,59339,59340,12549,12550,12551,12552,12553,12554,12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570,12571,12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583,12584,12585,59341,59342,59343,59344,59345,59346,59347,59348,59349,59350,59351,59352,59353,59354,59355,59356,59357,59358,59359,59360,59361,12321,12322,12323,12324,12325,12326,12327,12328,12329,12963,13198,13199,13212,13213,13214,13217,13252,13262,13265,13266,13269,65072,65506,65508,59362,8481,12849,59363,8208,59364,59365,59366,12540,12443,12444,12541,12542,12294,12445,12446,65097,65098,65099,65100,65101,65102,65103,65104,65105,65106,65108,65109,65110,65111,65113,65114,65115,65116,65117,65118,65119,65120,65121,65122,65123,65124,65125,65126,65128,65129,65130,65131,12350,12272,12273,12274,12275,12276,12277,12278,12279,12280,12281,12282,12283,12295,59380,59381,59382,59383,59384,59385,59386,59387,59388,59389,59390,59391,59392,9472,9473,9474,9475,9476,9477,9478,9479,9480,9481,9482,9483,9484,9485,9486,9487,9488,9489,9490,9491,9492,9493,9494,9495,9496,9497,9498,9499,9500,9501,9502,9503,9504,9505,9506,9507,9508,9509,9510,9511,9512,9513,9514,9515,9516,9517,9518,9519,9520,9521,9522,9523,9524,9525,9526,9527,9528,9529,9530,9531,9532,9533,9534,9535,9536,9537,9538,9539,9540,9541,9542,9543,9544,9545,9546,9547,59393,59394,59395,59396,59397,59398,59399,59400,59401,59402,59403,59404,59405,59406,59407,29404,29405,29407,29410,29411,29412,29413,29414,29415,29418,29419,29429,29430,29433,29437,29438,29439,29440,29442,29444,29445,29446,29447,29448,29449,29451,29452,29453,29455,29456,29457,29458,29460,29464,29465,29466,29471,29472,29475,29476,29478,29479,29480,29485,29487,29488,29490,29491,29493,29494,29498,29499,29500,29501,29504,29505,29506,29507,29508,29509,29510,29511,29512,29513,29514,29515,29516,29518,29519,29521,29523,29524,29525,29526,29528,29529,29530,29531,29532,29533,29534,29535,29537,29538,29539,29540,29541,29542,29543,29544,29545,29546,29547,29550,29552,29553,57344,57345,57346,57347,57348,57349,57350,57351,57352,57353,57354,57355,57356,57357,57358,57359,57360,57361,57362,57363,57364,57365,57366,57367,57368,57369,57370,57371,57372,57373,57374,57375,57376,57377,57378,57379,57380,57381,57382,57383,57384,57385,57386,57387,57388,57389,57390,57391,57392,57393,57394,57395,57396,57397,57398,57399,57400,57401,57402,57403,57404,57405,57406,57407,57408,57409,57410,57411,57412,57413,57414,57415,57416,57417,57418,57419,57420,57421,57422,57423,57424,57425,57426,57427,57428,57429,57430,57431,57432,57433,57434,57435,57436,57437,29554,29555,29556,29557,29558,29559,29560,29561,29562,29563,29564,29565,29567,29568,29569,29570,29571,29573,29574,29576,29578,29580,29581,29583,29584,29586,29587,29588,29589,29591,29592,29593,29594,29596,29597,29598,29600,29601,29603,29604,29605,29606,29607,29608,29610,29612,29613,29617,29620,29621,29622,29624,29625,29628,29629,29630,29631,29633,29635,29636,29637,29638,29639,29643,29644,29646,29650,29651,29652,29653,29654,29655,29656,29658,29659,29660,29661,29663,29665,29666,29667,29668,29670,29672,29674,29675,29676,29678,29679,29680,29681,29683,29684,29685,29686,29687,57438,57439,57440,57441,57442,57443,57444,57445,57446,57447,57448,57449,57450,57451,57452,57453,57454,57455,57456,57457,57458,57459,57460,57461,57462,57463,57464,57465,57466,57467,57468,57469,57470,57471,57472,57473,57474,57475,57476,57477,57478,57479,57480,57481,57482,57483,57484,57485,57486,57487,57488,57489,57490,57491,57492,57493,57494,57495,57496,57497,57498,57499,57500,57501,57502,57503,57504,57505,57506,57507,57508,57509,57510,57511,57512,57513,57514,57515,57516,57517,57518,57519,57520,57521,57522,57523,57524,57525,57526,57527,57528,57529,57530,57531,29688,29689,29690,29691,29692,29693,29694,29695,29696,29697,29698,29700,29703,29704,29707,29708,29709,29710,29713,29714,29715,29716,29717,29718,29719,29720,29721,29724,29725,29726,29727,29728,29729,29731,29732,29735,29737,29739,29741,29743,29745,29746,29751,29752,29753,29754,29755,29757,29758,29759,29760,29762,29763,29764,29765,29766,29767,29768,29769,29770,29771,29772,29773,29774,29775,29776,29777,29778,29779,29780,29782,29784,29789,29792,29793,29794,29795,29796,29797,29798,29799,29800,29801,29802,29803,29804,29806,29807,29809,29810,29811,29812,29813,29816,29817,29818,57532,57533,57534,57535,57536,57537,57538,57539,57540,57541,57542,57543,57544,57545,57546,57547,57548,57549,57550,57551,57552,57553,57554,57555,57556,57557,57558,57559,57560,57561,57562,57563,57564,57565,57566,57567,57568,57569,57570,57571,57572,57573,57574,57575,57576,57577,57578,57579,57580,57581,57582,57583,57584,57585,57586,57587,57588,57589,57590,57591,57592,57593,57594,57595,57596,57597,57598,57599,57600,57601,57602,57603,57604,57605,57606,57607,57608,57609,57610,57611,57612,57613,57614,57615,57616,57617,57618,57619,57620,57621,57622,57623,57624,57625,29819,29820,29821,29823,29826,29828,29829,29830,29832,29833,29834,29836,29837,29839,29841,29842,29843,29844,29845,29846,29847,29848,29849,29850,29851,29853,29855,29856,29857,29858,29859,29860,29861,29862,29866,29867,29868,29869,29870,29871,29872,29873,29874,29875,29876,29877,29878,29879,29880,29881,29883,29884,29885,29886,29887,29888,29889,29890,29891,29892,29893,29894,29895,29896,29897,29898,29899,29900,29901,29902,29903,29904,29905,29907,29908,29909,29910,29911,29912,29913,29914,29915,29917,29919,29921,29925,29927,29928,29929,29930,29931,29932,29933,29936,29937,29938,57626,57627,57628,57629,57630,57631,57632,57633,57634,57635,57636,57637,57638,57639,57640,57641,57642,57643,57644,57645,57646,57647,57648,57649,57650,57651,57652,57653,57654,57655,57656,57657,57658,57659,57660,57661,57662,57663,57664,57665,57666,57667,57668,57669,57670,57671,57672,57673,57674,57675,57676,57677,57678,57679,57680,57681,57682,57683,57684,57685,57686,57687,57688,57689,57690,57691,57692,57693,57694,57695,57696,57697,57698,57699,57700,57701,57702,57703,57704,57705,57706,57707,57708,57709,57710,57711,57712,57713,57714,57715,57716,57717,57718,57719,29939,29941,29944,29945,29946,29947,29948,29949,29950,29952,29953,29954,29955,29957,29958,29959,29960,29961,29962,29963,29964,29966,29968,29970,29972,29973,29974,29975,29979,29981,29982,29984,29985,29986,29987,29988,29990,29991,29994,29998,30004,30006,30009,30012,30013,30015,30017,30018,30019,30020,30022,30023,30025,30026,30029,30032,30033,30034,30035,30037,30038,30039,30040,30045,30046,30047,30048,30049,30050,30051,30052,30055,30056,30057,30059,30060,30061,30062,30063,30064,30065,30067,30069,30070,30071,30074,30075,30076,30077,30078,30080,30081,30082,30084,30085,30087,57720,57721,57722,57723,57724,57725,57726,57727,57728,57729,57730,57731,57732,57733,57734,57735,57736,57737,57738,57739,57740,57741,57742,57743,57744,57745,57746,57747,57748,57749,57750,57751,57752,57753,57754,57755,57756,57757,57758,57759,57760,57761,57762,57763,57764,57765,57766,57767,57768,57769,57770,57771,57772,57773,57774,57775,57776,57777,57778,57779,57780,57781,57782,57783,57784,57785,57786,57787,57788,57789,57790,57791,57792,57793,57794,57795,57796,57797,57798,57799,57800,57801,57802,57803,57804,57805,57806,57807,57808,57809,57810,57811,57812,57813,30088,30089,30090,30092,30093,30094,30096,30099,30101,30104,30107,30108,30110,30114,30118,30119,30120,30121,30122,30125,30134,30135,30138,30139,30143,30144,30145,30150,30155,30156,30158,30159,30160,30161,30163,30167,30169,30170,30172,30173,30175,30176,30177,30181,30185,30188,30189,30190,30191,30194,30195,30197,30198,30199,30200,30202,30203,30205,30206,30210,30212,30214,30215,30216,30217,30219,30221,30222,30223,30225,30226,30227,30228,30230,30234,30236,30237,30238,30241,30243,30247,30248,30252,30254,30255,30257,30258,30262,30263,30265,30266,30267,30269,30273,30274,30276,57814,57815,57816,57817,57818,57819,57820,57821,57822,57823,57824,57825,57826,57827,57828,57829,57830,57831,57832,57833,57834,57835,57836,57837,57838,57839,57840,57841,57842,57843,57844,57845,57846,57847,57848,57849,57850,57851,57852,57853,57854,57855,57856,57857,57858,57859,57860,57861,57862,57863,57864,57865,57866,57867,57868,57869,57870,57871,57872,57873,57874,57875,57876,57877,57878,57879,57880,57881,57882,57883,57884,57885,57886,57887,57888,57889,57890,57891,57892,57893,57894,57895,57896,57897,57898,57899,57900,57901,57902,57903,57904,57905,57906,57907,30277,30278,30279,30280,30281,30282,30283,30286,30287,30288,30289,30290,30291,30293,30295,30296,30297,30298,30299,30301,30303,30304,30305,30306,30308,30309,30310,30311,30312,30313,30314,30316,30317,30318,30320,30321,30322,30323,30324,30325,30326,30327,30329,30330,30332,30335,30336,30337,30339,30341,30345,30346,30348,30349,30351,30352,30354,30356,30357,30359,30360,30362,30363,30364,30365,30366,30367,30368,30369,30370,30371,30373,30374,30375,30376,30377,30378,30379,30380,30381,30383,30384,30387,30389,30390,30391,30392,30393,30394,30395,30396,30397,30398,30400,30401,30403,21834,38463,22467,25384,21710,21769,21696,30353,30284,34108,30702,33406,30861,29233,38552,38797,27688,23433,20474,25353,26263,23736,33018,26696,32942,26114,30414,20985,25942,29100,32753,34948,20658,22885,25034,28595,33453,25420,25170,21485,21543,31494,20843,30116,24052,25300,36299,38774,25226,32793,22365,38712,32610,29240,30333,26575,30334,25670,20336,36133,25308,31255,26001,29677,25644,25203,33324,39041,26495,29256,25198,25292,20276,29923,21322,21150,32458,37030,24110,26758,27036,33152,32465,26834,30917,34444,38225,20621,35876,33502,32990,21253,35090,21093,30404,30407,30409,30411,30412,30419,30421,30425,30426,30428,30429,30430,30432,30433,30434,30435,30436,30438,30439,30440,30441,30442,30443,30444,30445,30448,30451,30453,30454,30455,30458,30459,30461,30463,30464,30466,30467,30469,30470,30474,30476,30478,30479,30480,30481,30482,30483,30484,30485,30486,30487,30488,30491,30492,30493,30494,30497,30499,30500,30501,30503,30506,30507,30508,30510,30512,30513,30514,30515,30516,30521,30523,30525,30526,30527,30530,30532,30533,30534,30536,30537,30538,30539,30540,30541,30542,30543,30546,30547,30548,30549,30550,30551,30552,30553,30556,34180,38649,20445,22561,39281,23453,25265,25253,26292,35961,40077,29190,26479,30865,24754,21329,21271,36744,32972,36125,38049,20493,29384,22791,24811,28953,34987,22868,33519,26412,31528,23849,32503,29997,27893,36454,36856,36924,40763,27604,37145,31508,24444,30887,34006,34109,27605,27609,27606,24065,24199,30201,38381,25949,24330,24517,36767,22721,33218,36991,38491,38829,36793,32534,36140,25153,20415,21464,21342,36776,36777,36779,36941,26631,24426,33176,34920,40150,24971,21035,30250,24428,25996,28626,28392,23486,25672,20853,20912,26564,19993,31177,39292,28851,30557,30558,30559,30560,30564,30567,30569,30570,30573,30574,30575,30576,30577,30578,30579,30580,30581,30582,30583,30584,30586,30587,30588,30593,30594,30595,30598,30599,30600,30601,30602,30603,30607,30608,30611,30612,30613,30614,30615,30616,30617,30618,30619,30620,30621,30622,30625,30627,30628,30630,30632,30635,30637,30638,30639,30641,30642,30644,30646,30647,30648,30649,30650,30652,30654,30656,30657,30658,30659,30660,30661,30662,30663,30664,30665,30666,30667,30668,30670,30671,30672,30673,30674,30675,30676,30677,30678,30680,30681,30682,30685,30686,30687,30688,30689,30692,30149,24182,29627,33760,25773,25320,38069,27874,21338,21187,25615,38082,31636,20271,24091,33334,33046,33162,28196,27850,39539,25429,21340,21754,34917,22496,19981,24067,27493,31807,37096,24598,25830,29468,35009,26448,25165,36130,30572,36393,37319,24425,33756,34081,39184,21442,34453,27531,24813,24808,28799,33485,33329,20179,27815,34255,25805,31961,27133,26361,33609,21397,31574,20391,20876,27979,23618,36461,25554,21449,33580,33590,26597,30900,25661,23519,23700,24046,35815,25286,26612,35962,25600,25530,34633,39307,35863,32544,38130,20135,38416,39076,26124,29462,30694,30696,30698,30703,30704,30705,30706,30708,30709,30711,30713,30714,30715,30716,30723,30724,30725,30726,30727,30728,30730,30731,30734,30735,30736,30739,30741,30745,30747,30750,30752,30753,30754,30756,30760,30762,30763,30766,30767,30769,30770,30771,30773,30774,30781,30783,30785,30786,30787,30788,30790,30792,30793,30794,30795,30797,30799,30801,30803,30804,30808,30809,30810,30811,30812,30814,30815,30816,30817,30818,30819,30820,30821,30822,30823,30824,30825,30831,30832,30833,30834,30835,30836,30837,30838,30840,30841,30842,30843,30845,30846,30847,30848,30849,30850,30851,22330,23581,24120,38271,20607,32928,21378,25950,30021,21809,20513,36229,25220,38046,26397,22066,28526,24034,21557,28818,36710,25199,25764,25507,24443,28552,37108,33251,36784,23576,26216,24561,27785,38472,36225,34924,25745,31216,22478,27225,25104,21576,20056,31243,24809,28548,35802,25215,36894,39563,31204,21507,30196,25345,21273,27744,36831,24347,39536,32827,40831,20360,23610,36196,32709,26021,28861,20805,20914,34411,23815,23456,25277,37228,30068,36364,31264,24833,31609,20167,32504,30597,19985,33261,21021,20986,27249,21416,36487,38148,38607,28353,38500,26970,30852,30853,30854,30856,30858,30859,30863,30864,30866,30868,30869,30870,30873,30877,30878,30880,30882,30884,30886,30888,30889,30890,30891,30892,30893,30894,30895,30901,30902,30903,30904,30906,30907,30908,30909,30911,30912,30914,30915,30916,30918,30919,30920,30924,30925,30926,30927,30929,30930,30931,30934,30935,30936,30938,30939,30940,30941,30942,30943,30944,30945,30946,30947,30948,30949,30950,30951,30953,30954,30955,30957,30958,30959,30960,30961,30963,30965,30966,30968,30969,30971,30972,30973,30974,30975,30976,30978,30979,30980,30982,30983,30984,30985,30986,30987,30988,30784,20648,30679,25616,35302,22788,25571,24029,31359,26941,20256,33337,21912,20018,30126,31383,24162,24202,38383,21019,21561,28810,25462,38180,22402,26149,26943,37255,21767,28147,32431,34850,25139,32496,30133,33576,30913,38604,36766,24904,29943,35789,27492,21050,36176,27425,32874,33905,22257,21254,20174,19995,20945,31895,37259,31751,20419,36479,31713,31388,25703,23828,20652,33030,30209,31929,28140,32736,26449,23384,23544,30923,25774,25619,25514,25387,38169,25645,36798,31572,30249,25171,22823,21574,27513,20643,25140,24102,27526,20195,36151,34955,24453,36910,30989,30990,30991,30992,30993,30994,30996,30997,30998,30999,31000,31001,31002,31003,31004,31005,31007,31008,31009,31010,31011,31013,31014,31015,31016,31017,31018,31019,31020,31021,31022,31023,31024,31025,31026,31027,31029,31030,31031,31032,31033,31037,31039,31042,31043,31044,31045,31047,31050,31051,31052,31053,31054,31055,31056,31057,31058,31060,31061,31064,31065,31073,31075,31076,31078,31081,31082,31083,31084,31086,31088,31089,31090,31091,31092,31093,31094,31097,31099,31100,31101,31102,31103,31106,31107,31110,31111,31112,31113,31115,31116,31117,31118,31120,31121,31122,24608,32829,25285,20025,21333,37112,25528,32966,26086,27694,20294,24814,28129,35806,24377,34507,24403,25377,20826,33633,26723,20992,25443,36424,20498,23707,31095,23548,21040,31291,24764,36947,30423,24503,24471,30340,36460,28783,30331,31561,30634,20979,37011,22564,20302,28404,36842,25932,31515,29380,28068,32735,23265,25269,24213,22320,33922,31532,24093,24351,36882,32532,39072,25474,28359,30872,28857,20856,38747,22443,30005,20291,30008,24215,24806,22880,28096,27583,30857,21500,38613,20939,20993,25481,21514,38035,35843,36300,29241,30879,34678,36845,35853,21472,31123,31124,31125,31126,31127,31128,31129,31131,31132,31133,31134,31135,31136,31137,31138,31139,31140,31141,31142,31144,31145,31146,31147,31148,31149,31150,31151,31152,31153,31154,31156,31157,31158,31159,31160,31164,31167,31170,31172,31173,31175,31176,31178,31180,31182,31183,31184,31187,31188,31190,31191,31193,31194,31195,31196,31197,31198,31200,31201,31202,31205,31208,31210,31212,31214,31217,31218,31219,31220,31221,31222,31223,31225,31226,31228,31230,31231,31233,31236,31237,31239,31240,31241,31242,31244,31247,31248,31249,31250,31251,31253,31254,31256,31257,31259,31260,19969,30447,21486,38025,39030,40718,38189,23450,35746,20002,19996,20908,33891,25026,21160,26635,20375,24683,20923,27934,20828,25238,26007,38497,35910,36887,30168,37117,30563,27602,29322,29420,35835,22581,30585,36172,26460,38208,32922,24230,28193,22930,31471,30701,38203,27573,26029,32526,22534,20817,38431,23545,22697,21544,36466,25958,39039,22244,38045,30462,36929,25479,21702,22810,22842,22427,36530,26421,36346,33333,21057,24816,22549,34558,23784,40517,20420,39069,35769,23077,24694,21380,25212,36943,37122,39295,24681,32780,20799,32819,23572,39285,27953,20108,31261,31263,31265,31266,31268,31269,31270,31271,31272,31273,31274,31275,31276,31277,31278,31279,31280,31281,31282,31284,31285,31286,31288,31290,31294,31296,31297,31298,31299,31300,31301,31303,31304,31305,31306,31307,31308,31309,31310,31311,31312,31314,31315,31316,31317,31318,31320,31321,31322,31323,31324,31325,31326,31327,31328,31329,31330,31331,31332,31333,31334,31335,31336,31337,31338,31339,31340,31341,31342,31343,31345,31346,31347,31349,31355,31356,31357,31358,31362,31365,31367,31369,31370,31371,31372,31374,31375,31376,31379,31380,31385,31386,31387,31390,31393,31394,36144,21457,32602,31567,20240,20047,38400,27861,29648,34281,24070,30058,32763,27146,30718,38034,32321,20961,28902,21453,36820,33539,36137,29359,39277,27867,22346,33459,26041,32938,25151,38450,22952,20223,35775,32442,25918,33778,38750,21857,39134,32933,21290,35837,21536,32954,24223,27832,36153,33452,37210,21545,27675,20998,32439,22367,28954,27774,31881,22859,20221,24575,24868,31914,20016,23553,26539,34562,23792,38155,39118,30127,28925,36898,20911,32541,35773,22857,20964,20315,21542,22827,25975,32932,23413,25206,25282,36752,24133,27679,31526,20239,20440,26381,31395,31396,31399,31401,31402,31403,31406,31407,31408,31409,31410,31412,31413,31414,31415,31416,31417,31418,31419,31420,31421,31422,31424,31425,31426,31427,31428,31429,31430,31431,31432,31433,31434,31436,31437,31438,31439,31440,31441,31442,31443,31444,31445,31447,31448,31450,31451,31452,31453,31457,31458,31460,31463,31464,31465,31466,31467,31468,31470,31472,31473,31474,31475,31476,31477,31478,31479,31480,31483,31484,31486,31488,31489,31490,31493,31495,31497,31500,31501,31502,31504,31506,31507,31510,31511,31512,31514,31516,31517,31519,31521,31522,31523,31527,31529,31533,28014,28074,31119,34993,24343,29995,25242,36741,20463,37340,26023,33071,33105,24220,33104,36212,21103,35206,36171,22797,20613,20184,38428,29238,33145,36127,23500,35747,38468,22919,32538,21648,22134,22030,35813,25913,27010,38041,30422,28297,24178,29976,26438,26577,31487,32925,36214,24863,31174,25954,36195,20872,21018,38050,32568,32923,32434,23703,28207,26464,31705,30347,39640,33167,32660,31957,25630,38224,31295,21578,21733,27468,25601,25096,40509,33011,30105,21106,38761,33883,26684,34532,38401,38548,38124,20010,21508,32473,26681,36319,32789,26356,24218,32697,31535,31536,31538,31540,31541,31542,31543,31545,31547,31549,31551,31552,31553,31554,31555,31556,31558,31560,31562,31565,31566,31571,31573,31575,31577,31580,31582,31583,31585,31587,31588,31589,31590,31591,31592,31593,31594,31595,31596,31597,31599,31600,31603,31604,31606,31608,31610,31612,31613,31615,31617,31618,31619,31620,31622,31623,31624,31625,31626,31627,31628,31630,31631,31633,31634,31635,31638,31640,31641,31642,31643,31646,31647,31648,31651,31652,31653,31662,31663,31664,31666,31667,31669,31670,31671,31673,31674,31675,31676,31677,31678,31679,31680,31682,31683,31684,22466,32831,26775,24037,25915,21151,24685,40858,20379,36524,20844,23467,24339,24041,27742,25329,36129,20849,38057,21246,27807,33503,29399,22434,26500,36141,22815,36764,33735,21653,31629,20272,27837,23396,22993,40723,21476,34506,39592,35895,32929,25925,39038,22266,38599,21038,29916,21072,23521,25346,35074,20054,25296,24618,26874,20851,23448,20896,35266,31649,39302,32592,24815,28748,36143,20809,24191,36891,29808,35268,22317,30789,24402,40863,38394,36712,39740,35809,30328,26690,26588,36330,36149,21053,36746,28378,26829,38149,37101,22269,26524,35065,36807,21704,31685,31688,31689,31690,31691,31693,31694,31695,31696,31698,31700,31701,31702,31703,31704,31707,31708,31710,31711,31712,31714,31715,31716,31719,31720,31721,31723,31724,31725,31727,31728,31730,31731,31732,31733,31734,31736,31737,31738,31739,31741,31743,31744,31745,31746,31747,31748,31749,31750,31752,31753,31754,31757,31758,31760,31761,31762,31763,31764,31765,31767,31768,31769,31770,31771,31772,31773,31774,31776,31777,31778,31779,31780,31781,31784,31785,31787,31788,31789,31790,31791,31792,31793,31794,31795,31796,31797,31798,31799,31801,31802,31803,31804,31805,31806,31810,39608,23401,28023,27686,20133,23475,39559,37219,25000,37039,38889,21547,28085,23506,20989,21898,32597,32752,25788,25421,26097,25022,24717,28938,27735,27721,22831,26477,33322,22741,22158,35946,27627,37085,22909,32791,21495,28009,21621,21917,33655,33743,26680,31166,21644,20309,21512,30418,35977,38402,27827,28088,36203,35088,40548,36154,22079,40657,30165,24456,29408,24680,21756,20136,27178,34913,24658,36720,21700,28888,34425,40511,27946,23439,24344,32418,21897,20399,29492,21564,21402,20505,21518,21628,20046,24573,29786,22774,33899,32993,34676,29392,31946,28246,31811,31812,31813,31814,31815,31816,31817,31818,31819,31820,31822,31823,31824,31825,31826,31827,31828,31829,31830,31831,31832,31833,31834,31835,31836,31837,31838,31839,31840,31841,31842,31843,31844,31845,31846,31847,31848,31849,31850,31851,31852,31853,31854,31855,31856,31857,31858,31861,31862,31863,31864,31865,31866,31870,31871,31872,31873,31874,31875,31876,31877,31878,31879,31880,31882,31883,31884,31885,31886,31887,31888,31891,31892,31894,31897,31898,31899,31904,31905,31907,31910,31911,31912,31913,31915,31916,31917,31919,31920,31924,31925,31926,31927,31928,31930,31931,24359,34382,21804,25252,20114,27818,25143,33457,21719,21326,29502,28369,30011,21010,21270,35805,27088,24458,24576,28142,22351,27426,29615,26707,36824,32531,25442,24739,21796,30186,35938,28949,28067,23462,24187,33618,24908,40644,30970,34647,31783,30343,20976,24822,29004,26179,24140,24653,35854,28784,25381,36745,24509,24674,34516,22238,27585,24724,24935,21321,24800,26214,36159,31229,20250,28905,27719,35763,35826,32472,33636,26127,23130,39746,27985,28151,35905,27963,20249,28779,33719,25110,24785,38669,36135,31096,20987,22334,22522,26426,30072,31293,31215,31637,31935,31936,31938,31939,31940,31942,31945,31947,31950,31951,31952,31953,31954,31955,31956,31960,31962,31963,31965,31966,31969,31970,31971,31972,31973,31974,31975,31977,31978,31979,31980,31981,31982,31984,31985,31986,31987,31988,31989,31990,31991,31993,31994,31996,31997,31998,31999,32000,32001,32002,32003,32004,32005,32006,32007,32008,32009,32011,32012,32013,32014,32015,32016,32017,32018,32019,32020,32021,32022,32023,32024,32025,32026,32027,32028,32029,32030,32031,32033,32035,32036,32037,32038,32040,32041,32042,32044,32045,32046,32048,32049,32050,32051,32052,32053,32054,32908,39269,36857,28608,35749,40481,23020,32489,32521,21513,26497,26840,36753,31821,38598,21450,24613,30142,27762,21363,23241,32423,25380,20960,33034,24049,34015,25216,20864,23395,20238,31085,21058,24760,27982,23492,23490,35745,35760,26082,24524,38469,22931,32487,32426,22025,26551,22841,20339,23478,21152,33626,39050,36158,30002,38078,20551,31292,20215,26550,39550,23233,27516,30417,22362,23574,31546,38388,29006,20860,32937,33392,22904,32516,33575,26816,26604,30897,30839,25315,25441,31616,20461,21098,20943,33616,27099,37492,36341,36145,35265,38190,31661,20214,32055,32056,32057,32058,32059,32060,32061,32062,32063,32064,32065,32066,32067,32068,32069,32070,32071,32072,32073,32074,32075,32076,32077,32078,32079,32080,32081,32082,32083,32084,32085,32086,32087,32088,32089,32090,32091,32092,32093,32094,32095,32096,32097,32098,32099,32100,32101,32102,32103,32104,32105,32106,32107,32108,32109,32111,32112,32113,32114,32115,32116,32117,32118,32120,32121,32122,32123,32124,32125,32126,32127,32128,32129,32130,32131,32132,32133,32134,32135,32136,32137,32138,32139,32140,32141,32142,32143,32144,32145,32146,32147,32148,32149,32150,32151,32152,20581,33328,21073,39279,28176,28293,28071,24314,20725,23004,23558,27974,27743,30086,33931,26728,22870,35762,21280,37233,38477,34121,26898,30977,28966,33014,20132,37066,27975,39556,23047,22204,25605,38128,30699,20389,33050,29409,35282,39290,32564,32478,21119,25945,37237,36735,36739,21483,31382,25581,25509,30342,31224,34903,38454,25130,21163,33410,26708,26480,25463,30571,31469,27905,32467,35299,22992,25106,34249,33445,30028,20511,20171,30117,35819,23626,24062,31563,26020,37329,20170,27941,35167,32039,38182,20165,35880,36827,38771,26187,31105,36817,28908,28024,32153,32154,32155,32156,32157,32158,32159,32160,32161,32162,32163,32164,32165,32167,32168,32169,32170,32171,32172,32173,32175,32176,32177,32178,32179,32180,32181,32182,32183,32184,32185,32186,32187,32188,32189,32190,32191,32192,32193,32194,32195,32196,32197,32198,32199,32200,32201,32202,32203,32204,32205,32206,32207,32208,32209,32210,32211,32212,32213,32214,32215,32216,32217,32218,32219,32220,32221,32222,32223,32224,32225,32226,32227,32228,32229,32230,32231,32232,32233,32234,32235,32236,32237,32238,32239,32240,32241,32242,32243,32244,32245,32246,32247,32248,32249,32250,23613,21170,33606,20834,33550,30555,26230,40120,20140,24778,31934,31923,32463,20117,35686,26223,39048,38745,22659,25964,38236,24452,30153,38742,31455,31454,20928,28847,31384,25578,31350,32416,29590,38893,20037,28792,20061,37202,21417,25937,26087,33276,33285,21646,23601,30106,38816,25304,29401,30141,23621,39545,33738,23616,21632,30697,20030,27822,32858,25298,25454,24040,20855,36317,36382,38191,20465,21477,24807,28844,21095,25424,40515,23071,20518,30519,21367,32482,25733,25899,25225,25496,20500,29237,35273,20915,35776,32477,22343,33740,38055,20891,21531,23803,32251,32252,32253,32254,32255,32256,32257,32258,32259,32260,32261,32262,32263,32264,32265,32266,32267,32268,32269,32270,32271,32272,32273,32274,32275,32276,32277,32278,32279,32280,32281,32282,32283,32284,32285,32286,32287,32288,32289,32290,32291,32292,32293,32294,32295,32296,32297,32298,32299,32300,32301,32302,32303,32304,32305,32306,32307,32308,32309,32310,32311,32312,32313,32314,32316,32317,32318,32319,32320,32322,32323,32324,32325,32326,32328,32329,32330,32331,32332,32333,32334,32335,32336,32337,32338,32339,32340,32341,32342,32343,32344,32345,32346,32347,32348,32349,20426,31459,27994,37089,39567,21888,21654,21345,21679,24320,25577,26999,20975,24936,21002,22570,21208,22350,30733,30475,24247,24951,31968,25179,25239,20130,28821,32771,25335,28900,38752,22391,33499,26607,26869,30933,39063,31185,22771,21683,21487,28212,20811,21051,23458,35838,32943,21827,22438,24691,22353,21549,31354,24656,23380,25511,25248,21475,25187,23495,26543,21741,31391,33510,37239,24211,35044,22840,22446,25358,36328,33007,22359,31607,20393,24555,23485,27454,21281,31568,29378,26694,30719,30518,26103,20917,20111,30420,23743,31397,33909,22862,39745,20608,32350,32351,32352,32353,32354,32355,32356,32357,32358,32359,32360,32361,32362,32363,32364,32365,32366,32367,32368,32369,32370,32371,32372,32373,32374,32375,32376,32377,32378,32379,32380,32381,32382,32383,32384,32385,32387,32388,32389,32390,32391,32392,32393,32394,32395,32396,32397,32398,32399,32400,32401,32402,32403,32404,32405,32406,32407,32408,32409,32410,32412,32413,32414,32430,32436,32443,32444,32470,32484,32492,32505,32522,32528,32542,32567,32569,32571,32572,32573,32574,32575,32576,32577,32579,32582,32583,32584,32585,32586,32587,32588,32589,32590,32591,32594,32595,39304,24871,28291,22372,26118,25414,22256,25324,25193,24275,38420,22403,25289,21895,34593,33098,36771,21862,33713,26469,36182,34013,23146,26639,25318,31726,38417,20848,28572,35888,25597,35272,25042,32518,28866,28389,29701,27028,29436,24266,37070,26391,28010,25438,21171,29282,32769,20332,23013,37226,28889,28061,21202,20048,38647,38253,34174,30922,32047,20769,22418,25794,32907,31867,27882,26865,26974,20919,21400,26792,29313,40654,31729,29432,31163,28435,29702,26446,37324,40100,31036,33673,33620,21519,26647,20029,21385,21169,30782,21382,21033,20616,20363,20432,32598,32601,32603,32604,32605,32606,32608,32611,32612,32613,32614,32615,32619,32620,32621,32623,32624,32627,32629,32630,32631,32632,32634,32635,32636,32637,32639,32640,32642,32643,32644,32645,32646,32647,32648,32649,32651,32653,32655,32656,32657,32658,32659,32661,32662,32663,32664,32665,32667,32668,32672,32674,32675,32677,32678,32680,32681,32682,32683,32684,32685,32686,32689,32691,32692,32693,32694,32695,32698,32699,32702,32704,32706,32707,32708,32710,32711,32712,32713,32715,32717,32719,32720,32721,32722,32723,32726,32727,32729,32730,32731,32732,32733,32734,32738,32739,30178,31435,31890,27813,38582,21147,29827,21737,20457,32852,33714,36830,38256,24265,24604,28063,24088,25947,33080,38142,24651,28860,32451,31918,20937,26753,31921,33391,20004,36742,37327,26238,20142,35845,25769,32842,20698,30103,29134,23525,36797,28518,20102,25730,38243,24278,26009,21015,35010,28872,21155,29454,29747,26519,30967,38678,20020,37051,40158,28107,20955,36161,21533,25294,29618,33777,38646,40836,38083,20278,32666,20940,28789,38517,23725,39046,21478,20196,28316,29705,27060,30827,39311,30041,21016,30244,27969,26611,20845,40857,32843,21657,31548,31423,32740,32743,32744,32746,32747,32748,32749,32751,32754,32756,32757,32758,32759,32760,32761,32762,32765,32766,32767,32770,32775,32776,32777,32778,32782,32783,32785,32787,32794,32795,32797,32798,32799,32801,32803,32804,32811,32812,32813,32814,32815,32816,32818,32820,32825,32826,32828,32830,32832,32833,32836,32837,32839,32840,32841,32846,32847,32848,32849,32851,32853,32854,32855,32857,32859,32860,32861,32862,32863,32864,32865,32866,32867,32868,32869,32870,32871,32872,32875,32876,32877,32878,32879,32880,32882,32883,32884,32885,32886,32887,32888,32889,32890,32891,32892,32893,38534,22404,25314,38471,27004,23044,25602,31699,28431,38475,33446,21346,39045,24208,28809,25523,21348,34383,40065,40595,30860,38706,36335,36162,40575,28510,31108,24405,38470,25134,39540,21525,38109,20387,26053,23653,23649,32533,34385,27695,24459,29575,28388,32511,23782,25371,23402,28390,21365,20081,25504,30053,25249,36718,20262,20177,27814,32438,35770,33821,34746,32599,36923,38179,31657,39585,35064,33853,27931,39558,32476,22920,40635,29595,30721,34434,39532,39554,22043,21527,22475,20080,40614,21334,36808,33033,30610,39314,34542,28385,34067,26364,24930,28459,32894,32897,32898,32901,32904,32906,32909,32910,32911,32912,32913,32914,32916,32917,32919,32921,32926,32931,32934,32935,32936,32940,32944,32947,32949,32950,32952,32953,32955,32965,32967,32968,32969,32970,32971,32975,32976,32977,32978,32979,32980,32981,32984,32991,32992,32994,32995,32998,33006,33013,33015,33017,33019,33022,33023,33024,33025,33027,33028,33029,33031,33032,33035,33036,33045,33047,33049,33051,33052,33053,33055,33056,33057,33058,33059,33060,33061,33062,33063,33064,33065,33066,33067,33069,33070,33072,33075,33076,33077,33079,33081,33082,33083,33084,33085,33087,35881,33426,33579,30450,27667,24537,33725,29483,33541,38170,27611,30683,38086,21359,33538,20882,24125,35980,36152,20040,29611,26522,26757,37238,38665,29028,27809,30473,23186,38209,27599,32654,26151,23504,22969,23194,38376,38391,20204,33804,33945,27308,30431,38192,29467,26790,23391,30511,37274,38753,31964,36855,35868,24357,31859,31192,35269,27852,34588,23494,24130,26825,30496,32501,20885,20813,21193,23081,32517,38754,33495,25551,30596,34256,31186,28218,24217,22937,34065,28781,27665,25279,30399,25935,24751,38397,26126,34719,40483,38125,21517,21629,35884,25720,33088,33089,33090,33091,33092,33093,33095,33097,33101,33102,33103,33106,33110,33111,33112,33115,33116,33117,33118,33119,33121,33122,33123,33124,33126,33128,33130,33131,33132,33135,33138,33139,33141,33142,33143,33144,33153,33155,33156,33157,33158,33159,33161,33163,33164,33165,33166,33168,33170,33171,33172,33173,33174,33175,33177,33178,33182,33183,33184,33185,33186,33188,33189,33191,33193,33195,33196,33197,33198,33199,33200,33201,33202,33204,33205,33206,33207,33208,33209,33212,33213,33214,33215,33220,33221,33223,33224,33225,33227,33229,33230,33231,33232,33233,33234,33235,25721,34321,27169,33180,30952,25705,39764,25273,26411,33707,22696,40664,27819,28448,23518,38476,35851,29279,26576,25287,29281,20137,22982,27597,22675,26286,24149,21215,24917,26408,30446,30566,29287,31302,25343,21738,21584,38048,37027,23068,32435,27670,20035,22902,32784,22856,21335,30007,38590,22218,25376,33041,24700,38393,28118,21602,39297,20869,23273,33021,22958,38675,20522,27877,23612,25311,20320,21311,33147,36870,28346,34091,25288,24180,30910,25781,25467,24565,23064,37247,40479,23615,25423,32834,23421,21870,38218,38221,28037,24744,26592,29406,20957,23425,33236,33237,33238,33239,33240,33241,33242,33243,33244,33245,33246,33247,33248,33249,33250,33252,33253,33254,33256,33257,33259,33262,33263,33264,33265,33266,33269,33270,33271,33272,33273,33274,33277,33279,33283,33287,33288,33289,33290,33291,33294,33295,33297,33299,33301,33302,33303,33304,33305,33306,33309,33312,33316,33317,33318,33319,33321,33326,33330,33338,33340,33341,33343,33344,33345,33346,33347,33349,33350,33352,33354,33356,33357,33358,33360,33361,33362,33363,33364,33365,33366,33367,33369,33371,33372,33373,33374,33376,33377,33378,33379,33380,33381,33382,33383,33385,25319,27870,29275,25197,38062,32445,33043,27987,20892,24324,22900,21162,24594,22899,26262,34384,30111,25386,25062,31983,35834,21734,27431,40485,27572,34261,21589,20598,27812,21866,36276,29228,24085,24597,29750,25293,25490,29260,24472,28227,27966,25856,28504,30424,30928,30460,30036,21028,21467,20051,24222,26049,32810,32982,25243,21638,21032,28846,34957,36305,27873,21624,32986,22521,35060,36180,38506,37197,20329,27803,21943,30406,30768,25256,28921,28558,24429,34028,26842,30844,31735,33192,26379,40527,25447,30896,22383,30738,38713,25209,25259,21128,29749,27607,33386,33387,33388,33389,33393,33397,33398,33399,33400,33403,33404,33408,33409,33411,33413,33414,33415,33417,33420,33424,33427,33428,33429,33430,33434,33435,33438,33440,33442,33443,33447,33458,33461,33462,33466,33467,33468,33471,33472,33474,33475,33477,33478,33481,33488,33494,33497,33498,33501,33506,33511,33512,33513,33514,33516,33517,33518,33520,33522,33523,33525,33526,33528,33530,33532,33533,33534,33535,33536,33546,33547,33549,33552,33554,33555,33558,33560,33561,33565,33566,33567,33568,33569,33570,33571,33572,33573,33574,33577,33578,33582,33584,33586,33591,33595,33597,21860,33086,30130,30382,21305,30174,20731,23617,35692,31687,20559,29255,39575,39128,28418,29922,31080,25735,30629,25340,39057,36139,21697,32856,20050,22378,33529,33805,24179,20973,29942,35780,23631,22369,27900,39047,23110,30772,39748,36843,31893,21078,25169,38138,20166,33670,33889,33769,33970,22484,26420,22275,26222,28006,35889,26333,28689,26399,27450,26646,25114,22971,19971,20932,28422,26578,27791,20854,26827,22855,27495,30054,23822,33040,40784,26071,31048,31041,39569,36215,23682,20062,20225,21551,22865,30732,22120,27668,36804,24323,27773,27875,35755,25488,33598,33599,33601,33602,33604,33605,33608,33610,33611,33612,33613,33614,33619,33621,33622,33623,33624,33625,33629,33634,33648,33649,33650,33651,33652,33653,33654,33657,33658,33662,33663,33664,33665,33666,33667,33668,33671,33672,33674,33675,33676,33677,33679,33680,33681,33684,33685,33686,33687,33689,33690,33693,33695,33697,33698,33699,33700,33701,33702,33703,33708,33709,33710,33711,33717,33723,33726,33727,33730,33731,33732,33734,33736,33737,33739,33741,33742,33744,33745,33746,33747,33749,33751,33753,33754,33755,33758,33762,33763,33764,33766,33767,33768,33771,33772,33773,24688,27965,29301,25190,38030,38085,21315,36801,31614,20191,35878,20094,40660,38065,38067,21069,28508,36963,27973,35892,22545,23884,27424,27465,26538,21595,33108,32652,22681,34103,24378,25250,27207,38201,25970,24708,26725,30631,20052,20392,24039,38808,25772,32728,23789,20431,31373,20999,33540,19988,24623,31363,38054,20405,20146,31206,29748,21220,33465,25810,31165,23517,27777,38738,36731,27682,20542,21375,28165,25806,26228,27696,24773,39031,35831,24198,29756,31351,31179,19992,37041,29699,27714,22234,37195,27845,36235,21306,34502,26354,36527,23624,39537,28192,33774,33775,33779,33780,33781,33782,33783,33786,33787,33788,33790,33791,33792,33794,33797,33799,33800,33801,33802,33808,33810,33811,33812,33813,33814,33815,33817,33818,33819,33822,33823,33824,33825,33826,33827,33833,33834,33835,33836,33837,33838,33839,33840,33842,33843,33844,33845,33846,33847,33849,33850,33851,33854,33855,33856,33857,33858,33859,33860,33861,33863,33864,33865,33866,33867,33868,33869,33870,33871,33872,33874,33875,33876,33877,33878,33880,33885,33886,33887,33888,33890,33892,33893,33894,33895,33896,33898,33902,33903,33904,33906,33908,33911,33913,33915,33916,21462,23094,40843,36259,21435,22280,39079,26435,37275,27849,20840,30154,25331,29356,21048,21149,32570,28820,30264,21364,40522,27063,30830,38592,35033,32676,28982,29123,20873,26579,29924,22756,25880,22199,35753,39286,25200,32469,24825,28909,22764,20161,20154,24525,38887,20219,35748,20995,22922,32427,25172,20173,26085,25102,33592,33993,33635,34701,29076,28342,23481,32466,20887,25545,26580,32905,33593,34837,20754,23418,22914,36785,20083,27741,20837,35109,36719,38446,34122,29790,38160,38384,28070,33509,24369,25746,27922,33832,33134,40131,22622,36187,19977,21441,33917,33918,33919,33920,33921,33923,33924,33925,33926,33930,33933,33935,33936,33937,33938,33939,33940,33941,33942,33944,33946,33947,33949,33950,33951,33952,33954,33955,33956,33957,33958,33959,33960,33961,33962,33963,33964,33965,33966,33968,33969,33971,33973,33974,33975,33979,33980,33982,33984,33986,33987,33989,33990,33991,33992,33995,33996,33998,33999,34002,34004,34005,34007,34008,34009,34010,34011,34012,34014,34017,34018,34020,34023,34024,34025,34026,34027,34029,34030,34031,34033,34034,34035,34036,34037,34038,34039,34040,34041,34042,34043,34045,34046,34048,34049,34050,20254,25955,26705,21971,20007,25620,39578,25195,23234,29791,33394,28073,26862,20711,33678,30722,26432,21049,27801,32433,20667,21861,29022,31579,26194,29642,33515,26441,23665,21024,29053,34923,38378,38485,25797,36193,33203,21892,27733,25159,32558,22674,20260,21830,36175,26188,19978,23578,35059,26786,25422,31245,28903,33421,21242,38902,23569,21736,37045,32461,22882,36170,34503,33292,33293,36198,25668,23556,24913,28041,31038,35774,30775,30003,21627,20280,36523,28145,23072,32453,31070,27784,23457,23158,29978,32958,24910,28183,22768,29983,29989,29298,21319,32499,34051,34052,34053,34054,34055,34056,34057,34058,34059,34061,34062,34063,34064,34066,34068,34069,34070,34072,34073,34075,34076,34077,34078,34080,34082,34083,34084,34085,34086,34087,34088,34089,34090,34093,34094,34095,34096,34097,34098,34099,34100,34101,34102,34110,34111,34112,34113,34114,34116,34117,34118,34119,34123,34124,34125,34126,34127,34128,34129,34130,34131,34132,34133,34135,34136,34138,34139,34140,34141,34143,34144,34145,34146,34147,34149,34150,34151,34153,34154,34155,34156,34157,34158,34159,34160,34161,34163,34165,34166,34167,34168,34172,34173,34175,34176,34177,30465,30427,21097,32988,22307,24072,22833,29422,26045,28287,35799,23608,34417,21313,30707,25342,26102,20160,39135,34432,23454,35782,21490,30690,20351,23630,39542,22987,24335,31034,22763,19990,26623,20107,25325,35475,36893,21183,26159,21980,22124,36866,20181,20365,37322,39280,27663,24066,24643,23460,35270,35797,25910,25163,39318,23432,23551,25480,21806,21463,30246,20861,34092,26530,26803,27530,25234,36755,21460,33298,28113,30095,20070,36174,23408,29087,34223,26257,26329,32626,34560,40653,40736,23646,26415,36848,26641,26463,25101,31446,22661,24246,25968,28465,34178,34179,34182,34184,34185,34186,34187,34188,34189,34190,34192,34193,34194,34195,34196,34197,34198,34199,34200,34201,34202,34205,34206,34207,34208,34209,34210,34211,34213,34214,34215,34217,34219,34220,34221,34225,34226,34227,34228,34229,34230,34232,34234,34235,34236,34237,34238,34239,34240,34242,34243,34244,34245,34246,34247,34248,34250,34251,34252,34253,34254,34257,34258,34260,34262,34263,34264,34265,34266,34267,34269,34270,34271,34272,34273,34274,34275,34277,34278,34279,34280,34282,34283,34284,34285,34286,34287,34288,34289,34290,34291,34292,34293,34294,34295,34296,24661,21047,32781,25684,34928,29993,24069,26643,25332,38684,21452,29245,35841,27700,30561,31246,21550,30636,39034,33308,35828,30805,26388,28865,26031,25749,22070,24605,31169,21496,19997,27515,32902,23546,21987,22235,20282,20284,39282,24051,26494,32824,24578,39042,36865,23435,35772,35829,25628,33368,25822,22013,33487,37221,20439,32032,36895,31903,20723,22609,28335,23487,35785,32899,37240,33948,31639,34429,38539,38543,32485,39635,30862,23681,31319,36930,38567,31071,23385,25439,31499,34001,26797,21766,32553,29712,32034,38145,25152,22604,20182,23427,22905,22612,34297,34298,34300,34301,34302,34304,34305,34306,34307,34308,34310,34311,34312,34313,34314,34315,34316,34317,34318,34319,34320,34322,34323,34324,34325,34327,34328,34329,34330,34331,34332,34333,34334,34335,34336,34337,34338,34339,34340,34341,34342,34344,34346,34347,34348,34349,34350,34351,34352,34353,34354,34355,34356,34357,34358,34359,34361,34362,34363,34365,34366,34367,34368,34369,34370,34371,34372,34373,34374,34375,34376,34377,34378,34379,34380,34386,34387,34389,34390,34391,34392,34393,34395,34396,34397,34399,34400,34401,34403,34404,34405,34406,34407,34408,34409,34410,29549,25374,36427,36367,32974,33492,25260,21488,27888,37214,22826,24577,27760,22349,25674,36138,30251,28393,22363,27264,30192,28525,35885,35848,22374,27631,34962,30899,25506,21497,28845,27748,22616,25642,22530,26848,33179,21776,31958,20504,36538,28108,36255,28907,25487,28059,28372,32486,33796,26691,36867,28120,38518,35752,22871,29305,34276,33150,30140,35466,26799,21076,36386,38161,25552,39064,36420,21884,20307,26367,22159,24789,28053,21059,23625,22825,28155,22635,30000,29980,24684,33300,33094,25361,26465,36834,30522,36339,36148,38081,24086,21381,21548,28867,34413,34415,34416,34418,34419,34420,34421,34422,34423,34424,34435,34436,34437,34438,34439,34440,34441,34446,34447,34448,34449,34450,34452,34454,34455,34456,34457,34458,34459,34462,34463,34464,34465,34466,34469,34470,34475,34477,34478,34482,34483,34487,34488,34489,34491,34492,34493,34494,34495,34497,34498,34499,34501,34504,34508,34509,34514,34515,34517,34518,34519,34522,34524,34525,34528,34529,34530,34531,34533,34534,34535,34536,34538,34539,34540,34543,34549,34550,34551,34554,34555,34556,34557,34559,34561,34564,34565,34566,34571,34572,34574,34575,34576,34577,34580,34582,27712,24311,20572,20141,24237,25402,33351,36890,26704,37230,30643,21516,38108,24420,31461,26742,25413,31570,32479,30171,20599,25237,22836,36879,20984,31171,31361,22270,24466,36884,28034,23648,22303,21520,20820,28237,22242,25512,39059,33151,34581,35114,36864,21534,23663,33216,25302,25176,33073,40501,38464,39534,39548,26925,22949,25299,21822,25366,21703,34521,27964,23043,29926,34972,27498,22806,35916,24367,28286,29609,39037,20024,28919,23436,30871,25405,26202,30358,24779,23451,23113,19975,33109,27754,29579,20129,26505,32593,24448,26106,26395,24536,22916,23041,34585,34587,34589,34591,34592,34596,34598,34599,34600,34602,34603,34604,34605,34607,34608,34610,34611,34613,34614,34616,34617,34618,34620,34621,34624,34625,34626,34627,34628,34629,34630,34634,34635,34637,34639,34640,34641,34642,34644,34645,34646,34648,34650,34651,34652,34653,34654,34655,34657,34658,34662,34663,34664,34665,34666,34667,34668,34669,34671,34673,34674,34675,34677,34679,34680,34681,34682,34687,34688,34689,34692,34694,34695,34697,34698,34700,34702,34703,34704,34705,34706,34708,34709,34710,34712,34713,34714,34715,34716,34717,34718,34720,34721,34722,34723,34724,24013,24494,21361,38886,36829,26693,22260,21807,24799,20026,28493,32500,33479,33806,22996,20255,20266,23614,32428,26410,34074,21619,30031,32963,21890,39759,20301,28205,35859,23561,24944,21355,30239,28201,34442,25991,38395,32441,21563,31283,32010,38382,21985,32705,29934,25373,34583,28065,31389,25105,26017,21351,25569,27779,24043,21596,38056,20044,27745,35820,23627,26080,33436,26791,21566,21556,27595,27494,20116,25410,21320,33310,20237,20398,22366,25098,38654,26212,29289,21247,21153,24735,35823,26132,29081,26512,35199,30802,30717,26224,22075,21560,38177,29306,34725,34726,34727,34729,34730,34734,34736,34737,34738,34740,34742,34743,34744,34745,34747,34748,34750,34751,34753,34754,34755,34756,34757,34759,34760,34761,34764,34765,34766,34767,34768,34772,34773,34774,34775,34776,34777,34778,34780,34781,34782,34783,34785,34786,34787,34788,34790,34791,34792,34793,34795,34796,34797,34799,34800,34801,34802,34803,34804,34805,34806,34807,34808,34810,34811,34812,34813,34815,34816,34817,34818,34820,34821,34822,34823,34824,34825,34827,34828,34829,34830,34831,34832,34833,34834,34836,34839,34840,34841,34842,34844,34845,34846,34847,34848,34851,31232,24687,24076,24713,33181,22805,24796,29060,28911,28330,27728,29312,27268,34989,24109,20064,23219,21916,38115,27927,31995,38553,25103,32454,30606,34430,21283,38686,36758,26247,23777,20384,29421,19979,21414,22799,21523,25472,38184,20808,20185,40092,32420,21688,36132,34900,33335,38386,28046,24358,23244,26174,38505,29616,29486,21439,33146,39301,32673,23466,38519,38480,32447,30456,21410,38262,39321,31665,35140,28248,20065,32724,31077,35814,24819,21709,20139,39033,24055,27233,20687,21521,35937,33831,30813,38660,21066,21742,22179,38144,28040,23477,28102,26195,34852,34853,34854,34855,34856,34857,34858,34859,34860,34861,34862,34863,34864,34865,34867,34868,34869,34870,34871,34872,34874,34875,34877,34878,34879,34881,34882,34883,34886,34887,34888,34889,34890,34891,34894,34895,34896,34897,34898,34899,34901,34902,34904,34906,34907,34908,34909,34910,34911,34912,34918,34919,34922,34925,34927,34929,34931,34932,34933,34934,34936,34937,34938,34939,34940,34944,34947,34950,34951,34953,34954,34956,34958,34959,34960,34961,34963,34964,34965,34967,34968,34969,34970,34971,34973,34974,34975,34976,34977,34979,34981,34982,34983,34984,34985,34986,23567,23389,26657,32918,21880,31505,25928,26964,20123,27463,34638,38795,21327,25375,25658,37034,26012,32961,35856,20889,26800,21368,34809,25032,27844,27899,35874,23633,34218,33455,38156,27427,36763,26032,24571,24515,20449,34885,26143,33125,29481,24826,20852,21009,22411,24418,37026,34892,37266,24184,26447,24615,22995,20804,20982,33016,21256,27769,38596,29066,20241,20462,32670,26429,21957,38152,31168,34966,32483,22687,25100,38656,34394,22040,39035,24464,35768,33988,37207,21465,26093,24207,30044,24676,32110,23167,32490,32493,36713,21927,23459,24748,26059,29572,34988,34990,34991,34992,34994,34995,34996,34997,34998,35000,35001,35002,35003,35005,35006,35007,35008,35011,35012,35015,35016,35018,35019,35020,35021,35023,35024,35025,35027,35030,35031,35034,35035,35036,35037,35038,35040,35041,35046,35047,35049,35050,35051,35052,35053,35054,35055,35058,35061,35062,35063,35066,35067,35069,35071,35072,35073,35075,35076,35077,35078,35079,35080,35081,35083,35084,35085,35086,35087,35089,35092,35093,35094,35095,35096,35100,35101,35102,35103,35104,35106,35107,35108,35110,35111,35112,35113,35116,35117,35118,35119,35121,35122,35123,35125,35127,36873,30307,30505,32474,38772,34203,23398,31348,38634,34880,21195,29071,24490,26092,35810,23547,39535,24033,27529,27739,35757,35759,36874,36805,21387,25276,40486,40493,21568,20011,33469,29273,34460,23830,34905,28079,38597,21713,20122,35766,28937,21693,38409,28895,28153,30416,20005,30740,34578,23721,24310,35328,39068,38414,28814,27839,22852,25513,30524,34893,28436,33395,22576,29141,21388,30746,38593,21761,24422,28976,23476,35866,39564,27523,22830,40495,31207,26472,25196,20335,30113,32650,27915,38451,27687,20208,30162,20859,26679,28478,36992,33136,22934,29814,35128,35129,35130,35131,35132,35133,35134,35135,35136,35138,35139,35141,35142,35143,35144,35145,35146,35147,35148,35149,35150,35151,35152,35153,35154,35155,35156,35157,35158,35159,35160,35161,35162,35163,35164,35165,35168,35169,35170,35171,35172,35173,35175,35176,35177,35178,35179,35180,35181,35182,35183,35184,35185,35186,35187,35188,35189,35190,35191,35192,35193,35194,35196,35197,35198,35200,35202,35204,35205,35207,35208,35209,35210,35211,35212,35213,35214,35215,35216,35217,35218,35219,35220,35221,35222,35223,35224,35225,35226,35227,35228,35229,35230,35231,35232,35233,25671,23591,36965,31377,35875,23002,21676,33280,33647,35201,32768,26928,22094,32822,29239,37326,20918,20063,39029,25494,19994,21494,26355,33099,22812,28082,19968,22777,21307,25558,38129,20381,20234,34915,39056,22839,36951,31227,20202,33008,30097,27778,23452,23016,24413,26885,34433,20506,24050,20057,30691,20197,33402,25233,26131,37009,23673,20159,24441,33222,36920,32900,30123,20134,35028,24847,27589,24518,20041,30410,28322,35811,35758,35850,35793,24322,32764,32716,32462,33589,33643,22240,27575,38899,38452,23035,21535,38134,28139,23493,39278,23609,24341,38544,35234,35235,35236,35237,35238,35239,35240,35241,35242,35243,35244,35245,35246,35247,35248,35249,35250,35251,35252,35253,35254,35255,35256,35257,35258,35259,35260,35261,35262,35263,35264,35267,35277,35283,35284,35285,35287,35288,35289,35291,35293,35295,35296,35297,35298,35300,35303,35304,35305,35306,35308,35309,35310,35312,35313,35314,35316,35317,35318,35319,35320,35321,35322,35323,35324,35325,35326,35327,35329,35330,35331,35332,35333,35334,35336,35337,35338,35339,35340,35341,35342,35343,35344,35345,35346,35347,35348,35349,35350,35351,35352,35353,35354,35355,35356,35357,21360,33521,27185,23156,40560,24212,32552,33721,33828,33829,33639,34631,36814,36194,30408,24433,39062,30828,26144,21727,25317,20323,33219,30152,24248,38605,36362,34553,21647,27891,28044,27704,24703,21191,29992,24189,20248,24736,24551,23588,30001,37038,38080,29369,27833,28216,37193,26377,21451,21491,20305,37321,35825,21448,24188,36802,28132,20110,30402,27014,34398,24858,33286,20313,20446,36926,40060,24841,28189,28180,38533,20104,23089,38632,19982,23679,31161,23431,35821,32701,29577,22495,33419,37057,21505,36935,21947,23786,24481,24840,27442,29425,32946,35465,35358,35359,35360,35361,35362,35363,35364,35365,35366,35367,35368,35369,35370,35371,35372,35373,35374,35375,35376,35377,35378,35379,35380,35381,35382,35383,35384,35385,35386,35387,35388,35389,35391,35392,35393,35394,35395,35396,35397,35398,35399,35401,35402,35403,35404,35405,35406,35407,35408,35409,35410,35411,35412,35413,35414,35415,35416,35417,35418,35419,35420,35421,35422,35423,35424,35425,35426,35427,35428,35429,35430,35431,35432,35433,35434,35435,35436,35437,35438,35439,35440,35441,35442,35443,35444,35445,35446,35447,35448,35450,35451,35452,35453,35454,35455,35456,28020,23507,35029,39044,35947,39533,40499,28170,20900,20803,22435,34945,21407,25588,36757,22253,21592,22278,29503,28304,32536,36828,33489,24895,24616,38498,26352,32422,36234,36291,38053,23731,31908,26376,24742,38405,32792,20113,37095,21248,38504,20801,36816,34164,37213,26197,38901,23381,21277,30776,26434,26685,21705,28798,23472,36733,20877,22312,21681,25874,26242,36190,36163,33039,33900,36973,31967,20991,34299,26531,26089,28577,34468,36481,22122,36896,30338,28790,29157,36131,25321,21017,27901,36156,24590,22686,24974,26366,36192,25166,21939,28195,26413,36711,35457,35458,35459,35460,35461,35462,35463,35464,35467,35468,35469,35470,35471,35472,35473,35474,35476,35477,35478,35479,35480,35481,35482,35483,35484,35485,35486,35487,35488,35489,35490,35491,35492,35493,35494,35495,35496,35497,35498,35499,35500,35501,35502,35503,35504,35505,35506,35507,35508,35509,35510,35511,35512,35513,35514,35515,35516,35517,35518,35519,35520,35521,35522,35523,35524,35525,35526,35527,35528,35529,35530,35531,35532,35533,35534,35535,35536,35537,35538,35539,35540,35541,35542,35543,35544,35545,35546,35547,35548,35549,35550,35551,35552,35553,35554,35555,38113,38392,30504,26629,27048,21643,20045,28856,35784,25688,25995,23429,31364,20538,23528,30651,27617,35449,31896,27838,30415,26025,36759,23853,23637,34360,26632,21344,25112,31449,28251,32509,27167,31456,24432,28467,24352,25484,28072,26454,19976,24080,36134,20183,32960,30260,38556,25307,26157,25214,27836,36213,29031,32617,20806,32903,21484,36974,25240,21746,34544,36761,32773,38167,34071,36825,27993,29645,26015,30495,29956,30759,33275,36126,38024,20390,26517,30137,35786,38663,25391,38215,38453,33976,25379,30529,24449,29424,20105,24596,25972,25327,27491,25919,35556,35557,35558,35559,35560,35561,35562,35563,35564,35565,35566,35567,35568,35569,35570,35571,35572,35573,35574,35575,35576,35577,35578,35579,35580,35581,35582,35583,35584,35585,35586,35587,35588,35589,35590,35592,35593,35594,35595,35596,35597,35598,35599,35600,35601,35602,35603,35604,35605,35606,35607,35608,35609,35610,35611,35612,35613,35614,35615,35616,35617,35618,35619,35620,35621,35623,35624,35625,35626,35627,35628,35629,35630,35631,35632,35633,35634,35635,35636,35637,35638,35639,35640,35641,35642,35643,35644,35645,35646,35647,35648,35649,35650,35651,35652,35653,24103,30151,37073,35777,33437,26525,25903,21553,34584,30693,32930,33026,27713,20043,32455,32844,30452,26893,27542,25191,20540,20356,22336,25351,27490,36286,21482,26088,32440,24535,25370,25527,33267,33268,32622,24092,23769,21046,26234,31209,31258,36136,28825,30164,28382,27835,31378,20013,30405,24544,38047,34935,32456,31181,32959,37325,20210,20247,33311,21608,24030,27954,35788,31909,36724,32920,24090,21650,30385,23449,26172,39588,29664,26666,34523,26417,29482,35832,35803,36880,31481,28891,29038,25284,30633,22065,20027,33879,26609,21161,34496,36142,38136,31569,35654,35655,35656,35657,35658,35659,35660,35661,35662,35663,35664,35665,35666,35667,35668,35669,35670,35671,35672,35673,35674,35675,35676,35677,35678,35679,35680,35681,35682,35683,35684,35685,35687,35688,35689,35690,35691,35693,35694,35695,35696,35697,35698,35699,35700,35701,35702,35703,35704,35705,35706,35707,35708,35709,35710,35711,35712,35713,35714,35715,35716,35717,35718,35719,35720,35721,35722,35723,35724,35725,35726,35727,35728,35729,35730,35731,35732,35733,35734,35735,35736,35737,35738,35739,35740,35741,35742,35743,35756,35761,35771,35783,35792,35818,35849,35870,20303,27880,31069,39547,25235,29226,25341,19987,30742,36716,25776,36186,31686,26729,24196,35013,22918,25758,22766,29366,26894,38181,36861,36184,22368,32512,35846,20934,25417,25305,21331,26700,29730,33537,37196,21828,30528,28796,27978,20857,21672,36164,23039,28363,28100,23388,32043,20180,31869,28371,23376,33258,28173,23383,39683,26837,36394,23447,32508,24635,32437,37049,36208,22863,25549,31199,36275,21330,26063,31062,35781,38459,32452,38075,32386,22068,37257,26368,32618,23562,36981,26152,24038,20304,26590,20570,20316,22352,24231,59408,59409,59410,59411,59412,35896,35897,35898,35899,35900,35901,35902,35903,35904,35906,35907,35908,35909,35912,35914,35915,35917,35918,35919,35920,35921,35922,35923,35924,35926,35927,35928,35929,35931,35932,35933,35934,35935,35936,35939,35940,35941,35942,35943,35944,35945,35948,35949,35950,35951,35952,35953,35954,35956,35957,35958,35959,35963,35964,35965,35966,35967,35968,35969,35971,35972,35974,35975,35976,35979,35981,35982,35983,35984,35985,35986,35987,35989,35990,35991,35993,35994,35995,35996,35997,35998,35999,36000,36001,36002,36003,36004,36005,36006,36007,36008,36009,36010,36011,36012,36013,20109,19980,20800,19984,24319,21317,19989,20120,19998,39730,23404,22121,20008,31162,20031,21269,20039,22829,29243,21358,27664,22239,32996,39319,27603,30590,40727,20022,20127,40720,20060,20073,20115,33416,23387,21868,22031,20164,21389,21405,21411,21413,21422,38757,36189,21274,21493,21286,21294,21310,36188,21350,21347,20994,21000,21006,21037,21043,21055,21056,21068,21086,21089,21084,33967,21117,21122,21121,21136,21139,20866,32596,20155,20163,20169,20162,20200,20193,20203,20190,20251,20211,20258,20324,20213,20261,20263,20233,20267,20318,20327,25912,20314,20317,36014,36015,36016,36017,36018,36019,36020,36021,36022,36023,36024,36025,36026,36027,36028,36029,36030,36031,36032,36033,36034,36035,36036,36037,36038,36039,36040,36041,36042,36043,36044,36045,36046,36047,36048,36049,36050,36051,36052,36053,36054,36055,36056,36057,36058,36059,36060,36061,36062,36063,36064,36065,36066,36067,36068,36069,36070,36071,36072,36073,36074,36075,36076,36077,36078,36079,36080,36081,36082,36083,36084,36085,36086,36087,36088,36089,36090,36091,36092,36093,36094,36095,36096,36097,36098,36099,36100,36101,36102,36103,36104,36105,36106,36107,36108,36109,20319,20311,20274,20285,20342,20340,20369,20361,20355,20367,20350,20347,20394,20348,20396,20372,20454,20456,20458,20421,20442,20451,20444,20433,20447,20472,20521,20556,20467,20524,20495,20526,20525,20478,20508,20492,20517,20520,20606,20547,20565,20552,20558,20588,20603,20645,20647,20649,20666,20694,20742,20717,20716,20710,20718,20743,20747,20189,27709,20312,20325,20430,40864,27718,31860,20846,24061,40649,39320,20865,22804,21241,21261,35335,21264,20971,22809,20821,20128,20822,20147,34926,34980,20149,33044,35026,31104,23348,34819,32696,20907,20913,20925,20924,36110,36111,36112,36113,36114,36115,36116,36117,36118,36119,36120,36121,36122,36123,36124,36128,36177,36178,36183,36191,36197,36200,36201,36202,36204,36206,36207,36209,36210,36216,36217,36218,36219,36220,36221,36222,36223,36224,36226,36227,36230,36231,36232,36233,36236,36237,36238,36239,36240,36242,36243,36245,36246,36247,36248,36249,36250,36251,36252,36253,36254,36256,36257,36258,36260,36261,36262,36263,36264,36265,36266,36267,36268,36269,36270,36271,36272,36274,36278,36279,36281,36283,36285,36288,36289,36290,36293,36295,36296,36297,36298,36301,36304,36306,36307,36308,20935,20886,20898,20901,35744,35750,35751,35754,35764,35765,35767,35778,35779,35787,35791,35790,35794,35795,35796,35798,35800,35801,35804,35807,35808,35812,35816,35817,35822,35824,35827,35830,35833,35836,35839,35840,35842,35844,35847,35852,35855,35857,35858,35860,35861,35862,35865,35867,35864,35869,35871,35872,35873,35877,35879,35882,35883,35886,35887,35890,35891,35893,35894,21353,21370,38429,38434,38433,38449,38442,38461,38460,38466,38473,38484,38495,38503,38508,38514,38516,38536,38541,38551,38576,37015,37019,37021,37017,37036,37025,37044,37043,37046,37050,36309,36312,36313,36316,36320,36321,36322,36325,36326,36327,36329,36333,36334,36336,36337,36338,36340,36342,36348,36350,36351,36352,36353,36354,36355,36356,36358,36359,36360,36363,36365,36366,36368,36369,36370,36371,36373,36374,36375,36376,36377,36378,36379,36380,36384,36385,36388,36389,36390,36391,36392,36395,36397,36400,36402,36403,36404,36406,36407,36408,36411,36412,36414,36415,36419,36421,36422,36428,36429,36430,36431,36432,36435,36436,36437,36438,36439,36440,36442,36443,36444,36445,36446,36447,36448,36449,36450,36451,36452,36453,36455,36456,36458,36459,36462,36465,37048,37040,37071,37061,37054,37072,37060,37063,37075,37094,37090,37084,37079,37083,37099,37103,37118,37124,37154,37150,37155,37169,37167,37177,37187,37190,21005,22850,21154,21164,21165,21182,21759,21200,21206,21232,21471,29166,30669,24308,20981,20988,39727,21430,24321,30042,24047,22348,22441,22433,22654,22716,22725,22737,22313,22316,22314,22323,22329,22318,22319,22364,22331,22338,22377,22405,22379,22406,22396,22395,22376,22381,22390,22387,22445,22436,22412,22450,22479,22439,22452,22419,22432,22485,22488,22490,22489,22482,22456,22516,22511,22520,22500,22493,36467,36469,36471,36472,36473,36474,36475,36477,36478,36480,36482,36483,36484,36486,36488,36489,36490,36491,36492,36493,36494,36497,36498,36499,36501,36502,36503,36504,36505,36506,36507,36509,36511,36512,36513,36514,36515,36516,36517,36518,36519,36520,36521,36522,36525,36526,36528,36529,36531,36532,36533,36534,36535,36536,36537,36539,36540,36541,36542,36543,36544,36545,36546,36547,36548,36549,36550,36551,36552,36553,36554,36555,36556,36557,36559,36560,36561,36562,36563,36564,36565,36566,36567,36568,36569,36570,36571,36572,36573,36574,36575,36576,36577,36578,36579,36580,22539,22541,22525,22509,22528,22558,22553,22596,22560,22629,22636,22657,22665,22682,22656,39336,40729,25087,33401,33405,33407,33423,33418,33448,33412,33422,33425,33431,33433,33451,33464,33470,33456,33480,33482,33507,33432,33463,33454,33483,33484,33473,33449,33460,33441,33450,33439,33476,33486,33444,33505,33545,33527,33508,33551,33543,33500,33524,33490,33496,33548,33531,33491,33553,33562,33542,33556,33557,33504,33493,33564,33617,33627,33628,33544,33682,33596,33588,33585,33691,33630,33583,33615,33607,33603,33631,33600,33559,33632,33581,33594,33587,33638,33637,36581,36582,36583,36584,36585,36586,36587,36588,36589,36590,36591,36592,36593,36594,36595,36596,36597,36598,36599,36600,36601,36602,36603,36604,36605,36606,36607,36608,36609,36610,36611,36612,36613,36614,36615,36616,36617,36618,36619,36620,36621,36622,36623,36624,36625,36626,36627,36628,36629,36630,36631,36632,36633,36634,36635,36636,36637,36638,36639,36640,36641,36642,36643,36644,36645,36646,36647,36648,36649,36650,36651,36652,36653,36654,36655,36656,36657,36658,36659,36660,36661,36662,36663,36664,36665,36666,36667,36668,36669,36670,36671,36672,36673,36674,36675,36676,33640,33563,33641,33644,33642,33645,33646,33712,33656,33715,33716,33696,33706,33683,33692,33669,33660,33718,33705,33661,33720,33659,33688,33694,33704,33722,33724,33729,33793,33765,33752,22535,33816,33803,33757,33789,33750,33820,33848,33809,33798,33748,33759,33807,33795,33784,33785,33770,33733,33728,33830,33776,33761,33884,33873,33882,33881,33907,33927,33928,33914,33929,33912,33852,33862,33897,33910,33932,33934,33841,33901,33985,33997,34000,34022,33981,34003,33994,33983,33978,34016,33953,33977,33972,33943,34021,34019,34060,29965,34104,34032,34105,34079,34106,36677,36678,36679,36680,36681,36682,36683,36684,36685,36686,36687,36688,36689,36690,36691,36692,36693,36694,36695,36696,36697,36698,36699,36700,36701,36702,36703,36704,36705,36706,36707,36708,36709,36714,36736,36748,36754,36765,36768,36769,36770,36772,36773,36774,36775,36778,36780,36781,36782,36783,36786,36787,36788,36789,36791,36792,36794,36795,36796,36799,36800,36803,36806,36809,36810,36811,36812,36813,36815,36818,36822,36823,36826,36832,36833,36835,36839,36844,36847,36849,36850,36852,36853,36854,36858,36859,36860,36862,36863,36871,36872,36876,36878,36883,36885,36888,34134,34107,34047,34044,34137,34120,34152,34148,34142,34170,30626,34115,34162,34171,34212,34216,34183,34191,34169,34222,34204,34181,34233,34231,34224,34259,34241,34268,34303,34343,34309,34345,34326,34364,24318,24328,22844,22849,32823,22869,22874,22872,21263,23586,23589,23596,23604,25164,25194,25247,25275,25290,25306,25303,25326,25378,25334,25401,25419,25411,25517,25590,25457,25466,25486,25524,25453,25516,25482,25449,25518,25532,25586,25592,25568,25599,25540,25566,25550,25682,25542,25534,25669,25665,25611,25627,25632,25612,25638,25633,25694,25732,25709,25750,36889,36892,36899,36900,36901,36903,36904,36905,36906,36907,36908,36912,36913,36914,36915,36916,36919,36921,36922,36925,36927,36928,36931,36933,36934,36936,36937,36938,36939,36940,36942,36948,36949,36950,36953,36954,36956,36957,36958,36959,36960,36961,36964,36966,36967,36969,36970,36971,36972,36975,36976,36977,36978,36979,36982,36983,36984,36985,36986,36987,36988,36990,36993,36996,36997,36998,36999,37001,37002,37004,37005,37006,37007,37008,37010,37012,37014,37016,37018,37020,37022,37023,37024,37028,37029,37031,37032,37033,37035,37037,37042,37047,37052,37053,37055,37056,25722,25783,25784,25753,25786,25792,25808,25815,25828,25826,25865,25893,25902,24331,24530,29977,24337,21343,21489,21501,21481,21480,21499,21522,21526,21510,21579,21586,21587,21588,21590,21571,21537,21591,21593,21539,21554,21634,21652,21623,21617,21604,21658,21659,21636,21622,21606,21661,21712,21677,21698,21684,21714,21671,21670,21715,21716,21618,21667,21717,21691,21695,21708,21721,21722,21724,21673,21674,21668,21725,21711,21726,21787,21735,21792,21757,21780,21747,21794,21795,21775,21777,21799,21802,21863,21903,21941,21833,21869,21825,21845,21823,21840,21820,37058,37059,37062,37064,37065,37067,37068,37069,37074,37076,37077,37078,37080,37081,37082,37086,37087,37088,37091,37092,37093,37097,37098,37100,37102,37104,37105,37106,37107,37109,37110,37111,37113,37114,37115,37116,37119,37120,37121,37123,37125,37126,37127,37128,37129,37130,37131,37132,37133,37134,37135,37136,37137,37138,37139,37140,37141,37142,37143,37144,37146,37147,37148,37149,37151,37152,37153,37156,37157,37158,37159,37160,37161,37162,37163,37164,37165,37166,37168,37170,37171,37172,37173,37174,37175,37176,37178,37179,37180,37181,37182,37183,37184,37185,37186,37188,21815,21846,21877,21878,21879,21811,21808,21852,21899,21970,21891,21937,21945,21896,21889,21919,21886,21974,21905,21883,21983,21949,21950,21908,21913,21994,22007,21961,22047,21969,21995,21996,21972,21990,21981,21956,21999,21989,22002,22003,21964,21965,21992,22005,21988,36756,22046,22024,22028,22017,22052,22051,22014,22016,22055,22061,22104,22073,22103,22060,22093,22114,22105,22108,22092,22100,22150,22116,22129,22123,22139,22140,22149,22163,22191,22228,22231,22237,22241,22261,22251,22265,22271,22276,22282,22281,22300,24079,24089,24084,24081,24113,24123,24124,37189,37191,37192,37201,37203,37204,37205,37206,37208,37209,37211,37212,37215,37216,37222,37223,37224,37227,37229,37235,37242,37243,37244,37248,37249,37250,37251,37252,37254,37256,37258,37262,37263,37267,37268,37269,37270,37271,37272,37273,37276,37277,37278,37279,37280,37281,37284,37285,37286,37287,37288,37289,37291,37292,37296,37297,37298,37299,37302,37303,37304,37305,37307,37308,37309,37310,37311,37312,37313,37314,37315,37316,37317,37318,37320,37323,37328,37330,37331,37332,37333,37334,37335,37336,37337,37338,37339,37341,37342,37343,37344,37345,37346,37347,37348,37349,24119,24132,24148,24155,24158,24161,23692,23674,23693,23696,23702,23688,23704,23705,23697,23706,23708,23733,23714,23741,23724,23723,23729,23715,23745,23735,23748,23762,23780,23755,23781,23810,23811,23847,23846,23854,23844,23838,23814,23835,23896,23870,23860,23869,23916,23899,23919,23901,23915,23883,23882,23913,23924,23938,23961,23965,35955,23991,24005,24435,24439,24450,24455,24457,24460,24469,24473,24476,24488,24493,24501,24508,34914,24417,29357,29360,29364,29367,29368,29379,29377,29390,29389,29394,29416,29423,29417,29426,29428,29431,29441,29427,29443,29434,37350,37351,37352,37353,37354,37355,37356,37357,37358,37359,37360,37361,37362,37363,37364,37365,37366,37367,37368,37369,37370,37371,37372,37373,37374,37375,37376,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37387,37388,37389,37390,37391,37392,37393,37394,37395,37396,37397,37398,37399,37400,37401,37402,37403,37404,37405,37406,37407,37408,37409,37410,37411,37412,37413,37414,37415,37416,37417,37418,37419,37420,37421,37422,37423,37424,37425,37426,37427,37428,37429,37430,37431,37432,37433,37434,37435,37436,37437,37438,37439,37440,37441,37442,37443,37444,37445,29435,29463,29459,29473,29450,29470,29469,29461,29474,29497,29477,29484,29496,29489,29520,29517,29527,29536,29548,29551,29566,33307,22821,39143,22820,22786,39267,39271,39272,39273,39274,39275,39276,39284,39287,39293,39296,39300,39303,39306,39309,39312,39313,39315,39316,39317,24192,24209,24203,24214,24229,24224,24249,24245,24254,24243,36179,24274,24273,24283,24296,24298,33210,24516,24521,24534,24527,24579,24558,24580,24545,24548,24574,24581,24582,24554,24557,24568,24601,24629,24614,24603,24591,24589,24617,24619,24586,24639,24609,24696,24697,24699,24698,24642,37446,37447,37448,37449,37450,37451,37452,37453,37454,37455,37456,37457,37458,37459,37460,37461,37462,37463,37464,37465,37466,37467,37468,37469,37470,37471,37472,37473,37474,37475,37476,37477,37478,37479,37480,37481,37482,37483,37484,37485,37486,37487,37488,37489,37490,37491,37493,37494,37495,37496,37497,37498,37499,37500,37501,37502,37503,37504,37505,37506,37507,37508,37509,37510,37511,37512,37513,37514,37515,37516,37517,37519,37520,37521,37522,37523,37524,37525,37526,37527,37528,37529,37530,37531,37532,37533,37534,37535,37536,37537,37538,37539,37540,37541,37542,37543,24682,24701,24726,24730,24749,24733,24707,24722,24716,24731,24812,24763,24753,24797,24792,24774,24794,24756,24864,24870,24853,24867,24820,24832,24846,24875,24906,24949,25004,24980,24999,25015,25044,25077,24541,38579,38377,38379,38385,38387,38389,38390,38396,38398,38403,38404,38406,38408,38410,38411,38412,38413,38415,38418,38421,38422,38423,38425,38426,20012,29247,25109,27701,27732,27740,27722,27811,27781,27792,27796,27788,27752,27753,27764,27766,27782,27817,27856,27860,27821,27895,27896,27889,27863,27826,27872,27862,27898,27883,27886,27825,27859,27887,27902,37544,37545,37546,37547,37548,37549,37551,37552,37553,37554,37555,37556,37557,37558,37559,37560,37561,37562,37563,37564,37565,37566,37567,37568,37569,37570,37571,37572,37573,37574,37575,37577,37578,37579,37580,37581,37582,37583,37584,37585,37586,37587,37588,37589,37590,37591,37592,37593,37594,37595,37596,37597,37598,37599,37600,37601,37602,37603,37604,37605,37606,37607,37608,37609,37610,37611,37612,37613,37614,37615,37616,37617,37618,37619,37620,37621,37622,37623,37624,37625,37626,37627,37628,37629,37630,37631,37632,37633,37634,37635,37636,37637,37638,37639,37640,37641,27961,27943,27916,27971,27976,27911,27908,27929,27918,27947,27981,27950,27957,27930,27983,27986,27988,27955,28049,28015,28062,28064,27998,28051,28052,27996,28000,28028,28003,28186,28103,28101,28126,28174,28095,28128,28177,28134,28125,28121,28182,28075,28172,28078,28203,28270,28238,28267,28338,28255,28294,28243,28244,28210,28197,28228,28383,28337,28312,28384,28461,28386,28325,28327,28349,28347,28343,28375,28340,28367,28303,28354,28319,28514,28486,28487,28452,28437,28409,28463,28470,28491,28532,28458,28425,28457,28553,28557,28556,28536,28530,28540,28538,28625,37642,37643,37644,37645,37646,37647,37648,37649,37650,37651,37652,37653,37654,37655,37656,37657,37658,37659,37660,37661,37662,37663,37664,37665,37666,37667,37668,37669,37670,37671,37672,37673,37674,37675,37676,37677,37678,37679,37680,37681,37682,37683,37684,37685,37686,37687,37688,37689,37690,37691,37692,37693,37695,37696,37697,37698,37699,37700,37701,37702,37703,37704,37705,37706,37707,37708,37709,37710,37711,37712,37713,37714,37715,37716,37717,37718,37719,37720,37721,37722,37723,37724,37725,37726,37727,37728,37729,37730,37731,37732,37733,37734,37735,37736,37737,37739,28617,28583,28601,28598,28610,28641,28654,28638,28640,28655,28698,28707,28699,28729,28725,28751,28766,23424,23428,23445,23443,23461,23480,29999,39582,25652,23524,23534,35120,23536,36423,35591,36790,36819,36821,36837,36846,36836,36841,36838,36851,36840,36869,36868,36875,36902,36881,36877,36886,36897,36917,36918,36909,36911,36932,36945,36946,36944,36968,36952,36962,36955,26297,36980,36989,36994,37000,36995,37003,24400,24407,24406,24408,23611,21675,23632,23641,23409,23651,23654,32700,24362,24361,24365,33396,24380,39739,23662,22913,22915,22925,22953,22954,22947,37740,37741,37742,37743,37744,37745,37746,37747,37748,37749,37750,37751,37752,37753,37754,37755,37756,37757,37758,37759,37760,37761,37762,37763,37764,37765,37766,37767,37768,37769,37770,37771,37772,37773,37774,37776,37777,37778,37779,37780,37781,37782,37783,37784,37785,37786,37787,37788,37789,37790,37791,37792,37793,37794,37795,37796,37797,37798,37799,37800,37801,37802,37803,37804,37805,37806,37807,37808,37809,37810,37811,37812,37813,37814,37815,37816,37817,37818,37819,37820,37821,37822,37823,37824,37825,37826,37827,37828,37829,37830,37831,37832,37833,37835,37836,37837,22935,22986,22955,22942,22948,22994,22962,22959,22999,22974,23045,23046,23005,23048,23011,23000,23033,23052,23049,23090,23092,23057,23075,23059,23104,23143,23114,23125,23100,23138,23157,33004,23210,23195,23159,23162,23230,23275,23218,23250,23252,23224,23264,23267,23281,23254,23270,23256,23260,23305,23319,23318,23346,23351,23360,23573,23580,23386,23397,23411,23377,23379,23394,39541,39543,39544,39546,39551,39549,39552,39553,39557,39560,39562,39568,39570,39571,39574,39576,39579,39580,39581,39583,39584,39586,39587,39589,39591,32415,32417,32419,32421,32424,32425,37838,37839,37840,37841,37842,37843,37844,37845,37847,37848,37849,37850,37851,37852,37853,37854,37855,37856,37857,37858,37859,37860,37861,37862,37863,37864,37865,37866,37867,37868,37869,37870,37871,37872,37873,37874,37875,37876,37877,37878,37879,37880,37881,37882,37883,37884,37885,37886,37887,37888,37889,37890,37891,37892,37893,37894,37895,37896,37897,37898,37899,37900,37901,37902,37903,37904,37905,37906,37907,37908,37909,37910,37911,37912,37913,37914,37915,37916,37917,37918,37919,37920,37921,37922,37923,37924,37925,37926,37927,37928,37929,37930,37931,37932,37933,37934,32429,32432,32446,32448,32449,32450,32457,32459,32460,32464,32468,32471,32475,32480,32481,32488,32491,32494,32495,32497,32498,32525,32502,32506,32507,32510,32513,32514,32515,32519,32520,32523,32524,32527,32529,32530,32535,32537,32540,32539,32543,32545,32546,32547,32548,32549,32550,32551,32554,32555,32556,32557,32559,32560,32561,32562,32563,32565,24186,30079,24027,30014,37013,29582,29585,29614,29602,29599,29647,29634,29649,29623,29619,29632,29641,29640,29669,29657,39036,29706,29673,29671,29662,29626,29682,29711,29738,29787,29734,29733,29736,29744,29742,29740,37935,37936,37937,37938,37939,37940,37941,37942,37943,37944,37945,37946,37947,37948,37949,37951,37952,37953,37954,37955,37956,37957,37958,37959,37960,37961,37962,37963,37964,37965,37966,37967,37968,37969,37970,37971,37972,37973,37974,37975,37976,37977,37978,37979,37980,37981,37982,37983,37984,37985,37986,37987,37988,37989,37990,37991,37992,37993,37994,37996,37997,37998,37999,38000,38001,38002,38003,38004,38005,38006,38007,38008,38009,38010,38011,38012,38013,38014,38015,38016,38017,38018,38019,38020,38033,38038,38040,38087,38095,38099,38100,38106,38118,38139,38172,38176,29723,29722,29761,29788,29783,29781,29785,29815,29805,29822,29852,29838,29824,29825,29831,29835,29854,29864,29865,29840,29863,29906,29882,38890,38891,38892,26444,26451,26462,26440,26473,26533,26503,26474,26483,26520,26535,26485,26536,26526,26541,26507,26487,26492,26608,26633,26584,26634,26601,26544,26636,26585,26549,26586,26547,26589,26624,26563,26552,26594,26638,26561,26621,26674,26675,26720,26721,26702,26722,26692,26724,26755,26653,26709,26726,26689,26727,26688,26686,26698,26697,26665,26805,26767,26740,26743,26771,26731,26818,26990,26876,26911,26912,26873,38183,38195,38205,38211,38216,38219,38229,38234,38240,38254,38260,38261,38263,38264,38265,38266,38267,38268,38269,38270,38272,38273,38274,38275,38276,38277,38278,38279,38280,38281,38282,38283,38284,38285,38286,38287,38288,38289,38290,38291,38292,38293,38294,38295,38296,38297,38298,38299,38300,38301,38302,38303,38304,38305,38306,38307,38308,38309,38310,38311,38312,38313,38314,38315,38316,38317,38318,38319,38320,38321,38322,38323,38324,38325,38326,38327,38328,38329,38330,38331,38332,38333,38334,38335,38336,38337,38338,38339,38340,38341,38342,38343,38344,38345,38346,38347,26916,26864,26891,26881,26967,26851,26896,26993,26937,26976,26946,26973,27012,26987,27008,27032,27000,26932,27084,27015,27016,27086,27017,26982,26979,27001,27035,27047,27067,27051,27053,27092,27057,27073,27082,27103,27029,27104,27021,27135,27183,27117,27159,27160,27237,27122,27204,27198,27296,27216,27227,27189,27278,27257,27197,27176,27224,27260,27281,27280,27305,27287,27307,29495,29522,27521,27522,27527,27524,27538,27539,27533,27546,27547,27553,27562,36715,36717,36721,36722,36723,36725,36726,36728,36727,36729,36730,36732,36734,36737,36738,36740,36743,36747,38348,38349,38350,38351,38352,38353,38354,38355,38356,38357,38358,38359,38360,38361,38362,38363,38364,38365,38366,38367,38368,38369,38370,38371,38372,38373,38374,38375,38380,38399,38407,38419,38424,38427,38430,38432,38435,38436,38437,38438,38439,38440,38441,38443,38444,38445,38447,38448,38455,38456,38457,38458,38462,38465,38467,38474,38478,38479,38481,38482,38483,38486,38487,38488,38489,38490,38492,38493,38494,38496,38499,38501,38502,38507,38509,38510,38511,38512,38513,38515,38520,38521,38522,38523,38524,38525,38526,38527,38528,38529,38530,38531,38532,38535,38537,38538,36749,36750,36751,36760,36762,36558,25099,25111,25115,25119,25122,25121,25125,25124,25132,33255,29935,29940,29951,29967,29969,29971,25908,26094,26095,26096,26122,26137,26482,26115,26133,26112,28805,26359,26141,26164,26161,26166,26165,32774,26207,26196,26177,26191,26198,26209,26199,26231,26244,26252,26279,26269,26302,26331,26332,26342,26345,36146,36147,36150,36155,36157,36160,36165,36166,36168,36169,36167,36173,36181,36185,35271,35274,35275,35276,35278,35279,35280,35281,29294,29343,29277,29286,29295,29310,29311,29316,29323,29325,29327,29330,25352,25394,25520,38540,38542,38545,38546,38547,38549,38550,38554,38555,38557,38558,38559,38560,38561,38562,38563,38564,38565,38566,38568,38569,38570,38571,38572,38573,38574,38575,38577,38578,38580,38581,38583,38584,38586,38587,38591,38594,38595,38600,38602,38603,38608,38609,38611,38612,38614,38615,38616,38617,38618,38619,38620,38621,38622,38623,38625,38626,38627,38628,38629,38630,38631,38635,38636,38637,38638,38640,38641,38642,38644,38645,38648,38650,38651,38652,38653,38655,38658,38659,38661,38666,38667,38668,38672,38673,38674,38676,38677,38679,38680,38681,38682,38683,38685,38687,38688,25663,25816,32772,27626,27635,27645,27637,27641,27653,27655,27654,27661,27669,27672,27673,27674,27681,27689,27684,27690,27698,25909,25941,25963,29261,29266,29270,29232,34402,21014,32927,32924,32915,32956,26378,32957,32945,32939,32941,32948,32951,32999,33000,33001,33002,32987,32962,32964,32985,32973,32983,26384,32989,33003,33009,33012,33005,33037,33038,33010,33020,26389,33042,35930,33078,33054,33068,33048,33074,33096,33100,33107,33140,33113,33114,33137,33120,33129,33148,33149,33133,33127,22605,23221,33160,33154,33169,28373,33187,33194,33228,26406,33226,33211,38689,38690,38691,38692,38693,38694,38695,38696,38697,38699,38700,38702,38703,38705,38707,38708,38709,38710,38711,38714,38715,38716,38717,38719,38720,38721,38722,38723,38724,38725,38726,38727,38728,38729,38730,38731,38732,38733,38734,38735,38736,38737,38740,38741,38743,38744,38746,38748,38749,38751,38755,38756,38758,38759,38760,38762,38763,38764,38765,38766,38767,38768,38769,38770,38773,38775,38776,38777,38778,38779,38781,38782,38783,38784,38785,38786,38787,38788,38790,38791,38792,38793,38794,38796,38798,38799,38800,38803,38805,38806,38807,38809,38810,38811,38812,38813,33217,33190,27428,27447,27449,27459,27462,27481,39121,39122,39123,39125,39129,39130,27571,24384,27586,35315,26000,40785,26003,26044,26054,26052,26051,26060,26062,26066,26070,28800,28828,28822,28829,28859,28864,28855,28843,28849,28904,28874,28944,28947,28950,28975,28977,29043,29020,29032,28997,29042,29002,29048,29050,29080,29107,29109,29096,29088,29152,29140,29159,29177,29213,29224,28780,28952,29030,29113,25150,25149,25155,25160,25161,31035,31040,31046,31049,31067,31068,31059,31066,31074,31063,31072,31087,31079,31098,31109,31114,31130,31143,31155,24529,24528,38814,38815,38817,38818,38820,38821,38822,38823,38824,38825,38826,38828,38830,38832,38833,38835,38837,38838,38839,38840,38841,38842,38843,38844,38845,38846,38847,38848,38849,38850,38851,38852,38853,38854,38855,38856,38857,38858,38859,38860,38861,38862,38863,38864,38865,38866,38867,38868,38869,38870,38871,38872,38873,38874,38875,38876,38877,38878,38879,38880,38881,38882,38883,38884,38885,38888,38894,38895,38896,38897,38898,38900,38903,38904,38905,38906,38907,38908,38909,38910,38911,38912,38913,38914,38915,38916,38917,38918,38919,38920,38921,38922,38923,38924,38925,38926,24636,24669,24666,24679,24641,24665,24675,24747,24838,24845,24925,25001,24989,25035,25041,25094,32896,32895,27795,27894,28156,30710,30712,30720,30729,30743,30744,30737,26027,30765,30748,30749,30777,30778,30779,30751,30780,30757,30764,30755,30761,30798,30829,30806,30807,30758,30800,30791,30796,30826,30875,30867,30874,30855,30876,30881,30883,30898,30905,30885,30932,30937,30921,30956,30962,30981,30964,30995,31012,31006,31028,40859,40697,40699,40700,30449,30468,30477,30457,30471,30472,30490,30498,30489,30509,30502,30517,30520,30544,30545,30535,30531,30554,30568,38927,38928,38929,38930,38931,38932,38933,38934,38935,38936,38937,38938,38939,38940,38941,38942,38943,38944,38945,38946,38947,38948,38949,38950,38951,38952,38953,38954,38955,38956,38957,38958,38959,38960,38961,38962,38963,38964,38965,38966,38967,38968,38969,38970,38971,38972,38973,38974,38975,38976,38977,38978,38979,38980,38981,38982,38983,38984,38985,38986,38987,38988,38989,38990,38991,38992,38993,38994,38995,38996,38997,38998,38999,39000,39001,39002,39003,39004,39005,39006,39007,39008,39009,39010,39011,39012,39013,39014,39015,39016,39017,39018,39019,39020,39021,39022,30562,30565,30591,30605,30589,30592,30604,30609,30623,30624,30640,30645,30653,30010,30016,30030,30027,30024,30043,30066,30073,30083,32600,32609,32607,35400,32616,32628,32625,32633,32641,32638,30413,30437,34866,38021,38022,38023,38027,38026,38028,38029,38031,38032,38036,38039,38037,38042,38043,38044,38051,38052,38059,38058,38061,38060,38063,38064,38066,38068,38070,38071,38072,38073,38074,38076,38077,38079,38084,38088,38089,38090,38091,38092,38093,38094,38096,38097,38098,38101,38102,38103,38105,38104,38107,38110,38111,38112,38114,38116,38117,38119,38120,38122,39023,39024,39025,39026,39027,39028,39051,39054,39058,39061,39065,39075,39080,39081,39082,39083,39084,39085,39086,39087,39088,39089,39090,39091,39092,39093,39094,39095,39096,39097,39098,39099,39100,39101,39102,39103,39104,39105,39106,39107,39108,39109,39110,39111,39112,39113,39114,39115,39116,39117,39119,39120,39124,39126,39127,39131,39132,39133,39136,39137,39138,39139,39140,39141,39142,39145,39146,39147,39148,39149,39150,39151,39152,39153,39154,39155,39156,39157,39158,39159,39160,39161,39162,39163,39164,39165,39166,39167,39168,39169,39170,39171,39172,39173,39174,39175,38121,38123,38126,38127,38131,38132,38133,38135,38137,38140,38141,38143,38147,38146,38150,38151,38153,38154,38157,38158,38159,38162,38163,38164,38165,38166,38168,38171,38173,38174,38175,38178,38186,38187,38185,38188,38193,38194,38196,38198,38199,38200,38204,38206,38207,38210,38197,38212,38213,38214,38217,38220,38222,38223,38226,38227,38228,38230,38231,38232,38233,38235,38238,38239,38237,38241,38242,38244,38245,38246,38247,38248,38249,38250,38251,38252,38255,38257,38258,38259,38202,30695,30700,38601,31189,31213,31203,31211,31238,23879,31235,31234,31262,31252,39176,39177,39178,39179,39180,39182,39183,39185,39186,39187,39188,39189,39190,39191,39192,39193,39194,39195,39196,39197,39198,39199,39200,39201,39202,39203,39204,39205,39206,39207,39208,39209,39210,39211,39212,39213,39215,39216,39217,39218,39219,39220,39221,39222,39223,39224,39225,39226,39227,39228,39229,39230,39231,39232,39233,39234,39235,39236,39237,39238,39239,39240,39241,39242,39243,39244,39245,39246,39247,39248,39249,39250,39251,39254,39255,39256,39257,39258,39259,39260,39261,39262,39263,39264,39265,39266,39268,39270,39283,39288,39289,39291,39294,39298,39299,39305,31289,31287,31313,40655,39333,31344,30344,30350,30355,30361,30372,29918,29920,29996,40480,40482,40488,40489,40490,40491,40492,40498,40497,40502,40504,40503,40505,40506,40510,40513,40514,40516,40518,40519,40520,40521,40523,40524,40526,40529,40533,40535,40538,40539,40540,40542,40547,40550,40551,40552,40553,40554,40555,40556,40561,40557,40563,30098,30100,30102,30112,30109,30124,30115,30131,30132,30136,30148,30129,30128,30147,30146,30166,30157,30179,30184,30182,30180,30187,30183,30211,30193,30204,30207,30224,30208,30213,30220,30231,30218,30245,30232,30229,30233,39308,39310,39322,39323,39324,39325,39326,39327,39328,39329,39330,39331,39332,39334,39335,39337,39338,39339,39340,39341,39342,39343,39344,39345,39346,39347,39348,39349,39350,39351,39352,39353,39354,39355,39356,39357,39358,39359,39360,39361,39362,39363,39364,39365,39366,39367,39368,39369,39370,39371,39372,39373,39374,39375,39376,39377,39378,39379,39380,39381,39382,39383,39384,39385,39386,39387,39388,39389,39390,39391,39392,39393,39394,39395,39396,39397,39398,39399,39400,39401,39402,39403,39404,39405,39406,39407,39408,39409,39410,39411,39412,39413,39414,39415,39416,39417,30235,30268,30242,30240,30272,30253,30256,30271,30261,30275,30270,30259,30285,30302,30292,30300,30294,30315,30319,32714,31462,31352,31353,31360,31366,31368,31381,31398,31392,31404,31400,31405,31411,34916,34921,34930,34941,34943,34946,34978,35014,34999,35004,35017,35042,35022,35043,35045,35057,35098,35068,35048,35070,35056,35105,35097,35091,35099,35082,35124,35115,35126,35137,35174,35195,30091,32997,30386,30388,30684,32786,32788,32790,32796,32800,32802,32805,32806,32807,32809,32808,32817,32779,32821,32835,32838,32845,32850,32873,32881,35203,39032,39040,39043,39418,39419,39420,39421,39422,39423,39424,39425,39426,39427,39428,39429,39430,39431,39432,39433,39434,39435,39436,39437,39438,39439,39440,39441,39442,39443,39444,39445,39446,39447,39448,39449,39450,39451,39452,39453,39454,39455,39456,39457,39458,39459,39460,39461,39462,39463,39464,39465,39466,39467,39468,39469,39470,39471,39472,39473,39474,39475,39476,39477,39478,39479,39480,39481,39482,39483,39484,39485,39486,39487,39488,39489,39490,39491,39492,39493,39494,39495,39496,39497,39498,39499,39500,39501,39502,39503,39504,39505,39506,39507,39508,39509,39510,39511,39512,39513,39049,39052,39053,39055,39060,39066,39067,39070,39071,39073,39074,39077,39078,34381,34388,34412,34414,34431,34426,34428,34427,34472,34445,34443,34476,34461,34471,34467,34474,34451,34473,34486,34500,34485,34510,34480,34490,34481,34479,34505,34511,34484,34537,34545,34546,34541,34547,34512,34579,34526,34548,34527,34520,34513,34563,34567,34552,34568,34570,34573,34569,34595,34619,34590,34597,34606,34586,34622,34632,34612,34609,34601,34615,34623,34690,34594,34685,34686,34683,34656,34672,34636,34670,34699,34643,34659,34684,34660,34649,34661,34707,34735,34728,34770,39514,39515,39516,39517,39518,39519,39520,39521,39522,39523,39524,39525,39526,39527,39528,39529,39530,39531,39538,39555,39561,39565,39566,39572,39573,39577,39590,39593,39594,39595,39596,39597,39598,39599,39602,39603,39604,39605,39609,39611,39613,39614,39615,39619,39620,39622,39623,39624,39625,39626,39629,39630,39631,39632,39634,39636,39637,39638,39639,39641,39642,39643,39644,39645,39646,39648,39650,39651,39652,39653,39655,39656,39657,39658,39660,39662,39664,39665,39666,39667,39668,39669,39670,39671,39672,39674,39676,39677,39678,39679,39680,39681,39682,39684,39685,39686,34758,34696,34693,34733,34711,34691,34731,34789,34732,34741,34739,34763,34771,34749,34769,34752,34762,34779,34794,34784,34798,34838,34835,34814,34826,34843,34849,34873,34876,32566,32578,32580,32581,33296,31482,31485,31496,31491,31492,31509,31498,31531,31503,31559,31544,31530,31513,31534,31537,31520,31525,31524,31539,31550,31518,31576,31578,31557,31605,31564,31581,31584,31598,31611,31586,31602,31601,31632,31654,31655,31672,31660,31645,31656,31621,31658,31644,31650,31659,31668,31697,31681,31692,31709,31706,31717,31718,31722,31756,31742,31740,31759,31766,31755,39687,39689,39690,39691,39692,39693,39694,39696,39697,39698,39700,39701,39702,39703,39704,39705,39706,39707,39708,39709,39710,39712,39713,39714,39716,39717,39718,39719,39720,39721,39722,39723,39724,39725,39726,39728,39729,39731,39732,39733,39734,39735,39736,39737,39738,39741,39742,39743,39744,39750,39754,39755,39756,39758,39760,39762,39763,39765,39766,39767,39768,39769,39770,39771,39772,39773,39774,39775,39776,39777,39778,39779,39780,39781,39782,39783,39784,39785,39786,39787,39788,39789,39790,39791,39792,39793,39794,39795,39796,39797,39798,39799,39800,39801,39802,39803,31775,31786,31782,31800,31809,31808,33278,33281,33282,33284,33260,34884,33313,33314,33315,33325,33327,33320,33323,33336,33339,33331,33332,33342,33348,33353,33355,33359,33370,33375,33384,34942,34949,34952,35032,35039,35166,32669,32671,32679,32687,32688,32690,31868,25929,31889,31901,31900,31902,31906,31922,31932,31933,31937,31943,31948,31949,31944,31941,31959,31976,33390,26280,32703,32718,32725,32741,32737,32742,32745,32750,32755,31992,32119,32166,32174,32327,32411,40632,40628,36211,36228,36244,36241,36273,36199,36205,35911,35913,37194,37200,37198,37199,37220,39804,39805,39806,39807,39808,39809,39810,39811,39812,39813,39814,39815,39816,39817,39818,39819,39820,39821,39822,39823,39824,39825,39826,39827,39828,39829,39830,39831,39832,39833,39834,39835,39836,39837,39838,39839,39840,39841,39842,39843,39844,39845,39846,39847,39848,39849,39850,39851,39852,39853,39854,39855,39856,39857,39858,39859,39860,39861,39862,39863,39864,39865,39866,39867,39868,39869,39870,39871,39872,39873,39874,39875,39876,39877,39878,39879,39880,39881,39882,39883,39884,39885,39886,39887,39888,39889,39890,39891,39892,39893,39894,39895,39896,39897,39898,39899,37218,37217,37232,37225,37231,37245,37246,37234,37236,37241,37260,37253,37264,37261,37265,37282,37283,37290,37293,37294,37295,37301,37300,37306,35925,40574,36280,36331,36357,36441,36457,36277,36287,36284,36282,36292,36310,36311,36314,36318,36302,36303,36315,36294,36332,36343,36344,36323,36345,36347,36324,36361,36349,36372,36381,36383,36396,36398,36387,36399,36410,36416,36409,36405,36413,36401,36425,36417,36418,36433,36434,36426,36464,36470,36476,36463,36468,36485,36495,36500,36496,36508,36510,35960,35970,35978,35973,35992,35988,26011,35286,35294,35290,35292,39900,39901,39902,39903,39904,39905,39906,39907,39908,39909,39910,39911,39912,39913,39914,39915,39916,39917,39918,39919,39920,39921,39922,39923,39924,39925,39926,39927,39928,39929,39930,39931,39932,39933,39934,39935,39936,39937,39938,39939,39940,39941,39942,39943,39944,39945,39946,39947,39948,39949,39950,39951,39952,39953,39954,39955,39956,39957,39958,39959,39960,39961,39962,39963,39964,39965,39966,39967,39968,39969,39970,39971,39972,39973,39974,39975,39976,39977,39978,39979,39980,39981,39982,39983,39984,39985,39986,39987,39988,39989,39990,39991,39992,39993,39994,39995,35301,35307,35311,35390,35622,38739,38633,38643,38639,38662,38657,38664,38671,38670,38698,38701,38704,38718,40832,40835,40837,40838,40839,40840,40841,40842,40844,40702,40715,40717,38585,38588,38589,38606,38610,30655,38624,37518,37550,37576,37694,37738,37834,37775,37950,37995,40063,40066,40069,40070,40071,40072,31267,40075,40078,40080,40081,40082,40084,40085,40090,40091,40094,40095,40096,40097,40098,40099,40101,40102,40103,40104,40105,40107,40109,40110,40112,40113,40114,40115,40116,40117,40118,40119,40122,40123,40124,40125,40132,40133,40134,40135,40138,40139,39996,39997,39998,39999,40000,40001,40002,40003,40004,40005,40006,40007,40008,40009,40010,40011,40012,40013,40014,40015,40016,40017,40018,40019,40020,40021,40022,40023,40024,40025,40026,40027,40028,40029,40030,40031,40032,40033,40034,40035,40036,40037,40038,40039,40040,40041,40042,40043,40044,40045,40046,40047,40048,40049,40050,40051,40052,40053,40054,40055,40056,40057,40058,40059,40061,40062,40064,40067,40068,40073,40074,40076,40079,40083,40086,40087,40088,40089,40093,40106,40108,40111,40121,40126,40127,40128,40129,40130,40136,40137,40145,40146,40154,40155,40160,40161,40140,40141,40142,40143,40144,40147,40148,40149,40151,40152,40153,40156,40157,40159,40162,38780,38789,38801,38802,38804,38831,38827,38819,38834,38836,39601,39600,39607,40536,39606,39610,39612,39617,39616,39621,39618,39627,39628,39633,39749,39747,39751,39753,39752,39757,39761,39144,39181,39214,39253,39252,39647,39649,39654,39663,39659,39675,39661,39673,39688,39695,39699,39711,39715,40637,40638,32315,40578,40583,40584,40587,40594,37846,40605,40607,40667,40668,40669,40672,40671,40674,40681,40679,40677,40682,40687,40738,40748,40751,40761,40759,40765,40766,40772,40163,40164,40165,40166,40167,40168,40169,40170,40171,40172,40173,40174,40175,40176,40177,40178,40179,40180,40181,40182,40183,40184,40185,40186,40187,40188,40189,40190,40191,40192,40193,40194,40195,40196,40197,40198,40199,40200,40201,40202,40203,40204,40205,40206,40207,40208,40209,40210,40211,40212,40213,40214,40215,40216,40217,40218,40219,40220,40221,40222,40223,40224,40225,40226,40227,40228,40229,40230,40231,40232,40233,40234,40235,40236,40237,40238,40239,40240,40241,40242,40243,40244,40245,40246,40247,40248,40249,40250,40251,40252,40253,40254,40255,40256,40257,40258,57908,57909,57910,57911,57912,57913,57914,57915,57916,57917,57918,57919,57920,57921,57922,57923,57924,57925,57926,57927,57928,57929,57930,57931,57932,57933,57934,57935,57936,57937,57938,57939,57940,57941,57942,57943,57944,57945,57946,57947,57948,57949,57950,57951,57952,57953,57954,57955,57956,57957,57958,57959,57960,57961,57962,57963,57964,57965,57966,57967,57968,57969,57970,57971,57972,57973,57974,57975,57976,57977,57978,57979,57980,57981,57982,57983,57984,57985,57986,57987,57988,57989,57990,57991,57992,57993,57994,57995,57996,57997,57998,57999,58000,58001,40259,40260,40261,40262,40263,40264,40265,40266,40267,40268,40269,40270,40271,40272,40273,40274,40275,40276,40277,40278,40279,40280,40281,40282,40283,40284,40285,40286,40287,40288,40289,40290,40291,40292,40293,40294,40295,40296,40297,40298,40299,40300,40301,40302,40303,40304,40305,40306,40307,40308,40309,40310,40311,40312,40313,40314,40315,40316,40317,40318,40319,40320,40321,40322,40323,40324,40325,40326,40327,40328,40329,40330,40331,40332,40333,40334,40335,40336,40337,40338,40339,40340,40341,40342,40343,40344,40345,40346,40347,40348,40349,40350,40351,40352,40353,40354,58002,58003,58004,58005,58006,58007,58008,58009,58010,58011,58012,58013,58014,58015,58016,58017,58018,58019,58020,58021,58022,58023,58024,58025,58026,58027,58028,58029,58030,58031,58032,58033,58034,58035,58036,58037,58038,58039,58040,58041,58042,58043,58044,58045,58046,58047,58048,58049,58050,58051,58052,58053,58054,58055,58056,58057,58058,58059,58060,58061,58062,58063,58064,58065,58066,58067,58068,58069,58070,58071,58072,58073,58074,58075,58076,58077,58078,58079,58080,58081,58082,58083,58084,58085,58086,58087,58088,58089,58090,58091,58092,58093,58094,58095,40355,40356,40357,40358,40359,40360,40361,40362,40363,40364,40365,40366,40367,40368,40369,40370,40371,40372,40373,40374,40375,40376,40377,40378,40379,40380,40381,40382,40383,40384,40385,40386,40387,40388,40389,40390,40391,40392,40393,40394,40395,40396,40397,40398,40399,40400,40401,40402,40403,40404,40405,40406,40407,40408,40409,40410,40411,40412,40413,40414,40415,40416,40417,40418,40419,40420,40421,40422,40423,40424,40425,40426,40427,40428,40429,40430,40431,40432,40433,40434,40435,40436,40437,40438,40439,40440,40441,40442,40443,40444,40445,40446,40447,40448,40449,40450,58096,58097,58098,58099,58100,58101,58102,58103,58104,58105,58106,58107,58108,58109,58110,58111,58112,58113,58114,58115,58116,58117,58118,58119,58120,58121,58122,58123,58124,58125,58126,58127,58128,58129,58130,58131,58132,58133,58134,58135,58136,58137,58138,58139,58140,58141,58142,58143,58144,58145,58146,58147,58148,58149,58150,58151,58152,58153,58154,58155,58156,58157,58158,58159,58160,58161,58162,58163,58164,58165,58166,58167,58168,58169,58170,58171,58172,58173,58174,58175,58176,58177,58178,58179,58180,58181,58182,58183,58184,58185,58186,58187,58188,58189,40451,40452,40453,40454,40455,40456,40457,40458,40459,40460,40461,40462,40463,40464,40465,40466,40467,40468,40469,40470,40471,40472,40473,40474,40475,40476,40477,40478,40484,40487,40494,40496,40500,40507,40508,40512,40525,40528,40530,40531,40532,40534,40537,40541,40543,40544,40545,40546,40549,40558,40559,40562,40564,40565,40566,40567,40568,40569,40570,40571,40572,40573,40576,40577,40579,40580,40581,40582,40585,40586,40588,40589,40590,40591,40592,40593,40596,40597,40598,40599,40600,40601,40602,40603,40604,40606,40608,40609,40610,40611,40612,40613,40615,40616,40617,40618,58190,58191,58192,58193,58194,58195,58196,58197,58198,58199,58200,58201,58202,58203,58204,58205,58206,58207,58208,58209,58210,58211,58212,58213,58214,58215,58216,58217,58218,58219,58220,58221,58222,58223,58224,58225,58226,58227,58228,58229,58230,58231,58232,58233,58234,58235,58236,58237,58238,58239,58240,58241,58242,58243,58244,58245,58246,58247,58248,58249,58250,58251,58252,58253,58254,58255,58256,58257,58258,58259,58260,58261,58262,58263,58264,58265,58266,58267,58268,58269,58270,58271,58272,58273,58274,58275,58276,58277,58278,58279,58280,58281,58282,58283,40619,40620,40621,40622,40623,40624,40625,40626,40627,40629,40630,40631,40633,40634,40636,40639,40640,40641,40642,40643,40645,40646,40647,40648,40650,40651,40652,40656,40658,40659,40661,40662,40663,40665,40666,40670,40673,40675,40676,40678,40680,40683,40684,40685,40686,40688,40689,40690,40691,40692,40693,40694,40695,40696,40698,40701,40703,40704,40705,40706,40707,40708,40709,40710,40711,40712,40713,40714,40716,40719,40721,40722,40724,40725,40726,40728,40730,40731,40732,40733,40734,40735,40737,40739,40740,40741,40742,40743,40744,40745,40746,40747,40749,40750,40752,40753,58284,58285,58286,58287,58288,58289,58290,58291,58292,58293,58294,58295,58296,58297,58298,58299,58300,58301,58302,58303,58304,58305,58306,58307,58308,58309,58310,58311,58312,58313,58314,58315,58316,58317,58318,58319,58320,58321,58322,58323,58324,58325,58326,58327,58328,58329,58330,58331,58332,58333,58334,58335,58336,58337,58338,58339,58340,58341,58342,58343,58344,58345,58346,58347,58348,58349,58350,58351,58352,58353,58354,58355,58356,58357,58358,58359,58360,58361,58362,58363,58364,58365,58366,58367,58368,58369,58370,58371,58372,58373,58374,58375,58376,58377,40754,40755,40756,40757,40758,40760,40762,40764,40767,40768,40769,40770,40771,40773,40774,40775,40776,40777,40778,40779,40780,40781,40782,40783,40786,40787,40788,40789,40790,40791,40792,40793,40794,40795,40796,40797,40798,40799,40800,40801,40802,40803,40804,40805,40806,40807,40808,40809,40810,40811,40812,40813,40814,40815,40816,40817,40818,40819,40820,40821,40822,40823,40824,40825,40826,40827,40828,40829,40830,40833,40834,40845,40846,40847,40848,40849,40850,40851,40852,40853,40854,40855,40856,40860,40861,40862,40865,40866,40867,40868,40869,63788,63865,63893,63975,63985,58378,58379,58380,58381,58382,58383,58384,58385,58386,58387,58388,58389,58390,58391,58392,58393,58394,58395,58396,58397,58398,58399,58400,58401,58402,58403,58404,58405,58406,58407,58408,58409,58410,58411,58412,58413,58414,58415,58416,58417,58418,58419,58420,58421,58422,58423,58424,58425,58426,58427,58428,58429,58430,58431,58432,58433,58434,58435,58436,58437,58438,58439,58440,58441,58442,58443,58444,58445,58446,58447,58448,58449,58450,58451,58452,58453,58454,58455,58456,58457,58458,58459,58460,58461,58462,58463,58464,58465,58466,58467,58468,58469,58470,58471,64012,64013,64014,64015,64017,64019,64020,64024,64031,64032,64033,64035,64036,64039,64040,64041,11905,59414,59415,59416,11908,13427,13383,11912,11915,59422,13726,13850,13838,11916,11927,14702,14616,59430,14799,14815,14963,14800,59435,59436,15182,15470,15584,11943,59441,59442,11946,16470,16735,11950,17207,11955,11958,11959,59451,17329,17324,11963,17373,17622,18017,17996,59459,18211,18217,18300,18317,11978,18759,18810,18813,18818,18819,18821,18822,18847,18843,18871,18870,59476,59477,19619,19615,19616,19617,19575,19618,19731,19732,19733,19734,19735,19736,19737,19886,59492,58472,58473,58474,58475,58476,58477,58478,58479,58480,58481,58482,58483,58484,58485,58486,58487,58488,58489,58490,58491,58492,58493,58494,58495,58496,58497,58498,58499,58500,58501,58502,58503,58504,58505,58506,58507,58508,58509,58510,58511,58512,58513,58514,58515,58516,58517,58518,58519,58520,58521,58522,58523,58524,58525,58526,58527,58528,58529,58530,58531,58532,58533,58534,58535,58536,58537,58538,58539,58540,58541,58542,58543,58544,58545,58546,58547,58548,58549,58550,58551,58552,58553,58554,58555,58556,58557,58558,58559,58560,58561,58562,58563,58564,58565],
- "gb18030-ranges":[[0,128],[36,165],[38,169],[45,178],[50,184],[81,216],[89,226],[95,235],[96,238],[100,244],[103,248],[104,251],[105,253],[109,258],[126,276],[133,284],[148,300],[172,325],[175,329],[179,334],[208,364],[306,463],[307,465],[308,467],[309,469],[310,471],[311,473],[312,475],[313,477],[341,506],[428,594],[443,610],[544,712],[545,716],[558,730],[741,930],[742,938],[749,962],[750,970],[805,1026],[819,1104],[820,1106],[7922,8209],[7924,8215],[7925,8218],[7927,8222],[7934,8231],[7943,8241],[7944,8244],[7945,8246],[7950,8252],[8062,8365],[8148,8452],[8149,8454],[8152,8458],[8164,8471],[8174,8482],[8236,8556],[8240,8570],[8262,8596],[8264,8602],[8374,8713],[8380,8720],[8381,8722],[8384,8726],[8388,8731],[8390,8737],[8392,8740],[8393,8742],[8394,8748],[8396,8751],[8401,8760],[8406,8766],[8416,8777],[8419,8781],[8424,8787],[8437,8802],[8439,8808],[8445,8816],[8482,8854],[8485,8858],[8496,8870],[8521,8896],[8603,8979],[8936,9322],[8946,9372],[9046,9548],[9050,9588],[9063,9616],[9066,9622],[9076,9634],[9092,9652],[9100,9662],[9108,9672],[9111,9676],[9113,9680],[9131,9702],[9162,9735],[9164,9738],[9218,9793],[9219,9795],[11329,11906],[11331,11909],[11334,11913],[11336,11917],[11346,11928],[11361,11944],[11363,11947],[11366,11951],[11370,11956],[11372,11960],[11375,11964],[11389,11979],[11682,12284],[11686,12292],[11687,12312],[11692,12319],[11694,12330],[11714,12351],[11716,12436],[11723,12447],[11725,12535],[11730,12543],[11736,12586],[11982,12842],[11989,12850],[12102,12964],[12336,13200],[12348,13215],[12350,13218],[12384,13253],[12393,13263],[12395,13267],[12397,13270],[12510,13384],[12553,13428],[12851,13727],[12962,13839],[12973,13851],[13738,14617],[13823,14703],[13919,14801],[13933,14816],[14080,14964],[14298,15183],[14585,15471],[14698,15585],[15583,16471],[15847,16736],[16318,17208],[16434,17325],[16438,17330],[16481,17374],[16729,17623],[17102,17997],[17122,18018],[17315,18212],[17320,18218],[17402,18301],[17418,18318],[17859,18760],[17909,18811],[17911,18814],[17915,18820],[17916,18823],[17936,18844],[17939,18848],[17961,18872],[18664,19576],[18703,19620],[18814,19738],[18962,19887],[19043,40870],[33469,59244],[33470,59336],[33471,59367],[33484,59413],[33485,59417],[33490,59423],[33497,59431],[33501,59437],[33505,59443],[33513,59452],[33520,59460],[33536,59478],[33550,59493],[37845,63789],[37921,63866],[37948,63894],[38029,63976],[38038,63986],[38064,64016],[38065,64018],[38066,64021],[38069,64025],[38075,64034],[38076,64037],[38078,64042],[39108,65074],[39109,65093],[39113,65107],[39114,65112],[39115,65127],[39116,65132],[39265,65375],[39394,65510],[189000,65536]],
- "jis0208":[12288,12289,12290,65292,65294,12539,65306,65307,65311,65281,12443,12444,180,65344,168,65342,65507,65343,12541,12542,12445,12446,12291,20189,12293,12294,12295,12540,8213,8208,65295,65340,65374,8741,65372,8230,8229,8216,8217,8220,8221,65288,65289,12308,12309,65339,65341,65371,65373,12296,12297,12298,12299,12300,12301,12302,12303,12304,12305,65291,65293,177,215,247,65309,8800,65308,65310,8806,8807,8734,8756,9794,9792,176,8242,8243,8451,65509,65284,65504,65505,65285,65283,65286,65290,65312,167,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,9661,9660,8251,12306,8594,8592,8593,8595,12307,null,null,null,null,null,null,null,null,null,null,null,8712,8715,8838,8839,8834,8835,8746,8745,null,null,null,null,null,null,null,null,8743,8744,65506,8658,8660,8704,8707,null,null,null,null,null,null,null,null,null,null,null,8736,8869,8978,8706,8711,8801,8786,8810,8811,8730,8765,8733,8757,8747,8748,null,null,null,null,null,null,null,8491,8240,9839,9837,9834,8224,8225,182,null,null,null,null,9711,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,null,null,null,null,null,null,null,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,null,null,null,null,null,null,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,null,null,null,null,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,null,null,null,null,null,null,null,null,null,null,null,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,null,null,null,null,null,null,null,null,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,null,null,null,null,null,null,null,null,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,null,null,null,null,null,null,null,null,null,null,null,null,null,9472,9474,9484,9488,9496,9492,9500,9516,9508,9524,9532,9473,9475,9487,9491,9499,9495,9507,9523,9515,9531,9547,9504,9519,9512,9527,9535,9501,9520,9509,9528,9538,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9322,9323,9324,9325,9326,9327,9328,9329,9330,9331,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,null,13129,13076,13090,13133,13080,13095,13059,13110,13137,13143,13069,13094,13091,13099,13130,13115,13212,13213,13214,13198,13199,13252,13217,null,null,null,null,null,null,null,null,13179,12317,12319,8470,13261,8481,12964,12965,12966,12967,12968,12849,12850,12857,13182,13181,13180,8786,8801,8747,8750,8721,8730,8869,8736,8735,8895,8757,8745,8746,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20124,21782,23043,38463,21696,24859,25384,23030,36898,33909,33564,31312,24746,25569,28197,26093,33894,33446,39925,26771,22311,26017,25201,23451,22992,34427,39156,32098,32190,39822,25110,31903,34999,23433,24245,25353,26263,26696,38343,38797,26447,20197,20234,20301,20381,20553,22258,22839,22996,23041,23561,24799,24847,24944,26131,26885,28858,30031,30064,31227,32173,32239,32963,33806,34915,35586,36949,36986,21307,20117,20133,22495,32946,37057,30959,19968,22769,28322,36920,31282,33576,33419,39983,20801,21360,21693,21729,22240,23035,24341,39154,28139,32996,34093,38498,38512,38560,38907,21515,21491,23431,28879,32701,36802,38632,21359,40284,31418,19985,30867,33276,28198,22040,21764,27421,34074,39995,23013,21417,28006,29916,38287,22082,20113,36939,38642,33615,39180,21473,21942,23344,24433,26144,26355,26628,27704,27891,27945,29787,30408,31310,38964,33521,34907,35424,37613,28082,30123,30410,39365,24742,35585,36234,38322,27022,21421,20870,22290,22576,22852,23476,24310,24616,25513,25588,27839,28436,28814,28948,29017,29141,29503,32257,33398,33489,34199,36960,37467,40219,22633,26044,27738,29989,20985,22830,22885,24448,24540,25276,26106,27178,27431,27572,29579,32705,35158,40236,40206,40644,23713,27798,33659,20740,23627,25014,33222,26742,29281,20057,20474,21368,24681,28201,31311,38899,19979,21270,20206,20309,20285,20385,20339,21152,21487,22025,22799,23233,23478,23521,31185,26247,26524,26550,27468,27827,28779,29634,31117,31166,31292,31623,33457,33499,33540,33655,33775,33747,34662,35506,22057,36008,36838,36942,38686,34442,20420,23784,25105,29273,30011,33253,33469,34558,36032,38597,39187,39381,20171,20250,35299,22238,22602,22730,24315,24555,24618,24724,24674,25040,25106,25296,25913,39745,26214,26800,28023,28784,30028,30342,32117,33445,34809,38283,38542,35997,20977,21182,22806,21683,23475,23830,24936,27010,28079,30861,33995,34903,35442,37799,39608,28012,39336,34521,22435,26623,34510,37390,21123,22151,21508,24275,25313,25785,26684,26680,27579,29554,30906,31339,35226,35282,36203,36611,37101,38307,38548,38761,23398,23731,27005,38989,38990,25499,31520,27179,27263,26806,39949,28511,21106,21917,24688,25324,27963,28167,28369,33883,35088,36676,19988,39993,21494,26907,27194,38788,26666,20828,31427,33970,37340,37772,22107,40232,26658,33541,33841,31909,21000,33477,29926,20094,20355,20896,23506,21002,21208,21223,24059,21914,22570,23014,23436,23448,23515,24178,24185,24739,24863,24931,25022,25563,25954,26577,26707,26874,27454,27475,27735,28450,28567,28485,29872,29976,30435,30475,31487,31649,31777,32233,32566,32752,32925,33382,33694,35251,35532,36011,36996,37969,38291,38289,38306,38501,38867,39208,33304,20024,21547,23736,24012,29609,30284,30524,23721,32747,36107,38593,38929,38996,39000,20225,20238,21361,21916,22120,22522,22855,23305,23492,23696,24076,24190,24524,25582,26426,26071,26082,26399,26827,26820,27231,24112,27589,27671,27773,30079,31048,23395,31232,32000,24509,35215,35352,36020,36215,36556,36637,39138,39438,39740,20096,20605,20736,22931,23452,25135,25216,25836,27450,29344,30097,31047,32681,34811,35516,35696,25516,33738,38816,21513,21507,21931,26708,27224,35440,30759,26485,40653,21364,23458,33050,34384,36870,19992,20037,20167,20241,21450,21560,23470,24339,24613,25937,26429,27714,27762,27875,28792,29699,31350,31406,31496,32026,31998,32102,26087,29275,21435,23621,24040,25298,25312,25369,28192,34394,35377,36317,37624,28417,31142,39770,20136,20139,20140,20379,20384,20689,20807,31478,20849,20982,21332,21281,21375,21483,21932,22659,23777,24375,24394,24623,24656,24685,25375,25945,27211,27841,29378,29421,30703,33016,33029,33288,34126,37111,37857,38911,39255,39514,20208,20957,23597,26241,26989,23616,26354,26997,29577,26704,31873,20677,21220,22343,24062,37670,26020,27427,27453,29748,31105,31165,31563,32202,33465,33740,34943,35167,35641,36817,37329,21535,37504,20061,20534,21477,21306,29399,29590,30697,33510,36527,39366,39368,39378,20855,24858,34398,21936,31354,20598,23507,36935,38533,20018,27355,37351,23633,23624,25496,31391,27795,38772,36705,31402,29066,38536,31874,26647,32368,26705,37740,21234,21531,34219,35347,32676,36557,37089,21350,34952,31041,20418,20670,21009,20804,21843,22317,29674,22411,22865,24418,24452,24693,24950,24935,25001,25522,25658,25964,26223,26690,28179,30054,31293,31995,32076,32153,32331,32619,33550,33610,34509,35336,35427,35686,36605,38938,40335,33464,36814,39912,21127,25119,25731,28608,38553,26689,20625,27424,27770,28500,31348,32080,34880,35363,26376,20214,20537,20518,20581,20860,21048,21091,21927,22287,22533,23244,24314,25010,25080,25331,25458,26908,27177,29309,29356,29486,30740,30831,32121,30476,32937,35211,35609,36066,36562,36963,37749,38522,38997,39443,40568,20803,21407,21427,24187,24358,28187,28304,29572,29694,32067,33335,35328,35578,38480,20046,20491,21476,21628,22266,22993,23396,24049,24235,24359,25144,25925,26543,28246,29392,31946,34996,32929,32993,33776,34382,35463,36328,37431,38599,39015,40723,20116,20114,20237,21320,21577,21566,23087,24460,24481,24735,26791,27278,29786,30849,35486,35492,35703,37264,20062,39881,20132,20348,20399,20505,20502,20809,20844,21151,21177,21246,21402,21475,21521,21518,21897,22353,22434,22909,23380,23389,23439,24037,24039,24055,24184,24195,24218,24247,24344,24658,24908,25239,25304,25511,25915,26114,26179,26356,26477,26657,26775,27083,27743,27946,28009,28207,28317,30002,30343,30828,31295,31968,32005,32024,32094,32177,32789,32771,32943,32945,33108,33167,33322,33618,34892,34913,35611,36002,36092,37066,37237,37489,30783,37628,38308,38477,38917,39321,39640,40251,21083,21163,21495,21512,22741,25335,28640,35946,36703,40633,20811,21051,21578,22269,31296,37239,40288,40658,29508,28425,33136,29969,24573,24794,39592,29403,36796,27492,38915,20170,22256,22372,22718,23130,24680,25031,26127,26118,26681,26801,28151,30165,32058,33390,39746,20123,20304,21449,21766,23919,24038,24046,26619,27801,29811,30722,35408,37782,35039,22352,24231,25387,20661,20652,20877,26368,21705,22622,22971,23472,24425,25165,25505,26685,27507,28168,28797,37319,29312,30741,30758,31085,25998,32048,33756,35009,36617,38555,21092,22312,26448,32618,36001,20916,22338,38442,22586,27018,32948,21682,23822,22524,30869,40442,20316,21066,21643,25662,26152,26388,26613,31364,31574,32034,37679,26716,39853,31545,21273,20874,21047,23519,25334,25774,25830,26413,27578,34217,38609,30352,39894,25420,37638,39851,30399,26194,19977,20632,21442,23665,24808,25746,25955,26719,29158,29642,29987,31639,32386,34453,35715,36059,37240,39184,26028,26283,27531,20181,20180,20282,20351,21050,21496,21490,21987,22235,22763,22987,22985,23039,23376,23629,24066,24107,24535,24605,25351,25903,23388,26031,26045,26088,26525,27490,27515,27663,29509,31049,31169,31992,32025,32043,32930,33026,33267,35222,35422,35433,35430,35468,35566,36039,36060,38604,39164,27503,20107,20284,20365,20816,23383,23546,24904,25345,26178,27425,28363,27835,29246,29885,30164,30913,31034,32780,32819,33258,33940,36766,27728,40575,24335,35672,40235,31482,36600,23437,38635,19971,21489,22519,22833,23241,23460,24713,28287,28422,30142,36074,23455,34048,31712,20594,26612,33437,23649,34122,32286,33294,20889,23556,25448,36198,26012,29038,31038,32023,32773,35613,36554,36974,34503,37034,20511,21242,23610,26451,28796,29237,37196,37320,37675,33509,23490,24369,24825,20027,21462,23432,25163,26417,27530,29417,29664,31278,33131,36259,37202,39318,20754,21463,21610,23551,25480,27193,32172,38656,22234,21454,21608,23447,23601,24030,20462,24833,25342,27954,31168,31179,32066,32333,32722,33261,33311,33936,34886,35186,35728,36468,36655,36913,37195,37228,38598,37276,20160,20303,20805,21313,24467,25102,26580,27713,28171,29539,32294,37325,37507,21460,22809,23487,28113,31069,32302,31899,22654,29087,20986,34899,36848,20426,23803,26149,30636,31459,33308,39423,20934,24490,26092,26991,27529,28147,28310,28516,30462,32020,24033,36981,37255,38918,20966,21021,25152,26257,26329,28186,24246,32210,32626,26360,34223,34295,35576,21161,21465,22899,24207,24464,24661,37604,38500,20663,20767,21213,21280,21319,21484,21736,21830,21809,22039,22888,22974,23100,23477,23558,23567,23569,23578,24196,24202,24288,24432,25215,25220,25307,25484,25463,26119,26124,26157,26230,26494,26786,27167,27189,27836,28040,28169,28248,28988,28966,29031,30151,30465,30813,30977,31077,31216,31456,31505,31911,32057,32918,33750,33931,34121,34909,35059,35359,35388,35412,35443,35937,36062,37284,37478,37758,37912,38556,38808,19978,19976,19998,20055,20887,21104,22478,22580,22732,23330,24120,24773,25854,26465,26454,27972,29366,30067,31331,33976,35698,37304,37664,22065,22516,39166,25325,26893,27542,29165,32340,32887,33394,35302,39135,34645,36785,23611,20280,20449,20405,21767,23072,23517,23529,24515,24910,25391,26032,26187,26862,27035,28024,28145,30003,30137,30495,31070,31206,32051,33251,33455,34218,35242,35386,36523,36763,36914,37341,38663,20154,20161,20995,22645,22764,23563,29978,23613,33102,35338,36805,38499,38765,31525,35535,38920,37218,22259,21416,36887,21561,22402,24101,25512,27700,28810,30561,31883,32736,34928,36930,37204,37648,37656,38543,29790,39620,23815,23913,25968,26530,36264,38619,25454,26441,26905,33733,38935,38592,35070,28548,25722,23544,19990,28716,30045,26159,20932,21046,21218,22995,24449,24615,25104,25919,25972,26143,26228,26866,26646,27491,28165,29298,29983,30427,31934,32854,22768,35069,35199,35488,35475,35531,36893,37266,38738,38745,25993,31246,33030,38587,24109,24796,25114,26021,26132,26512,30707,31309,31821,32318,33034,36012,36196,36321,36447,30889,20999,25305,25509,25666,25240,35373,31363,31680,35500,38634,32118,33292,34633,20185,20808,21315,21344,23459,23554,23574,24029,25126,25159,25776,26643,26676,27849,27973,27927,26579,28508,29006,29053,26059,31359,31661,32218,32330,32680,33146,33307,33337,34214,35438,36046,36341,36984,36983,37549,37521,38275,39854,21069,21892,28472,28982,20840,31109,32341,33203,31950,22092,22609,23720,25514,26366,26365,26970,29401,30095,30094,30990,31062,31199,31895,32032,32068,34311,35380,38459,36961,40736,20711,21109,21452,21474,20489,21930,22766,22863,29245,23435,23652,21277,24803,24819,25436,25475,25407,25531,25805,26089,26361,24035,27085,27133,28437,29157,20105,30185,30456,31379,31967,32207,32156,32865,33609,33624,33900,33980,34299,35013,36208,36865,36973,37783,38684,39442,20687,22679,24974,33235,34101,36104,36896,20419,20596,21063,21363,24687,25417,26463,28204,36275,36895,20439,23646,36042,26063,32154,21330,34966,20854,25539,23384,23403,23562,25613,26449,36956,20182,22810,22826,27760,35409,21822,22549,22949,24816,25171,26561,33333,26965,38464,39364,39464,20307,22534,23550,32784,23729,24111,24453,24608,24907,25140,26367,27888,28382,32974,33151,33492,34955,36024,36864,36910,38538,40667,39899,20195,21488,22823,31532,37261,38988,40441,28381,28711,21331,21828,23429,25176,25246,25299,27810,28655,29730,35351,37944,28609,35582,33592,20967,34552,21482,21481,20294,36948,36784,22890,33073,24061,31466,36799,26842,35895,29432,40008,27197,35504,20025,21336,22022,22374,25285,25506,26086,27470,28129,28251,28845,30701,31471,31658,32187,32829,32966,34507,35477,37723,22243,22727,24382,26029,26262,27264,27573,30007,35527,20516,30693,22320,24347,24677,26234,27744,30196,31258,32622,33268,34584,36933,39347,31689,30044,31481,31569,33988,36880,31209,31378,33590,23265,30528,20013,20210,23449,24544,25277,26172,26609,27880,34411,34935,35387,37198,37619,39376,27159,28710,29482,33511,33879,36015,19969,20806,20939,21899,23541,24086,24115,24193,24340,24373,24427,24500,25074,25361,26274,26397,28526,29266,30010,30522,32884,33081,33144,34678,35519,35548,36229,36339,37530,38263,38914,40165,21189,25431,30452,26389,27784,29645,36035,37806,38515,27941,22684,26894,27084,36861,37786,30171,36890,22618,26626,25524,27131,20291,28460,26584,36795,34086,32180,37716,26943,28528,22378,22775,23340,32044,29226,21514,37347,40372,20141,20302,20572,20597,21059,35998,21576,22564,23450,24093,24213,24237,24311,24351,24716,25269,25402,25552,26799,27712,30855,31118,31243,32224,33351,35330,35558,36420,36883,37048,37165,37336,40718,27877,25688,25826,25973,28404,30340,31515,36969,37841,28346,21746,24505,25764,36685,36845,37444,20856,22635,22825,23637,24215,28155,32399,29980,36028,36578,39003,28857,20253,27583,28593,30000,38651,20814,21520,22581,22615,22956,23648,24466,26007,26460,28193,30331,33759,36077,36884,37117,37709,30757,30778,21162,24230,22303,22900,24594,20498,20826,20908,20941,20992,21776,22612,22616,22871,23445,23798,23947,24764,25237,25645,26481,26691,26812,26847,30423,28120,28271,28059,28783,29128,24403,30168,31095,31561,31572,31570,31958,32113,21040,33891,34153,34276,35342,35588,35910,36367,36867,36879,37913,38518,38957,39472,38360,20685,21205,21516,22530,23566,24999,25758,27934,30643,31461,33012,33796,36947,37509,23776,40199,21311,24471,24499,28060,29305,30563,31167,31716,27602,29420,35501,26627,27233,20984,31361,26932,23626,40182,33515,23493,37193,28702,22136,23663,24775,25958,27788,35930,36929,38931,21585,26311,37389,22856,37027,20869,20045,20970,34201,35598,28760,25466,37707,26978,39348,32260,30071,21335,26976,36575,38627,27741,20108,23612,24336,36841,21250,36049,32905,34425,24319,26085,20083,20837,22914,23615,38894,20219,22922,24525,35469,28641,31152,31074,23527,33905,29483,29105,24180,24565,25467,25754,29123,31896,20035,24316,20043,22492,22178,24745,28611,32013,33021,33075,33215,36786,35223,34468,24052,25226,25773,35207,26487,27874,27966,29750,30772,23110,32629,33453,39340,20467,24259,25309,25490,25943,26479,30403,29260,32972,32954,36649,37197,20493,22521,23186,26757,26995,29028,29437,36023,22770,36064,38506,36889,34687,31204,30695,33833,20271,21093,21338,25293,26575,27850,30333,31636,31893,33334,34180,36843,26333,28448,29190,32283,33707,39361,40614,20989,31665,30834,31672,32903,31560,27368,24161,32908,30033,30048,20843,37474,28300,30330,37271,39658,20240,32624,25244,31567,38309,40169,22138,22617,34532,38588,20276,21028,21322,21453,21467,24070,25644,26001,26495,27710,27726,29256,29359,29677,30036,32321,33324,34281,36009,31684,37318,29033,38930,39151,25405,26217,30058,30436,30928,34115,34542,21290,21329,21542,22915,24199,24444,24754,25161,25209,25259,26000,27604,27852,30130,30382,30865,31192,32203,32631,32933,34987,35513,36027,36991,38750,39131,27147,31800,20633,23614,24494,26503,27608,29749,30473,32654,40763,26570,31255,21305,30091,39661,24422,33181,33777,32920,24380,24517,30050,31558,36924,26727,23019,23195,32016,30334,35628,20469,24426,27161,27703,28418,29922,31080,34920,35413,35961,24287,25551,30149,31186,33495,37672,37618,33948,34541,39981,21697,24428,25996,27996,28693,36007,36051,38971,25935,29942,19981,20184,22496,22827,23142,23500,20904,24067,24220,24598,25206,25975,26023,26222,28014,29238,31526,33104,33178,33433,35676,36000,36070,36212,38428,38468,20398,25771,27494,33310,33889,34154,37096,23553,26963,39080,33914,34135,20239,21103,24489,24133,26381,31119,33145,35079,35206,28149,24343,25173,27832,20175,29289,39826,20998,21563,22132,22707,24996,25198,28954,22894,31881,31966,32027,38640,25991,32862,19993,20341,20853,22592,24163,24179,24330,26564,20006,34109,38281,38491,31859,38913,20731,22721,30294,30887,21029,30629,34065,31622,20559,22793,29255,31687,32232,36794,36820,36941,20415,21193,23081,24321,38829,20445,33303,37610,22275,25429,27497,29995,35036,36628,31298,21215,22675,24917,25098,26286,27597,31807,33769,20515,20472,21253,21574,22577,22857,23453,23792,23791,23849,24214,25265,25447,25918,26041,26379,27861,27873,28921,30770,32299,32990,33459,33804,34028,34562,35090,35370,35914,37030,37586,39165,40179,40300,20047,20129,20621,21078,22346,22952,24125,24536,24537,25151,26292,26395,26576,26834,20882,32033,32938,33192,35584,35980,36031,37502,38450,21536,38956,21271,20693,21340,22696,25778,26420,29287,30566,31302,37350,21187,27809,27526,22528,24140,22868,26412,32763,20961,30406,25705,30952,39764,40635,22475,22969,26151,26522,27598,21737,27097,24149,33180,26517,39850,26622,40018,26717,20134,20451,21448,25273,26411,27819,36804,20397,32365,40639,19975,24930,28288,28459,34067,21619,26410,39749,24051,31637,23724,23494,34588,28234,34001,31252,33032,22937,31885,27665,30496,21209,22818,28961,29279,30683,38695,40289,26891,23167,23064,20901,21517,21629,26126,30431,36855,37528,40180,23018,29277,28357,20813,26825,32191,32236,38754,40634,25720,27169,33538,22916,23391,27611,29467,30450,32178,32791,33945,20786,26408,40665,30446,26466,21247,39173,23588,25147,31870,36016,21839,24758,32011,38272,21249,20063,20918,22812,29242,32822,37326,24357,30690,21380,24441,32004,34220,35379,36493,38742,26611,34222,37971,24841,24840,27833,30290,35565,36664,21807,20305,20778,21191,21451,23461,24189,24736,24962,25558,26377,26586,28263,28044,29494,29495,30001,31056,35029,35480,36938,37009,37109,38596,34701,22805,20104,20313,19982,35465,36671,38928,20653,24188,22934,23481,24248,25562,25594,25793,26332,26954,27096,27915,28342,29076,29992,31407,32650,32768,33865,33993,35201,35617,36362,36965,38525,39178,24958,25233,27442,27779,28020,32716,32764,28096,32645,34746,35064,26469,33713,38972,38647,27931,32097,33853,37226,20081,21365,23888,27396,28651,34253,34349,35239,21033,21519,23653,26446,26792,29702,29827,30178,35023,35041,37324,38626,38520,24459,29575,31435,33870,25504,30053,21129,27969,28316,29705,30041,30827,31890,38534,31452,40845,20406,24942,26053,34396,20102,20142,20698,20001,20940,23534,26009,26753,28092,29471,30274,30637,31260,31975,33391,35538,36988,37327,38517,38936,21147,32209,20523,21400,26519,28107,29136,29747,33256,36650,38563,40023,40607,29792,22593,28057,32047,39006,20196,20278,20363,20919,21169,23994,24604,29618,31036,33491,37428,38583,38646,38666,40599,40802,26278,27508,21015,21155,28872,35010,24265,24651,24976,28451,29001,31806,32244,32879,34030,36899,37676,21570,39791,27347,28809,36034,36335,38706,21172,23105,24266,24324,26391,27004,27028,28010,28431,29282,29436,31725,32769,32894,34635,37070,20845,40595,31108,32907,37682,35542,20525,21644,35441,27498,36036,33031,24785,26528,40434,20121,20120,39952,35435,34241,34152,26880,28286,30871,33109,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,24332,19984,19989,20010,20017,20022,20028,20031,20034,20054,20056,20098,20101,35947,20106,33298,24333,20110,20126,20127,20128,20130,20144,20147,20150,20174,20173,20164,20166,20162,20183,20190,20205,20191,20215,20233,20314,20272,20315,20317,20311,20295,20342,20360,20367,20376,20347,20329,20336,20369,20335,20358,20374,20760,20436,20447,20430,20440,20443,20433,20442,20432,20452,20453,20506,20520,20500,20522,20517,20485,20252,20470,20513,20521,20524,20478,20463,20497,20486,20547,20551,26371,20565,20560,20552,20570,20566,20588,20600,20608,20634,20613,20660,20658,20681,20682,20659,20674,20694,20702,20709,20717,20707,20718,20729,20725,20745,20737,20738,20758,20757,20756,20762,20769,20794,20791,20796,20795,20799,20800,20818,20812,20820,20834,31480,20841,20842,20846,20864,20866,22232,20876,20873,20879,20881,20883,20885,20886,20900,20902,20898,20905,20906,20907,20915,20913,20914,20912,20917,20925,20933,20937,20955,20960,34389,20969,20973,20976,20981,20990,20996,21003,21012,21006,21031,21034,21038,21043,21049,21071,21060,21067,21068,21086,21076,21098,21108,21097,21107,21119,21117,21133,21140,21138,21105,21128,21137,36776,36775,21164,21165,21180,21173,21185,21197,21207,21214,21219,21222,39149,21216,21235,21237,21240,21241,21254,21256,30008,21261,21264,21263,21269,21274,21283,21295,21297,21299,21304,21312,21318,21317,19991,21321,21325,20950,21342,21353,21358,22808,21371,21367,21378,21398,21408,21414,21413,21422,21424,21430,21443,31762,38617,21471,26364,29166,21486,21480,21485,21498,21505,21565,21568,21548,21549,21564,21550,21558,21545,21533,21582,21647,21621,21646,21599,21617,21623,21616,21650,21627,21632,21622,21636,21648,21638,21703,21666,21688,21669,21676,21700,21704,21672,21675,21698,21668,21694,21692,21720,21733,21734,21775,21780,21757,21742,21741,21754,21730,21817,21824,21859,21836,21806,21852,21829,21846,21847,21816,21811,21853,21913,21888,21679,21898,21919,21883,21886,21912,21918,21934,21884,21891,21929,21895,21928,21978,21957,21983,21956,21980,21988,21972,22036,22007,22038,22014,22013,22043,22009,22094,22096,29151,22068,22070,22066,22072,22123,22116,22063,22124,22122,22150,22144,22154,22176,22164,22159,22181,22190,22198,22196,22210,22204,22209,22211,22208,22216,22222,22225,22227,22231,22254,22265,22272,22271,22276,22281,22280,22283,22285,22291,22296,22294,21959,22300,22310,22327,22328,22350,22331,22336,22351,22377,22464,22408,22369,22399,22409,22419,22432,22451,22436,22442,22448,22467,22470,22484,22482,22483,22538,22486,22499,22539,22553,22557,22642,22561,22626,22603,22640,27584,22610,22589,22649,22661,22713,22687,22699,22714,22750,22715,22712,22702,22725,22739,22737,22743,22745,22744,22757,22748,22756,22751,22767,22778,22777,22779,22780,22781,22786,22794,22800,22811,26790,22821,22828,22829,22834,22840,22846,31442,22869,22864,22862,22874,22872,22882,22880,22887,22892,22889,22904,22913,22941,20318,20395,22947,22962,22982,23016,23004,22925,23001,23002,23077,23071,23057,23068,23049,23066,23104,23148,23113,23093,23094,23138,23146,23194,23228,23230,23243,23234,23229,23267,23255,23270,23273,23254,23290,23291,23308,23307,23318,23346,23248,23338,23350,23358,23363,23365,23360,23377,23381,23386,23387,23397,23401,23408,23411,23413,23416,25992,23418,23424,23427,23462,23480,23491,23495,23497,23508,23504,23524,23526,23522,23518,23525,23531,23536,23542,23539,23557,23559,23560,23565,23571,23584,23586,23592,23608,23609,23617,23622,23630,23635,23632,23631,23409,23660,23662,20066,23670,23673,23692,23697,23700,22939,23723,23739,23734,23740,23735,23749,23742,23751,23769,23785,23805,23802,23789,23948,23786,23819,23829,23831,23900,23839,23835,23825,23828,23842,23834,23833,23832,23884,23890,23886,23883,23916,23923,23926,23943,23940,23938,23970,23965,23980,23982,23997,23952,23991,23996,24009,24013,24019,24018,24022,24027,24043,24050,24053,24075,24090,24089,24081,24091,24118,24119,24132,24131,24128,24142,24151,24148,24159,24162,24164,24135,24181,24182,24186,40636,24191,24224,24257,24258,24264,24272,24271,24278,24291,24285,24282,24283,24290,24289,24296,24297,24300,24305,24307,24304,24308,24312,24318,24323,24329,24413,24412,24331,24337,24342,24361,24365,24376,24385,24392,24396,24398,24367,24401,24406,24407,24409,24417,24429,24435,24439,24451,24450,24447,24458,24456,24465,24455,24478,24473,24472,24480,24488,24493,24508,24534,24571,24548,24568,24561,24541,24755,24575,24609,24672,24601,24592,24617,24590,24625,24603,24597,24619,24614,24591,24634,24666,24641,24682,24695,24671,24650,24646,24653,24675,24643,24676,24642,24684,24683,24665,24705,24717,24807,24707,24730,24708,24731,24726,24727,24722,24743,24715,24801,24760,24800,24787,24756,24560,24765,24774,24757,24792,24909,24853,24838,24822,24823,24832,24820,24826,24835,24865,24827,24817,24845,24846,24903,24894,24872,24871,24906,24895,24892,24876,24884,24893,24898,24900,24947,24951,24920,24921,24922,24939,24948,24943,24933,24945,24927,24925,24915,24949,24985,24982,24967,25004,24980,24986,24970,24977,25003,25006,25036,25034,25033,25079,25032,25027,25030,25018,25035,32633,25037,25062,25059,25078,25082,25076,25087,25085,25084,25086,25088,25096,25097,25101,25100,25108,25115,25118,25121,25130,25134,25136,25138,25139,25153,25166,25182,25187,25179,25184,25192,25212,25218,25225,25214,25234,25235,25238,25300,25219,25236,25303,25297,25275,25295,25343,25286,25812,25288,25308,25292,25290,25282,25287,25243,25289,25356,25326,25329,25383,25346,25352,25327,25333,25424,25406,25421,25628,25423,25494,25486,25472,25515,25462,25507,25487,25481,25503,25525,25451,25449,25534,25577,25536,25542,25571,25545,25554,25590,25540,25622,25652,25606,25619,25638,25654,25885,25623,25640,25615,25703,25711,25718,25678,25898,25749,25747,25765,25769,25736,25788,25818,25810,25797,25799,25787,25816,25794,25841,25831,33289,25824,25825,25260,25827,25839,25900,25846,25844,25842,25850,25856,25853,25880,25884,25861,25892,25891,25899,25908,25909,25911,25910,25912,30027,25928,25942,25941,25933,25944,25950,25949,25970,25976,25986,25987,35722,26011,26015,26027,26039,26051,26054,26049,26052,26060,26066,26075,26073,26080,26081,26097,26482,26122,26115,26107,26483,26165,26166,26164,26140,26191,26180,26185,26177,26206,26205,26212,26215,26216,26207,26210,26224,26243,26248,26254,26249,26244,26264,26269,26305,26297,26313,26302,26300,26308,26296,26326,26330,26336,26175,26342,26345,26352,26357,26359,26383,26390,26398,26406,26407,38712,26414,26431,26422,26433,26424,26423,26438,26462,26464,26457,26467,26468,26505,26480,26537,26492,26474,26508,26507,26534,26529,26501,26551,26607,26548,26604,26547,26601,26552,26596,26590,26589,26594,26606,26553,26574,26566,26599,27292,26654,26694,26665,26688,26701,26674,26702,26803,26667,26713,26723,26743,26751,26783,26767,26797,26772,26781,26779,26755,27310,26809,26740,26805,26784,26810,26895,26765,26750,26881,26826,26888,26840,26914,26918,26849,26892,26829,26836,26855,26837,26934,26898,26884,26839,26851,26917,26873,26848,26863,26920,26922,26906,26915,26913,26822,27001,26999,26972,27000,26987,26964,27006,26990,26937,26996,26941,26969,26928,26977,26974,26973,27009,26986,27058,27054,27088,27071,27073,27091,27070,27086,23528,27082,27101,27067,27075,27047,27182,27025,27040,27036,27029,27060,27102,27112,27138,27163,27135,27402,27129,27122,27111,27141,27057,27166,27117,27156,27115,27146,27154,27329,27171,27155,27204,27148,27250,27190,27256,27207,27234,27225,27238,27208,27192,27170,27280,27277,27296,27268,27298,27299,27287,34327,27323,27331,27330,27320,27315,27308,27358,27345,27359,27306,27354,27370,27387,27397,34326,27386,27410,27414,39729,27423,27448,27447,30428,27449,39150,27463,27459,27465,27472,27481,27476,27483,27487,27489,27512,27513,27519,27520,27524,27523,27533,27544,27541,27550,27556,27562,27563,27567,27570,27569,27571,27575,27580,27590,27595,27603,27615,27628,27627,27635,27631,40638,27656,27667,27668,27675,27684,27683,27742,27733,27746,27754,27778,27789,27802,27777,27803,27774,27752,27763,27794,27792,27844,27889,27859,27837,27863,27845,27869,27822,27825,27838,27834,27867,27887,27865,27882,27935,34893,27958,27947,27965,27960,27929,27957,27955,27922,27916,28003,28051,28004,27994,28025,27993,28046,28053,28644,28037,28153,28181,28170,28085,28103,28134,28088,28102,28140,28126,28108,28136,28114,28101,28154,28121,28132,28117,28138,28142,28205,28270,28206,28185,28274,28255,28222,28195,28267,28203,28278,28237,28191,28227,28218,28238,28196,28415,28189,28216,28290,28330,28312,28361,28343,28371,28349,28335,28356,28338,28372,28373,28303,28325,28354,28319,28481,28433,28748,28396,28408,28414,28479,28402,28465,28399,28466,28364,28478,28435,28407,28550,28538,28536,28545,28544,28527,28507,28659,28525,28546,28540,28504,28558,28561,28610,28518,28595,28579,28577,28580,28601,28614,28586,28639,28629,28652,28628,28632,28657,28654,28635,28681,28683,28666,28689,28673,28687,28670,28699,28698,28532,28701,28696,28703,28720,28734,28722,28753,28771,28825,28818,28847,28913,28844,28856,28851,28846,28895,28875,28893,28889,28937,28925,28956,28953,29029,29013,29064,29030,29026,29004,29014,29036,29071,29179,29060,29077,29096,29100,29143,29113,29118,29138,29129,29140,29134,29152,29164,29159,29173,29180,29177,29183,29197,29200,29211,29224,29229,29228,29232,29234,29243,29244,29247,29248,29254,29259,29272,29300,29310,29314,29313,29319,29330,29334,29346,29351,29369,29362,29379,29382,29380,29390,29394,29410,29408,29409,29433,29431,20495,29463,29450,29468,29462,29469,29492,29487,29481,29477,29502,29518,29519,40664,29527,29546,29544,29552,29560,29557,29563,29562,29640,29619,29646,29627,29632,29669,29678,29662,29858,29701,29807,29733,29688,29746,29754,29781,29759,29791,29785,29761,29788,29801,29808,29795,29802,29814,29822,29835,29854,29863,29898,29903,29908,29681,29920,29923,29927,29929,29934,29938,29936,29937,29944,29943,29956,29955,29957,29964,29966,29965,29973,29971,29982,29990,29996,30012,30020,30029,30026,30025,30043,30022,30042,30057,30052,30055,30059,30061,30072,30070,30086,30087,30068,30090,30089,30082,30100,30106,30109,30117,30115,30146,30131,30147,30133,30141,30136,30140,30129,30157,30154,30162,30169,30179,30174,30206,30207,30204,30209,30192,30202,30194,30195,30219,30221,30217,30239,30247,30240,30241,30242,30244,30260,30256,30267,30279,30280,30278,30300,30296,30305,30306,30312,30313,30314,30311,30316,30320,30322,30326,30328,30332,30336,30339,30344,30347,30350,30358,30355,30361,30362,30384,30388,30392,30393,30394,30402,30413,30422,30418,30430,30433,30437,30439,30442,34351,30459,30472,30471,30468,30505,30500,30494,30501,30502,30491,30519,30520,30535,30554,30568,30571,30555,30565,30591,30590,30585,30606,30603,30609,30624,30622,30640,30646,30649,30655,30652,30653,30651,30663,30669,30679,30682,30684,30691,30702,30716,30732,30738,31014,30752,31018,30789,30862,30836,30854,30844,30874,30860,30883,30901,30890,30895,30929,30918,30923,30932,30910,30908,30917,30922,30956,30951,30938,30973,30964,30983,30994,30993,31001,31020,31019,31040,31072,31063,31071,31066,31061,31059,31098,31103,31114,31133,31143,40779,31146,31150,31155,31161,31162,31177,31189,31207,31212,31201,31203,31240,31245,31256,31257,31264,31263,31104,31281,31291,31294,31287,31299,31319,31305,31329,31330,31337,40861,31344,31353,31357,31368,31383,31381,31384,31382,31401,31432,31408,31414,31429,31428,31423,36995,31431,31434,31437,31439,31445,31443,31449,31450,31453,31457,31458,31462,31469,31472,31490,31503,31498,31494,31539,31512,31513,31518,31541,31528,31542,31568,31610,31492,31565,31499,31564,31557,31605,31589,31604,31591,31600,31601,31596,31598,31645,31640,31647,31629,31644,31642,31627,31634,31631,31581,31641,31691,31681,31692,31695,31668,31686,31709,31721,31761,31764,31718,31717,31840,31744,31751,31763,31731,31735,31767,31757,31734,31779,31783,31786,31775,31799,31787,31805,31820,31811,31828,31823,31808,31824,31832,31839,31844,31830,31845,31852,31861,31875,31888,31908,31917,31906,31915,31905,31912,31923,31922,31921,31918,31929,31933,31936,31941,31938,31960,31954,31964,31970,39739,31983,31986,31988,31990,31994,32006,32002,32028,32021,32010,32069,32075,32046,32050,32063,32053,32070,32115,32086,32078,32114,32104,32110,32079,32099,32147,32137,32091,32143,32125,32155,32186,32174,32163,32181,32199,32189,32171,32317,32162,32175,32220,32184,32159,32176,32216,32221,32228,32222,32251,32242,32225,32261,32266,32291,32289,32274,32305,32287,32265,32267,32290,32326,32358,32315,32309,32313,32323,32311,32306,32314,32359,32349,32342,32350,32345,32346,32377,32362,32361,32380,32379,32387,32213,32381,36782,32383,32392,32393,32396,32402,32400,32403,32404,32406,32398,32411,32412,32568,32570,32581,32588,32589,32590,32592,32593,32597,32596,32600,32607,32608,32616,32617,32615,32632,32642,32646,32643,32648,32647,32652,32660,32670,32669,32666,32675,32687,32690,32697,32686,32694,32696,35697,32709,32710,32714,32725,32724,32737,32742,32745,32755,32761,39132,32774,32772,32779,32786,32792,32793,32796,32801,32808,32831,32827,32842,32838,32850,32856,32858,32863,32866,32872,32883,32882,32880,32886,32889,32893,32895,32900,32902,32901,32923,32915,32922,32941,20880,32940,32987,32997,32985,32989,32964,32986,32982,33033,33007,33009,33051,33065,33059,33071,33099,38539,33094,33086,33107,33105,33020,33137,33134,33125,33126,33140,33155,33160,33162,33152,33154,33184,33173,33188,33187,33119,33171,33193,33200,33205,33214,33208,33213,33216,33218,33210,33225,33229,33233,33241,33240,33224,33242,33247,33248,33255,33274,33275,33278,33281,33282,33285,33287,33290,33293,33296,33302,33321,33323,33336,33331,33344,33369,33368,33373,33370,33375,33380,33378,33384,33386,33387,33326,33393,33399,33400,33406,33421,33426,33451,33439,33467,33452,33505,33507,33503,33490,33524,33523,33530,33683,33539,33531,33529,33502,33542,33500,33545,33497,33589,33588,33558,33586,33585,33600,33593,33616,33605,33583,33579,33559,33560,33669,33690,33706,33695,33698,33686,33571,33678,33671,33674,33660,33717,33651,33653,33696,33673,33704,33780,33811,33771,33742,33789,33795,33752,33803,33729,33783,33799,33760,33778,33805,33826,33824,33725,33848,34054,33787,33901,33834,33852,34138,33924,33911,33899,33965,33902,33922,33897,33862,33836,33903,33913,33845,33994,33890,33977,33983,33951,34009,33997,33979,34010,34000,33985,33990,34006,33953,34081,34047,34036,34071,34072,34092,34079,34069,34068,34044,34112,34147,34136,34120,34113,34306,34123,34133,34176,34212,34184,34193,34186,34216,34157,34196,34203,34282,34183,34204,34167,34174,34192,34249,34234,34255,34233,34256,34261,34269,34277,34268,34297,34314,34323,34315,34302,34298,34310,34338,34330,34352,34367,34381,20053,34388,34399,34407,34417,34451,34467,34473,34474,34443,34444,34486,34479,34500,34502,34480,34505,34851,34475,34516,34526,34537,34540,34527,34523,34543,34578,34566,34568,34560,34563,34555,34577,34569,34573,34553,34570,34612,34623,34615,34619,34597,34601,34586,34656,34655,34680,34636,34638,34676,34647,34664,34670,34649,34643,34659,34666,34821,34722,34719,34690,34735,34763,34749,34752,34768,38614,34731,34756,34739,34759,34758,34747,34799,34802,34784,34831,34829,34814,34806,34807,34830,34770,34833,34838,34837,34850,34849,34865,34870,34873,34855,34875,34884,34882,34898,34905,34910,34914,34923,34945,34942,34974,34933,34941,34997,34930,34946,34967,34962,34990,34969,34978,34957,34980,34992,35007,34993,35011,35012,35028,35032,35033,35037,35065,35074,35068,35060,35048,35058,35076,35084,35082,35091,35139,35102,35109,35114,35115,35137,35140,35131,35126,35128,35148,35101,35168,35166,35174,35172,35181,35178,35183,35188,35191,35198,35203,35208,35210,35219,35224,35233,35241,35238,35244,35247,35250,35258,35261,35263,35264,35290,35292,35293,35303,35316,35320,35331,35350,35344,35340,35355,35357,35365,35382,35393,35419,35410,35398,35400,35452,35437,35436,35426,35461,35458,35460,35496,35489,35473,35493,35494,35482,35491,35524,35533,35522,35546,35563,35571,35559,35556,35569,35604,35552,35554,35575,35550,35547,35596,35591,35610,35553,35606,35600,35607,35616,35635,38827,35622,35627,35646,35624,35649,35660,35663,35662,35657,35670,35675,35674,35691,35679,35692,35695,35700,35709,35712,35724,35726,35730,35731,35734,35737,35738,35898,35905,35903,35912,35916,35918,35920,35925,35938,35948,35960,35962,35970,35977,35973,35978,35981,35982,35988,35964,35992,25117,36013,36010,36029,36018,36019,36014,36022,36040,36033,36068,36067,36058,36093,36090,36091,36100,36101,36106,36103,36111,36109,36112,40782,36115,36045,36116,36118,36199,36205,36209,36211,36225,36249,36290,36286,36282,36303,36314,36310,36300,36315,36299,36330,36331,36319,36323,36348,36360,36361,36351,36381,36382,36368,36383,36418,36405,36400,36404,36426,36423,36425,36428,36432,36424,36441,36452,36448,36394,36451,36437,36470,36466,36476,36481,36487,36485,36484,36491,36490,36499,36497,36500,36505,36522,36513,36524,36528,36550,36529,36542,36549,36552,36555,36571,36579,36604,36603,36587,36606,36618,36613,36629,36626,36633,36627,36636,36639,36635,36620,36646,36659,36667,36665,36677,36674,36670,36684,36681,36678,36686,36695,36700,36706,36707,36708,36764,36767,36771,36781,36783,36791,36826,36837,36834,36842,36847,36999,36852,36869,36857,36858,36881,36885,36897,36877,36894,36886,36875,36903,36918,36917,36921,36856,36943,36944,36945,36946,36878,36937,36926,36950,36952,36958,36968,36975,36982,38568,36978,36994,36989,36993,36992,37002,37001,37007,37032,37039,37041,37045,37090,37092,25160,37083,37122,37138,37145,37170,37168,37194,37206,37208,37219,37221,37225,37235,37234,37259,37257,37250,37282,37291,37295,37290,37301,37300,37306,37312,37313,37321,37323,37328,37334,37343,37345,37339,37372,37365,37366,37406,37375,37396,37420,37397,37393,37470,37463,37445,37449,37476,37448,37525,37439,37451,37456,37532,37526,37523,37531,37466,37583,37561,37559,37609,37647,37626,37700,37678,37657,37666,37658,37667,37690,37685,37691,37724,37728,37756,37742,37718,37808,37804,37805,37780,37817,37846,37847,37864,37861,37848,37827,37853,37840,37832,37860,37914,37908,37907,37891,37895,37904,37942,37931,37941,37921,37946,37953,37970,37956,37979,37984,37986,37982,37994,37417,38000,38005,38007,38013,37978,38012,38014,38017,38015,38274,38279,38282,38292,38294,38296,38297,38304,38312,38311,38317,38332,38331,38329,38334,38346,28662,38339,38349,38348,38357,38356,38358,38364,38369,38373,38370,38433,38440,38446,38447,38466,38476,38479,38475,38519,38492,38494,38493,38495,38502,38514,38508,38541,38552,38549,38551,38570,38567,38577,38578,38576,38580,38582,38584,38585,38606,38603,38601,38605,35149,38620,38669,38613,38649,38660,38662,38664,38675,38670,38673,38671,38678,38681,38692,38698,38704,38713,38717,38718,38724,38726,38728,38722,38729,38748,38752,38756,38758,38760,21202,38763,38769,38777,38789,38780,38785,38778,38790,38795,38799,38800,38812,38824,38822,38819,38835,38836,38851,38854,38856,38859,38876,38893,40783,38898,31455,38902,38901,38927,38924,38968,38948,38945,38967,38973,38982,38991,38987,39019,39023,39024,39025,39028,39027,39082,39087,39089,39094,39108,39107,39110,39145,39147,39171,39177,39186,39188,39192,39201,39197,39198,39204,39200,39212,39214,39229,39230,39234,39241,39237,39248,39243,39249,39250,39244,39253,39319,39320,39333,39341,39342,39356,39391,39387,39389,39384,39377,39405,39406,39409,39410,39419,39416,39425,39439,39429,39394,39449,39467,39479,39493,39490,39488,39491,39486,39509,39501,39515,39511,39519,39522,39525,39524,39529,39531,39530,39597,39600,39612,39616,39631,39633,39635,39636,39646,39647,39650,39651,39654,39663,39659,39662,39668,39665,39671,39675,39686,39704,39706,39711,39714,39715,39717,39719,39720,39721,39722,39726,39727,39730,39748,39747,39759,39757,39758,39761,39768,39796,39827,39811,39825,39830,39831,39839,39840,39848,39860,39872,39882,39865,39878,39887,39889,39890,39907,39906,39908,39892,39905,39994,39922,39921,39920,39957,39956,39945,39955,39948,39942,39944,39954,39946,39940,39982,39963,39973,39972,39969,39984,40007,39986,40006,39998,40026,40032,40039,40054,40056,40167,40172,40176,40201,40200,40171,40195,40198,40234,40230,40367,40227,40223,40260,40213,40210,40257,40255,40254,40262,40264,40285,40286,40292,40273,40272,40281,40306,40329,40327,40363,40303,40314,40346,40356,40361,40370,40388,40385,40379,40376,40378,40390,40399,40386,40409,40403,40440,40422,40429,40431,40445,40474,40475,40478,40565,40569,40573,40577,40584,40587,40588,40594,40597,40593,40605,40613,40617,40632,40618,40621,38753,40652,40654,40655,40656,40660,40668,40670,40669,40672,40677,40680,40687,40692,40694,40695,40697,40699,40700,40701,40711,40712,30391,40725,40737,40748,40766,40778,40786,40788,40803,40799,40800,40801,40806,40807,40812,40810,40823,40818,40822,40853,40860,40864,22575,27079,36953,29796,20956,29081,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32394,35100,37704,37512,34012,20425,28859,26161,26824,37625,26363,24389,20008,20193,20220,20224,20227,20281,20310,20370,20362,20378,20372,20429,20544,20514,20479,20510,20550,20592,20546,20628,20724,20696,20810,20836,20893,20926,20972,21013,21148,21158,21184,21211,21248,21255,21284,21362,21395,21426,21469,64014,21660,21642,21673,21759,21894,22361,22373,22444,22472,22471,64015,64016,22686,22706,22795,22867,22875,22877,22883,22948,22970,23382,23488,29999,23512,23532,23582,23718,23738,23797,23847,23891,64017,23874,23917,23992,23993,24016,24353,24372,24423,24503,24542,24669,24709,24714,24798,24789,24864,24818,24849,24887,24880,24984,25107,25254,25589,25696,25757,25806,25934,26112,26133,26171,26121,26158,26142,26148,26213,26199,26201,64018,26227,26265,26272,26290,26303,26362,26382,63785,26470,26555,26706,26560,26625,26692,26831,64019,26984,64020,27032,27106,27184,27243,27206,27251,27262,27362,27364,27606,27711,27740,27782,27759,27866,27908,28039,28015,28054,28076,28111,28152,28146,28156,28217,28252,28199,28220,28351,28552,28597,28661,28677,28679,28712,28805,28843,28943,28932,29020,28998,28999,64021,29121,29182,29361,29374,29476,64022,29559,29629,29641,29654,29667,29650,29703,29685,29734,29738,29737,29742,29794,29833,29855,29953,30063,30338,30364,30366,30363,30374,64023,30534,21167,30753,30798,30820,30842,31024,64024,64025,64026,31124,64027,31131,31441,31463,64028,31467,31646,64029,32072,32092,32183,32160,32214,32338,32583,32673,64030,33537,33634,33663,33735,33782,33864,33972,34131,34137,34155,64031,34224,64032,64033,34823,35061,35346,35383,35449,35495,35518,35551,64034,35574,35667,35711,36080,36084,36114,36214,64035,36559,64036,64037,36967,37086,64038,37141,37159,37338,37335,37342,37357,37358,37348,37349,37382,37392,37386,37434,37440,37436,37454,37465,37457,37433,37479,37543,37495,37496,37607,37591,37593,37584,64039,37589,37600,37587,37669,37665,37627,64040,37662,37631,37661,37634,37744,37719,37796,37830,37854,37880,37937,37957,37960,38290,63964,64041,38557,38575,38707,38715,38723,38733,38735,38737,38741,38999,39013,64042,64043,39207,64044,39326,39502,39641,39644,39797,39794,39823,39857,39867,39936,40304,40299,64045,40473,40657,null,null,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,65506,65508,65287,65282,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,65506,65508,65287,65282,12849,8470,8481,8757,32394,35100,37704,37512,34012,20425,28859,26161,26824,37625,26363,24389,20008,20193,20220,20224,20227,20281,20310,20370,20362,20378,20372,20429,20544,20514,20479,20510,20550,20592,20546,20628,20724,20696,20810,20836,20893,20926,20972,21013,21148,21158,21184,21211,21248,21255,21284,21362,21395,21426,21469,64014,21660,21642,21673,21759,21894,22361,22373,22444,22472,22471,64015,64016,22686,22706,22795,22867,22875,22877,22883,22948,22970,23382,23488,29999,23512,23532,23582,23718,23738,23797,23847,23891,64017,23874,23917,23992,23993,24016,24353,24372,24423,24503,24542,24669,24709,24714,24798,24789,24864,24818,24849,24887,24880,24984,25107,25254,25589,25696,25757,25806,25934,26112,26133,26171,26121,26158,26142,26148,26213,26199,26201,64018,26227,26265,26272,26290,26303,26362,26382,63785,26470,26555,26706,26560,26625,26692,26831,64019,26984,64020,27032,27106,27184,27243,27206,27251,27262,27362,27364,27606,27711,27740,27782,27759,27866,27908,28039,28015,28054,28076,28111,28152,28146,28156,28217,28252,28199,28220,28351,28552,28597,28661,28677,28679,28712,28805,28843,28943,28932,29020,28998,28999,64021,29121,29182,29361,29374,29476,64022,29559,29629,29641,29654,29667,29650,29703,29685,29734,29738,29737,29742,29794,29833,29855,29953,30063,30338,30364,30366,30363,30374,64023,30534,21167,30753,30798,30820,30842,31024,64024,64025,64026,31124,64027,31131,31441,31463,64028,31467,31646,64029,32072,32092,32183,32160,32214,32338,32583,32673,64030,33537,33634,33663,33735,33782,33864,33972,34131,34137,34155,64031,34224,64032,64033,34823,35061,35346,35383,35449,35495,35518,35551,64034,35574,35667,35711,36080,36084,36114,36214,64035,36559,64036,64037,36967,37086,64038,37141,37159,37338,37335,37342,37357,37358,37348,37349,37382,37392,37386,37434,37440,37436,37454,37465,37457,37433,37479,37543,37495,37496,37607,37591,37593,37584,64039,37589,37600,37587,37669,37665,37627,64040,37662,37631,37661,37634,37744,37719,37796,37830,37854,37880,37937,37957,37960,38290,63964,64041,38557,38575,38707,38715,38723,38733,38735,38737,38741,38999,39013,64042,64043,39207,64044,39326,39502,39641,39644,39797,39794,39823,39857,39867,39936,40304,40299,64045,40473,40657,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
- "jis0212":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,728,711,184,729,733,175,731,730,65374,900,901,null,null,null,null,null,null,null,null,161,166,191,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,186,170,169,174,8482,164,8470,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,902,904,905,906,938,null,908,null,910,939,null,911,null,null,null,null,940,941,942,943,970,912,972,962,973,971,944,974,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1038,1039,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1118,1119,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,198,272,null,294,null,306,null,321,319,null,330,216,338,null,358,222,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,230,273,240,295,305,307,312,322,320,329,331,248,339,223,359,254,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,193,192,196,194,258,461,256,260,197,195,262,264,268,199,266,270,201,200,203,202,282,278,274,280,null,284,286,290,288,292,205,204,207,206,463,304,298,302,296,308,310,313,317,315,323,327,325,209,211,210,214,212,465,336,332,213,340,344,342,346,348,352,350,356,354,218,217,220,219,364,467,368,362,370,366,360,471,475,473,469,372,221,376,374,377,381,379,null,null,null,null,null,null,null,225,224,228,226,259,462,257,261,229,227,263,265,269,231,267,271,233,232,235,234,283,279,275,281,501,285,287,null,289,293,237,236,239,238,464,null,299,303,297,309,311,314,318,316,324,328,326,241,243,242,246,244,466,337,333,245,341,345,343,347,349,353,351,357,355,250,249,252,251,365,468,369,363,371,367,361,472,476,474,470,373,253,255,375,378,382,380,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,19970,19972,19973,19980,19986,19999,20003,20004,20008,20011,20014,20015,20016,20021,20032,20033,20036,20039,20049,20058,20060,20067,20072,20073,20084,20085,20089,20095,20109,20118,20119,20125,20143,20153,20163,20176,20186,20187,20192,20193,20194,20200,20207,20209,20211,20213,20221,20222,20223,20224,20226,20227,20232,20235,20236,20242,20245,20246,20247,20249,20270,20273,20320,20275,20277,20279,20281,20283,20286,20288,20290,20296,20297,20299,20300,20306,20308,20310,20312,20319,20323,20330,20332,20334,20337,20343,20344,20345,20346,20349,20350,20353,20354,20356,20357,20361,20362,20364,20366,20368,20370,20371,20372,20375,20377,20378,20382,20383,20402,20407,20409,20411,20412,20413,20414,20416,20417,20421,20422,20424,20425,20427,20428,20429,20431,20434,20444,20448,20450,20464,20466,20476,20477,20479,20480,20481,20484,20487,20490,20492,20494,20496,20499,20503,20504,20507,20508,20509,20510,20514,20519,20526,20528,20530,20531,20533,20544,20545,20546,20549,20550,20554,20556,20558,20561,20562,20563,20567,20569,20575,20576,20578,20579,20582,20583,20586,20589,20592,20593,20539,20609,20611,20612,20614,20618,20622,20623,20624,20626,20627,20628,20630,20635,20636,20638,20639,20640,20641,20642,20650,20655,20656,20665,20666,20669,20672,20675,20676,20679,20684,20686,20688,20691,20692,20696,20700,20701,20703,20706,20708,20710,20712,20713,20719,20721,20726,20730,20734,20739,20742,20743,20744,20747,20748,20749,20750,20722,20752,20759,20761,20763,20764,20765,20766,20771,20775,20776,20780,20781,20783,20785,20787,20788,20789,20792,20793,20802,20810,20815,20819,20821,20823,20824,20831,20836,20838,20862,20867,20868,20875,20878,20888,20893,20897,20899,20909,20920,20922,20924,20926,20927,20930,20936,20943,20945,20946,20947,20949,20952,20958,20962,20965,20974,20978,20979,20980,20983,20993,20994,20997,21010,21011,21013,21014,21016,21026,21032,21041,21042,21045,21052,21061,21065,21077,21079,21080,21082,21084,21087,21088,21089,21094,21102,21111,21112,21113,21120,21122,21125,21130,21132,21139,21141,21142,21143,21144,21146,21148,21156,21157,21158,21159,21167,21168,21174,21175,21176,21178,21179,21181,21184,21188,21190,21192,21196,21199,21201,21204,21206,21211,21212,21217,21221,21224,21225,21226,21228,21232,21233,21236,21238,21239,21248,21251,21258,21259,21260,21265,21267,21272,21275,21276,21278,21279,21285,21287,21288,21289,21291,21292,21293,21296,21298,21301,21308,21309,21310,21314,21324,21323,21337,21339,21345,21347,21349,21356,21357,21362,21369,21374,21379,21383,21384,21390,21395,21396,21401,21405,21409,21412,21418,21419,21423,21426,21428,21429,21431,21432,21434,21437,21440,21445,21455,21458,21459,21461,21466,21469,21470,21472,21478,21479,21493,21506,21523,21530,21537,21543,21544,21546,21551,21553,21556,21557,21571,21572,21575,21581,21583,21598,21602,21604,21606,21607,21609,21611,21613,21614,21620,21631,21633,21635,21637,21640,21641,21645,21649,21653,21654,21660,21663,21665,21670,21671,21673,21674,21677,21678,21681,21687,21689,21690,21691,21695,21702,21706,21709,21710,21728,21738,21740,21743,21750,21756,21758,21759,21760,21761,21765,21768,21769,21772,21773,21774,21781,21802,21803,21810,21813,21814,21819,21820,21821,21825,21831,21833,21834,21837,21840,21841,21848,21850,21851,21854,21856,21857,21860,21862,21887,21889,21890,21894,21896,21902,21903,21905,21906,21907,21908,21911,21923,21924,21933,21938,21951,21953,21955,21958,21961,21963,21964,21966,21969,21970,21971,21975,21976,21979,21982,21986,21993,22006,22015,22021,22024,22026,22029,22030,22031,22032,22033,22034,22041,22060,22064,22067,22069,22071,22073,22075,22076,22077,22079,22080,22081,22083,22084,22086,22089,22091,22093,22095,22100,22110,22112,22113,22114,22115,22118,22121,22125,22127,22129,22130,22133,22148,22149,22152,22155,22156,22165,22169,22170,22173,22174,22175,22182,22183,22184,22185,22187,22188,22189,22193,22195,22199,22206,22213,22217,22218,22219,22223,22224,22220,22221,22233,22236,22237,22239,22241,22244,22245,22246,22247,22248,22257,22251,22253,22262,22263,22273,22274,22279,22282,22284,22289,22293,22298,22299,22301,22304,22306,22307,22308,22309,22313,22314,22316,22318,22319,22323,22324,22333,22334,22335,22341,22342,22348,22349,22354,22370,22373,22375,22376,22379,22381,22382,22383,22384,22385,22387,22388,22389,22391,22393,22394,22395,22396,22398,22401,22403,22412,22420,22423,22425,22426,22428,22429,22430,22431,22433,22421,22439,22440,22441,22444,22456,22461,22471,22472,22476,22479,22485,22493,22494,22500,22502,22503,22505,22509,22512,22517,22518,22520,22525,22526,22527,22531,22532,22536,22537,22497,22540,22541,22555,22558,22559,22560,22566,22567,22573,22578,22585,22591,22601,22604,22605,22607,22608,22613,22623,22625,22628,22631,22632,22648,22652,22655,22656,22657,22663,22664,22665,22666,22668,22669,22671,22672,22676,22678,22685,22688,22689,22690,22694,22697,22705,22706,22724,22716,22722,22728,22733,22734,22736,22738,22740,22742,22746,22749,22753,22754,22761,22771,22789,22790,22795,22796,22802,22803,22804,34369,22813,22817,22819,22820,22824,22831,22832,22835,22837,22838,22847,22851,22854,22866,22867,22873,22875,22877,22878,22879,22881,22883,22891,22893,22895,22898,22901,22902,22905,22907,22908,22923,22924,22926,22930,22933,22935,22943,22948,22951,22957,22958,22959,22960,22963,22967,22970,22972,22977,22979,22980,22984,22986,22989,22994,23005,23006,23007,23011,23012,23015,23022,23023,23025,23026,23028,23031,23040,23044,23052,23053,23054,23058,23059,23070,23075,23076,23079,23080,23082,23085,23088,23108,23109,23111,23112,23116,23120,23125,23134,23139,23141,23143,23149,23159,23162,23163,23166,23179,23184,23187,23190,23193,23196,23198,23199,23200,23202,23207,23212,23217,23218,23219,23221,23224,23226,23227,23231,23236,23238,23240,23247,23258,23260,23264,23269,23274,23278,23285,23286,23293,23296,23297,23304,23319,23348,23321,23323,23325,23329,23333,23341,23352,23361,23371,23372,23378,23382,23390,23400,23406,23407,23420,23421,23422,23423,23425,23428,23430,23434,23438,23440,23441,23443,23444,23446,23464,23465,23468,23469,23471,23473,23474,23479,23482,23484,23488,23489,23501,23503,23510,23511,23512,23513,23514,23520,23535,23537,23540,23549,23564,23575,23582,23583,23587,23590,23593,23595,23596,23598,23600,23602,23605,23606,23641,23642,23644,23650,23651,23655,23656,23657,23661,23664,23668,23669,23674,23675,23676,23677,23687,23688,23690,23695,23698,23709,23711,23712,23714,23715,23718,23722,23730,23732,23733,23738,23753,23755,23762,23773,23767,23790,23793,23794,23796,23809,23814,23821,23826,23851,23843,23844,23846,23847,23857,23860,23865,23869,23871,23874,23875,23878,23880,23893,23889,23897,23882,23903,23904,23905,23906,23908,23914,23917,23920,23929,23930,23934,23935,23937,23939,23944,23946,23954,23955,23956,23957,23961,23963,23967,23968,23975,23979,23984,23988,23992,23993,24003,24007,24011,24016,24014,24024,24025,24032,24036,24041,24056,24057,24064,24071,24077,24082,24084,24085,24088,24095,24096,24110,24104,24114,24117,24126,24139,24144,24137,24145,24150,24152,24155,24156,24158,24168,24170,24171,24172,24173,24174,24176,24192,24203,24206,24226,24228,24229,24232,24234,24236,24241,24243,24253,24254,24255,24262,24268,24267,24270,24273,24274,24276,24277,24284,24286,24293,24299,24322,24326,24327,24328,24334,24345,24348,24349,24353,24354,24355,24356,24360,24363,24364,24366,24368,24372,24374,24379,24381,24383,24384,24388,24389,24391,24397,24400,24404,24408,24411,24416,24419,24420,24423,24431,24434,24436,24437,24440,24442,24445,24446,24457,24461,24463,24470,24476,24477,24482,24487,24491,24484,24492,24495,24496,24497,24504,24516,24519,24520,24521,24523,24528,24529,24530,24531,24532,24542,24545,24546,24552,24553,24554,24556,24557,24558,24559,24562,24563,24566,24570,24572,24583,24586,24589,24595,24596,24599,24600,24602,24607,24612,24621,24627,24629,24640,24647,24648,24649,24652,24657,24660,24662,24663,24669,24673,24679,24689,24702,24703,24706,24710,24712,24714,24718,24721,24723,24725,24728,24733,24734,24738,24740,24741,24744,24752,24753,24759,24763,24766,24770,24772,24776,24777,24778,24779,24782,24783,24788,24789,24793,24795,24797,24798,24802,24805,24818,24821,24824,24828,24829,24834,24839,24842,24844,24848,24849,24850,24851,24852,24854,24855,24857,24860,24862,24866,24874,24875,24880,24881,24885,24886,24887,24889,24897,24901,24902,24905,24926,24928,24940,24946,24952,24955,24956,24959,24960,24961,24963,24964,24971,24973,24978,24979,24983,24984,24988,24989,24991,24992,24997,25000,25002,25005,25016,25017,25020,25024,25025,25026,25038,25039,25045,25052,25053,25054,25055,25057,25058,25063,25065,25061,25068,25069,25071,25089,25091,25092,25095,25107,25109,25116,25120,25122,25123,25127,25129,25131,25145,25149,25154,25155,25156,25158,25164,25168,25169,25170,25172,25174,25178,25180,25188,25197,25199,25203,25210,25213,25229,25230,25231,25232,25254,25256,25267,25270,25271,25274,25278,25279,25284,25294,25301,25302,25306,25322,25330,25332,25340,25341,25347,25348,25354,25355,25357,25360,25363,25366,25368,25385,25386,25389,25397,25398,25401,25404,25409,25410,25411,25412,25414,25418,25419,25422,25426,25427,25428,25432,25435,25445,25446,25452,25453,25457,25460,25461,25464,25468,25469,25471,25474,25476,25479,25482,25488,25492,25493,25497,25498,25502,25508,25510,25517,25518,25519,25533,25537,25541,25544,25550,25553,25555,25556,25557,25564,25568,25573,25578,25580,25586,25587,25589,25592,25593,25609,25610,25616,25618,25620,25624,25630,25632,25634,25636,25637,25641,25642,25647,25648,25653,25661,25663,25675,25679,25681,25682,25683,25684,25690,25691,25692,25693,25695,25696,25697,25699,25709,25715,25716,25723,25725,25733,25735,25743,25744,25745,25752,25753,25755,25757,25759,25761,25763,25766,25768,25772,25779,25789,25790,25791,25796,25801,25802,25803,25804,25806,25808,25809,25813,25815,25828,25829,25833,25834,25837,25840,25845,25847,25851,25855,25857,25860,25864,25865,25866,25871,25875,25876,25878,25881,25883,25886,25887,25890,25894,25897,25902,25905,25914,25916,25917,25923,25927,25929,25936,25938,25940,25951,25952,25959,25963,25978,25981,25985,25989,25994,26002,26005,26008,26013,26016,26019,26022,26030,26034,26035,26036,26047,26050,26056,26057,26062,26064,26068,26070,26072,26079,26096,26098,26100,26101,26105,26110,26111,26112,26116,26120,26121,26125,26129,26130,26133,26134,26141,26142,26145,26146,26147,26148,26150,26153,26154,26155,26156,26158,26160,26161,26163,26169,26167,26176,26181,26182,26186,26188,26193,26190,26199,26200,26201,26203,26204,26208,26209,26363,26218,26219,26220,26238,26227,26229,26239,26231,26232,26233,26235,26240,26236,26251,26252,26253,26256,26258,26265,26266,26267,26268,26271,26272,26276,26285,26289,26290,26293,26299,26303,26304,26306,26307,26312,26316,26318,26319,26324,26331,26335,26344,26347,26348,26350,26362,26373,26375,26382,26387,26393,26396,26400,26402,26419,26430,26437,26439,26440,26444,26452,26453,26461,26470,26476,26478,26484,26486,26491,26497,26500,26510,26511,26513,26515,26518,26520,26521,26523,26544,26545,26546,26549,26555,26556,26557,26617,26560,26562,26563,26565,26568,26569,26578,26583,26585,26588,26593,26598,26608,26610,26614,26615,26706,26644,26649,26653,26655,26664,26663,26668,26669,26671,26672,26673,26675,26683,26687,26692,26693,26698,26700,26709,26711,26712,26715,26731,26734,26735,26736,26737,26738,26741,26745,26746,26747,26748,26754,26756,26758,26760,26774,26776,26778,26780,26785,26787,26789,26793,26794,26798,26802,26811,26821,26824,26828,26831,26832,26833,26835,26838,26841,26844,26845,26853,26856,26858,26859,26860,26861,26864,26865,26869,26870,26875,26876,26877,26886,26889,26890,26896,26897,26899,26902,26903,26929,26931,26933,26936,26939,26946,26949,26953,26958,26967,26971,26979,26980,26981,26982,26984,26985,26988,26992,26993,26994,27002,27003,27007,27008,27021,27026,27030,27032,27041,27045,27046,27048,27051,27053,27055,27063,27064,27066,27068,27077,27080,27089,27094,27095,27106,27109,27118,27119,27121,27123,27125,27134,27136,27137,27139,27151,27153,27157,27162,27165,27168,27172,27176,27184,27186,27188,27191,27195,27198,27199,27205,27206,27209,27210,27214,27216,27217,27218,27221,27222,27227,27236,27239,27242,27249,27251,27262,27265,27267,27270,27271,27273,27275,27281,27291,27293,27294,27295,27301,27307,27311,27312,27313,27316,27325,27326,27327,27334,27337,27336,27340,27344,27348,27349,27350,27356,27357,27364,27367,27372,27376,27377,27378,27388,27389,27394,27395,27398,27399,27401,27407,27408,27409,27415,27419,27422,27428,27432,27435,27436,27439,27445,27446,27451,27455,27462,27466,27469,27474,27478,27480,27485,27488,27495,27499,27502,27504,27509,27517,27518,27522,27525,27543,27547,27551,27552,27554,27555,27560,27561,27564,27565,27566,27568,27576,27577,27581,27582,27587,27588,27593,27596,27606,27610,27617,27619,27622,27623,27630,27633,27639,27641,27647,27650,27652,27653,27657,27661,27662,27664,27666,27673,27679,27686,27687,27688,27692,27694,27699,27701,27702,27706,27707,27711,27722,27723,27725,27727,27730,27732,27737,27739,27740,27755,27757,27759,27764,27766,27768,27769,27771,27781,27782,27783,27785,27796,27797,27799,27800,27804,27807,27824,27826,27828,27842,27846,27853,27855,27856,27857,27858,27860,27862,27866,27868,27872,27879,27881,27883,27884,27886,27890,27892,27908,27911,27914,27918,27919,27921,27923,27930,27942,27943,27944,27751,27950,27951,27953,27961,27964,27967,27991,27998,27999,28001,28005,28007,28015,28016,28028,28034,28039,28049,28050,28052,28054,28055,28056,28074,28076,28084,28087,28089,28093,28095,28100,28104,28106,28110,28111,28118,28123,28125,28127,28128,28130,28133,28137,28143,28144,28148,28150,28156,28160,28164,28190,28194,28199,28210,28214,28217,28219,28220,28228,28229,28232,28233,28235,28239,28241,28242,28243,28244,28247,28252,28253,28254,28258,28259,28264,28275,28283,28285,28301,28307,28313,28320,28327,28333,28334,28337,28339,28347,28351,28352,28353,28355,28359,28360,28362,28365,28366,28367,28395,28397,28398,28409,28411,28413,28420,28424,28426,28428,28429,28438,28440,28442,28443,28454,28457,28458,28463,28464,28467,28470,28475,28476,28461,28495,28497,28498,28499,28503,28505,28506,28509,28510,28513,28514,28520,28524,28541,28542,28547,28551,28552,28555,28556,28557,28560,28562,28563,28564,28566,28570,28575,28576,28581,28582,28583,28584,28590,28591,28592,28597,28598,28604,28613,28615,28616,28618,28634,28638,28648,28649,28656,28661,28665,28668,28669,28672,28677,28678,28679,28685,28695,28704,28707,28719,28724,28727,28729,28732,28739,28740,28744,28745,28746,28747,28756,28757,28765,28766,28750,28772,28773,28780,28782,28789,28790,28798,28801,28805,28806,28820,28821,28822,28823,28824,28827,28836,28843,28848,28849,28852,28855,28874,28881,28883,28884,28885,28886,28888,28892,28900,28922,28931,28932,28933,28934,28935,28939,28940,28943,28958,28960,28971,28973,28975,28976,28977,28984,28993,28997,28998,28999,29002,29003,29008,29010,29015,29018,29020,29022,29024,29032,29049,29056,29061,29063,29068,29074,29082,29083,29088,29090,29103,29104,29106,29107,29114,29119,29120,29121,29124,29131,29132,29139,29142,29145,29146,29148,29176,29182,29184,29191,29192,29193,29203,29207,29210,29213,29215,29220,29227,29231,29236,29240,29241,29249,29250,29251,29253,29262,29263,29264,29267,29269,29270,29274,29276,29278,29280,29283,29288,29291,29294,29295,29297,29303,29304,29307,29308,29311,29316,29321,29325,29326,29331,29339,29352,29357,29358,29361,29364,29374,29377,29383,29385,29388,29397,29398,29400,29407,29413,29427,29428,29434,29435,29438,29442,29444,29445,29447,29451,29453,29458,29459,29464,29465,29470,29474,29476,29479,29480,29484,29489,29490,29493,29498,29499,29501,29507,29517,29520,29522,29526,29528,29533,29534,29535,29536,29542,29543,29545,29547,29548,29550,29551,29553,29559,29561,29564,29568,29569,29571,29573,29574,29582,29584,29587,29589,29591,29592,29596,29598,29599,29600,29602,29605,29606,29610,29611,29613,29621,29623,29625,29628,29629,29631,29637,29638,29641,29643,29644,29647,29650,29651,29654,29657,29661,29665,29667,29670,29671,29673,29684,29685,29687,29689,29690,29691,29693,29695,29696,29697,29700,29703,29706,29713,29722,29723,29732,29734,29736,29737,29738,29739,29740,29741,29742,29743,29744,29745,29753,29760,29763,29764,29766,29767,29771,29773,29777,29778,29783,29789,29794,29798,29799,29800,29803,29805,29806,29809,29810,29824,29825,29829,29830,29831,29833,29839,29840,29841,29842,29848,29849,29850,29852,29855,29856,29857,29859,29862,29864,29865,29866,29867,29870,29871,29873,29874,29877,29881,29883,29887,29896,29897,29900,29904,29907,29912,29914,29915,29918,29919,29924,29928,29930,29931,29935,29940,29946,29947,29948,29951,29958,29970,29974,29975,29984,29985,29988,29991,29993,29994,29999,30006,30009,30013,30014,30015,30016,30019,30023,30024,30030,30032,30034,30039,30046,30047,30049,30063,30065,30073,30074,30075,30076,30077,30078,30081,30085,30096,30098,30099,30101,30105,30108,30114,30116,30132,30138,30143,30144,30145,30148,30150,30156,30158,30159,30167,30172,30175,30176,30177,30180,30183,30188,30190,30191,30193,30201,30208,30210,30211,30212,30215,30216,30218,30220,30223,30226,30227,30229,30230,30233,30235,30236,30237,30238,30243,30245,30246,30249,30253,30258,30259,30261,30264,30265,30266,30268,30282,30272,30273,30275,30276,30277,30281,30283,30293,30297,30303,30308,30309,30317,30318,30319,30321,30324,30337,30341,30348,30349,30357,30363,30364,30365,30367,30368,30370,30371,30372,30373,30374,30375,30376,30378,30381,30397,30401,30405,30409,30411,30412,30414,30420,30425,30432,30438,30440,30444,30448,30449,30454,30457,30460,30464,30470,30474,30478,30482,30484,30485,30487,30489,30490,30492,30498,30504,30509,30510,30511,30516,30517,30518,30521,30525,30526,30530,30533,30534,30538,30541,30542,30543,30546,30550,30551,30556,30558,30559,30560,30562,30564,30567,30570,30572,30576,30578,30579,30580,30586,30589,30592,30596,30604,30605,30612,30613,30614,30618,30623,30626,30631,30634,30638,30639,30641,30645,30654,30659,30665,30673,30674,30677,30681,30686,30687,30688,30692,30694,30698,30700,30704,30705,30708,30712,30715,30725,30726,30729,30733,30734,30737,30749,30753,30754,30755,30765,30766,30768,30773,30775,30787,30788,30791,30792,30796,30798,30802,30812,30814,30816,30817,30819,30820,30824,30826,30830,30842,30846,30858,30863,30868,30872,30881,30877,30878,30879,30884,30888,30892,30893,30896,30897,30898,30899,30907,30909,30911,30919,30920,30921,30924,30926,30930,30931,30933,30934,30948,30939,30943,30944,30945,30950,30954,30962,30963,30976,30966,30967,30970,30971,30975,30982,30988,30992,31002,31004,31006,31007,31008,31013,31015,31017,31021,31025,31028,31029,31035,31037,31039,31044,31045,31046,31050,31051,31055,31057,31060,31064,31067,31068,31079,31081,31083,31090,31097,31099,31100,31102,31115,31116,31121,31123,31124,31125,31126,31128,31131,31132,31137,31144,31145,31147,31151,31153,31156,31160,31163,31170,31172,31175,31176,31178,31183,31188,31190,31194,31197,31198,31200,31202,31205,31210,31211,31213,31217,31224,31228,31234,31235,31239,31241,31242,31244,31249,31253,31259,31262,31265,31271,31275,31277,31279,31280,31284,31285,31288,31289,31290,31300,31301,31303,31304,31308,31317,31318,31321,31324,31325,31327,31328,31333,31335,31338,31341,31349,31352,31358,31360,31362,31365,31366,31370,31371,31376,31377,31380,31390,31392,31395,31404,31411,31413,31417,31419,31420,31430,31433,31436,31438,31441,31451,31464,31465,31467,31468,31473,31476,31483,31485,31486,31495,31508,31519,31523,31527,31529,31530,31531,31533,31534,31535,31536,31537,31540,31549,31551,31552,31553,31559,31566,31573,31584,31588,31590,31593,31594,31597,31599,31602,31603,31607,31620,31625,31630,31632,31633,31638,31643,31646,31648,31653,31660,31663,31664,31666,31669,31670,31674,31675,31676,31677,31682,31685,31688,31690,31700,31702,31703,31705,31706,31707,31720,31722,31730,31732,31733,31736,31737,31738,31740,31742,31745,31746,31747,31748,31750,31753,31755,31756,31758,31759,31769,31771,31776,31781,31782,31784,31788,31793,31795,31796,31798,31801,31802,31814,31818,31829,31825,31826,31827,31833,31834,31835,31836,31837,31838,31841,31843,31847,31849,31853,31854,31856,31858,31865,31868,31869,31878,31879,31887,31892,31902,31904,31910,31920,31926,31927,31930,31931,31932,31935,31940,31943,31944,31945,31949,31951,31955,31956,31957,31959,31961,31962,31965,31974,31977,31979,31989,32003,32007,32008,32009,32015,32017,32018,32019,32022,32029,32030,32035,32038,32042,32045,32049,32060,32061,32062,32064,32065,32071,32072,32077,32081,32083,32087,32089,32090,32092,32093,32101,32103,32106,32112,32120,32122,32123,32127,32129,32130,32131,32133,32134,32136,32139,32140,32141,32145,32150,32151,32157,32158,32166,32167,32170,32179,32182,32183,32185,32194,32195,32196,32197,32198,32204,32205,32206,32215,32217,32256,32226,32229,32230,32234,32235,32237,32241,32245,32246,32249,32250,32264,32272,32273,32277,32279,32284,32285,32288,32295,32296,32300,32301,32303,32307,32310,32319,32324,32325,32327,32334,32336,32338,32344,32351,32353,32354,32357,32363,32366,32367,32371,32376,32382,32385,32390,32391,32394,32397,32401,32405,32408,32410,32413,32414,32572,32571,32573,32574,32575,32579,32580,32583,32591,32594,32595,32603,32604,32605,32609,32611,32612,32613,32614,32621,32625,32637,32638,32639,32640,32651,32653,32655,32656,32657,32662,32663,32668,32673,32674,32678,32682,32685,32692,32700,32703,32704,32707,32712,32718,32719,32731,32735,32739,32741,32744,32748,32750,32751,32754,32762,32765,32766,32767,32775,32776,32778,32781,32782,32783,32785,32787,32788,32790,32797,32798,32799,32800,32804,32806,32812,32814,32816,32820,32821,32823,32825,32826,32828,32830,32832,32836,32864,32868,32870,32877,32881,32885,32897,32904,32910,32924,32926,32934,32935,32939,32952,32953,32968,32973,32975,32978,32980,32981,32983,32984,32992,33005,33006,33008,33010,33011,33014,33017,33018,33022,33027,33035,33046,33047,33048,33052,33054,33056,33060,33063,33068,33072,33077,33082,33084,33093,33095,33098,33100,33106,33111,33120,33121,33127,33128,33129,33133,33135,33143,33153,33168,33156,33157,33158,33163,33166,33174,33176,33179,33182,33186,33198,33202,33204,33211,33227,33219,33221,33226,33230,33231,33237,33239,33243,33245,33246,33249,33252,33259,33260,33264,33265,33266,33269,33270,33272,33273,33277,33279,33280,33283,33295,33299,33300,33305,33306,33309,33313,33314,33320,33330,33332,33338,33347,33348,33349,33350,33355,33358,33359,33361,33366,33372,33376,33379,33383,33389,33396,33403,33405,33407,33408,33409,33411,33412,33415,33417,33418,33422,33425,33428,33430,33432,33434,33435,33440,33441,33443,33444,33447,33448,33449,33450,33454,33456,33458,33460,33463,33466,33468,33470,33471,33478,33488,33493,33498,33504,33506,33508,33512,33514,33517,33519,33526,33527,33533,33534,33536,33537,33543,33544,33546,33547,33620,33563,33565,33566,33567,33569,33570,33580,33581,33582,33584,33587,33591,33594,33596,33597,33602,33603,33604,33607,33613,33614,33617,33621,33622,33623,33648,33656,33661,33663,33664,33666,33668,33670,33677,33682,33684,33685,33688,33689,33691,33692,33693,33702,33703,33705,33708,33726,33727,33728,33735,33737,33743,33744,33745,33748,33757,33619,33768,33770,33782,33784,33785,33788,33793,33798,33802,33807,33809,33813,33817,33709,33839,33849,33861,33863,33864,33866,33869,33871,33873,33874,33878,33880,33881,33882,33884,33888,33892,33893,33895,33898,33904,33907,33908,33910,33912,33916,33917,33921,33925,33938,33939,33941,33950,33958,33960,33961,33962,33967,33969,33972,33978,33981,33982,33984,33986,33991,33992,33996,33999,34003,34012,34023,34026,34031,34032,34033,34034,34039,34098,34042,34043,34045,34050,34051,34055,34060,34062,34064,34076,34078,34082,34083,34084,34085,34087,34090,34091,34095,34099,34100,34102,34111,34118,34127,34128,34129,34130,34131,34134,34137,34140,34141,34142,34143,34144,34145,34146,34148,34155,34159,34169,34170,34171,34173,34175,34177,34181,34182,34185,34187,34188,34191,34195,34200,34205,34207,34208,34210,34213,34215,34228,34230,34231,34232,34236,34237,34238,34239,34242,34247,34250,34251,34254,34221,34264,34266,34271,34272,34278,34280,34285,34291,34294,34300,34303,34304,34308,34309,34317,34318,34320,34321,34322,34328,34329,34331,34334,34337,34343,34345,34358,34360,34362,34364,34365,34368,34370,34374,34386,34387,34390,34391,34392,34393,34397,34400,34401,34402,34403,34404,34409,34412,34415,34421,34422,34423,34426,34445,34449,34454,34456,34458,34460,34465,34470,34471,34472,34477,34481,34483,34484,34485,34487,34488,34489,34495,34496,34497,34499,34501,34513,34514,34517,34519,34522,34524,34528,34531,34533,34535,34440,34554,34556,34557,34564,34565,34567,34571,34574,34575,34576,34579,34580,34585,34590,34591,34593,34595,34600,34606,34607,34609,34610,34617,34618,34620,34621,34622,34624,34627,34629,34637,34648,34653,34657,34660,34661,34671,34673,34674,34683,34691,34692,34693,34694,34695,34696,34697,34699,34700,34704,34707,34709,34711,34712,34713,34718,34720,34723,34727,34732,34733,34734,34737,34741,34750,34751,34753,34760,34761,34762,34766,34773,34774,34777,34778,34780,34783,34786,34787,34788,34794,34795,34797,34801,34803,34808,34810,34815,34817,34819,34822,34825,34826,34827,34832,34841,34834,34835,34836,34840,34842,34843,34844,34846,34847,34856,34861,34862,34864,34866,34869,34874,34876,34881,34883,34885,34888,34889,34890,34891,34894,34897,34901,34902,34904,34906,34908,34911,34912,34916,34921,34929,34937,34939,34944,34968,34970,34971,34972,34975,34976,34984,34986,35002,35005,35006,35008,35018,35019,35020,35021,35022,35025,35026,35027,35035,35038,35047,35055,35056,35057,35061,35063,35073,35078,35085,35086,35087,35093,35094,35096,35097,35098,35100,35104,35110,35111,35112,35120,35121,35122,35125,35129,35130,35134,35136,35138,35141,35142,35145,35151,35154,35159,35162,35163,35164,35169,35170,35171,35179,35182,35184,35187,35189,35194,35195,35196,35197,35209,35213,35216,35220,35221,35227,35228,35231,35232,35237,35248,35252,35253,35254,35255,35260,35284,35285,35286,35287,35288,35301,35305,35307,35309,35313,35315,35318,35321,35325,35327,35332,35333,35335,35343,35345,35346,35348,35349,35358,35360,35362,35364,35366,35371,35372,35375,35381,35383,35389,35390,35392,35395,35397,35399,35401,35405,35406,35411,35414,35415,35416,35420,35421,35425,35429,35431,35445,35446,35447,35449,35450,35451,35454,35455,35456,35459,35462,35467,35471,35472,35474,35478,35479,35481,35487,35495,35497,35502,35503,35507,35510,35511,35515,35518,35523,35526,35528,35529,35530,35537,35539,35540,35541,35543,35549,35551,35564,35568,35572,35573,35574,35580,35583,35589,35590,35595,35601,35612,35614,35615,35594,35629,35632,35639,35644,35650,35651,35652,35653,35654,35656,35666,35667,35668,35673,35661,35678,35683,35693,35702,35704,35705,35708,35710,35713,35716,35717,35723,35725,35727,35732,35733,35740,35742,35743,35896,35897,35901,35902,35909,35911,35913,35915,35919,35921,35923,35924,35927,35928,35931,35933,35929,35939,35940,35942,35944,35945,35949,35955,35957,35958,35963,35966,35974,35975,35979,35984,35986,35987,35993,35995,35996,36004,36025,36026,36037,36038,36041,36043,36047,36054,36053,36057,36061,36065,36072,36076,36079,36080,36082,36085,36087,36088,36094,36095,36097,36099,36105,36114,36119,36123,36197,36201,36204,36206,36223,36226,36228,36232,36237,36240,36241,36245,36254,36255,36256,36262,36267,36268,36271,36274,36277,36279,36281,36283,36288,36293,36294,36295,36296,36298,36302,36305,36308,36309,36311,36313,36324,36325,36327,36332,36336,36284,36337,36338,36340,36349,36353,36356,36357,36358,36363,36369,36372,36374,36384,36385,36386,36387,36390,36391,36401,36403,36406,36407,36408,36409,36413,36416,36417,36427,36429,36430,36431,36436,36443,36444,36445,36446,36449,36450,36457,36460,36461,36463,36464,36465,36473,36474,36475,36482,36483,36489,36496,36498,36501,36506,36507,36509,36510,36514,36519,36521,36525,36526,36531,36533,36538,36539,36544,36545,36547,36548,36551,36559,36561,36564,36572,36584,36590,36592,36593,36599,36601,36602,36589,36608,36610,36615,36616,36623,36624,36630,36631,36632,36638,36640,36641,36643,36645,36647,36648,36652,36653,36654,36660,36661,36662,36663,36666,36672,36673,36675,36679,36687,36689,36690,36691,36692,36693,36696,36701,36702,36709,36765,36768,36769,36772,36773,36774,36789,36790,36792,36798,36800,36801,36806,36810,36811,36813,36816,36818,36819,36821,36832,36835,36836,36840,36846,36849,36853,36854,36859,36862,36866,36868,36872,36876,36888,36891,36904,36905,36911,36906,36908,36909,36915,36916,36919,36927,36931,36932,36940,36955,36957,36962,36966,36967,36972,36976,36980,36985,36997,37000,37003,37004,37006,37008,37013,37015,37016,37017,37019,37024,37025,37026,37029,37040,37042,37043,37044,37046,37053,37068,37054,37059,37060,37061,37063,37064,37077,37079,37080,37081,37084,37085,37087,37093,37074,37110,37099,37103,37104,37108,37118,37119,37120,37124,37125,37126,37128,37133,37136,37140,37142,37143,37144,37146,37148,37150,37152,37157,37154,37155,37159,37161,37166,37167,37169,37172,37174,37175,37177,37178,37180,37181,37187,37191,37192,37199,37203,37207,37209,37210,37211,37217,37220,37223,37229,37236,37241,37242,37243,37249,37251,37253,37254,37258,37262,37265,37267,37268,37269,37272,37278,37281,37286,37288,37292,37293,37294,37296,37297,37298,37299,37302,37307,37308,37309,37311,37314,37315,37317,37331,37332,37335,37337,37338,37342,37348,37349,37353,37354,37356,37357,37358,37359,37360,37361,37367,37369,37371,37373,37376,37377,37380,37381,37382,37383,37385,37386,37388,37392,37394,37395,37398,37400,37404,37405,37411,37412,37413,37414,37416,37422,37423,37424,37427,37429,37430,37432,37433,37434,37436,37438,37440,37442,37443,37446,37447,37450,37453,37454,37455,37457,37464,37465,37468,37469,37472,37473,37477,37479,37480,37481,37486,37487,37488,37493,37494,37495,37496,37497,37499,37500,37501,37503,37512,37513,37514,37517,37518,37522,37527,37529,37535,37536,37540,37541,37543,37544,37547,37551,37554,37558,37560,37562,37563,37564,37565,37567,37568,37569,37570,37571,37573,37574,37575,37576,37579,37580,37581,37582,37584,37587,37589,37591,37592,37593,37596,37597,37599,37600,37601,37603,37605,37607,37608,37612,37614,37616,37625,37627,37631,37632,37634,37640,37645,37649,37652,37653,37660,37661,37662,37663,37665,37668,37669,37671,37673,37674,37683,37684,37686,37687,37703,37704,37705,37712,37713,37714,37717,37719,37720,37722,37726,37732,37733,37735,37737,37738,37741,37743,37744,37745,37747,37748,37750,37754,37757,37759,37760,37761,37762,37768,37770,37771,37773,37775,37778,37781,37784,37787,37790,37793,37795,37796,37798,37800,37803,37812,37813,37814,37818,37801,37825,37828,37829,37830,37831,37833,37834,37835,37836,37837,37843,37849,37852,37854,37855,37858,37862,37863,37881,37879,37880,37882,37883,37885,37889,37890,37892,37896,37897,37901,37902,37903,37909,37910,37911,37919,37934,37935,37937,37938,37939,37940,37947,37951,37949,37955,37957,37960,37962,37964,37973,37977,37980,37983,37985,37987,37992,37995,37997,37998,37999,38001,38002,38020,38019,38264,38265,38270,38276,38280,38284,38285,38286,38301,38302,38303,38305,38310,38313,38315,38316,38324,38326,38330,38333,38335,38342,38344,38345,38347,38352,38353,38354,38355,38361,38362,38365,38366,38367,38368,38372,38374,38429,38430,38434,38436,38437,38438,38444,38449,38451,38455,38456,38457,38458,38460,38461,38465,38482,38484,38486,38487,38488,38497,38510,38516,38523,38524,38526,38527,38529,38530,38531,38532,38537,38545,38550,38554,38557,38559,38564,38565,38566,38569,38574,38575,38579,38586,38602,38610,23986,38616,38618,38621,38622,38623,38633,38639,38641,38650,38658,38659,38661,38665,38682,38683,38685,38689,38690,38691,38696,38705,38707,38721,38723,38730,38734,38735,38741,38743,38744,38746,38747,38755,38759,38762,38766,38771,38774,38775,38776,38779,38781,38783,38784,38793,38805,38806,38807,38809,38810,38814,38815,38818,38828,38830,38833,38834,38837,38838,38840,38841,38842,38844,38846,38847,38849,38852,38853,38855,38857,38858,38860,38861,38862,38864,38865,38868,38871,38872,38873,38877,38878,38880,38875,38881,38884,38895,38897,38900,38903,38904,38906,38919,38922,38937,38925,38926,38932,38934,38940,38942,38944,38947,38950,38955,38958,38959,38960,38962,38963,38965,38949,38974,38980,38983,38986,38993,38994,38995,38998,38999,39001,39002,39010,39011,39013,39014,39018,39020,39083,39085,39086,39088,39092,39095,39096,39098,39099,39103,39106,39109,39112,39116,39137,39139,39141,39142,39143,39146,39155,39158,39170,39175,39176,39185,39189,39190,39191,39194,39195,39196,39199,39202,39206,39207,39211,39217,39218,39219,39220,39221,39225,39226,39227,39228,39232,39233,39238,39239,39240,39245,39246,39252,39256,39257,39259,39260,39262,39263,39264,39323,39325,39327,39334,39344,39345,39346,39349,39353,39354,39357,39359,39363,39369,39379,39380,39385,39386,39388,39390,39399,39402,39403,39404,39408,39412,39413,39417,39421,39422,39426,39427,39428,39435,39436,39440,39441,39446,39454,39456,39458,39459,39460,39463,39469,39470,39475,39477,39478,39480,39495,39489,39492,39498,39499,39500,39502,39505,39508,39510,39517,39594,39596,39598,39599,39602,39604,39605,39606,39609,39611,39614,39615,39617,39619,39622,39624,39630,39632,39634,39637,39638,39639,39643,39644,39648,39652,39653,39655,39657,39660,39666,39667,39669,39673,39674,39677,39679,39680,39681,39682,39683,39684,39685,39688,39689,39691,39692,39693,39694,39696,39698,39702,39705,39707,39708,39712,39718,39723,39725,39731,39732,39733,39735,39737,39738,39741,39752,39755,39756,39765,39766,39767,39771,39774,39777,39779,39781,39782,39784,39786,39787,39788,39789,39790,39795,39797,39799,39800,39801,39807,39808,39812,39813,39814,39815,39817,39818,39819,39821,39823,39824,39828,39834,39837,39838,39846,39847,39849,39852,39856,39857,39858,39863,39864,39867,39868,39870,39871,39873,39879,39880,39886,39888,39895,39896,39901,39903,39909,39911,39914,39915,39919,39923,39927,39928,39929,39930,39933,39935,39936,39938,39947,39951,39953,39958,39960,39961,39962,39964,39966,39970,39971,39974,39975,39976,39977,39978,39985,39989,39990,39991,39997,40001,40003,40004,40005,40009,40010,40014,40015,40016,40019,40020,40022,40024,40027,40029,40030,40031,40035,40041,40042,40028,40043,40040,40046,40048,40050,40053,40055,40059,40166,40178,40183,40185,40203,40194,40209,40215,40216,40220,40221,40222,40239,40240,40242,40243,40244,40250,40252,40261,40253,40258,40259,40263,40266,40275,40276,40287,40291,40290,40293,40297,40298,40299,40304,40310,40311,40315,40316,40318,40323,40324,40326,40330,40333,40334,40338,40339,40341,40342,40343,40344,40353,40362,40364,40366,40369,40373,40377,40380,40383,40387,40391,40393,40394,40404,40405,40406,40407,40410,40414,40415,40416,40421,40423,40425,40427,40430,40432,40435,40436,40446,40458,40450,40455,40462,40464,40465,40466,40469,40470,40473,40476,40477,40570,40571,40572,40576,40578,40579,40580,40581,40583,40590,40591,40598,40600,40603,40606,40612,40616,40620,40622,40623,40624,40627,40628,40629,40646,40648,40651,40661,40671,40676,40679,40684,40685,40686,40688,40689,40690,40693,40696,40703,40706,40707,40713,40719,40720,40721,40722,40724,40726,40727,40729,40730,40731,40735,40738,40742,40746,40747,40751,40753,40754,40756,40759,40761,40762,40764,40765,40767,40769,40771,40772,40773,40774,40775,40787,40789,40790,40791,40792,40794,40797,40798,40808,40809,40813,40814,40815,40816,40817,40819,40821,40826,40829,40847,40848,40849,40850,40852,40854,40855,40862,40865,40866,40867,40869,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],
- "ibm866":[1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488,9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575,9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1025,1105,1028,1108,1031,1111,1038,1118,176,8729,183,8730,8470,164,9632,160],
- "iso-8859-2":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,728,321,164,317,346,167,168,352,350,356,377,173,381,379,176,261,731,322,180,318,347,711,184,353,351,357,378,733,382,380,340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270,272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223,341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271,273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729],
- "iso-8859-3":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,294,728,163,164,null,292,167,168,304,350,286,308,173,null,379,176,295,178,179,180,181,293,183,184,305,351,287,309,189,null,380,192,193,194,null,196,266,264,199,200,201,202,203,204,205,206,207,null,209,210,211,212,288,214,215,284,217,218,219,220,364,348,223,224,225,226,null,228,267,265,231,232,233,234,235,236,237,238,239,null,241,242,243,244,289,246,247,285,249,250,251,252,365,349,729],
- "iso-8859-4":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,312,342,164,296,315,167,168,352,274,290,358,173,381,175,176,261,731,343,180,297,316,711,184,353,275,291,359,330,382,331,256,193,194,195,196,197,198,302,268,201,280,203,278,205,206,298,272,325,332,310,212,213,214,215,216,370,218,219,220,360,362,223,257,225,226,227,228,229,230,303,269,233,281,235,279,237,238,299,273,326,333,311,244,245,246,247,248,371,250,251,252,361,363,729],
- "iso-8859-5":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,173,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,8470,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,167,1118,1119],
- "iso-8859-6":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,null,null,null,164,null,null,null,null,null,null,null,1548,173,null,null,null,null,null,null,null,null,null,null,null,null,null,1563,null,null,null,1567,null,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,null,null,null,null,null,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,null,null,null,null,null,null,null,null,null,null,null,null,null],
- "iso-8859-7":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,8216,8217,163,8364,8367,166,167,168,169,890,171,172,173,null,8213,176,177,178,179,900,901,902,183,904,905,906,187,908,189,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,null,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,null],
- "iso-8859-8":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,null,162,163,164,165,166,167,168,169,215,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,247,187,188,189,190,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,8215,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,null,null,8206,8207,null],
- "iso-8859-10":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,274,290,298,296,310,167,315,272,352,358,381,173,362,330,176,261,275,291,299,297,311,183,316,273,353,359,382,8213,363,331,256,193,194,195,196,197,198,302,268,201,280,203,278,205,206,207,208,325,332,211,212,213,214,360,216,370,218,219,220,221,222,223,257,225,226,227,228,229,230,303,269,233,281,235,279,237,238,239,240,326,333,243,244,245,246,361,248,371,250,251,252,253,254,312],
- "iso-8859-13":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,8221,162,163,164,8222,166,167,216,169,342,171,172,173,174,198,176,177,178,179,8220,181,182,183,248,185,343,187,188,189,190,230,260,302,256,262,196,197,280,274,268,201,377,278,290,310,298,315,352,323,325,211,332,213,214,215,370,321,346,362,220,379,381,223,261,303,257,263,228,229,281,275,269,233,378,279,291,311,299,316,353,324,326,243,333,245,246,247,371,322,347,363,252,380,382,8217],
- "iso-8859-14":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,7682,7683,163,266,267,7690,167,7808,169,7810,7691,7922,173,174,376,7710,7711,288,289,7744,7745,182,7766,7809,7767,7811,7776,7923,7812,7813,7777,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,372,209,210,211,212,213,214,7786,216,217,218,219,220,221,374,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,373,241,242,243,244,245,246,7787,248,249,250,251,252,253,375,255],
- "iso-8859-15":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,8364,165,352,167,353,169,170,171,172,173,174,175,176,177,178,179,381,181,182,183,382,185,186,187,338,339,376,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255],
- "iso-8859-16":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,261,321,8364,8222,352,167,353,169,536,171,377,173,378,379,176,177,268,322,381,8221,182,183,382,269,537,187,338,339,376,380,192,193,194,258,196,262,198,199,200,201,202,203,204,205,206,207,272,323,210,211,212,336,214,346,368,217,218,219,220,280,538,223,224,225,226,259,228,263,230,231,232,233,234,235,236,237,238,239,273,324,242,243,244,337,246,347,369,249,250,251,252,281,539,255],
- "koi8-r":[9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9600,9604,9608,9612,9616,9617,9618,9619,8992,9632,8729,8730,8776,8804,8805,160,8993,176,178,183,247,9552,9553,9554,1105,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,1025,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,169,1102,1072,1073,1094,1076,1077,1092,1075,1093,1080,1081,1082,1083,1084,1085,1086,1087,1103,1088,1089,1090,1091,1078,1074,1100,1099,1079,1096,1101,1097,1095,1098,1070,1040,1041,1062,1044,1045,1060,1043,1061,1048,1049,1050,1051,1052,1053,1054,1055,1071,1056,1057,1058,1059,1046,1042,1068,1067,1047,1064,1069,1065,1063,1066],
- "koi8-u":[9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9600,9604,9608,9612,9616,9617,9618,9619,8992,9632,8729,8730,8776,8804,8805,160,8993,176,178,183,247,9552,9553,9554,1105,1108,9556,1110,1111,9559,9560,9561,9562,9563,1169,1118,9566,9567,9568,9569,1025,1028,9571,1030,1031,9574,9575,9576,9577,9578,1168,1038,169,1102,1072,1073,1094,1076,1077,1092,1075,1093,1080,1081,1082,1083,1084,1085,1086,1087,1103,1088,1089,1090,1091,1078,1074,1100,1099,1079,1096,1101,1097,1095,1098,1070,1040,1041,1062,1044,1045,1060,1043,1061,1048,1049,1050,1051,1052,1053,1054,1055,1071,1056,1057,1058,1059,1046,1042,1068,1067,1047,1064,1069,1065,1063,1066],
- "macintosh":[196,197,199,201,209,214,220,225,224,226,228,227,229,231,233,232,234,235,237,236,238,239,241,243,242,244,246,245,250,249,251,252,8224,176,162,163,167,8226,182,223,174,169,8482,180,168,8800,198,216,8734,177,8804,8805,165,181,8706,8721,8719,960,8747,170,186,937,230,248,191,161,172,8730,402,8776,8710,171,187,8230,160,192,195,213,338,339,8211,8212,8220,8221,8216,8217,247,9674,255,376,8260,8364,8249,8250,64257,64258,8225,183,8218,8222,8240,194,202,193,203,200,205,206,207,204,211,212,63743,210,218,219,217,305,710,732,175,728,729,730,184,733,731,711],
- "windows-874":[8364,129,130,131,132,8230,134,135,136,137,138,139,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,152,153,154,155,156,157,158,159,160,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,null,null,null,null,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,null,null,null,null],
- "windows-1250":[8364,129,8218,131,8222,8230,8224,8225,136,8240,352,8249,346,356,381,377,144,8216,8217,8220,8221,8226,8211,8212,152,8482,353,8250,347,357,382,378,160,711,728,321,164,260,166,167,168,169,350,171,172,173,174,379,176,177,731,322,180,181,182,183,184,261,351,187,317,733,318,380,340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270,272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223,341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271,273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729],
- "windows-1251":[1026,1027,8218,1107,8222,8230,8224,8225,8364,8240,1033,8249,1034,1036,1035,1039,1106,8216,8217,8220,8221,8226,8211,8212,152,8482,1113,8250,1114,1116,1115,1119,160,1038,1118,1032,164,1168,166,167,1025,169,1028,171,172,173,174,1031,176,177,1030,1110,1169,181,182,183,1105,8470,1108,187,1112,1029,1109,1111,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103],
- "windows-1252":[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255],
- "windows-1253":[8364,129,8218,402,8222,8230,8224,8225,136,8240,138,8249,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,152,8482,154,8250,156,157,158,159,160,901,902,163,164,165,166,167,168,169,null,171,172,173,174,8213,176,177,178,179,900,181,182,183,904,905,906,187,908,189,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,null,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,null],
- "windows-1254":[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,158,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,286,209,210,211,212,213,214,215,216,217,218,219,220,304,350,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,287,241,242,243,244,245,246,247,248,249,250,251,252,305,351,255],
- "windows-1255":[8364,129,8218,402,8222,8230,8224,8225,710,8240,138,8249,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,154,8250,156,157,158,159,160,161,162,163,8362,165,166,167,168,169,215,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,247,187,188,189,190,191,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1520,1521,1522,1523,1524,null,null,null,null,null,null,null,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,null,null,8206,8207,null],
- "windows-1256":[8364,1662,8218,402,8222,8230,8224,8225,710,8240,1657,8249,338,1670,1688,1672,1711,8216,8217,8220,8221,8226,8211,8212,1705,8482,1681,8250,339,8204,8205,1722,160,1548,162,163,164,165,166,167,168,169,1726,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,1563,187,188,189,190,1567,1729,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,215,1591,1592,1593,1594,1600,1601,1602,1603,224,1604,226,1605,1606,1607,1608,231,232,233,234,235,1609,1610,238,239,1611,1612,1613,1614,244,1615,1616,247,1617,249,1618,251,252,8206,8207,1746],
- "windows-1257":[8364,129,8218,131,8222,8230,8224,8225,136,8240,138,8249,140,168,711,184,144,8216,8217,8220,8221,8226,8211,8212,152,8482,154,8250,156,175,731,159,160,null,162,163,164,null,166,167,216,169,342,171,172,173,174,198,176,177,178,179,180,181,182,183,248,185,343,187,188,189,190,230,260,302,256,262,196,197,280,274,268,201,377,278,290,310,298,315,352,323,325,211,332,213,214,215,370,321,346,362,220,379,381,223,261,303,257,263,228,229,281,275,269,233,378,279,291,311,299,316,353,324,326,243,333,245,246,247,371,322,347,363,252,380,382,729],
- "windows-1258":[8364,129,8218,402,8222,8230,8224,8225,710,8240,138,8249,338,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,154,8250,339,157,158,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,258,196,197,198,199,200,201,202,203,768,205,206,207,272,209,777,211,212,416,214,215,216,217,218,219,220,431,771,223,224,225,226,259,228,229,230,231,232,233,234,235,769,237,238,239,273,241,803,243,244,417,246,247,248,249,250,251,252,432,8363,255],
- "x-mac-cyrillic":[1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,8224,176,1168,163,167,8226,182,1030,174,169,8482,1026,1106,8800,1027,1107,8734,177,8804,8805,1110,181,1169,1032,1028,1108,1031,1111,1033,1113,1034,1114,1112,1029,172,8730,402,8776,8710,171,187,8230,160,1035,1115,1036,1116,1109,8211,8212,8220,8221,8216,8217,247,8222,1038,1118,1039,1119,8470,1025,1105,1103,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,8364]
-}
-;}(this));
diff --git a/vendor/assets/javascripts/xterm/encoding.js b/vendor/assets/javascripts/xterm/encoding.js
deleted file mode 100644
index b5c8904f5a3..00000000000
--- a/vendor/assets/javascripts/xterm/encoding.js
+++ /dev/null
@@ -1,3309 +0,0 @@
-// This is free and unencumbered software released into the public domain.
-// See LICENSE.md for more information.
-
-// If we're in node require encoding-indexes and attach it to the global.
-/**
- * @fileoverview Global |this| required for resolving indexes in node.
- * @suppress {globalThis}
- */
-if (typeof module !== "undefined" && module.exports &&
- !this["encoding-indexes"]) {
- require("./encoding-indexes.js");
-}
-
-(function(global) {
- 'use strict';
-
- //
- // Utilities
- //
-
- /**
- * @param {number} a The number to test.
- * @param {number} min The minimum value in the range, inclusive.
- * @param {number} max The maximum value in the range, inclusive.
- * @return {boolean} True if a >= min and a <= max.
- */
- function inRange(a, min, max) {
- return min <= a && a <= max;
- }
-
- /**
- * @param {!Array.<*>} array The array to check.
- * @param {*} item The item to look for in the array.
- * @return {boolean} True if the item appears in the array.
- */
- function includes(array, item) {
- return array.indexOf(item) !== -1;
- }
-
- var floor = Math.floor;
-
- /**
- * @param {*} o
- * @return {Object}
- */
- function ToDictionary(o) {
- if (o === undefined) return {};
- if (o === Object(o)) return o;
- throw TypeError('Could not convert argument to dictionary');
- }
-
- /**
- * @param {string} string Input string of UTF-16 code units.
- * @return {!Array.<number>} Code points.
- */
- function stringToCodePoints(string) {
- // https://heycam.github.io/webidl/#dfn-obtain-unicode
-
- // 1. Let S be the DOMString value.
- var s = String(string);
-
- // 2. Let n be the length of S.
- var n = s.length;
-
- // 3. Initialize i to 0.
- var i = 0;
-
- // 4. Initialize U to be an empty sequence of Unicode characters.
- var u = [];
-
- // 5. While i < n:
- while (i < n) {
-
- // 1. Let c be the code unit in S at index i.
- var c = s.charCodeAt(i);
-
- // 2. Depending on the value of c:
-
- // c < 0xD800 or c > 0xDFFF
- if (c < 0xD800 || c > 0xDFFF) {
- // Append to U the Unicode character with code point c.
- u.push(c);
- }
-
- // 0xDC00 ≤ c ≤ 0xDFFF
- else if (0xDC00 <= c && c <= 0xDFFF) {
- // Append to U a U+FFFD REPLACEMENT CHARACTER.
- u.push(0xFFFD);
- }
-
- // 0xD800 ≤ c ≤ 0xDBFF
- else if (0xD800 <= c && c <= 0xDBFF) {
- // 1. If i = n−1, then append to U a U+FFFD REPLACEMENT
- // CHARACTER.
- if (i === n - 1) {
- u.push(0xFFFD);
- }
- // 2. Otherwise, i < n−1:
- else {
- // 1. Let d be the code unit in S at index i+1.
- var d = s.charCodeAt(i + 1);
-
- // 2. If 0xDC00 ≤ d ≤ 0xDFFF, then:
- if (0xDC00 <= d && d <= 0xDFFF) {
- // 1. Let a be c & 0x3FF.
- var a = c & 0x3FF;
-
- // 2. Let b be d & 0x3FF.
- var b = d & 0x3FF;
-
- // 3. Append to U the Unicode character with code point
- // 2^16+2^10*a+b.
- u.push(0x10000 + (a << 10) + b);
-
- // 4. Set i to i+1.
- i += 1;
- }
-
- // 3. Otherwise, d < 0xDC00 or d > 0xDFFF. Append to U a
- // U+FFFD REPLACEMENT CHARACTER.
- else {
- u.push(0xFFFD);
- }
- }
- }
-
- // 3. Set i to i+1.
- i += 1;
- }
-
- // 6. Return U.
- return u;
- }
-
- /**
- * @param {!Array.<number>} code_points Array of code points.
- * @return {string} string String of UTF-16 code units.
- */
- function codePointsToString(code_points) {
- var s = '';
- for (var i = 0; i < code_points.length; ++i) {
- var cp = code_points[i];
- if (cp <= 0xFFFF) {
- s += String.fromCharCode(cp);
- } else {
- cp -= 0x10000;
- s += String.fromCharCode((cp >> 10) + 0xD800,
- (cp & 0x3FF) + 0xDC00);
- }
- }
- return s;
- }
-
-
- //
- // Implementation of Encoding specification
- // https://encoding.spec.whatwg.org/
- //
-
- //
- // 4. Terminology
- //
-
- /**
- * An ASCII byte is a byte in the range 0x00 to 0x7F, inclusive.
- * @param {number} a The number to test.
- * @return {boolean} True if a is in the range 0x00 to 0x7F, inclusive.
- */
- function isASCIIByte(a) {
- return 0x00 <= a && a <= 0x7F;
- }
-
- /**
- * An ASCII code point is a code point in the range U+0000 to
- * U+007F, inclusive.
- */
- var isASCIICodePoint = isASCIIByte;
-
-
- /**
- * End-of-stream is a special token that signifies no more tokens
- * are in the stream.
- * @const
- */ var end_of_stream = -1;
-
- /**
- * A stream represents an ordered sequence of tokens.
- *
- * @constructor
- * @param {!(Array.<number>|Uint8Array)} tokens Array of tokens that provide
- * the stream.
- */
- function Stream(tokens) {
- /** @type {!Array.<number>} */
- this.tokens = [].slice.call(tokens);
- // Reversed as push/pop is more efficient than shift/unshift.
- this.tokens.reverse();
- }
-
- Stream.prototype = {
- /**
- * @return {boolean} True if end-of-stream has been hit.
- */
- endOfStream: function() {
- return !this.tokens.length;
- },
-
- /**
- * When a token is read from a stream, the first token in the
- * stream must be returned and subsequently removed, and
- * end-of-stream must be returned otherwise.
- *
- * @return {number} Get the next token from the stream, or
- * end_of_stream.
- */
- read: function() {
- if (!this.tokens.length)
- return end_of_stream;
- return this.tokens.pop();
- },
-
- /**
- * When one or more tokens are prepended to a stream, those tokens
- * must be inserted, in given order, before the first token in the
- * stream.
- *
- * @param {(number|!Array.<number>)} token The token(s) to prepend to the
- * stream.
- */
- prepend: function(token) {
- if (Array.isArray(token)) {
- var tokens = /**@type {!Array.<number>}*/(token);
- while (tokens.length)
- this.tokens.push(tokens.pop());
- } else {
- this.tokens.push(token);
- }
- },
-
- /**
- * When one or more tokens are pushed to a stream, those tokens
- * must be inserted, in given order, after the last token in the
- * stream.
- *
- * @param {(number|!Array.<number>)} token The tokens(s) to push to the
- * stream.
- */
- push: function(token) {
- if (Array.isArray(token)) {
- var tokens = /**@type {!Array.<number>}*/(token);
- while (tokens.length)
- this.tokens.unshift(tokens.shift());
- } else {
- this.tokens.unshift(token);
- }
- }
- };
-
- //
- // 5. Encodings
- //
-
- // 5.1 Encoders and decoders
-
- /** @const */
- var finished = -1;
-
- /**
- * @param {boolean} fatal If true, decoding errors raise an exception.
- * @param {number=} opt_code_point Override the standard fallback code point.
- * @return {number} The code point to insert on a decoding error.
- */
- function decoderError(fatal, opt_code_point) {
- if (fatal)
- throw TypeError('Decoder error');
- return opt_code_point || 0xFFFD;
- }
-
- /**
- * @param {number} code_point The code point that could not be encoded.
- * @return {number} Always throws, no value is actually returned.
- */
- function encoderError(code_point) {
- throw TypeError('The code point ' + code_point + ' could not be encoded.');
- }
-
- /** @interface */
- function Decoder() {}
- Decoder.prototype = {
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point, or |finished|.
- */
- handler: function(stream, bite) {}
- };
-
- /** @interface */
- function Encoder() {}
- Encoder.prototype = {
- /**
- * @param {Stream} stream The stream of code points being encoded.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit, or |finished|.
- */
- handler: function(stream, code_point) {}
- };
-
- // 5.2 Names and labels
-
- // TODO: Define @typedef for Encoding: {name:string,labels:Array.<string>}
- // https://github.com/google/closure-compiler/issues/247
-
- /**
- * @param {string} label The encoding label.
- * @return {?{name:string,labels:Array.<string>}}
- */
- function getEncoding(label) {
- // 1. Remove any leading and trailing ASCII whitespace from label.
- label = String(label).trim().toLowerCase();
-
- // 2. If label is an ASCII case-insensitive match for any of the
- // labels listed in the table below, return the corresponding
- // encoding, and failure otherwise.
- if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) {
- return label_to_encoding[label];
- }
- return null;
- }
-
- /**
- * Encodings table: https://encoding.spec.whatwg.org/encodings.json
- * @const
- * @type {!Array.<{
- * heading: string,
- * encodings: Array.<{name:string,labels:Array.<string>}>
- * }>}
- */
- var encodings = [
- {
- "encodings": [
- {
- "labels": [
- "unicode-1-1-utf-8",
- "utf-8",
- "utf8"
- ],
- "name": "UTF-8"
- }
- ],
- "heading": "The Encoding"
- },
- {
- "encodings": [
- {
- "labels": [
- "866",
- "cp866",
- "csibm866",
- "ibm866"
- ],
- "name": "IBM866"
- },
- {
- "labels": [
- "csisolatin2",
- "iso-8859-2",
- "iso-ir-101",
- "iso8859-2",
- "iso88592",
- "iso_8859-2",
- "iso_8859-2:1987",
- "l2",
- "latin2"
- ],
- "name": "ISO-8859-2"
- },
- {
- "labels": [
- "csisolatin3",
- "iso-8859-3",
- "iso-ir-109",
- "iso8859-3",
- "iso88593",
- "iso_8859-3",
- "iso_8859-3:1988",
- "l3",
- "latin3"
- ],
- "name": "ISO-8859-3"
- },
- {
- "labels": [
- "csisolatin4",
- "iso-8859-4",
- "iso-ir-110",
- "iso8859-4",
- "iso88594",
- "iso_8859-4",
- "iso_8859-4:1988",
- "l4",
- "latin4"
- ],
- "name": "ISO-8859-4"
- },
- {
- "labels": [
- "csisolatincyrillic",
- "cyrillic",
- "iso-8859-5",
- "iso-ir-144",
- "iso8859-5",
- "iso88595",
- "iso_8859-5",
- "iso_8859-5:1988"
- ],
- "name": "ISO-8859-5"
- },
- {
- "labels": [
- "arabic",
- "asmo-708",
- "csiso88596e",
- "csiso88596i",
- "csisolatinarabic",
- "ecma-114",
- "iso-8859-6",
- "iso-8859-6-e",
- "iso-8859-6-i",
- "iso-ir-127",
- "iso8859-6",
- "iso88596",
- "iso_8859-6",
- "iso_8859-6:1987"
- ],
- "name": "ISO-8859-6"
- },
- {
- "labels": [
- "csisolatingreek",
- "ecma-118",
- "elot_928",
- "greek",
- "greek8",
- "iso-8859-7",
- "iso-ir-126",
- "iso8859-7",
- "iso88597",
- "iso_8859-7",
- "iso_8859-7:1987",
- "sun_eu_greek"
- ],
- "name": "ISO-8859-7"
- },
- {
- "labels": [
- "csiso88598e",
- "csisolatinhebrew",
- "hebrew",
- "iso-8859-8",
- "iso-8859-8-e",
- "iso-ir-138",
- "iso8859-8",
- "iso88598",
- "iso_8859-8",
- "iso_8859-8:1988",
- "visual"
- ],
- "name": "ISO-8859-8"
- },
- {
- "labels": [
- "csiso88598i",
- "iso-8859-8-i",
- "logical"
- ],
- "name": "ISO-8859-8-I"
- },
- {
- "labels": [
- "csisolatin6",
- "iso-8859-10",
- "iso-ir-157",
- "iso8859-10",
- "iso885910",
- "l6",
- "latin6"
- ],
- "name": "ISO-8859-10"
- },
- {
- "labels": [
- "iso-8859-13",
- "iso8859-13",
- "iso885913"
- ],
- "name": "ISO-8859-13"
- },
- {
- "labels": [
- "iso-8859-14",
- "iso8859-14",
- "iso885914"
- ],
- "name": "ISO-8859-14"
- },
- {
- "labels": [
- "csisolatin9",
- "iso-8859-15",
- "iso8859-15",
- "iso885915",
- "iso_8859-15",
- "l9"
- ],
- "name": "ISO-8859-15"
- },
- {
- "labels": [
- "iso-8859-16"
- ],
- "name": "ISO-8859-16"
- },
- {
- "labels": [
- "cskoi8r",
- "koi",
- "koi8",
- "koi8-r",
- "koi8_r"
- ],
- "name": "KOI8-R"
- },
- {
- "labels": [
- "koi8-ru",
- "koi8-u"
- ],
- "name": "KOI8-U"
- },
- {
- "labels": [
- "csmacintosh",
- "mac",
- "macintosh",
- "x-mac-roman"
- ],
- "name": "macintosh"
- },
- {
- "labels": [
- "dos-874",
- "iso-8859-11",
- "iso8859-11",
- "iso885911",
- "tis-620",
- "windows-874"
- ],
- "name": "windows-874"
- },
- {
- "labels": [
- "cp1250",
- "windows-1250",
- "x-cp1250"
- ],
- "name": "windows-1250"
- },
- {
- "labels": [
- "cp1251",
- "windows-1251",
- "x-cp1251"
- ],
- "name": "windows-1251"
- },
- {
- "labels": [
- "ansi_x3.4-1968",
- "ascii",
- "cp1252",
- "cp819",
- "csisolatin1",
- "ibm819",
- "iso-8859-1",
- "iso-ir-100",
- "iso8859-1",
- "iso88591",
- "iso_8859-1",
- "iso_8859-1:1987",
- "l1",
- "latin1",
- "us-ascii",
- "windows-1252",
- "x-cp1252"
- ],
- "name": "windows-1252"
- },
- {
- "labels": [
- "cp1253",
- "windows-1253",
- "x-cp1253"
- ],
- "name": "windows-1253"
- },
- {
- "labels": [
- "cp1254",
- "csisolatin5",
- "iso-8859-9",
- "iso-ir-148",
- "iso8859-9",
- "iso88599",
- "iso_8859-9",
- "iso_8859-9:1989",
- "l5",
- "latin5",
- "windows-1254",
- "x-cp1254"
- ],
- "name": "windows-1254"
- },
- {
- "labels": [
- "cp1255",
- "windows-1255",
- "x-cp1255"
- ],
- "name": "windows-1255"
- },
- {
- "labels": [
- "cp1256",
- "windows-1256",
- "x-cp1256"
- ],
- "name": "windows-1256"
- },
- {
- "labels": [
- "cp1257",
- "windows-1257",
- "x-cp1257"
- ],
- "name": "windows-1257"
- },
- {
- "labels": [
- "cp1258",
- "windows-1258",
- "x-cp1258"
- ],
- "name": "windows-1258"
- },
- {
- "labels": [
- "x-mac-cyrillic",
- "x-mac-ukrainian"
- ],
- "name": "x-mac-cyrillic"
- }
- ],
- "heading": "Legacy single-byte encodings"
- },
- {
- "encodings": [
- {
- "labels": [
- "chinese",
- "csgb2312",
- "csiso58gb231280",
- "gb2312",
- "gb_2312",
- "gb_2312-80",
- "gbk",
- "iso-ir-58",
- "x-gbk"
- ],
- "name": "GBK"
- },
- {
- "labels": [
- "gb18030"
- ],
- "name": "gb18030"
- }
- ],
- "heading": "Legacy multi-byte Chinese (simplified) encodings"
- },
- {
- "encodings": [
- {
- "labels": [
- "big5",
- "big5-hkscs",
- "cn-big5",
- "csbig5",
- "x-x-big5"
- ],
- "name": "Big5"
- }
- ],
- "heading": "Legacy multi-byte Chinese (traditional) encodings"
- },
- {
- "encodings": [
- {
- "labels": [
- "cseucpkdfmtjapanese",
- "euc-jp",
- "x-euc-jp"
- ],
- "name": "EUC-JP"
- },
- {
- "labels": [
- "csiso2022jp",
- "iso-2022-jp"
- ],
- "name": "ISO-2022-JP"
- },
- {
- "labels": [
- "csshiftjis",
- "ms932",
- "ms_kanji",
- "shift-jis",
- "shift_jis",
- "sjis",
- "windows-31j",
- "x-sjis"
- ],
- "name": "Shift_JIS"
- }
- ],
- "heading": "Legacy multi-byte Japanese encodings"
- },
- {
- "encodings": [
- {
- "labels": [
- "cseuckr",
- "csksc56011987",
- "euc-kr",
- "iso-ir-149",
- "korean",
- "ks_c_5601-1987",
- "ks_c_5601-1989",
- "ksc5601",
- "ksc_5601",
- "windows-949"
- ],
- "name": "EUC-KR"
- }
- ],
- "heading": "Legacy multi-byte Korean encodings"
- },
- {
- "encodings": [
- {
- "labels": [
- "csiso2022kr",
- "hz-gb-2312",
- "iso-2022-cn",
- "iso-2022-cn-ext",
- "iso-2022-kr"
- ],
- "name": "replacement"
- },
- {
- "labels": [
- "utf-16be"
- ],
- "name": "UTF-16BE"
- },
- {
- "labels": [
- "utf-16",
- "utf-16le"
- ],
- "name": "UTF-16LE"
- },
- {
- "labels": [
- "x-user-defined"
- ],
- "name": "x-user-defined"
- }
- ],
- "heading": "Legacy miscellaneous encodings"
- }
- ];
-
- // Label to encoding registry.
- /** @type {Object.<string,{name:string,labels:Array.<string>}>} */
- var label_to_encoding = {};
- encodings.forEach(function(category) {
- category.encodings.forEach(function(encoding) {
- encoding.labels.forEach(function(label) {
- label_to_encoding[label] = encoding;
- });
- });
- });
-
- // Registry of of encoder/decoder factories, by encoding name.
- /** @type {Object.<string, function({fatal:boolean}): Encoder>} */
- var encoders = {};
- /** @type {Object.<string, function({fatal:boolean}): Decoder>} */
- var decoders = {};
-
- //
- // 6. Indexes
- //
-
- /**
- * @param {number} pointer The |pointer| to search for.
- * @param {(!Array.<?number>|undefined)} index The |index| to search within.
- * @return {?number} The code point corresponding to |pointer| in |index|,
- * or null if |code point| is not in |index|.
- */
- function indexCodePointFor(pointer, index) {
- if (!index) return null;
- return index[pointer] || null;
- }
-
- /**
- * @param {number} code_point The |code point| to search for.
- * @param {!Array.<?number>} index The |index| to search within.
- * @return {?number} The first pointer corresponding to |code point| in
- * |index|, or null if |code point| is not in |index|.
- */
- function indexPointerFor(code_point, index) {
- var pointer = index.indexOf(code_point);
- return pointer === -1 ? null : pointer;
- }
-
- /**
- * @param {string} name Name of the index.
- * @return {(!Array.<number>|!Array.<Array.<number>>)}
- * */
- function index(name) {
- if (!('encoding-indexes' in global)) {
- throw Error("Indexes missing." +
- " Did you forget to include encoding-indexes.js first?");
- }
- return global['encoding-indexes'][name];
- }
-
- /**
- * @param {number} pointer The |pointer| to search for in the gb18030 index.
- * @return {?number} The code point corresponding to |pointer| in |index|,
- * or null if |code point| is not in the gb18030 index.
- */
- function indexGB18030RangesCodePointFor(pointer) {
- // 1. If pointer is greater than 39419 and less than 189000, or
- // pointer is greater than 1237575, return null.
- if ((pointer > 39419 && pointer < 189000) || (pointer > 1237575))
- return null;
-
- // 2. If pointer is 7457, return code point U+E7C7.
- if (pointer === 7457) return 0xE7C7;
-
- // 3. Let offset be the last pointer in index gb18030 ranges that
- // is equal to or less than pointer and let code point offset be
- // its corresponding code point.
- var offset = 0;
- var code_point_offset = 0;
- var idx = index('gb18030-ranges');
- var i;
- for (i = 0; i < idx.length; ++i) {
- /** @type {!Array.<number>} */
- var entry = idx[i];
- if (entry[0] <= pointer) {
- offset = entry[0];
- code_point_offset = entry[1];
- } else {
- break;
- }
- }
-
- // 4. Return a code point whose value is code point offset +
- // pointer − offset.
- return code_point_offset + pointer - offset;
- }
-
- /**
- * @param {number} code_point The |code point| to locate in the gb18030 index.
- * @return {number} The first pointer corresponding to |code point| in the
- * gb18030 index.
- */
- function indexGB18030RangesPointerFor(code_point) {
- // 1. If code point is U+E7C7, return pointer 7457.
- if (code_point === 0xE7C7) return 7457;
-
- // 2. Let offset be the last code point in index gb18030 ranges
- // that is equal to or less than code point and let pointer offset
- // be its corresponding pointer.
- var offset = 0;
- var pointer_offset = 0;
- var idx = index('gb18030-ranges');
- var i;
- for (i = 0; i < idx.length; ++i) {
- /** @type {!Array.<number>} */
- var entry = idx[i];
- if (entry[1] <= code_point) {
- offset = entry[1];
- pointer_offset = entry[0];
- } else {
- break;
- }
- }
-
- // 3. Return a pointer whose value is pointer offset + code point
- // − offset.
- return pointer_offset + code_point - offset;
- }
-
- /**
- * @param {number} code_point The |code_point| to search for in the Shift_JIS
- * index.
- * @return {?number} The code point corresponding to |pointer| in |index|,
- * or null if |code point| is not in the Shift_JIS index.
- */
- function indexShiftJISPointerFor(code_point) {
- // 1. Let index be index jis0208 excluding all entries whose
- // pointer is in the range 8272 to 8835, inclusive.
- shift_jis_index = shift_jis_index ||
- index('jis0208').map(function(code_point, pointer) {
- return inRange(pointer, 8272, 8835) ? null : code_point;
- });
- var index_ = shift_jis_index;
-
- // 2. Return the index pointer for code point in index.
- return index_.indexOf(code_point);
- }
- var shift_jis_index;
-
- /**
- * @param {number} code_point The |code_point| to search for in the big5
- * index.
- * @return {?number} The code point corresponding to |pointer| in |index|,
- * or null if |code point| is not in the big5 index.
- */
- function indexBig5PointerFor(code_point) {
- // 1. Let index be index Big5 excluding all entries whose pointer
- big5_index_no_hkscs = big5_index_no_hkscs ||
- index('big5').map(function(code_point, pointer) {
- return (pointer < (0xA1 - 0x81) * 157) ? null : code_point;
- });
- var index_ = big5_index_no_hkscs;
-
- // 2. If code point is U+2550, U+255E, U+2561, U+256A, U+5341, or
- // U+5345, return the last pointer corresponding to code point in
- // index.
- if (code_point === 0x2550 || code_point === 0x255E ||
- code_point === 0x2561 || code_point === 0x256A ||
- code_point === 0x5341 || code_point === 0x5345) {
- return index_.lastIndexOf(code_point);
- }
-
- // 3. Return the index pointer for code point in index.
- return indexPointerFor(code_point, index_);
- }
- var big5_index_no_hkscs;
-
- //
- // 8. API
- //
-
- /** @const */ var DEFAULT_ENCODING = 'utf-8';
-
- // 8.1 Interface TextDecoder
-
- /**
- * @constructor
- * @param {string=} label The label of the encoding;
- * defaults to 'utf-8'.
- * @param {Object=} options
- */
- function TextDecoder(label, options) {
- // Web IDL conventions
- if (!(this instanceof TextDecoder))
- throw TypeError('Called as a function. Did you forget \'new\'?');
- label = label !== undefined ? String(label) : DEFAULT_ENCODING;
- options = ToDictionary(options);
-
- // A TextDecoder object has an associated encoding, decoder,
- // stream, ignore BOM flag (initially unset), BOM seen flag
- // (initially unset), error mode (initially replacement), and do
- // not flush flag (initially unset).
-
- /** @private */
- this._encoding = null;
- /** @private @type {?Decoder} */
- this._decoder = null;
- /** @private @type {boolean} */
- this._ignoreBOM = false;
- /** @private @type {boolean} */
- this._BOMseen = false;
- /** @private @type {string} */
- this._error_mode = 'replacement';
- /** @private @type {boolean} */
- this._do_not_flush = false;
-
-
- // 1. Let encoding be the result of getting an encoding from
- // label.
- var encoding = getEncoding(label);
-
- // 2. If encoding is failure or replacement, throw a RangeError.
- if (encoding === null || encoding.name === 'replacement')
- throw RangeError('Unknown encoding: ' + label);
- if (!decoders[encoding.name]) {
- throw Error('Decoder not present.' +
- ' Did you forget to include encoding-indexes.js first?');
- }
-
- // 3. Let dec be a new TextDecoder object.
- var dec = this;
-
- // 4. Set dec's encoding to encoding.
- dec._encoding = encoding;
-
- // 5. If options's fatal member is true, set dec's error mode to
- // fatal.
- if (Boolean(options['fatal']))
- dec._error_mode = 'fatal';
-
- // 6. If options's ignoreBOM member is true, set dec's ignore BOM
- // flag.
- if (Boolean(options['ignoreBOM']))
- dec._ignoreBOM = true;
-
- // For pre-ES5 runtimes:
- if (!Object.defineProperty) {
- this.encoding = dec._encoding.name.toLowerCase();
- this.fatal = dec._error_mode === 'fatal';
- this.ignoreBOM = dec._ignoreBOM;
- }
-
- // 7. Return dec.
- return dec;
- }
-
- if (Object.defineProperty) {
- // The encoding attribute's getter must return encoding's name.
- Object.defineProperty(TextDecoder.prototype, 'encoding', {
- /** @this {TextDecoder} */
- get: function() { return this._encoding.name.toLowerCase(); }
- });
-
- // The fatal attribute's getter must return true if error mode
- // is fatal, and false otherwise.
- Object.defineProperty(TextDecoder.prototype, 'fatal', {
- /** @this {TextDecoder} */
- get: function() { return this._error_mode === 'fatal'; }
- });
-
- // The ignoreBOM attribute's getter must return true if ignore
- // BOM flag is set, and false otherwise.
- Object.defineProperty(TextDecoder.prototype, 'ignoreBOM', {
- /** @this {TextDecoder} */
- get: function() { return this._ignoreBOM; }
- });
- }
-
- /**
- * @param {BufferSource=} input The buffer of bytes to decode.
- * @param {Object=} options
- * @return {string} The decoded string.
- */
- TextDecoder.prototype.decode = function decode(input, options) {
- var bytes;
- if (typeof input === 'object' && input instanceof ArrayBuffer) {
- bytes = new Uint8Array(input);
- } else if (typeof input === 'object' && 'buffer' in input &&
- input.buffer instanceof ArrayBuffer) {
- bytes = new Uint8Array(input.buffer,
- input.byteOffset,
- input.byteLength);
- } else {
- bytes = new Uint8Array(0);
- }
-
- options = ToDictionary(options);
-
- // 1. If the do not flush flag is unset, set decoder to a new
- // encoding's decoder, set stream to a new stream, and unset the
- // BOM seen flag.
- if (!this._do_not_flush) {
- this._decoder = decoders[this._encoding.name]({
- fatal: this._error_mode === 'fatal'});
- this._BOMseen = false;
- }
-
- // 2. If options's stream is true, set the do not flush flag, and
- // unset the do not flush flag otherwise.
- this._do_not_flush = Boolean(options['stream']);
-
- // 3. If input is given, push a copy of input to stream.
- // TODO: Align with spec algorithm - maintain stream on instance.
- var input_stream = new Stream(bytes);
-
- // 4. Let output be a new stream.
- var output = [];
-
- /** @type {?(number|!Array.<number>)} */
- var result;
-
- // 5. While true:
- while (true) {
- // 1. Let token be the result of reading from stream.
- var token = input_stream.read();
-
- // 2. If token is end-of-stream and the do not flush flag is
- // set, return output, serialized.
- // TODO: Align with spec algorithm.
- if (token === end_of_stream)
- break;
-
- // 3. Otherwise, run these subsubsteps:
-
- // 1. Let result be the result of processing token for decoder,
- // stream, output, and error mode.
- result = this._decoder.handler(input_stream, token);
-
- // 2. If result is finished, return output, serialized.
- if (result === finished)
- break;
-
- if (result !== null) {
- if (Array.isArray(result))
- output.push.apply(output, /**@type {!Array.<number>}*/(result));
- else
- output.push(result);
- }
-
- // 3. Otherwise, if result is error, throw a TypeError.
- // (Thrown in handler)
-
- // 4. Otherwise, do nothing.
- }
- // TODO: Align with spec algorithm.
- if (!this._do_not_flush) {
- do {
- result = this._decoder.handler(input_stream, input_stream.read());
- if (result === finished)
- break;
- if (result === null)
- continue;
- if (Array.isArray(result))
- output.push.apply(output, /**@type {!Array.<number>}*/(result));
- else
- output.push(result);
- } while (!input_stream.endOfStream());
- this._decoder = null;
- }
-
- // A TextDecoder object also has an associated serialize stream
- // algorithm...
- /**
- * @param {!Array.<number>} stream
- * @return {string}
- * @this {TextDecoder}
- */
- function serializeStream(stream) {
- // 1. Let token be the result of reading from stream.
- // (Done in-place on array, rather than as a stream)
-
- // 2. If encoding is UTF-8, UTF-16BE, or UTF-16LE, and ignore
- // BOM flag and BOM seen flag are unset, run these subsubsteps:
- if (includes(['UTF-8', 'UTF-16LE', 'UTF-16BE'], this._encoding.name) &&
- !this._ignoreBOM && !this._BOMseen) {
- if (stream.length > 0 && stream[0] === 0xFEFF) {
- // 1. If token is U+FEFF, set BOM seen flag.
- this._BOMseen = true;
- stream.shift();
- } else if (stream.length > 0) {
- // 2. Otherwise, if token is not end-of-stream, set BOM seen
- // flag and append token to stream.
- this._BOMseen = true;
- } else {
- // 3. Otherwise, if token is not end-of-stream, append token
- // to output.
- // (no-op)
- }
- }
- // 4. Otherwise, return output.
- return codePointsToString(stream);
- }
-
- return serializeStream.call(this, output);
- };
-
- // 8.2 Interface TextEncoder
-
- /**
- * @constructor
- * @param {string=} label The label of the encoding. NONSTANDARD.
- * @param {Object=} options NONSTANDARD.
- */
- function TextEncoder(label, options) {
- // Web IDL conventions
- if (!(this instanceof TextEncoder))
- throw TypeError('Called as a function. Did you forget \'new\'?');
- options = ToDictionary(options);
-
- // A TextEncoder object has an associated encoding and encoder.
-
- /** @private */
- this._encoding = null;
- /** @private @type {?Encoder} */
- this._encoder = null;
-
- // Non-standard
- /** @private @type {boolean} */
- this._do_not_flush = false;
- /** @private @type {string} */
- this._fatal = Boolean(options['fatal']) ? 'fatal' : 'replacement';
-
- // 1. Let enc be a new TextEncoder object.
- var enc = this;
-
- // 2. Set enc's encoding to UTF-8's encoder.
- if (Boolean(options['NONSTANDARD_allowLegacyEncoding'])) {
- // NONSTANDARD behavior.
- label = label !== undefined ? String(label) : DEFAULT_ENCODING;
- var encoding = getEncoding(label);
- if (encoding === null || encoding.name === 'replacement')
- throw RangeError('Unknown encoding: ' + label);
- if (!encoders[encoding.name]) {
- throw Error('Encoder not present.' +
- ' Did you forget to include encoding-indexes.js first?');
- }
- enc._encoding = encoding;
- } else {
- // Standard behavior.
- enc._encoding = getEncoding('utf-8');
-
- if (label !== undefined && 'console' in global) {
- console.warn('TextEncoder constructor called with encoding label, '
- + 'which is ignored.');
- }
- }
-
- // For pre-ES5 runtimes:
- if (!Object.defineProperty)
- this.encoding = enc._encoding.name.toLowerCase();
-
- // 3. Return enc.
- return enc;
- }
-
- if (Object.defineProperty) {
- // The encoding attribute's getter must return encoding's name.
- Object.defineProperty(TextEncoder.prototype, 'encoding', {
- /** @this {TextEncoder} */
- get: function() { return this._encoding.name.toLowerCase(); }
- });
- }
-
- /**
- * @param {string=} opt_string The string to encode.
- * @param {Object=} options
- * @return {!Uint8Array} Encoded bytes, as a Uint8Array.
- */
- TextEncoder.prototype.encode = function encode(opt_string, options) {
- opt_string = opt_string === undefined ? '' : String(opt_string);
- options = ToDictionary(options);
-
- // NOTE: This option is nonstandard. None of the encodings
- // permitted for encoding (i.e. UTF-8, UTF-16) are stateful when
- // the input is a USVString so streaming is not necessary.
- if (!this._do_not_flush)
- this._encoder = encoders[this._encoding.name]({
- fatal: this._fatal === 'fatal'});
- this._do_not_flush = Boolean(options['stream']);
-
- // 1. Convert input to a stream.
- var input = new Stream(stringToCodePoints(opt_string));
-
- // 2. Let output be a new stream
- var output = [];
-
- /** @type {?(number|!Array.<number>)} */
- var result;
- // 3. While true, run these substeps:
- while (true) {
- // 1. Let token be the result of reading from input.
- var token = input.read();
- if (token === end_of_stream)
- break;
- // 2. Let result be the result of processing token for encoder,
- // input, output.
- result = this._encoder.handler(input, token);
- if (result === finished)
- break;
- if (Array.isArray(result))
- output.push.apply(output, /**@type {!Array.<number>}*/(result));
- else
- output.push(result);
- }
- // TODO: Align with spec algorithm.
- if (!this._do_not_flush) {
- while (true) {
- result = this._encoder.handler(input, input.read());
- if (result === finished)
- break;
- if (Array.isArray(result))
- output.push.apply(output, /**@type {!Array.<number>}*/(result));
- else
- output.push(result);
- }
- this._encoder = null;
- }
- // 3. If result is finished, convert output into a byte sequence,
- // and then return a Uint8Array object wrapping an ArrayBuffer
- // containing output.
- return new Uint8Array(output);
- };
-
-
- //
- // 9. The encoding
- //
-
- // 9.1 utf-8
-
- // 9.1.1 utf-8 decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {{fatal: boolean}} options
- */
- function UTF8Decoder(options) {
- var fatal = options.fatal;
-
- // utf-8's decoder's has an associated utf-8 code point, utf-8
- // bytes seen, and utf-8 bytes needed (all initially 0), a utf-8
- // lower boundary (initially 0x80), and a utf-8 upper boundary
- // (initially 0xBF).
- var /** @type {number} */ utf8_code_point = 0,
- /** @type {number} */ utf8_bytes_seen = 0,
- /** @type {number} */ utf8_bytes_needed = 0,
- /** @type {number} */ utf8_lower_boundary = 0x80,
- /** @type {number} */ utf8_upper_boundary = 0xBF;
-
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // 1. If byte is end-of-stream and utf-8 bytes needed is not 0,
- // set utf-8 bytes needed to 0 and return error.
- if (bite === end_of_stream && utf8_bytes_needed !== 0) {
- utf8_bytes_needed = 0;
- return decoderError(fatal);
- }
-
- // 2. If byte is end-of-stream, return finished.
- if (bite === end_of_stream)
- return finished;
-
- // 3. If utf-8 bytes needed is 0, based on byte:
- if (utf8_bytes_needed === 0) {
-
- // 0x00 to 0x7F
- if (inRange(bite, 0x00, 0x7F)) {
- // Return a code point whose value is byte.
- return bite;
- }
-
- // 0xC2 to 0xDF
- else if (inRange(bite, 0xC2, 0xDF)) {
- // 1. Set utf-8 bytes needed to 1.
- utf8_bytes_needed = 1;
-
- // 2. Set UTF-8 code point to byte & 0x1F.
- utf8_code_point = bite & 0x1F;
- }
-
- // 0xE0 to 0xEF
- else if (inRange(bite, 0xE0, 0xEF)) {
- // 1. If byte is 0xE0, set utf-8 lower boundary to 0xA0.
- if (bite === 0xE0)
- utf8_lower_boundary = 0xA0;
- // 2. If byte is 0xED, set utf-8 upper boundary to 0x9F.
- if (bite === 0xED)
- utf8_upper_boundary = 0x9F;
- // 3. Set utf-8 bytes needed to 2.
- utf8_bytes_needed = 2;
- // 4. Set UTF-8 code point to byte & 0xF.
- utf8_code_point = bite & 0xF;
- }
-
- // 0xF0 to 0xF4
- else if (inRange(bite, 0xF0, 0xF4)) {
- // 1. If byte is 0xF0, set utf-8 lower boundary to 0x90.
- if (bite === 0xF0)
- utf8_lower_boundary = 0x90;
- // 2. If byte is 0xF4, set utf-8 upper boundary to 0x8F.
- if (bite === 0xF4)
- utf8_upper_boundary = 0x8F;
- // 3. Set utf-8 bytes needed to 3.
- utf8_bytes_needed = 3;
- // 4. Set UTF-8 code point to byte & 0x7.
- utf8_code_point = bite & 0x7;
- }
-
- // Otherwise
- else {
- // Return error.
- return decoderError(fatal);
- }
-
- // Return continue.
- return null;
- }
-
- // 4. If byte is not in the range utf-8 lower boundary to utf-8
- // upper boundary, inclusive, run these substeps:
- if (!inRange(bite, utf8_lower_boundary, utf8_upper_boundary)) {
-
- // 1. Set utf-8 code point, utf-8 bytes needed, and utf-8
- // bytes seen to 0, set utf-8 lower boundary to 0x80, and set
- // utf-8 upper boundary to 0xBF.
- utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0;
- utf8_lower_boundary = 0x80;
- utf8_upper_boundary = 0xBF;
-
- // 2. Prepend byte to stream.
- stream.prepend(bite);
-
- // 3. Return error.
- return decoderError(fatal);
- }
-
- // 5. Set utf-8 lower boundary to 0x80 and utf-8 upper boundary
- // to 0xBF.
- utf8_lower_boundary = 0x80;
- utf8_upper_boundary = 0xBF;
-
- // 6. Set UTF-8 code point to (UTF-8 code point << 6) | (byte &
- // 0x3F)
- utf8_code_point = (utf8_code_point << 6) | (bite & 0x3F);
-
- // 7. Increase utf-8 bytes seen by one.
- utf8_bytes_seen += 1;
-
- // 8. If utf-8 bytes seen is not equal to utf-8 bytes needed,
- // continue.
- if (utf8_bytes_seen !== utf8_bytes_needed)
- return null;
-
- // 9. Let code point be utf-8 code point.
- var code_point = utf8_code_point;
-
- // 10. Set utf-8 code point, utf-8 bytes needed, and utf-8 bytes
- // seen to 0.
- utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0;
-
- // 11. Return a code point whose value is code point.
- return code_point;
- };
- }
-
- // 9.1.2 utf-8 encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {{fatal: boolean}} options
- */
- function UTF8Encoder(options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1. If code point is end-of-stream, return finished.
- if (code_point === end_of_stream)
- return finished;
-
- // 2. If code point is an ASCII code point, return a byte whose
- // value is code point.
- if (isASCIICodePoint(code_point))
- return code_point;
-
- // 3. Set count and offset based on the range code point is in:
- var count, offset;
- // U+0080 to U+07FF, inclusive:
- if (inRange(code_point, 0x0080, 0x07FF)) {
- // 1 and 0xC0
- count = 1;
- offset = 0xC0;
- }
- // U+0800 to U+FFFF, inclusive:
- else if (inRange(code_point, 0x0800, 0xFFFF)) {
- // 2 and 0xE0
- count = 2;
- offset = 0xE0;
- }
- // U+10000 to U+10FFFF, inclusive:
- else if (inRange(code_point, 0x10000, 0x10FFFF)) {
- // 3 and 0xF0
- count = 3;
- offset = 0xF0;
- }
-
- // 4. Let bytes be a byte sequence whose first byte is (code
- // point >> (6 × count)) + offset.
- var bytes = [(code_point >> (6 * count)) + offset];
-
- // 5. Run these substeps while count is greater than 0:
- while (count > 0) {
-
- // 1. Set temp to code point >> (6 × (count − 1)).
- var temp = code_point >> (6 * (count - 1));
-
- // 2. Append to bytes 0x80 | (temp & 0x3F).
- bytes.push(0x80 | (temp & 0x3F));
-
- // 3. Decrease count by one.
- count -= 1;
- }
-
- // 6. Return bytes bytes, in order.
- return bytes;
- };
- }
-
- /** @param {{fatal: boolean}} options */
- encoders['UTF-8'] = function(options) {
- return new UTF8Encoder(options);
- };
- /** @param {{fatal: boolean}} options */
- decoders['UTF-8'] = function(options) {
- return new UTF8Decoder(options);
- };
-
- //
- // 10. Legacy single-byte encodings
- //
-
- // 10.1 single-byte decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {!Array.<number>} index The encoding index.
- * @param {{fatal: boolean}} options
- */
- function SingleByteDecoder(index, options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // 1. If byte is end-of-stream, return finished.
- if (bite === end_of_stream)
- return finished;
-
- // 2. If byte is an ASCII byte, return a code point whose value
- // is byte.
- if (isASCIIByte(bite))
- return bite;
-
- // 3. Let code point be the index code point for byte − 0x80 in
- // index single-byte.
- var code_point = index[bite - 0x80];
-
- // 4. If code point is null, return error.
- if (code_point === null)
- return decoderError(fatal);
-
- // 5. Return a code point whose value is code point.
- return code_point;
- };
- }
-
- // 10.2 single-byte encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {!Array.<?number>} index The encoding index.
- * @param {{fatal: boolean}} options
- */
- function SingleByteEncoder(index, options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1. If code point is end-of-stream, return finished.
- if (code_point === end_of_stream)
- return finished;
-
- // 2. If code point is an ASCII code point, return a byte whose
- // value is code point.
- if (isASCIICodePoint(code_point))
- return code_point;
-
- // 3. Let pointer be the index pointer for code point in index
- // single-byte.
- var pointer = indexPointerFor(code_point, index);
-
- // 4. If pointer is null, return error with code point.
- if (pointer === null)
- encoderError(code_point);
-
- // 5. Return a byte whose value is pointer + 0x80.
- return pointer + 0x80;
- };
- }
-
- (function() {
- if (!('encoding-indexes' in global))
- return;
- encodings.forEach(function(category) {
- if (category.heading !== 'Legacy single-byte encodings')
- return;
- category.encodings.forEach(function(encoding) {
- var name = encoding.name;
- var idx = index(name.toLowerCase());
- /** @param {{fatal: boolean}} options */
- decoders[name] = function(options) {
- return new SingleByteDecoder(idx, options);
- };
- /** @param {{fatal: boolean}} options */
- encoders[name] = function(options) {
- return new SingleByteEncoder(idx, options);
- };
- });
- });
- }());
-
- //
- // 11. Legacy multi-byte Chinese (simplified) encodings
- //
-
- // 11.1 gbk
-
- // 11.1.1 gbk decoder
- // gbk's decoder is gb18030's decoder.
- /** @param {{fatal: boolean}} options */
- decoders['GBK'] = function(options) {
- return new GB18030Decoder(options);
- };
-
- // 11.1.2 gbk encoder
- // gbk's encoder is gb18030's encoder with its gbk flag set.
- /** @param {{fatal: boolean}} options */
- encoders['GBK'] = function(options) {
- return new GB18030Encoder(options, true);
- };
-
- // 11.2 gb18030
-
- // 11.2.1 gb18030 decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {{fatal: boolean}} options
- */
- function GB18030Decoder(options) {
- var fatal = options.fatal;
- // gb18030's decoder has an associated gb18030 first, gb18030
- // second, and gb18030 third (all initially 0x00).
- var /** @type {number} */ gb18030_first = 0x00,
- /** @type {number} */ gb18030_second = 0x00,
- /** @type {number} */ gb18030_third = 0x00;
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // 1. If byte is end-of-stream and gb18030 first, gb18030
- // second, and gb18030 third are 0x00, return finished.
- if (bite === end_of_stream && gb18030_first === 0x00 &&
- gb18030_second === 0x00 && gb18030_third === 0x00) {
- return finished;
- }
- // 2. If byte is end-of-stream, and gb18030 first, gb18030
- // second, or gb18030 third is not 0x00, set gb18030 first,
- // gb18030 second, and gb18030 third to 0x00, and return error.
- if (bite === end_of_stream &&
- (gb18030_first !== 0x00 || gb18030_second !== 0x00 ||
- gb18030_third !== 0x00)) {
- gb18030_first = 0x00;
- gb18030_second = 0x00;
- gb18030_third = 0x00;
- decoderError(fatal);
- }
- var code_point;
- // 3. If gb18030 third is not 0x00, run these substeps:
- if (gb18030_third !== 0x00) {
- // 1. Let code point be null.
- code_point = null;
- // 2. If byte is in the range 0x30 to 0x39, inclusive, set
- // code point to the index gb18030 ranges code point for
- // (((gb18030 first − 0x81) × 10 + gb18030 second − 0x30) ×
- // 126 + gb18030 third − 0x81) × 10 + byte − 0x30.
- if (inRange(bite, 0x30, 0x39)) {
- code_point = indexGB18030RangesCodePointFor(
- (((gb18030_first - 0x81) * 10 + gb18030_second - 0x30) * 126 +
- gb18030_third - 0x81) * 10 + bite - 0x30);
- }
-
- // 3. Let buffer be a byte sequence consisting of gb18030
- // second, gb18030 third, and byte, in order.
- var buffer = [gb18030_second, gb18030_third, bite];
-
- // 4. Set gb18030 first, gb18030 second, and gb18030 third to
- // 0x00.
- gb18030_first = 0x00;
- gb18030_second = 0x00;
- gb18030_third = 0x00;
-
- // 5. If code point is null, prepend buffer to stream and
- // return error.
- if (code_point === null) {
- stream.prepend(buffer);
- return decoderError(fatal);
- }
-
- // 6. Return a code point whose value is code point.
- return code_point;
- }
-
- // 4. If gb18030 second is not 0x00, run these substeps:
- if (gb18030_second !== 0x00) {
-
- // 1. If byte is in the range 0x81 to 0xFE, inclusive, set
- // gb18030 third to byte and return continue.
- if (inRange(bite, 0x81, 0xFE)) {
- gb18030_third = bite;
- return null;
- }
-
- // 2. Prepend gb18030 second followed by byte to stream, set
- // gb18030 first and gb18030 second to 0x00, and return error.
- stream.prepend([gb18030_second, bite]);
- gb18030_first = 0x00;
- gb18030_second = 0x00;
- return decoderError(fatal);
- }
-
- // 5. If gb18030 first is not 0x00, run these substeps:
- if (gb18030_first !== 0x00) {
-
- // 1. If byte is in the range 0x30 to 0x39, inclusive, set
- // gb18030 second to byte and return continue.
- if (inRange(bite, 0x30, 0x39)) {
- gb18030_second = bite;
- return null;
- }
-
- // 2. Let lead be gb18030 first, let pointer be null, and set
- // gb18030 first to 0x00.
- var lead = gb18030_first;
- var pointer = null;
- gb18030_first = 0x00;
-
- // 3. Let offset be 0x40 if byte is less than 0x7F and 0x41
- // otherwise.
- var offset = bite < 0x7F ? 0x40 : 0x41;
-
- // 4. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80
- // to 0xFE, inclusive, set pointer to (lead − 0x81) × 190 +
- // (byte − offset).
- if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFE))
- pointer = (lead - 0x81) * 190 + (bite - offset);
-
- // 5. Let code point be null if pointer is null and the index
- // code point for pointer in index gb18030 otherwise.
- code_point = pointer === null ? null :
- indexCodePointFor(pointer, index('gb18030'));
-
- // 6. If code point is null and byte is an ASCII byte, prepend
- // byte to stream.
- if (code_point === null && isASCIIByte(bite))
- stream.prepend(bite);
-
- // 7. If code point is null, return error.
- if (code_point === null)
- return decoderError(fatal);
-
- // 8. Return a code point whose value is code point.
- return code_point;
- }
-
- // 6. If byte is an ASCII byte, return a code point whose value
- // is byte.
- if (isASCIIByte(bite))
- return bite;
-
- // 7. If byte is 0x80, return code point U+20AC.
- if (bite === 0x80)
- return 0x20AC;
-
- // 8. If byte is in the range 0x81 to 0xFE, inclusive, set
- // gb18030 first to byte and return continue.
- if (inRange(bite, 0x81, 0xFE)) {
- gb18030_first = bite;
- return null;
- }
-
- // 9. Return error.
- return decoderError(fatal);
- };
- }
-
- // 11.2.2 gb18030 encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {{fatal: boolean}} options
- * @param {boolean=} gbk_flag
- */
- function GB18030Encoder(options, gbk_flag) {
- var fatal = options.fatal;
- // gb18030's decoder has an associated gbk flag (initially unset).
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1. If code point is end-of-stream, return finished.
- if (code_point === end_of_stream)
- return finished;
-
- // 2. If code point is an ASCII code point, return a byte whose
- // value is code point.
- if (isASCIICodePoint(code_point))
- return code_point;
-
- // 3. If code point is U+E5E5, return error with code point.
- if (code_point === 0xE5E5)
- return encoderError(code_point);
-
- // 4. If the gbk flag is set and code point is U+20AC, return
- // byte 0x80.
- if (gbk_flag && code_point === 0x20AC)
- return 0x80;
-
- // 5. Let pointer be the index pointer for code point in index
- // gb18030.
- var pointer = indexPointerFor(code_point, index('gb18030'));
-
- // 6. If pointer is not null, run these substeps:
- if (pointer !== null) {
-
- // 1. Let lead be floor(pointer / 190) + 0x81.
- var lead = floor(pointer / 190) + 0x81;
-
- // 2. Let trail be pointer % 190.
- var trail = pointer % 190;
-
- // 3. Let offset be 0x40 if trail is less than 0x3F and 0x41 otherwise.
- var offset = trail < 0x3F ? 0x40 : 0x41;
-
- // 4. Return two bytes whose values are lead and trail + offset.
- return [lead, trail + offset];
- }
-
- // 7. If gbk flag is set, return error with code point.
- if (gbk_flag)
- return encoderError(code_point);
-
- // 8. Set pointer to the index gb18030 ranges pointer for code
- // point.
- pointer = indexGB18030RangesPointerFor(code_point);
-
- // 9. Let byte1 be floor(pointer / 10 / 126 / 10).
- var byte1 = floor(pointer / 10 / 126 / 10);
-
- // 10. Set pointer to pointer − byte1 × 10 × 126 × 10.
- pointer = pointer - byte1 * 10 * 126 * 10;
-
- // 11. Let byte2 be floor(pointer / 10 / 126).
- var byte2 = floor(pointer / 10 / 126);
-
- // 12. Set pointer to pointer − byte2 × 10 × 126.
- pointer = pointer - byte2 * 10 * 126;
-
- // 13. Let byte3 be floor(pointer / 10).
- var byte3 = floor(pointer / 10);
-
- // 14. Let byte4 be pointer − byte3 × 10.
- var byte4 = pointer - byte3 * 10;
-
- // 15. Return four bytes whose values are byte1 + 0x81, byte2 +
- // 0x30, byte3 + 0x81, byte4 + 0x30.
- return [byte1 + 0x81,
- byte2 + 0x30,
- byte3 + 0x81,
- byte4 + 0x30];
- };
- }
-
- /** @param {{fatal: boolean}} options */
- encoders['gb18030'] = function(options) {
- return new GB18030Encoder(options);
- };
- /** @param {{fatal: boolean}} options */
- decoders['gb18030'] = function(options) {
- return new GB18030Decoder(options);
- };
-
-
- //
- // 12. Legacy multi-byte Chinese (traditional) encodings
- //
-
- // 12.1 Big5
-
- // 12.1.1 Big5 decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {{fatal: boolean}} options
- */
- function Big5Decoder(options) {
- var fatal = options.fatal;
- // Big5's decoder has an associated Big5 lead (initially 0x00).
- var /** @type {number} */ Big5_lead = 0x00;
-
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // 1. If byte is end-of-stream and Big5 lead is not 0x00, set
- // Big5 lead to 0x00 and return error.
- if (bite === end_of_stream && Big5_lead !== 0x00) {
- Big5_lead = 0x00;
- return decoderError(fatal);
- }
-
- // 2. If byte is end-of-stream and Big5 lead is 0x00, return
- // finished.
- if (bite === end_of_stream && Big5_lead === 0x00)
- return finished;
-
- // 3. If Big5 lead is not 0x00, let lead be Big5 lead, let
- // pointer be null, set Big5 lead to 0x00, and then run these
- // substeps:
- if (Big5_lead !== 0x00) {
- var lead = Big5_lead;
- var pointer = null;
- Big5_lead = 0x00;
-
- // 1. Let offset be 0x40 if byte is less than 0x7F and 0x62
- // otherwise.
- var offset = bite < 0x7F ? 0x40 : 0x62;
-
- // 2. If byte is in the range 0x40 to 0x7E, inclusive, or 0xA1
- // to 0xFE, inclusive, set pointer to (lead − 0x81) × 157 +
- // (byte − offset).
- if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0xA1, 0xFE))
- pointer = (lead - 0x81) * 157 + (bite - offset);
-
- // 3. If there is a row in the table below whose first column
- // is pointer, return the two code points listed in its second
- // column
- // Pointer | Code points
- // --------+--------------
- // 1133 | U+00CA U+0304
- // 1135 | U+00CA U+030C
- // 1164 | U+00EA U+0304
- // 1166 | U+00EA U+030C
- switch (pointer) {
- case 1133: return [0x00CA, 0x0304];
- case 1135: return [0x00CA, 0x030C];
- case 1164: return [0x00EA, 0x0304];
- case 1166: return [0x00EA, 0x030C];
- }
-
- // 4. Let code point be null if pointer is null and the index
- // code point for pointer in index Big5 otherwise.
- var code_point = (pointer === null) ? null :
- indexCodePointFor(pointer, index('big5'));
-
- // 5. If code point is null and byte is an ASCII byte, prepend
- // byte to stream.
- if (code_point === null && isASCIIByte(bite))
- stream.prepend(bite);
-
- // 6. If code point is null, return error.
- if (code_point === null)
- return decoderError(fatal);
-
- // 7. Return a code point whose value is code point.
- return code_point;
- }
-
- // 4. If byte is an ASCII byte, return a code point whose value
- // is byte.
- if (isASCIIByte(bite))
- return bite;
-
- // 5. If byte is in the range 0x81 to 0xFE, inclusive, set Big5
- // lead to byte and return continue.
- if (inRange(bite, 0x81, 0xFE)) {
- Big5_lead = bite;
- return null;
- }
-
- // 6. Return error.
- return decoderError(fatal);
- };
- }
-
- // 12.1.2 Big5 encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {{fatal: boolean}} options
- */
- function Big5Encoder(options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1. If code point is end-of-stream, return finished.
- if (code_point === end_of_stream)
- return finished;
-
- // 2. If code point is an ASCII code point, return a byte whose
- // value is code point.
- if (isASCIICodePoint(code_point))
- return code_point;
-
- // 3. Let pointer be the index Big5 pointer for code point.
- var pointer = indexBig5PointerFor(code_point);
-
- // 4. If pointer is null, return error with code point.
- if (pointer === null)
- return encoderError(code_point);
-
- // 5. Let lead be floor(pointer / 157) + 0x81.
- var lead = floor(pointer / 157) + 0x81;
-
- // 6. If lead is less than 0xA1, return error with code point.
- if (lead < 0xA1)
- return encoderError(code_point);
-
- // 7. Let trail be pointer % 157.
- var trail = pointer % 157;
-
- // 8. Let offset be 0x40 if trail is less than 0x3F and 0x62
- // otherwise.
- var offset = trail < 0x3F ? 0x40 : 0x62;
-
- // Return two bytes whose values are lead and trail + offset.
- return [lead, trail + offset];
- };
- }
-
- /** @param {{fatal: boolean}} options */
- encoders['Big5'] = function(options) {
- return new Big5Encoder(options);
- };
- /** @param {{fatal: boolean}} options */
- decoders['Big5'] = function(options) {
- return new Big5Decoder(options);
- };
-
-
- //
- // 13. Legacy multi-byte Japanese encodings
- //
-
- // 13.1 euc-jp
-
- // 13.1.1 euc-jp decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {{fatal: boolean}} options
- */
- function EUCJPDecoder(options) {
- var fatal = options.fatal;
-
- // euc-jp's decoder has an associated euc-jp jis0212 flag
- // (initially unset) and euc-jp lead (initially 0x00).
- var /** @type {boolean} */ eucjp_jis0212_flag = false,
- /** @type {number} */ eucjp_lead = 0x00;
-
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // 1. If byte is end-of-stream and euc-jp lead is not 0x00, set
- // euc-jp lead to 0x00, and return error.
- if (bite === end_of_stream && eucjp_lead !== 0x00) {
- eucjp_lead = 0x00;
- return decoderError(fatal);
- }
-
- // 2. If byte is end-of-stream and euc-jp lead is 0x00, return
- // finished.
- if (bite === end_of_stream && eucjp_lead === 0x00)
- return finished;
-
- // 3. If euc-jp lead is 0x8E and byte is in the range 0xA1 to
- // 0xDF, inclusive, set euc-jp lead to 0x00 and return a code
- // point whose value is 0xFF61 − 0xA1 + byte.
- if (eucjp_lead === 0x8E && inRange(bite, 0xA1, 0xDF)) {
- eucjp_lead = 0x00;
- return 0xFF61 - 0xA1 + bite;
- }
-
- // 4. If euc-jp lead is 0x8F and byte is in the range 0xA1 to
- // 0xFE, inclusive, set the euc-jp jis0212 flag, set euc-jp lead
- // to byte, and return continue.
- if (eucjp_lead === 0x8F && inRange(bite, 0xA1, 0xFE)) {
- eucjp_jis0212_flag = true;
- eucjp_lead = bite;
- return null;
- }
-
- // 5. If euc-jp lead is not 0x00, let lead be euc-jp lead, set
- // euc-jp lead to 0x00, and run these substeps:
- if (eucjp_lead !== 0x00) {
- var lead = eucjp_lead;
- eucjp_lead = 0x00;
-
- // 1. Let code point be null.
- var code_point = null;
-
- // 2. If lead and byte are both in the range 0xA1 to 0xFE,
- // inclusive, set code point to the index code point for (lead
- // − 0xA1) × 94 + byte − 0xA1 in index jis0208 if the euc-jp
- // jis0212 flag is unset and in index jis0212 otherwise.
- if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) {
- code_point = indexCodePointFor(
- (lead - 0xA1) * 94 + (bite - 0xA1),
- index(!eucjp_jis0212_flag ? 'jis0208' : 'jis0212'));
- }
-
- // 3. Unset the euc-jp jis0212 flag.
- eucjp_jis0212_flag = false;
-
- // 4. If byte is not in the range 0xA1 to 0xFE, inclusive,
- // prepend byte to stream.
- if (!inRange(bite, 0xA1, 0xFE))
- stream.prepend(bite);
-
- // 5. If code point is null, return error.
- if (code_point === null)
- return decoderError(fatal);
-
- // 6. Return a code point whose value is code point.
- return code_point;
- }
-
- // 6. If byte is an ASCII byte, return a code point whose value
- // is byte.
- if (isASCIIByte(bite))
- return bite;
-
- // 7. If byte is 0x8E, 0x8F, or in the range 0xA1 to 0xFE,
- // inclusive, set euc-jp lead to byte and return continue.
- if (bite === 0x8E || bite === 0x8F || inRange(bite, 0xA1, 0xFE)) {
- eucjp_lead = bite;
- return null;
- }
-
- // 8. Return error.
- return decoderError(fatal);
- };
- }
-
- // 13.1.2 euc-jp encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {{fatal: boolean}} options
- */
- function EUCJPEncoder(options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1. If code point is end-of-stream, return finished.
- if (code_point === end_of_stream)
- return finished;
-
- // 2. If code point is an ASCII code point, return a byte whose
- // value is code point.
- if (isASCIICodePoint(code_point))
- return code_point;
-
- // 3. If code point is U+00A5, return byte 0x5C.
- if (code_point === 0x00A5)
- return 0x5C;
-
- // 4. If code point is U+203E, return byte 0x7E.
- if (code_point === 0x203E)
- return 0x7E;
-
- // 5. If code point is in the range U+FF61 to U+FF9F, inclusive,
- // return two bytes whose values are 0x8E and code point −
- // 0xFF61 + 0xA1.
- if (inRange(code_point, 0xFF61, 0xFF9F))
- return [0x8E, code_point - 0xFF61 + 0xA1];
-
- // 6. If code point is U+2212, set it to U+FF0D.
- if (code_point === 0x2212)
- code_point = 0xFF0D;
-
- // 7. Let pointer be the index pointer for code point in index
- // jis0208.
- var pointer = indexPointerFor(code_point, index('jis0208'));
-
- // 8. If pointer is null, return error with code point.
- if (pointer === null)
- return encoderError(code_point);
-
- // 9. Let lead be floor(pointer / 94) + 0xA1.
- var lead = floor(pointer / 94) + 0xA1;
-
- // 10. Let trail be pointer % 94 + 0xA1.
- var trail = pointer % 94 + 0xA1;
-
- // 11. Return two bytes whose values are lead and trail.
- return [lead, trail];
- };
- }
-
- /** @param {{fatal: boolean}} options */
- encoders['EUC-JP'] = function(options) {
- return new EUCJPEncoder(options);
- };
- /** @param {{fatal: boolean}} options */
- decoders['EUC-JP'] = function(options) {
- return new EUCJPDecoder(options);
- };
-
- // 13.2 iso-2022-jp
-
- // 13.2.1 iso-2022-jp decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {{fatal: boolean}} options
- */
- function ISO2022JPDecoder(options) {
- var fatal = options.fatal;
- /** @enum */
- var states = {
- ASCII: 0,
- Roman: 1,
- Katakana: 2,
- LeadByte: 3,
- TrailByte: 4,
- EscapeStart: 5,
- Escape: 6
- };
- // iso-2022-jp's decoder has an associated iso-2022-jp decoder
- // state (initially ASCII), iso-2022-jp decoder output state
- // (initially ASCII), iso-2022-jp lead (initially 0x00), and
- // iso-2022-jp output flag (initially unset).
- var /** @type {number} */ iso2022jp_decoder_state = states.ASCII,
- /** @type {number} */ iso2022jp_decoder_output_state = states.ASCII,
- /** @type {number} */ iso2022jp_lead = 0x00,
- /** @type {boolean} */ iso2022jp_output_flag = false;
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // switching on iso-2022-jp decoder state:
- switch (iso2022jp_decoder_state) {
- default:
- case states.ASCII:
- // ASCII
- // Based on byte:
-
- // 0x1B
- if (bite === 0x1B) {
- // Set iso-2022-jp decoder state to escape start and return
- // continue.
- iso2022jp_decoder_state = states.EscapeStart;
- return null;
- }
-
- // 0x00 to 0x7F, excluding 0x0E, 0x0F, and 0x1B
- if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E
- && bite !== 0x0F && bite !== 0x1B) {
- // Unset the iso-2022-jp output flag and return a code point
- // whose value is byte.
- iso2022jp_output_flag = false;
- return bite;
- }
-
- // end-of-stream
- if (bite === end_of_stream) {
- // Return finished.
- return finished;
- }
-
- // Otherwise
- // Unset the iso-2022-jp output flag and return error.
- iso2022jp_output_flag = false;
- return decoderError(fatal);
-
- case states.Roman:
- // Roman
- // Based on byte:
-
- // 0x1B
- if (bite === 0x1B) {
- // Set iso-2022-jp decoder state to escape start and return
- // continue.
- iso2022jp_decoder_state = states.EscapeStart;
- return null;
- }
-
- // 0x5C
- if (bite === 0x5C) {
- // Unset the iso-2022-jp output flag and return code point
- // U+00A5.
- iso2022jp_output_flag = false;
- return 0x00A5;
- }
-
- // 0x7E
- if (bite === 0x7E) {
- // Unset the iso-2022-jp output flag and return code point
- // U+203E.
- iso2022jp_output_flag = false;
- return 0x203E;
- }
-
- // 0x00 to 0x7F, excluding 0x0E, 0x0F, 0x1B, 0x5C, and 0x7E
- if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E && bite !== 0x0F
- && bite !== 0x1B && bite !== 0x5C && bite !== 0x7E) {
- // Unset the iso-2022-jp output flag and return a code point
- // whose value is byte.
- iso2022jp_output_flag = false;
- return bite;
- }
-
- // end-of-stream
- if (bite === end_of_stream) {
- // Return finished.
- return finished;
- }
-
- // Otherwise
- // Unset the iso-2022-jp output flag and return error.
- iso2022jp_output_flag = false;
- return decoderError(fatal);
-
- case states.Katakana:
- // Katakana
- // Based on byte:
-
- // 0x1B
- if (bite === 0x1B) {
- // Set iso-2022-jp decoder state to escape start and return
- // continue.
- iso2022jp_decoder_state = states.EscapeStart;
- return null;
- }
-
- // 0x21 to 0x5F
- if (inRange(bite, 0x21, 0x5F)) {
- // Unset the iso-2022-jp output flag and return a code point
- // whose value is 0xFF61 − 0x21 + byte.
- iso2022jp_output_flag = false;
- return 0xFF61 - 0x21 + bite;
- }
-
- // end-of-stream
- if (bite === end_of_stream) {
- // Return finished.
- return finished;
- }
-
- // Otherwise
- // Unset the iso-2022-jp output flag and return error.
- iso2022jp_output_flag = false;
- return decoderError(fatal);
-
- case states.LeadByte:
- // Lead byte
- // Based on byte:
-
- // 0x1B
- if (bite === 0x1B) {
- // Set iso-2022-jp decoder state to escape start and return
- // continue.
- iso2022jp_decoder_state = states.EscapeStart;
- return null;
- }
-
- // 0x21 to 0x7E
- if (inRange(bite, 0x21, 0x7E)) {
- // Unset the iso-2022-jp output flag, set iso-2022-jp lead
- // to byte, iso-2022-jp decoder state to trail byte, and
- // return continue.
- iso2022jp_output_flag = false;
- iso2022jp_lead = bite;
- iso2022jp_decoder_state = states.TrailByte;
- return null;
- }
-
- // end-of-stream
- if (bite === end_of_stream) {
- // Return finished.
- return finished;
- }
-
- // Otherwise
- // Unset the iso-2022-jp output flag and return error.
- iso2022jp_output_flag = false;
- return decoderError(fatal);
-
- case states.TrailByte:
- // Trail byte
- // Based on byte:
-
- // 0x1B
- if (bite === 0x1B) {
- // Set iso-2022-jp decoder state to escape start and return
- // continue.
- iso2022jp_decoder_state = states.EscapeStart;
- return decoderError(fatal);
- }
-
- // 0x21 to 0x7E
- if (inRange(bite, 0x21, 0x7E)) {
- // 1. Set the iso-2022-jp decoder state to lead byte.
- iso2022jp_decoder_state = states.LeadByte;
-
- // 2. Let pointer be (iso-2022-jp lead − 0x21) × 94 + byte − 0x21.
- var pointer = (iso2022jp_lead - 0x21) * 94 + bite - 0x21;
-
- // 3. Let code point be the index code point for pointer in
- // index jis0208.
- var code_point = indexCodePointFor(pointer, index('jis0208'));
-
- // 4. If code point is null, return error.
- if (code_point === null)
- return decoderError(fatal);
-
- // 5. Return a code point whose value is code point.
- return code_point;
- }
-
- // end-of-stream
- if (bite === end_of_stream) {
- // Set the iso-2022-jp decoder state to lead byte, prepend
- // byte to stream, and return error.
- iso2022jp_decoder_state = states.LeadByte;
- stream.prepend(bite);
- return decoderError(fatal);
- }
-
- // Otherwise
- // Set iso-2022-jp decoder state to lead byte and return
- // error.
- iso2022jp_decoder_state = states.LeadByte;
- return decoderError(fatal);
-
- case states.EscapeStart:
- // Escape start
-
- // 1. If byte is either 0x24 or 0x28, set iso-2022-jp lead to
- // byte, iso-2022-jp decoder state to escape, and return
- // continue.
- if (bite === 0x24 || bite === 0x28) {
- iso2022jp_lead = bite;
- iso2022jp_decoder_state = states.Escape;
- return null;
- }
-
- // 2. Prepend byte to stream.
- stream.prepend(bite);
-
- // 3. Unset the iso-2022-jp output flag, set iso-2022-jp
- // decoder state to iso-2022-jp decoder output state, and
- // return error.
- iso2022jp_output_flag = false;
- iso2022jp_decoder_state = iso2022jp_decoder_output_state;
- return decoderError(fatal);
-
- case states.Escape:
- // Escape
-
- // 1. Let lead be iso-2022-jp lead and set iso-2022-jp lead to
- // 0x00.
- var lead = iso2022jp_lead;
- iso2022jp_lead = 0x00;
-
- // 2. Let state be null.
- var state = null;
-
- // 3. If lead is 0x28 and byte is 0x42, set state to ASCII.
- if (lead === 0x28 && bite === 0x42)
- state = states.ASCII;
-
- // 4. If lead is 0x28 and byte is 0x4A, set state to Roman.
- if (lead === 0x28 && bite === 0x4A)
- state = states.Roman;
-
- // 5. If lead is 0x28 and byte is 0x49, set state to Katakana.
- if (lead === 0x28 && bite === 0x49)
- state = states.Katakana;
-
- // 6. If lead is 0x24 and byte is either 0x40 or 0x42, set
- // state to lead byte.
- if (lead === 0x24 && (bite === 0x40 || bite === 0x42))
- state = states.LeadByte;
-
- // 7. If state is non-null, run these substeps:
- if (state !== null) {
- // 1. Set iso-2022-jp decoder state and iso-2022-jp decoder
- // output state to states.
- iso2022jp_decoder_state = iso2022jp_decoder_state = state;
-
- // 2. Let output flag be the iso-2022-jp output flag.
- var output_flag = iso2022jp_output_flag;
-
- // 3. Set the iso-2022-jp output flag.
- iso2022jp_output_flag = true;
-
- // 4. Return continue, if output flag is unset, and error
- // otherwise.
- return !output_flag ? null : decoderError(fatal);
- }
-
- // 8. Prepend lead and byte to stream.
- stream.prepend([lead, bite]);
-
- // 9. Unset the iso-2022-jp output flag, set iso-2022-jp
- // decoder state to iso-2022-jp decoder output state and
- // return error.
- iso2022jp_output_flag = false;
- iso2022jp_decoder_state = iso2022jp_decoder_output_state;
- return decoderError(fatal);
- }
- };
- }
-
- // 13.2.2 iso-2022-jp encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {{fatal: boolean}} options
- */
- function ISO2022JPEncoder(options) {
- var fatal = options.fatal;
- // iso-2022-jp's encoder has an associated iso-2022-jp encoder
- // state which is one of ASCII, Roman, and jis0208 (initially
- // ASCII).
- /** @enum */
- var states = {
- ASCII: 0,
- Roman: 1,
- jis0208: 2
- };
- var /** @type {number} */ iso2022jp_state = states.ASCII;
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1. If code point is end-of-stream and iso-2022-jp encoder
- // state is not ASCII, prepend code point to stream, set
- // iso-2022-jp encoder state to ASCII, and return three bytes
- // 0x1B 0x28 0x42.
- if (code_point === end_of_stream &&
- iso2022jp_state !== states.ASCII) {
- stream.prepend(code_point);
- iso2022jp_state = states.ASCII;
- return [0x1B, 0x28, 0x42];
- }
-
- // 2. If code point is end-of-stream and iso-2022-jp encoder
- // state is ASCII, return finished.
- if (code_point === end_of_stream && iso2022jp_state === states.ASCII)
- return finished;
-
- // 3. If ISO-2022-JP encoder state is ASCII or Roman, and code
- // point is U+000E, U+000F, or U+001B, return error with U+FFFD.
- if ((iso2022jp_state === states.ASCII ||
- iso2022jp_state === states.Roman) &&
- (code_point === 0x000E || code_point === 0x000F ||
- code_point === 0x001B)) {
- return encoderError(0xFFFD);
- }
-
- // 4. If iso-2022-jp encoder state is ASCII and code point is an
- // ASCII code point, return a byte whose value is code point.
- if (iso2022jp_state === states.ASCII &&
- isASCIICodePoint(code_point))
- return code_point;
-
- // 5. If iso-2022-jp encoder state is Roman and code point is an
- // ASCII code point, excluding U+005C and U+007E, or is U+00A5
- // or U+203E, run these substeps:
- if (iso2022jp_state === states.Roman &&
- ((isASCIICodePoint(code_point) &&
- code_point !== 0x005C && code_point !== 0x007E) ||
- (code_point == 0x00A5 || code_point == 0x203E))) {
-
- // 1. If code point is an ASCII code point, return a byte
- // whose value is code point.
- if (isASCIICodePoint(code_point))
- return code_point;
-
- // 2. If code point is U+00A5, return byte 0x5C.
- if (code_point === 0x00A5)
- return 0x5C;
-
- // 3. If code point is U+203E, return byte 0x7E.
- if (code_point === 0x203E)
- return 0x7E;
- }
-
- // 6. If code point is an ASCII code point, and iso-2022-jp
- // encoder state is not ASCII, prepend code point to stream, set
- // iso-2022-jp encoder state to ASCII, and return three bytes
- // 0x1B 0x28 0x42.
- if (isASCIICodePoint(code_point) &&
- iso2022jp_state !== states.ASCII) {
- stream.prepend(code_point);
- iso2022jp_state = states.ASCII;
- return [0x1B, 0x28, 0x42];
- }
-
- // 7. If code point is either U+00A5 or U+203E, and iso-2022-jp
- // encoder state is not Roman, prepend code point to stream, set
- // iso-2022-jp encoder state to Roman, and return three bytes
- // 0x1B 0x28 0x4A.
- if ((code_point === 0x00A5 || code_point === 0x203E) &&
- iso2022jp_state !== states.Roman) {
- stream.prepend(code_point);
- iso2022jp_state = states.Roman;
- return [0x1B, 0x28, 0x4A];
- }
-
- // 8. If code point is U+2212, set it to U+FF0D.
- if (code_point === 0x2212)
- code_point = 0xFF0D;
-
- // 9. Let pointer be the index pointer for code point in index
- // jis0208.
- var pointer = indexPointerFor(code_point, index('jis0208'));
-
- // 10. If pointer is null, return error with code point.
- if (pointer === null)
- return encoderError(code_point);
-
- // 11. If iso-2022-jp encoder state is not jis0208, prepend code
- // point to stream, set iso-2022-jp encoder state to jis0208,
- // and return three bytes 0x1B 0x24 0x42.
- if (iso2022jp_state !== states.jis0208) {
- stream.prepend(code_point);
- iso2022jp_state = states.jis0208;
- return [0x1B, 0x24, 0x42];
- }
-
- // 12. Let lead be floor(pointer / 94) + 0x21.
- var lead = floor(pointer / 94) + 0x21;
-
- // 13. Let trail be pointer % 94 + 0x21.
- var trail = pointer % 94 + 0x21;
-
- // 14. Return two bytes whose values are lead and trail.
- return [lead, trail];
- };
- }
-
- /** @param {{fatal: boolean}} options */
- encoders['ISO-2022-JP'] = function(options) {
- return new ISO2022JPEncoder(options);
- };
- /** @param {{fatal: boolean}} options */
- decoders['ISO-2022-JP'] = function(options) {
- return new ISO2022JPDecoder(options);
- };
-
- // 13.3 Shift_JIS
-
- // 13.3.1 Shift_JIS decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {{fatal: boolean}} options
- */
- function ShiftJISDecoder(options) {
- var fatal = options.fatal;
- // Shift_JIS's decoder has an associated Shift_JIS lead (initially
- // 0x00).
- var /** @type {number} */ Shift_JIS_lead = 0x00;
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // 1. If byte is end-of-stream and Shift_JIS lead is not 0x00,
- // set Shift_JIS lead to 0x00 and return error.
- if (bite === end_of_stream && Shift_JIS_lead !== 0x00) {
- Shift_JIS_lead = 0x00;
- return decoderError(fatal);
- }
-
- // 2. If byte is end-of-stream and Shift_JIS lead is 0x00,
- // return finished.
- if (bite === end_of_stream && Shift_JIS_lead === 0x00)
- return finished;
-
- // 3. If Shift_JIS lead is not 0x00, let lead be Shift_JIS lead,
- // let pointer be null, set Shift_JIS lead to 0x00, and then run
- // these substeps:
- if (Shift_JIS_lead !== 0x00) {
- var lead = Shift_JIS_lead;
- var pointer = null;
- Shift_JIS_lead = 0x00;
-
- // 1. Let offset be 0x40, if byte is less than 0x7F, and 0x41
- // otherwise.
- var offset = (bite < 0x7F) ? 0x40 : 0x41;
-
- // 2. Let lead offset be 0x81, if lead is less than 0xA0, and
- // 0xC1 otherwise.
- var lead_offset = (lead < 0xA0) ? 0x81 : 0xC1;
-
- // 3. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80
- // to 0xFC, inclusive, set pointer to (lead − lead offset) ×
- // 188 + byte − offset.
- if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFC))
- pointer = (lead - lead_offset) * 188 + bite - offset;
-
- // 4. If pointer is in the range 8836 to 10715, inclusive,
- // return a code point whose value is 0xE000 − 8836 + pointer.
- if (inRange(pointer, 8836, 10715))
- return 0xE000 - 8836 + pointer;
-
- // 5. Let code point be null, if pointer is null, and the
- // index code point for pointer in index jis0208 otherwise.
- var code_point = (pointer === null) ? null :
- indexCodePointFor(pointer, index('jis0208'));
-
- // 6. If code point is null and byte is an ASCII byte, prepend
- // byte to stream.
- if (code_point === null && isASCIIByte(bite))
- stream.prepend(bite);
-
- // 7. If code point is null, return error.
- if (code_point === null)
- return decoderError(fatal);
-
- // 8. Return a code point whose value is code point.
- return code_point;
- }
-
- // 4. If byte is an ASCII byte or 0x80, return a code point
- // whose value is byte.
- if (isASCIIByte(bite) || bite === 0x80)
- return bite;
-
- // 5. If byte is in the range 0xA1 to 0xDF, inclusive, return a
- // code point whose value is 0xFF61 − 0xA1 + byte.
- if (inRange(bite, 0xA1, 0xDF))
- return 0xFF61 - 0xA1 + bite;
-
- // 6. If byte is in the range 0x81 to 0x9F, inclusive, or 0xE0
- // to 0xFC, inclusive, set Shift_JIS lead to byte and return
- // continue.
- if (inRange(bite, 0x81, 0x9F) || inRange(bite, 0xE0, 0xFC)) {
- Shift_JIS_lead = bite;
- return null;
- }
-
- // 7. Return error.
- return decoderError(fatal);
- };
- }
-
- // 13.3.2 Shift_JIS encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {{fatal: boolean}} options
- */
- function ShiftJISEncoder(options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1. If code point is end-of-stream, return finished.
- if (code_point === end_of_stream)
- return finished;
-
- // 2. If code point is an ASCII code point or U+0080, return a
- // byte whose value is code point.
- if (isASCIICodePoint(code_point) || code_point === 0x0080)
- return code_point;
-
- // 3. If code point is U+00A5, return byte 0x5C.
- if (code_point === 0x00A5)
- return 0x5C;
-
- // 4. If code point is U+203E, return byte 0x7E.
- if (code_point === 0x203E)
- return 0x7E;
-
- // 5. If code point is in the range U+FF61 to U+FF9F, inclusive,
- // return a byte whose value is code point − 0xFF61 + 0xA1.
- if (inRange(code_point, 0xFF61, 0xFF9F))
- return code_point - 0xFF61 + 0xA1;
-
- // 6. If code point is U+2212, set it to U+FF0D.
- if (code_point === 0x2212)
- code_point = 0xFF0D;
-
- // 7. Let pointer be the index Shift_JIS pointer for code point.
- var pointer = indexShiftJISPointerFor(code_point);
-
- // 8. If pointer is null, return error with code point.
- if (pointer === null)
- return encoderError(code_point);
-
- // 9. Let lead be floor(pointer / 188).
- var lead = floor(pointer / 188);
-
- // 10. Let lead offset be 0x81, if lead is less than 0x1F, and
- // 0xC1 otherwise.
- var lead_offset = (lead < 0x1F) ? 0x81 : 0xC1;
-
- // 11. Let trail be pointer % 188.
- var trail = pointer % 188;
-
- // 12. Let offset be 0x40, if trail is less than 0x3F, and 0x41
- // otherwise.
- var offset = (trail < 0x3F) ? 0x40 : 0x41;
-
- // 13. Return two bytes whose values are lead + lead offset and
- // trail + offset.
- return [lead + lead_offset, trail + offset];
- };
- }
-
- /** @param {{fatal: boolean}} options */
- encoders['Shift_JIS'] = function(options) {
- return new ShiftJISEncoder(options);
- };
- /** @param {{fatal: boolean}} options */
- decoders['Shift_JIS'] = function(options) {
- return new ShiftJISDecoder(options);
- };
-
- //
- // 14. Legacy multi-byte Korean encodings
- //
-
- // 14.1 euc-kr
-
- // 14.1.1 euc-kr decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {{fatal: boolean}} options
- */
- function EUCKRDecoder(options) {
- var fatal = options.fatal;
-
- // euc-kr's decoder has an associated euc-kr lead (initially 0x00).
- var /** @type {number} */ euckr_lead = 0x00;
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // 1. If byte is end-of-stream and euc-kr lead is not 0x00, set
- // euc-kr lead to 0x00 and return error.
- if (bite === end_of_stream && euckr_lead !== 0) {
- euckr_lead = 0x00;
- return decoderError(fatal);
- }
-
- // 2. If byte is end-of-stream and euc-kr lead is 0x00, return
- // finished.
- if (bite === end_of_stream && euckr_lead === 0)
- return finished;
-
- // 3. If euc-kr lead is not 0x00, let lead be euc-kr lead, let
- // pointer be null, set euc-kr lead to 0x00, and then run these
- // substeps:
- if (euckr_lead !== 0x00) {
- var lead = euckr_lead;
- var pointer = null;
- euckr_lead = 0x00;
-
- // 1. If byte is in the range 0x41 to 0xFE, inclusive, set
- // pointer to (lead − 0x81) × 190 + (byte − 0x41).
- if (inRange(bite, 0x41, 0xFE))
- pointer = (lead - 0x81) * 190 + (bite - 0x41);
-
- // 2. Let code point be null, if pointer is null, and the
- // index code point for pointer in index euc-kr otherwise.
- var code_point = (pointer === null)
- ? null : indexCodePointFor(pointer, index('euc-kr'));
-
- // 3. If code point is null and byte is an ASCII byte, prepend
- // byte to stream.
- if (pointer === null && isASCIIByte(bite))
- stream.prepend(bite);
-
- // 4. If code point is null, return error.
- if (code_point === null)
- return decoderError(fatal);
-
- // 5. Return a code point whose value is code point.
- return code_point;
- }
-
- // 4. If byte is an ASCII byte, return a code point whose value
- // is byte.
- if (isASCIIByte(bite))
- return bite;
-
- // 5. If byte is in the range 0x81 to 0xFE, inclusive, set
- // euc-kr lead to byte and return continue.
- if (inRange(bite, 0x81, 0xFE)) {
- euckr_lead = bite;
- return null;
- }
-
- // 6. Return error.
- return decoderError(fatal);
- };
- }
-
- // 14.1.2 euc-kr encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {{fatal: boolean}} options
- */
- function EUCKREncoder(options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1. If code point is end-of-stream, return finished.
- if (code_point === end_of_stream)
- return finished;
-
- // 2. If code point is an ASCII code point, return a byte whose
- // value is code point.
- if (isASCIICodePoint(code_point))
- return code_point;
-
- // 3. Let pointer be the index pointer for code point in index
- // euc-kr.
- var pointer = indexPointerFor(code_point, index('euc-kr'));
-
- // 4. If pointer is null, return error with code point.
- if (pointer === null)
- return encoderError(code_point);
-
- // 5. Let lead be floor(pointer / 190) + 0x81.
- var lead = floor(pointer / 190) + 0x81;
-
- // 6. Let trail be pointer % 190 + 0x41.
- var trail = (pointer % 190) + 0x41;
-
- // 7. Return two bytes whose values are lead and trail.
- return [lead, trail];
- };
- }
-
- /** @param {{fatal: boolean}} options */
- encoders['EUC-KR'] = function(options) {
- return new EUCKREncoder(options);
- };
- /** @param {{fatal: boolean}} options */
- decoders['EUC-KR'] = function(options) {
- return new EUCKRDecoder(options);
- };
-
-
- //
- // 15. Legacy miscellaneous encodings
- //
-
- // 15.1 replacement
-
- // Not needed - API throws RangeError
-
- // 15.2 Common infrastructure for utf-16be and utf-16le
-
- /**
- * @param {number} code_unit
- * @param {boolean} utf16be
- * @return {!Array.<number>} bytes
- */
- function convertCodeUnitToBytes(code_unit, utf16be) {
- // 1. Let byte1 be code unit >> 8.
- var byte1 = code_unit >> 8;
-
- // 2. Let byte2 be code unit & 0x00FF.
- var byte2 = code_unit & 0x00FF;
-
- // 3. Then return the bytes in order:
- // utf-16be flag is set: byte1, then byte2.
- if (utf16be)
- return [byte1, byte2];
- // utf-16be flag is unset: byte2, then byte1.
- return [byte2, byte1];
- }
-
- // 15.2.1 shared utf-16 decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {boolean} utf16_be True if big-endian, false if little-endian.
- * @param {{fatal: boolean}} options
- */
- function UTF16Decoder(utf16_be, options) {
- var fatal = options.fatal;
- var /** @type {?number} */ utf16_lead_byte = null,
- /** @type {?number} */ utf16_lead_surrogate = null;
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // 1. If byte is end-of-stream and either utf-16 lead byte or
- // utf-16 lead surrogate is not null, set utf-16 lead byte and
- // utf-16 lead surrogate to null, and return error.
- if (bite === end_of_stream && (utf16_lead_byte !== null ||
- utf16_lead_surrogate !== null)) {
- return decoderError(fatal);
- }
-
- // 2. If byte is end-of-stream and utf-16 lead byte and utf-16
- // lead surrogate are null, return finished.
- if (bite === end_of_stream && utf16_lead_byte === null &&
- utf16_lead_surrogate === null) {
- return finished;
- }
-
- // 3. If utf-16 lead byte is null, set utf-16 lead byte to byte
- // and return continue.
- if (utf16_lead_byte === null) {
- utf16_lead_byte = bite;
- return null;
- }
-
- // 4. Let code unit be the result of:
- var code_unit;
- if (utf16_be) {
- // utf-16be decoder flag is set
- // (utf-16 lead byte << 8) + byte.
- code_unit = (utf16_lead_byte << 8) + bite;
- } else {
- // utf-16be decoder flag is unset
- // (byte << 8) + utf-16 lead byte.
- code_unit = (bite << 8) + utf16_lead_byte;
- }
- // Then set utf-16 lead byte to null.
- utf16_lead_byte = null;
-
- // 5. If utf-16 lead surrogate is not null, let lead surrogate
- // be utf-16 lead surrogate, set utf-16 lead surrogate to null,
- // and then run these substeps:
- if (utf16_lead_surrogate !== null) {
- var lead_surrogate = utf16_lead_surrogate;
- utf16_lead_surrogate = null;
-
- // 1. If code unit is in the range U+DC00 to U+DFFF,
- // inclusive, return a code point whose value is 0x10000 +
- // ((lead surrogate − 0xD800) << 10) + (code unit − 0xDC00).
- if (inRange(code_unit, 0xDC00, 0xDFFF)) {
- return 0x10000 + (lead_surrogate - 0xD800) * 0x400 +
- (code_unit - 0xDC00);
- }
-
- // 2. Prepend the sequence resulting of converting code unit
- // to bytes using utf-16be decoder flag to stream and return
- // error.
- stream.prepend(convertCodeUnitToBytes(code_unit, utf16_be));
- return decoderError(fatal);
- }
-
- // 6. If code unit is in the range U+D800 to U+DBFF, inclusive,
- // set utf-16 lead surrogate to code unit and return continue.
- if (inRange(code_unit, 0xD800, 0xDBFF)) {
- utf16_lead_surrogate = code_unit;
- return null;
- }
-
- // 7. If code unit is in the range U+DC00 to U+DFFF, inclusive,
- // return error.
- if (inRange(code_unit, 0xDC00, 0xDFFF))
- return decoderError(fatal);
-
- // 8. Return code point code unit.
- return code_unit;
- };
- }
-
- // 15.2.2 shared utf-16 encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {boolean} utf16_be True if big-endian, false if little-endian.
- * @param {{fatal: boolean}} options
- */
- function UTF16Encoder(utf16_be, options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1. If code point is end-of-stream, return finished.
- if (code_point === end_of_stream)
- return finished;
-
- // 2. If code point is in the range U+0000 to U+FFFF, inclusive,
- // return the sequence resulting of converting code point to
- // bytes using utf-16be encoder flag.
- if (inRange(code_point, 0x0000, 0xFFFF))
- return convertCodeUnitToBytes(code_point, utf16_be);
-
- // 3. Let lead be ((code point − 0x10000) >> 10) + 0xD800,
- // converted to bytes using utf-16be encoder flag.
- var lead = convertCodeUnitToBytes(
- ((code_point - 0x10000) >> 10) + 0xD800, utf16_be);
-
- // 4. Let trail be ((code point − 0x10000) & 0x3FF) + 0xDC00,
- // converted to bytes using utf-16be encoder flag.
- var trail = convertCodeUnitToBytes(
- ((code_point - 0x10000) & 0x3FF) + 0xDC00, utf16_be);
-
- // 5. Return a byte sequence of lead followed by trail.
- return lead.concat(trail);
- };
- }
-
- // 15.3 utf-16be
- // 15.3.1 utf-16be decoder
- /** @param {{fatal: boolean}} options */
- encoders['UTF-16BE'] = function(options) {
- return new UTF16Encoder(true, options);
- };
- // 15.3.2 utf-16be encoder
- /** @param {{fatal: boolean}} options */
- decoders['UTF-16BE'] = function(options) {
- return new UTF16Decoder(true, options);
- };
-
- // 15.4 utf-16le
- // 15.4.1 utf-16le decoder
- /** @param {{fatal: boolean}} options */
- encoders['UTF-16LE'] = function(options) {
- return new UTF16Encoder(false, options);
- };
- // 15.4.2 utf-16le encoder
- /** @param {{fatal: boolean}} options */
- decoders['UTF-16LE'] = function(options) {
- return new UTF16Decoder(false, options);
- };
-
- // 15.5 x-user-defined
-
- // 15.5.1 x-user-defined decoder
- /**
- * @constructor
- * @implements {Decoder}
- * @param {{fatal: boolean}} options
- */
- function XUserDefinedDecoder(options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream The stream of bytes being decoded.
- * @param {number} bite The next byte read from the stream.
- * @return {?(number|!Array.<number>)} The next code point(s)
- * decoded, or null if not enough data exists in the input
- * stream to decode a complete code point.
- */
- this.handler = function(stream, bite) {
- // 1. If byte is end-of-stream, return finished.
- if (bite === end_of_stream)
- return finished;
-
- // 2. If byte is an ASCII byte, return a code point whose value
- // is byte.
- if (isASCIIByte(bite))
- return bite;
-
- // 3. Return a code point whose value is 0xF780 + byte − 0x80.
- return 0xF780 + bite - 0x80;
- };
- }
-
- // 15.5.2 x-user-defined encoder
- /**
- * @constructor
- * @implements {Encoder}
- * @param {{fatal: boolean}} options
- */
- function XUserDefinedEncoder(options) {
- var fatal = options.fatal;
- /**
- * @param {Stream} stream Input stream.
- * @param {number} code_point Next code point read from the stream.
- * @return {(number|!Array.<number>)} Byte(s) to emit.
- */
- this.handler = function(stream, code_point) {
- // 1.If code point is end-of-stream, return finished.
- if (code_point === end_of_stream)
- return finished;
-
- // 2. If code point is an ASCII code point, return a byte whose
- // value is code point.
- if (isASCIICodePoint(code_point))
- return code_point;
-
- // 3. If code point is in the range U+F780 to U+F7FF, inclusive,
- // return a byte whose value is code point − 0xF780 + 0x80.
- if (inRange(code_point, 0xF780, 0xF7FF))
- return code_point - 0xF780 + 0x80;
-
- // 4. Return error with code point.
- return encoderError(code_point);
- };
- }
-
- /** @param {{fatal: boolean}} options */
- encoders['x-user-defined'] = function(options) {
- return new XUserDefinedEncoder(options);
- };
- /** @param {{fatal: boolean}} options */
- decoders['x-user-defined'] = function(options) {
- return new XUserDefinedDecoder(options);
- };
-
- if (!global['TextEncoder'])
- global['TextEncoder'] = TextEncoder;
- if (!global['TextDecoder'])
- global['TextDecoder'] = TextDecoder;
-
- if (typeof module !== "undefined" && module.exports) {
- module.exports = {
- TextEncoder: global['TextEncoder'],
- TextDecoder: global['TextDecoder'],
- EncodingIndexes: global["encoding-indexes"]
- };
- }
-}(this));
diff --git a/vendor/assets/javascripts/xterm/fit.js b/vendor/assets/javascripts/xterm/fit.js
deleted file mode 100644
index 55438452cad..00000000000
--- a/vendor/assets/javascripts/xterm/fit.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Fit terminal columns and rows to the dimensions of its
- * DOM element.
- *
- * Approach:
- * - Rows: Truncate the division of the terminal parent element height
- * by the terminal row height
- *
- * - Columns: Truncate the division of the terminal parent element width by
- * the terminal character width (apply display: inline at the
- * terminal row and truncate its width with the current number
- * of columns)
- */
-(function (fit) {
- if (typeof exports === 'object' && typeof module === 'object') {
- /*
- * CommonJS environment
- */
- module.exports = fit(require('./xterm'));
- } else if (typeof define == 'function') {
- /*
- * Require.js is available
- */
- define(['./xterm'], fit);
- } else {
- /*
- * Plain browser environment
- */
- fit(window.Terminal);
- }
-})(function (Xterm) {
- /**
- * This module provides methods for fitting a terminal's size to a parent container.
- *
- * @module xterm/addons/fit/fit
- */
- var exports = {};
-
- exports.proposeGeometry = function (term) {
- var parentElementStyle = window.getComputedStyle(term.element.parentElement),
- parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')),
- parentElementWidth = parseInt(parentElementStyle.getPropertyValue('width')),
- elementStyle = window.getComputedStyle(term.element),
- elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')),
- elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')),
- availableHeight = parentElementHeight - elementPaddingVer,
- availableWidth = parentElementWidth - elementPaddingHor,
- container = term.rowContainer,
- subjectRow = term.rowContainer.firstElementChild,
- contentBuffer = subjectRow.innerHTML,
- characterHeight,
- rows,
- characterWidth,
- cols,
- geometry;
-
- subjectRow.style.display = 'inline';
- subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
- characterWidth = subjectRow.getBoundingClientRect().width;
- subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
- characterHeight = parseInt(subjectRow.offsetHeight);
- subjectRow.innerHTML = contentBuffer;
-
- rows = parseInt(availableHeight / characterHeight);
- cols = parseInt(availableWidth / characterWidth) - 1;
-
- geometry = {cols: cols, rows: rows};
- return geometry;
- };
-
- exports.fit = function (term) {
- var geometry = exports.proposeGeometry(term);
-
- term.resize(geometry.cols, geometry.rows);
- };
-
- Xterm.prototype.proposeGeometry = function () {
- return exports.proposeGeometry(this);
- };
-
- Xterm.prototype.fit = function () {
- return exports.fit(this);
- };
-
- return exports;
-});
diff --git a/vendor/assets/javascripts/xterm/xterm.js b/vendor/assets/javascripts/xterm/xterm.js
deleted file mode 100644
index 11ce3c73db9..00000000000
--- a/vendor/assets/javascripts/xterm/xterm.js
+++ /dev/null
@@ -1,2235 +0,0 @@
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Terminal = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-/**
- * xterm.js: xterm, in the browser
- * Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
- */
-
-/**
- * Encapsulates the logic for handling compositionstart, compositionupdate and compositionend
- * events, displaying the in-progress composition to the UI and forwarding the final composition
- * to the handler.
- * @param {HTMLTextAreaElement} textarea The textarea that xterm uses for input.
- * @param {HTMLElement} compositionView The element to display the in-progress composition in.
- * @param {Terminal} terminal The Terminal to forward the finished composition to.
- */
-function CompositionHelper(textarea, compositionView, terminal) {
- this.textarea = textarea;
- this.compositionView = compositionView;
- this.terminal = terminal;
-
- // Whether input composition is currently happening, eg. via a mobile keyboard, speech input
- // or IME. This variable determines whether the compositionText should be displayed on the UI.
- this.isComposing = false;
-
- // The input currently being composed, eg. via a mobile keyboard, speech input or IME.
- this.compositionText = null;
-
- // The position within the input textarea's value of the current composition.
- this.compositionPosition = { start: null, end: null };
-
- // Whether a composition is in the process of being sent, setting this to false will cancel
- // any in-progress composition.
- this.isSendingComposition = false;
-}
-
-/**
- * Handles the compositionstart event, activating the composition view.
- */
-CompositionHelper.prototype.compositionstart = function () {
- this.isComposing = true;
- this.compositionPosition.start = this.textarea.value.length;
- this.compositionView.textContent = '';
- this.compositionView.classList.add('active');
-};
-
-/**
- * Handles the compositionupdate event, updating the composition view.
- * @param {CompositionEvent} ev The event.
- */
-CompositionHelper.prototype.compositionupdate = function (ev) {
- this.compositionView.textContent = ev.data;
- this.updateCompositionElements();
- var self = this;
- setTimeout(function () {
- self.compositionPosition.end = self.textarea.value.length;
- }, 0);
-};
-
-/**
- * Handles the compositionend event, hiding the composition view and sending the composition to
- * the handler.
- */
-CompositionHelper.prototype.compositionend = function () {
- this.finalizeComposition(true);
-};
-
-/**
- * Handles the keydown event, routing any necessary events to the CompositionHelper functions.
- * @return Whether the Terminal should continue processing the keydown event.
- */
-CompositionHelper.prototype.keydown = function (ev) {
- if (this.isComposing || this.isSendingComposition) {
- if (ev.keyCode === 229) {
- // Continue composing if the keyCode is the "composition character"
- return false;
- } else if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {
- // Continue composing if the keyCode is a modifier key
- return false;
- } else {
- // Finish composition immediately. This is mainly here for the case where enter is
- // pressed and the handler needs to be triggered before the command is executed.
- this.finalizeComposition(false);
- }
- }
-
- if (ev.keyCode === 229) {
- // If the "composition character" is used but gets to this point it means a non-composition
- // character (eg. numbers and punctuation) was pressed when the IME was active.
- this.handleAnyTextareaChanges();
- return false;
- }
-
- return true;
-};
-
-/**
- * Finalizes the composition, resuming regular input actions. This is called when a composition
- * is ending.
- * @param {boolean} waitForPropogation Whether to wait for events to propogate before sending
- * the input. This should be false if a non-composition keystroke is entered before the
- * compositionend event is triggered, such as enter, so that the composition is send before
- * the command is executed.
- */
-CompositionHelper.prototype.finalizeComposition = function (waitForPropogation) {
- this.compositionView.classList.remove('active');
- this.isComposing = false;
- this.clearTextareaPosition();
-
- if (!waitForPropogation) {
- // Cancel any delayed composition send requests and send the input immediately.
- this.isSendingComposition = false;
- var input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end);
- this.terminal.handler(input);
- } else {
- // Make a deep copy of the composition position here as a new compositionstart event may
- // fire before the setTimeout executes.
- var currentCompositionPosition = {
- start: this.compositionPosition.start,
- end: this.compositionPosition.end
- };
-
- // Since composition* events happen before the changes take place in the textarea on most
- // browsers, use a setTimeout with 0ms time to allow the native compositionend event to
- // complete. This ensures the correct character is retrieved, this solution was used
- // because:
- // - The compositionend event's data property is unreliable, at least on Chromium
- // - The last compositionupdate event's data property does not always accurately describe
- // the character, a counter example being Korean where an ending consonsant can move to
- // the following character if the following input is a vowel.
- var self = this;
- this.isSendingComposition = true;
- setTimeout(function () {
- // Ensure that the input has not already been sent
- if (self.isSendingComposition) {
- self.isSendingComposition = false;
- var input;
- if (self.isComposing) {
- // Use the end position to get the string if a new composition has started.
- input = self.textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);
- } else {
- // Don't use the end position here in order to pick up any characters after the
- // composition has finished, for example when typing a non-composition character
- // (eg. 2) after a composition character.
- input = self.textarea.value.substring(currentCompositionPosition.start);
- }
- self.terminal.handler(input);
- }
- }, 0);
- }
-};
-
-/**
- * Apply any changes made to the textarea after the current event chain is allowed to complete.
- * This should be called when not currently composing but a keydown event with the "composition
- * character" (229) is triggered, in order to allow non-composition text to be entered when an
- * IME is active.
- */
-CompositionHelper.prototype.handleAnyTextareaChanges = function () {
- var oldValue = this.textarea.value;
- var self = this;
- setTimeout(function () {
- // Ignore if a composition has started since the timeout
- if (!self.isComposing) {
- var newValue = self.textarea.value;
- var diff = newValue.replace(oldValue, '');
- if (diff.length > 0) {
- self.terminal.handler(diff);
- }
- }
- }, 0);
-};
-
-/**
- * Positions the composition view on top of the cursor and the textarea just below it (so the
- * IME helper dialog is positioned correctly).
- */
-CompositionHelper.prototype.updateCompositionElements = function (dontRecurse) {
- if (!this.isComposing) {
- return;
- }
- var cursor = this.terminal.element.querySelector('.terminal-cursor');
- if (cursor) {
- // Take .xterm-rows offsetTop into account as well in case it's positioned absolutely within
- // the .xterm element.
- var xtermRows = this.terminal.element.querySelector('.xterm-rows');
- var cursorTop = xtermRows.offsetTop + cursor.offsetTop;
-
- this.compositionView.style.left = cursor.offsetLeft + 'px';
- this.compositionView.style.top = cursorTop + 'px';
- this.compositionView.style.height = cursor.offsetHeight + 'px';
- this.compositionView.style.lineHeight = cursor.offsetHeight + 'px';
- // Sync the textarea to the exact position of the composition view so the IME knows where the
- // text is.
- var compositionViewBounds = this.compositionView.getBoundingClientRect();
- this.textarea.style.left = cursor.offsetLeft + 'px';
- this.textarea.style.top = cursorTop + 'px';
- this.textarea.style.width = compositionViewBounds.width + 'px';
- this.textarea.style.height = compositionViewBounds.height + 'px';
- this.textarea.style.lineHeight = compositionViewBounds.height + 'px';
- }
- if (!dontRecurse) {
- setTimeout(this.updateCompositionElements.bind(this, true), 0);
- }
-};
-
-/**
- * Clears the textarea's position so that the cursor does not blink on IE.
- * @private
- */
-CompositionHelper.prototype.clearTextareaPosition = function () {
- this.textarea.style.left = '';
- this.textarea.style.top = '';
-};
-
-exports.CompositionHelper = CompositionHelper;
-
-},{}],2:[function(_dereq_,module,exports){
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-/**
- * xterm.js: xterm, in the browser
- * Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
- */
-
-function EventEmitter() {
- this._events = this._events || {};
-}
-
-EventEmitter.prototype.addListener = function (type, listener) {
- this._events[type] = this._events[type] || [];
- this._events[type].push(listener);
-};
-
-EventEmitter.prototype.on = EventEmitter.prototype.addListener;
-
-EventEmitter.prototype.removeListener = function (type, listener) {
- if (!this._events[type]) return;
-
- var obj = this._events[type],
- i = obj.length;
-
- while (i--) {
- if (obj[i] === listener || obj[i].listener === listener) {
- obj.splice(i, 1);
- return;
- }
- }
-};
-
-EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
-
-EventEmitter.prototype.removeAllListeners = function (type) {
- if (this._events[type]) delete this._events[type];
-};
-
-EventEmitter.prototype.once = function (type, listener) {
- var self = this;
- function on() {
- var args = Array.prototype.slice.call(arguments);
- this.removeListener(type, on);
- return listener.apply(this, args);
- }
- on.listener = listener;
- return this.on(type, on);
-};
-
-EventEmitter.prototype.emit = function (type) {
- if (!this._events[type]) return;
-
- var args = Array.prototype.slice.call(arguments, 1),
- obj = this._events[type],
- l = obj.length,
- i = 0;
-
- for (; i < l; i++) {
- obj[i].apply(this, args);
- }
-};
-
-EventEmitter.prototype.listeners = function (type) {
- return this._events[type] = this._events[type] || [];
-};
-
-exports.EventEmitter = EventEmitter;
-
-},{}],3:[function(_dereq_,module,exports){
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-/**
- * xterm.js: xterm, in the browser
- * Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
- */
-
-/**
- * Represents the viewport of a terminal, the visible area within the larger buffer of output.
- * Logic for the virtual scroll bar is included in this object.
- * @param {Terminal} terminal The Terminal object.
- * @param {HTMLElement} viewportElement The DOM element acting as the viewport
- * @param {HTMLElement} charMeasureElement A DOM element used to measure the character size of
- * the terminal.
- */
-function Viewport(terminal, viewportElement, scrollArea, charMeasureElement) {
- this.terminal = terminal;
- this.viewportElement = viewportElement;
- this.scrollArea = scrollArea;
- this.charMeasureElement = charMeasureElement;
- this.currentRowHeight = 0;
- this.lastRecordedBufferLength = 0;
- this.lastRecordedViewportHeight = 0;
-
- this.terminal.on('scroll', this.syncScrollArea.bind(this));
- this.terminal.on('resize', this.syncScrollArea.bind(this));
- this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));
-
- this.syncScrollArea();
-}
-
-/**
- * Refreshes row height, setting line-height, viewport height and scroll area height if
- * necessary.
- * @param {number|undefined} charSize A character size measurement bounding rect object, if it
- * doesn't exist it will be created.
- */
-Viewport.prototype.refresh = function (charSize) {
- var size = charSize || this.charMeasureElement.getBoundingClientRect();
- if (size.height > 0) {
- var rowHeightChanged = size.height !== this.currentRowHeight;
- if (rowHeightChanged) {
- this.currentRowHeight = size.height;
- this.viewportElement.style.lineHeight = size.height + 'px';
- this.terminal.rowContainer.style.lineHeight = size.height + 'px';
- }
- var viewportHeightChanged = this.lastRecordedViewportHeight !== this.terminal.rows;
- if (rowHeightChanged || viewportHeightChanged) {
- this.lastRecordedViewportHeight = this.terminal.rows;
- this.viewportElement.style.height = size.height * this.terminal.rows + 'px';
- }
- this.scrollArea.style.height = size.height * this.lastRecordedBufferLength + 'px';
- }
-};
-
-/**
- * Updates dimensions and synchronizes the scroll area if necessary.
- */
-Viewport.prototype.syncScrollArea = function () {
- if (this.lastRecordedBufferLength !== this.terminal.lines.length) {
- // If buffer height changed
- this.lastRecordedBufferLength = this.terminal.lines.length;
- this.refresh();
- } else if (this.lastRecordedViewportHeight !== this.terminal.rows) {
- // If viewport height changed
- this.refresh();
- } else {
- // If size has changed, refresh viewport
- var size = this.charMeasureElement.getBoundingClientRect();
- if (size.height !== this.currentRowHeight) {
- this.refresh(size);
- }
- }
-
- // Sync scrollTop
- var scrollTop = this.terminal.ydisp * this.currentRowHeight;
- if (this.viewportElement.scrollTop !== scrollTop) {
- this.viewportElement.scrollTop = scrollTop;
- }
-};
-
-/**
- * Handles scroll events on the viewport, calculating the new viewport and requesting the
- * terminal to scroll to it.
- * @param {Event} ev The scroll event.
- */
-Viewport.prototype.onScroll = function (ev) {
- var newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);
- var diff = newRow - this.terminal.ydisp;
- this.terminal.scrollDisp(diff, true);
-};
-
-/**
- * Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual
- * scrolling to `onScroll`, this event needs to be attached manually by the consumer of
- * `Viewport`.
- * @param {WheelEvent} ev The mouse wheel event.
- */
-Viewport.prototype.onWheel = function (ev) {
- if (ev.deltaY === 0) {
- // Do nothing if it's not a vertical scroll event
- return;
- }
- // Fallback to WheelEvent.DOM_DELTA_PIXEL
- var multiplier = 1;
- if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
- multiplier = this.currentRowHeight;
- } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
- multiplier = this.currentRowHeight * this.terminal.rows;
- }
- this.viewportElement.scrollTop += ev.deltaY * multiplier;
- // Prevent the page from scrolling when the terminal scrolls
- ev.preventDefault();
-};
-
-exports.Viewport = Viewport;
-
-},{}],4:[function(_dereq_,module,exports){
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-/**
- * xterm.js: xterm, in the browser
- * Copyright (c) 2016, SourceLair Private Company <www.sourcelair.com> (MIT License)
- */
-
-/**
- * Clipboard handler module. This module contains methods for handling all
- * clipboard-related events appropriately in the terminal.
- * @module xterm/handlers/Clipboard
- */
-
-/**
- * Prepares text copied from terminal selection, to be saved in the clipboard by:
- * 1. stripping all trailing white spaces
- * 2. converting all non-breaking spaces to regular spaces
- * @param {string} text The copied text that needs processing for storing in clipboard
- * @returns {string}
- */
-function prepareTextForClipboard(text) {
- var space = String.fromCharCode(32),
- nonBreakingSpace = String.fromCharCode(160),
- allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'),
- processedText = text.split('\n').map(function (line) {
- // Strip all trailing white spaces and convert all non-breaking spaces
- // to regular spaces.
- var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space);
-
- return processedLine;
- }).join('\n');
-
- return processedText;
-}
-
-/**
- * Binds copy functionality to the given terminal.
- * @param {ClipboardEvent} ev The original copy event to be handled
- */
-function copyHandler(ev, term) {
- var copiedText = window.getSelection().toString(),
- text = prepareTextForClipboard(copiedText);
-
- if (term.browser.isMSIE) {
- window.clipboardData.setData('Text', text);
- } else {
- ev.clipboardData.setData('text/plain', text);
- }
-
- ev.preventDefault(); // Prevent or the original text will be copied.
-}
-
-/**
- * Redirect the clipboard's data to the terminal's input handler.
- * @param {ClipboardEvent} ev The original paste event to be handled
- * @param {Terminal} term The terminal on which to apply the handled paste event
- */
-function pasteHandler(ev, term) {
- ev.stopPropagation();
-
- var dispatchPaste = function dispatchPaste(text) {
- term.handler(text);
- term.textarea.value = '';
- return term.cancel(ev);
- };
-
- if (term.browser.isMSIE) {
- if (window.clipboardData) {
- var text = window.clipboardData.getData('Text');
- dispatchPaste(text);
- }
- } else {
- if (ev.clipboardData) {
- var text = ev.clipboardData.getData('text/plain');
- dispatchPaste(text);
- }
- }
-}
-
-/**
- * Bind to right-click event and allow right-click copy and paste.
- *
- * **Logic**
- * If text is selected and right-click happens on selected text, then
- * do nothing to allow seamless copying.
- * If no text is selected or right-click is outside of the selection
- * area, then bring the terminal's input below the cursor, in order to
- * trigger the event on the textarea and allow-right click paste, without
- * caring about disappearing selection.
- * @param {ClipboardEvent} ev The original paste event to be handled
- * @param {Terminal} term The terminal on which to apply the handled paste event
- */
-function rightClickHandler(ev, term) {
- var s = document.getSelection(),
- selectedText = prepareTextForClipboard(s.toString()),
- clickIsOnSelection = false;
-
- if (s.rangeCount) {
- var r = s.getRangeAt(0),
- cr = r.getClientRects(),
- x = ev.clientX,
- y = ev.clientY,
- i,
- rect;
-
- for (i = 0; i < cr.length; i++) {
- rect = cr[i];
- clickIsOnSelection = x > rect.left && x < rect.right && y > rect.top && y < rect.bottom;
-
- if (clickIsOnSelection) {
- break;
- }
- }
- // If we clicked on selection and selection is not a single space,
- // then mark the right click as copy-only. We check for the single
- // space selection, as this can happen when clicking on an &nbsp;
- // and there is not much pointing in copying a single space.
- if (selectedText.match(/^\s$/) || !selectedText.length) {
- clickIsOnSelection = false;
- }
- }
-
- // Bring textarea at the cursor position
- if (!clickIsOnSelection) {
- term.textarea.style.position = 'fixed';
- term.textarea.style.width = '20px';
- term.textarea.style.height = '20px';
- term.textarea.style.left = x - 10 + 'px';
- term.textarea.style.top = y - 10 + 'px';
- term.textarea.style.zIndex = 1000;
- term.textarea.focus();
-
- // Reset the terminal textarea's styling
- setTimeout(function () {
- term.textarea.style.position = null;
- term.textarea.style.width = null;
- term.textarea.style.height = null;
- term.textarea.style.left = null;
- term.textarea.style.top = null;
- term.textarea.style.zIndex = null;
- }, 4);
- }
-}
-
-exports.prepareTextForClipboard = prepareTextForClipboard;
-exports.copyHandler = copyHandler;
-exports.pasteHandler = pasteHandler;
-exports.rightClickHandler = rightClickHandler;
-
-},{}],5:[function(_dereq_,module,exports){
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.isMSWindows = exports.isIphone = exports.isIpad = exports.isMac = exports.isMSIE = exports.isFirefox = undefined;
-
-var _Generic = _dereq_('./Generic.js');
-
-var isNode = typeof navigator == 'undefined' ? true : false; /**
- * xterm.js: xterm, in the browser
- * Copyright (c) 2016, SourceLair Private Company <www.sourcelair.com> (MIT License)
- */
-
-/**
- * Browser utilities module. This module contains attributes and methods to help with
- * identifying the current browser and platform.
- * @module xterm/utils/Browser
- */
-
-var userAgent = isNode ? 'node' : navigator.userAgent;
-var platform = isNode ? 'node' : navigator.platform;
-
-var isFirefox = exports.isFirefox = !!~userAgent.indexOf('Firefox');
-var isMSIE = exports.isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident');
-
-// Find the users platform. We use this to interpret the meta key
-// and ISO third level shifts.
-// http://stackoverflow.com/q/19877924/577598
-var isMac = exports.isMac = (0, _Generic.contains)(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform);
-var isIpad = exports.isIpad = platform === 'iPad';
-var isIphone = exports.isIphone = platform === 'iPhone';
-var isMSWindows = exports.isMSWindows = (0, _Generic.contains)(['Windows', 'Win16', 'Win32', 'WinCE'], platform);
-
-},{"./Generic.js":6}],6:[function(_dereq_,module,exports){
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-/**
- * xterm.js: xterm, in the browser
- * Copyright (c) 2016, SourceLair Private Company <www.sourcelair.com> (MIT License)
- */
-
-/**
- * Generic utilities module. This module contains generic methods that can be helpful at
- * different parts of the code base.
- * @module xterm/utils/Generic
- */
-
-/**
- * Return if the given array contains the given element
- * @param {Array} array The array to search for the given element.
- * @param {Object} el The element to look for into the array
- */
-var contains = exports.contains = function contains(arr, el) {
- return arr.indexOf(el) >= 0;
-};
-
-},{}],7:[function(_dereq_,module,exports){
-'use strict';var _typeof=typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"?function(obj){return typeof obj;}:function(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj;};/**
- * xterm.js: xterm, in the browser
- * Copyright (c) 2014-2014, SourceLair Private Company <www.sourcelair.com> (MIT License)
- * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
- * https://github.com/chjj/term.js
- *
- * 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.
- *
- * Originally forked from (with the author's permission):
- * Fabrice Bellard's javascript vt100 for jslinux:
- * http://bellard.org/jslinux/
- * Copyright (c) 2011 Fabrice Bellard
- * The original design remains. The terminal itself
- * has been extended to include xterm CSI codes, among
- * other features.
- */var _CompositionHelper=_dereq_('./CompositionHelper.js');var _EventEmitter=_dereq_('./EventEmitter.js');var _Viewport=_dereq_('./Viewport.js');var _Clipboard=_dereq_('./handlers/Clipboard.js');var _Browser=_dereq_('./utils/Browser');var Browser=_interopRequireWildcard(_Browser);function _interopRequireWildcard(obj){if(obj&&obj.__esModule){return obj;}else{var newObj={};if(obj!=null){for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key))newObj[key]=obj[key];}}newObj.default=obj;return newObj;}}/**
- * Terminal Emulation References:
- * http://vt100.net/
- * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt
- * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
- * http://invisible-island.net/vttest/
- * http://www.inwap.com/pdp10/ansicode.txt
- * http://linux.die.net/man/4/console_codes
- * http://linux.die.net/man/7/urxvt
- */// Let it work inside Node.js for automated testing purposes.
-var document=typeof window!='undefined'?window.document:null;/**
- * States
- */var normal=0,escaped=1,csi=2,osc=3,charset=4,dcs=5,ignore=6;/**
- * Terminal
- *//**
- * Creates a new `Terminal` object.
- *
- * @param {object} options An object containing a set of options, the available options are:
- * - `cursorBlink` (boolean): Whether the terminal cursor blinks
- * - `cols` (number): The number of columns of the terminal (horizontal size)
- * - `rows` (number): The number of rows of the terminal (vertical size)
- *
- * @public
- * @class Xterm Xterm
- * @alias module:xterm/src/xterm
- */function Terminal(options){var self=this;if(!(this instanceof Terminal)){return new Terminal(arguments[0],arguments[1],arguments[2]);}self.browser=Browser;self.cancel=Terminal.cancel;_EventEmitter.EventEmitter.call(this);if(typeof options==='number'){options={cols:arguments[0],rows:arguments[1],handler:arguments[2]};}options=options||{};Object.keys(Terminal.defaults).forEach(function(key){if(options[key]==null){options[key]=Terminal.options[key];if(Terminal[key]!==Terminal.defaults[key]){options[key]=Terminal[key];}}self[key]=options[key];});if(options.colors.length===8){options.colors=options.colors.concat(Terminal._colors.slice(8));}else if(options.colors.length===16){options.colors=options.colors.concat(Terminal._colors.slice(16));}else if(options.colors.length===10){options.colors=options.colors.slice(0,-2).concat(Terminal._colors.slice(8,-2),options.colors.slice(-2));}else if(options.colors.length===18){options.colors=options.colors.concat(Terminal._colors.slice(16,-2),options.colors.slice(-2));}this.colors=options.colors;this.options=options;// this.context = options.context || window;
-// this.document = options.document || document;
-this.parent=options.body||options.parent||(document?document.getElementsByTagName('body')[0]:null);this.cols=options.cols||options.geometry[0];this.rows=options.rows||options.geometry[1];this.geometry=[this.cols,this.rows];if(options.handler){this.on('data',options.handler);}/**
- * The scroll position of the y cursor, ie. ybase + y = the y position within the entire
- * buffer
- */this.ybase=0;/**
- * The scroll position of the viewport
- */this.ydisp=0;/**
- * The cursor's x position after ybase
- */this.x=0;/**
- * The cursor's y position after ybase
- */this.y=0;/**
- * Used to debounce the refresh function
- */this.isRefreshing=false;/**
- * Whether there is a full terminal refresh queued
- */this.cursorState=0;this.cursorHidden=false;this.convertEol;this.state=0;this.queue='';this.scrollTop=0;this.scrollBottom=this.rows-1;this.customKeydownHandler=null;// modes
-this.applicationKeypad=false;this.applicationCursor=false;this.originMode=false;this.insertMode=false;this.wraparoundMode=true;// defaults: xterm - true, vt100 - false
-this.normal=null;// charset
-this.charset=null;this.gcharset=null;this.glevel=0;this.charsets=[null];// mouse properties
-this.decLocator;this.x10Mouse;this.vt200Mouse;this.vt300Mouse;this.normalMouse;this.mouseEvents;this.sendFocus;this.utfMouse;this.sgrMouse;this.urxvtMouse;// misc
-this.element;this.children;this.refreshStart;this.refreshEnd;this.savedX;this.savedY;this.savedCols;// stream
-this.readable=true;this.writable=true;this.defAttr=0<<18|257<<9|256<<0;this.curAttr=this.defAttr;this.params=[];this.currentParam=0;this.prefix='';this.postfix='';// leftover surrogate high from previous write invocation
-this.surrogate_high='';/**
- * An array of all lines in the entire buffer, including the prompt. The lines are array of
- * characters which are 2-length arrays where [0] is an attribute and [1] is the character.
- */this.lines=[];var i=this.rows;while(i--){this.lines.push(this.blankLine());}this.tabs;this.setupStops();// Store if user went browsing history in scrollback
-this.userScrolling=false;}inherits(Terminal,_EventEmitter.EventEmitter);/**
- * back_color_erase feature for xterm.
- */Terminal.prototype.eraseAttr=function(){// if (this.is('screen')) return this.defAttr;
-return this.defAttr&~0x1ff|this.curAttr&0x1ff;};/**
- * Colors
- */// Colors 0-15
-Terminal.tangoColors=[// dark:
-'#2e3436','#cc0000','#4e9a06','#c4a000','#3465a4','#75507b','#06989a','#d3d7cf',// bright:
-'#555753','#ef2929','#8ae234','#fce94f','#729fcf','#ad7fa8','#34e2e2','#eeeeec'];// Colors 0-15 + 16-255
-// Much thanks to TooTallNate for writing this.
-Terminal.colors=function(){var colors=Terminal.tangoColors.slice(),r=[0x00,0x5f,0x87,0xaf,0xd7,0xff],i;// 16-231
-i=0;for(;i<216;i++){out(r[i/36%6|0],r[i/6%6|0],r[i%6]);}// 232-255 (grey)
-i=0;for(;i<24;i++){r=8+i*10;out(r,r,r);}function out(r,g,b){colors.push('#'+hex(r)+hex(g)+hex(b));}function hex(c){c=c.toString(16);return c.length<2?'0'+c:c;}return colors;}();Terminal._colors=Terminal.colors.slice();Terminal.vcolors=function(){var out=[],colors=Terminal.colors,i=0,color;for(;i<256;i++){color=parseInt(colors[i].substring(1),16);out.push([color>>16&0xff,color>>8&0xff,color&0xff]);}return out;}();/**
- * Options
- */Terminal.defaults={colors:Terminal.colors,theme:'default',convertEol:false,termName:'xterm',geometry:[80,24],cursorBlink:false,visualBell:false,popOnBell:false,scrollback:1000,screenKeys:false,debug:false,cancelEvents:false// programFeatures: false,
-// focusKeys: false,
-};Terminal.options={};Terminal.focus=null;each(keys(Terminal.defaults),function(key){Terminal[key]=Terminal.defaults[key];Terminal.options[key]=Terminal.defaults[key];});/**
- * Focus the terminal. Delegates focus handling to the terminal's DOM element.
- */Terminal.prototype.focus=function(){return this.textarea.focus();};/**
- * Retrieves an option's value from the terminal.
- * @param {string} key The option key.
- */Terminal.prototype.getOption=function(key,value){if(!(key in Terminal.defaults)){throw new Error('No option with key "'+key+'"');}if(typeof this.options[key]!=='undefined'){return this.options[key];}return this[key];};/**
- * Sets an option on the terminal.
- * @param {string} key The option key.
- * @param {string} value The option value.
- */Terminal.prototype.setOption=function(key,value){if(!(key in Terminal.defaults)){throw new Error('No option with key "'+key+'"');}this[key]=value;this.options[key]=value;};/**
- * Binds the desired focus behavior on a given terminal object.
- *
- * @static
- */Terminal.bindFocus=function(term){on(term.textarea,'focus',function(ev){if(term.sendFocus){term.send('\x1b[I');}term.element.classList.add('focus');term.showCursor();Terminal.focus=term;term.emit('focus',{terminal:term});});};/**
- * Blur the terminal. Delegates blur handling to the terminal's DOM element.
- */Terminal.prototype.blur=function(){return this.textarea.blur();};/**
- * Binds the desired blur behavior on a given terminal object.
- *
- * @static
- */Terminal.bindBlur=function(term){on(term.textarea,'blur',function(ev){term.refresh(term.y,term.y);if(term.sendFocus){term.send('\x1b[O');}term.element.classList.remove('focus');Terminal.focus=null;term.emit('blur',{terminal:term});});};/**
- * Initialize default behavior
- */Terminal.prototype.initGlobal=function(){var term=this;Terminal.bindKeys(this);Terminal.bindFocus(this);Terminal.bindBlur(this);// Bind clipboard functionality
-on(this.element,'copy',function(ev){_Clipboard.copyHandler.call(this,ev,term);});on(this.textarea,'paste',function(ev){_Clipboard.pasteHandler.call(this,ev,term);});function rightClickHandlerWrapper(ev){_Clipboard.rightClickHandler.call(this,ev,term);}if(term.browser.isFirefox){on(this.element,'mousedown',function(ev){if(ev.button==2){rightClickHandlerWrapper(ev);}});}else{on(this.element,'contextmenu',rightClickHandlerWrapper);}};/**
- * Apply key handling to the terminal
- */Terminal.bindKeys=function(term){on(term.element,'keydown',function(ev){if(document.activeElement!=this){return;}term.keyDown(ev);},true);on(term.element,'keypress',function(ev){if(document.activeElement!=this){return;}term.keyPress(ev);},true);on(term.element,'keyup',term.focus.bind(term));on(term.textarea,'keydown',function(ev){term.keyDown(ev);},true);on(term.textarea,'keypress',function(ev){term.keyPress(ev);// Truncate the textarea's value, since it is not needed
-this.value='';},true);on(term.textarea,'compositionstart',term.compositionHelper.compositionstart.bind(term.compositionHelper));on(term.textarea,'compositionupdate',term.compositionHelper.compositionupdate.bind(term.compositionHelper));on(term.textarea,'compositionend',term.compositionHelper.compositionend.bind(term.compositionHelper));term.on('refresh',term.compositionHelper.updateCompositionElements.bind(term.compositionHelper));};/**
- * Insert the given row to the terminal or produce a new one
- * if no row argument is passed. Return the inserted row.
- * @param {HTMLElement} row (optional) The row to append to the terminal.
- */Terminal.prototype.insertRow=function(row){if((typeof row==='undefined'?'undefined':_typeof(row))!='object'){row=document.createElement('div');}this.rowContainer.appendChild(row);this.children.push(row);return row;};/**
- * Opens the terminal within an element.
- *
- * @param {HTMLElement} parent The element to create the terminal within.
- */Terminal.prototype.open=function(parent){var self=this,i=0,div;this.parent=parent||this.parent;if(!this.parent){throw new Error('Terminal requires a parent element.');}// Grab global elements
-this.context=this.parent.ownerDocument.defaultView;this.document=this.parent.ownerDocument;this.body=this.document.getElementsByTagName('body')[0];//Create main element container
-this.element=this.document.createElement('div');this.element.classList.add('terminal');this.element.classList.add('xterm');this.element.classList.add('xterm-theme-'+this.theme);this.element.style.height;this.element.setAttribute('tabindex',0);this.viewportElement=document.createElement('div');this.viewportElement.classList.add('xterm-viewport');this.element.appendChild(this.viewportElement);this.viewportScrollArea=document.createElement('div');this.viewportScrollArea.classList.add('xterm-scroll-area');this.viewportElement.appendChild(this.viewportScrollArea);// Create the container that will hold the lines of the terminal and then
-// produce the lines the lines.
-this.rowContainer=document.createElement('div');this.rowContainer.classList.add('xterm-rows');this.element.appendChild(this.rowContainer);this.children=[];// Create the container that will hold helpers like the textarea for
-// capturing DOM Events. Then produce the helpers.
-this.helperContainer=document.createElement('div');this.helperContainer.classList.add('xterm-helpers');// TODO: This should probably be inserted once it's filled to prevent an additional layout
-this.element.appendChild(this.helperContainer);this.textarea=document.createElement('textarea');this.textarea.classList.add('xterm-helper-textarea');this.textarea.setAttribute('autocorrect','off');this.textarea.setAttribute('autocapitalize','off');this.textarea.setAttribute('spellcheck','false');this.textarea.tabIndex=0;this.textarea.addEventListener('focus',function(){self.emit('focus',{terminal:self});});this.textarea.addEventListener('blur',function(){self.emit('blur',{terminal:self});});this.helperContainer.appendChild(this.textarea);this.compositionView=document.createElement('div');this.compositionView.classList.add('composition-view');this.compositionHelper=new _CompositionHelper.CompositionHelper(this.textarea,this.compositionView,this);this.helperContainer.appendChild(this.compositionView);this.charMeasureElement=document.createElement('div');this.charMeasureElement.classList.add('xterm-char-measure-element');this.charMeasureElement.innerHTML='W';this.helperContainer.appendChild(this.charMeasureElement);for(;i<this.rows;i++){this.insertRow();}this.parent.appendChild(this.element);this.viewport=new _Viewport.Viewport(this,this.viewportElement,this.viewportScrollArea,this.charMeasureElement);// Draw the screen.
-this.refresh(0,this.rows-1);// Initialize global actions that
-// need to be taken on the document.
-this.initGlobal();// Ensure there is a Terminal.focus.
-this.focus();on(this.element,'click',function(){var selection=document.getSelection(),collapsed=selection.isCollapsed,isRange=typeof collapsed=='boolean'?!collapsed:selection.type=='Range';if(!isRange){self.focus();}});// Listen for mouse events and translate
-// them into terminal mouse protocols.
-this.bindMouse();// Figure out whether boldness affects
-// the character width of monospace fonts.
-if(Terminal.brokenBold==null){Terminal.brokenBold=isBoldBroken(this.document);}this.emit('open');};/**
- * Attempts to load an add-on using CommonJS or RequireJS (whichever is available).
- * @param {string} addon The name of the addon to load
- * @static
- */Terminal.loadAddon=function(addon,callback){if((typeof exports==='undefined'?'undefined':_typeof(exports))==='object'&&(typeof module==='undefined'?'undefined':_typeof(module))==='object'){// CommonJS
-return _dereq_('../addons/'+addon);}else if(typeof define=='function'){// RequireJS
-return _dereq_(['../addons/'+addon+'/'+addon],callback);}else{console.error('Cannot load a module without a CommonJS or RequireJS environment.');return false;}};/**
- * XTerm mouse events
- * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
- * To better understand these
- * the xterm code is very helpful:
- * Relevant files:
- * button.c, charproc.c, misc.c
- * Relevant functions in xterm/button.c:
- * BtnCode, EmitButtonCode, EditorButton, SendMousePosition
- */Terminal.prototype.bindMouse=function(){var el=this.element,self=this,pressed=32;// mouseup, mousedown, wheel
-// left click: ^[[M 3<^[[M#3<
-// wheel up: ^[[M`3>
-function sendButton(ev){var button,pos;// get the xterm-style button
-button=getButton(ev);// get mouse coordinates
-pos=getCoords(ev);if(!pos)return;sendEvent(button,pos);switch(ev.overrideType||ev.type){case'mousedown':pressed=button;break;case'mouseup':// keep it at the left
-// button, just in case.
-pressed=32;break;case'wheel':// nothing. don't
-// interfere with
-// `pressed`.
-break;}}// motion example of a left click:
-// ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
-function sendMove(ev){var button=pressed,pos;pos=getCoords(ev);if(!pos)return;// buttons marked as motions
-// are incremented by 32
-button+=32;sendEvent(button,pos);}// encode button and
-// position to characters
-function encode(data,ch){if(!self.utfMouse){if(ch===255)return data.push(0);if(ch>127)ch=127;data.push(ch);}else{if(ch===2047)return data.push(0);if(ch<127){data.push(ch);}else{if(ch>2047)ch=2047;data.push(0xC0|ch>>6);data.push(0x80|ch&0x3F);}}}// send a mouse event:
-// regular/utf8: ^[[M Cb Cx Cy
-// urxvt: ^[[ Cb ; Cx ; Cy M
-// sgr: ^[[ Cb ; Cx ; Cy M/m
-// vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
-// locator: CSI P e ; P b ; P r ; P c ; P p & w
-function sendEvent(button,pos){// self.emit('mouse', {
-// x: pos.x - 32,
-// y: pos.x - 32,
-// button: button
-// });
-if(self.vt300Mouse){// NOTE: Unstable.
-// http://www.vt100.net/docs/vt3xx-gp/chapter15.html
-button&=3;pos.x-=32;pos.y-=32;var data='\x1b[24';if(button===0)data+='1';else if(button===1)data+='3';else if(button===2)data+='5';else if(button===3)return;else data+='0';data+='~['+pos.x+','+pos.y+']\r';self.send(data);return;}if(self.decLocator){// NOTE: Unstable.
-button&=3;pos.x-=32;pos.y-=32;if(button===0)button=2;else if(button===1)button=4;else if(button===2)button=6;else if(button===3)button=3;self.send('\x1b['+button+';'+(button===3?4:0)+';'+pos.y+';'+pos.x+';'+(pos.page||0)+'&w');return;}if(self.urxvtMouse){pos.x-=32;pos.y-=32;pos.x++;pos.y++;self.send('\x1b['+button+';'+pos.x+';'+pos.y+'M');return;}if(self.sgrMouse){pos.x-=32;pos.y-=32;self.send('\x1b[<'+((button&3)===3?button&~3:button)+';'+pos.x+';'+pos.y+((button&3)===3?'m':'M'));return;}var data=[];encode(data,button);encode(data,pos.x);encode(data,pos.y);self.send('\x1b[M'+String.fromCharCode.apply(String,data));}function getButton(ev){var button,shift,meta,ctrl,mod;// two low bits:
-// 0 = left
-// 1 = middle
-// 2 = right
-// 3 = release
-// wheel up/down:
-// 1, and 2 - with 64 added
-switch(ev.overrideType||ev.type){case'mousedown':button=ev.button!=null?+ev.button:ev.which!=null?ev.which-1:null;if(self.browser.isMSIE){button=button===1?0:button===4?1:button;}break;case'mouseup':button=3;break;case'DOMMouseScroll':button=ev.detail<0?64:65;break;case'wheel':button=ev.wheelDeltaY>0?64:65;break;}// next three bits are the modifiers:
-// 4 = shift, 8 = meta, 16 = control
-shift=ev.shiftKey?4:0;meta=ev.metaKey?8:0;ctrl=ev.ctrlKey?16:0;mod=shift|meta|ctrl;// no mods
-if(self.vt200Mouse){// ctrl only
-mod&=ctrl;}else if(!self.normalMouse){mod=0;}// increment to SP
-button=32+(mod<<2)+button;return button;}// mouse coordinates measured in cols/rows
-function getCoords(ev){var x,y,w,h,el;// ignore browsers without pageX for now
-if(ev.pageX==null)return;x=ev.pageX;y=ev.pageY;el=self.element;// should probably check offsetParent
-// but this is more portable
-while(el&&el!==self.document.documentElement){x-=el.offsetLeft;y-=el.offsetTop;el='offsetParent'in el?el.offsetParent:el.parentNode;}// convert to cols/rows
-w=self.element.clientWidth;h=self.element.clientHeight;x=Math.ceil(x/w*self.cols);y=Math.ceil(y/h*self.rows);// be sure to avoid sending
-// bad positions to the program
-if(x<0)x=0;if(x>self.cols)x=self.cols;if(y<0)y=0;if(y>self.rows)y=self.rows;// xterm sends raw bytes and
-// starts at 32 (SP) for each.
-x+=32;y+=32;return{x:x,y:y,type:'wheel'};}on(el,'mousedown',function(ev){if(!self.mouseEvents)return;// send the button
-sendButton(ev);// ensure focus
-self.focus();// fix for odd bug
-//if (self.vt200Mouse && !self.normalMouse) {
-if(self.vt200Mouse){ev.overrideType='mouseup';sendButton(ev);return self.cancel(ev);}// bind events
-if(self.normalMouse)on(self.document,'mousemove',sendMove);// x10 compatibility mode can't send button releases
-if(!self.x10Mouse){on(self.document,'mouseup',function up(ev){sendButton(ev);if(self.normalMouse)off(self.document,'mousemove',sendMove);off(self.document,'mouseup',up);return self.cancel(ev);});}return self.cancel(ev);});//if (self.normalMouse) {
-// on(self.document, 'mousemove', sendMove);
-//}
-on(el,'wheel',function(ev){if(!self.mouseEvents)return;if(self.x10Mouse||self.vt300Mouse||self.decLocator)return;sendButton(ev);return self.cancel(ev);});// allow wheel scrolling in
-// the shell for example
-on(el,'wheel',function(ev){if(self.mouseEvents)return;self.viewport.onWheel(ev);return self.cancel(ev);});};/**
- * Destroys the terminal.
- */Terminal.prototype.destroy=function(){this.readable=false;this.writable=false;this._events={};this.handler=function(){};this.write=function(){};if(this.element.parentNode){this.element.parentNode.removeChild(this.element);}//this.emit('close');
-};/**
- * Flags used to render terminal text properly
- */Terminal.flags={BOLD:1,UNDERLINE:2,BLINK:4,INVERSE:8,INVISIBLE:16};/**
- * Refreshes (re-renders) terminal content within two rows (inclusive)
- *
- * Rendering Engine:
- *
- * In the screen buffer, each character is stored as a an array with a character
- * and a 32-bit integer:
- * - First value: a utf-16 character.
- * - Second value:
- * - Next 9 bits: background color (0-511).
- * - Next 9 bits: foreground color (0-511).
- * - Next 14 bits: a mask for misc. flags:
- * - 1=bold
- * - 2=underline
- * - 4=blink
- * - 8=inverse
- * - 16=invisible
- *
- * @param {number} start The row to start from (between 0 and terminal's height terminal - 1)
- * @param {number} end The row to end at (between fromRow and terminal's height terminal - 1)
- * @param {boolean} queue Whether the refresh should ran right now or be queued
- */Terminal.prototype.refresh=function(start,end,queue){var self=this;// queue defaults to true
-queue=typeof queue=='undefined'?true:queue;/**
- * The refresh queue allows refresh to execute only approximately 30 times a second. For
- * commands that pass a significant amount of output to the write function, this prevents the
- * terminal from maxing out the CPU and making the UI unresponsive. While commands can still
- * run beyond what they do on the terminal, it is far better with a debounce in place as
- * every single terminal manipulation does not need to be constructed in the DOM.
- *
- * A side-effect of this is that it makes ^C to interrupt a process seem more responsive.
- */if(queue){// If refresh should be queued, order the refresh and return.
-if(this._refreshIsQueued){// If a refresh has already been queued, just order a full refresh next
-this._fullRefreshNext=true;}else{setTimeout(function(){self.refresh(start,end,false);},34);this._refreshIsQueued=true;}return;}// If refresh should be run right now (not be queued), release the lock
-this._refreshIsQueued=false;// If multiple refreshes were requested, make a full refresh.
-if(this._fullRefreshNext){start=0;end=this.rows-1;this._fullRefreshNext=false;// reset lock
-}var x,y,i,line,out,ch,ch_width,width,data,attr,bg,fg,flags,row,parent,focused=document.activeElement;// If this is a big refresh, remove the terminal rows from the DOM for faster calculations
-if(end-start>=this.rows/2){parent=this.element.parentNode;if(parent){this.element.removeChild(this.rowContainer);}}width=this.cols;y=start;if(end>=this.rows.length){this.log('`end` is too large. Most likely a bad CSR.');end=this.rows.length-1;}for(;y<=end;y++){row=y+this.ydisp;line=this.lines[row];out='';if(this.y===y-(this.ybase-this.ydisp)&&this.cursorState&&!this.cursorHidden){x=this.x;}else{x=-1;}attr=this.defAttr;i=0;for(;i<width;i++){data=line[i][0];ch=line[i][1];ch_width=line[i][2];if(!ch_width)continue;if(i===x)data=-1;if(data!==attr){if(attr!==this.defAttr){out+='</span>';}if(data!==this.defAttr){if(data===-1){out+='<span class="reverse-video terminal-cursor';if(this.cursorBlink){out+=' blinking';}out+='">';}else{var classNames=[];bg=data&0x1ff;fg=data>>9&0x1ff;flags=data>>18;if(flags&Terminal.flags.BOLD){if(!Terminal.brokenBold){classNames.push('xterm-bold');}// See: XTerm*boldColors
-if(fg<8)fg+=8;}if(flags&Terminal.flags.UNDERLINE){classNames.push('xterm-underline');}if(flags&Terminal.flags.BLINK){classNames.push('xterm-blink');}// If inverse flag is on, then swap the foreground and background variables.
-if(flags&Terminal.flags.INVERSE){/* One-line variable swap in JavaScript: http://stackoverflow.com/a/16201730 */bg=[fg,fg=bg][0];// Should inverse just be before the
-// above boldColors effect instead?
-if(flags&1&&fg<8)fg+=8;}if(flags&Terminal.flags.INVISIBLE){classNames.push('xterm-hidden');}/**
- * Weird situation: Invert flag used black foreground and white background results
- * in invalid background color, positioned at the 256 index of the 256 terminal
- * color map. Pin the colors manually in such a case.
- *
- * Source: https://github.com/sourcelair/xterm.js/issues/57
- */if(flags&Terminal.flags.INVERSE){if(bg==257){bg=15;}if(fg==256){fg=0;}}if(bg<256){classNames.push('xterm-bg-color-'+bg);}if(fg<256){classNames.push('xterm-color-'+fg);}out+='<span';if(classNames.length){out+=' class="'+classNames.join(' ')+'"';}out+='>';}}}switch(ch){case'&':out+='&amp;';break;case'<':out+='&lt;';break;case'>':out+='&gt;';break;default:if(ch<=' '){out+='&nbsp;';}else{out+=ch;}break;}attr=data;}if(attr!==this.defAttr){out+='</span>';}this.children[y].innerHTML=out;}if(parent){this.element.appendChild(this.rowContainer);}this.emit('refresh',{element:this.element,start:start,end:end});};/**
- * Display the cursor element
- */Terminal.prototype.showCursor=function(){if(!this.cursorState){this.cursorState=1;this.refresh(this.y,this.y);}};/**
- * Scroll the terminal
- */Terminal.prototype.scroll=function(){var row;if(++this.ybase===this.scrollback){this.ybase=this.ybase/2|0;this.lines=this.lines.slice(-(this.ybase+this.rows)+1);}if(!this.userScrolling){this.ydisp=this.ybase;}// last line
-row=this.ybase+this.rows-1;// subtract the bottom scroll region
-row-=this.rows-1-this.scrollBottom;if(row===this.lines.length){// potential optimization:
-// pushing is faster than splicing
-// when they amount to the same
-// behavior.
-this.lines.push(this.blankLine());}else{// add our new line
-this.lines.splice(row,0,this.blankLine());}if(this.scrollTop!==0){if(this.ybase!==0){this.ybase--;if(!this.userScrolling){this.ydisp=this.ybase;}}this.lines.splice(this.ybase+this.scrollTop,1);}// this.maxRange();
-this.updateRange(this.scrollTop);this.updateRange(this.scrollBottom);this.emit('scroll',this.ydisp);};/**
- * Scroll the display of the terminal
- * @param {number} disp The number of lines to scroll down (negatives scroll up).
- * @param {boolean} suppressScrollEvent Don't emit the scroll event as scrollDisp. This is used
- * to avoid unwanted events being handled by the veiwport when the event was triggered from the
- * viewport originally.
- */Terminal.prototype.scrollDisp=function(disp,suppressScrollEvent){if(disp<0){this.userScrolling=true;}else if(disp+this.ydisp>=this.ybase){this.userScrolling=false;}this.ydisp+=disp;if(this.ydisp>this.ybase){this.ydisp=this.ybase;}else if(this.ydisp<0){this.ydisp=0;}if(!suppressScrollEvent){this.emit('scroll',this.ydisp);}this.refresh(0,this.rows-1);};/**
- * Scroll the display of the terminal by a number of pages.
- * @param {number} pageCount The number of pages to scroll (negative scrolls up).
- */Terminal.prototype.scrollPages=function(pageCount){this.scrollDisp(pageCount*(this.rows-1));};/**
- * Scrolls the display of the terminal to the top.
- */Terminal.prototype.scrollToTop=function(){this.scrollDisp(-this.ydisp);};/**
- * Scrolls the display of the terminal to the bottom.
- */Terminal.prototype.scrollToBottom=function(){this.scrollDisp(this.ybase-this.ydisp);};/**
- * Writes text to the terminal.
- * @param {string} text The text to write to the terminal.
- */Terminal.prototype.write=function(data){var l=data.length,i=0,j,cs,ch,code,low,ch_width,row;this.refreshStart=this.y;this.refreshEnd=this.y;// apply leftover surrogate high from last write
-if(this.surrogate_high){data=this.surrogate_high+data;this.surrogate_high='';}for(;i<l;i++){ch=data[i];// FIXME: higher chars than 0xa0 are not allowed in escape sequences
-// --> maybe move to default
-code=data.charCodeAt(i);if(0xD800<=code&&code<=0xDBFF){// we got a surrogate high
-// get surrogate low (next 2 bytes)
-low=data.charCodeAt(i+1);if(isNaN(low)){// end of data stream, save surrogate high
-this.surrogate_high=ch;continue;}code=(code-0xD800)*0x400+(low-0xDC00)+0x10000;ch+=data.charAt(i+1);}// surrogate low - already handled above
-if(0xDC00<=code&&code<=0xDFFF)continue;switch(this.state){case normal:switch(ch){case'\x07':this.bell();break;// '\n', '\v', '\f'
-case'\n':case'\x0b':case'\x0c':if(this.convertEol){this.x=0;}this.y++;if(this.y>this.scrollBottom){this.y--;this.scroll();}break;// '\r'
-case'\r':this.x=0;break;// '\b'
-case'\x08':if(this.x>0){this.x--;}break;// '\t'
-case'\t':this.x=this.nextStop();break;// shift out
-case'\x0e':this.setgLevel(1);break;// shift in
-case'\x0f':this.setgLevel(0);break;// '\e'
-case'\x1b':this.state=escaped;break;default:// ' '
-// calculate print space
-// expensive call, therefore we save width in line buffer
-ch_width=wcwidth(code);if(ch>=' '){if(this.charset&&this.charset[ch]){ch=this.charset[ch];}row=this.y+this.ybase;// insert combining char in last cell
-// FIXME: needs handling after cursor jumps
-if(!ch_width&&this.x){// dont overflow left
-if(this.lines[row][this.x-1]){if(!this.lines[row][this.x-1][2]){// found empty cell after fullwidth, need to go 2 cells back
-if(this.lines[row][this.x-2])this.lines[row][this.x-2][1]+=ch;}else{this.lines[row][this.x-1][1]+=ch;}this.updateRange(this.y);}break;}// goto next line if ch would overflow
-// TODO: needs a global min terminal width of 2
-if(this.x+ch_width-1>=this.cols){// autowrap - DECAWM
-if(this.wraparoundMode){this.x=0;this.y++;if(this.y>this.scrollBottom){this.y--;this.scroll();}}else{this.x=this.cols-1;if(ch_width===2)// FIXME: check for xterm behavior
-continue;}}row=this.y+this.ybase;// insert mode: move characters to right
-if(this.insertMode){// do this twice for a fullwidth char
-for(var moves=0;moves<ch_width;++moves){// remove last cell, if it's width is 0
-// we have to adjust the second last cell as well
-var removed=this.lines[this.y+this.ybase].pop();if(removed[2]===0&&this.lines[row][this.cols-2]&&this.lines[row][this.cols-2][2]===2)this.lines[row][this.cols-2]=[this.curAttr,' ',1];// insert empty cell at cursor
-this.lines[row].splice(this.x,0,[this.curAttr,' ',1]);}}this.lines[row][this.x]=[this.curAttr,ch,ch_width];this.x++;this.updateRange(this.y);// fullwidth char - set next cell width to zero and advance cursor
-if(ch_width===2){this.lines[row][this.x]=[this.curAttr,'',0];this.x++;}}break;}break;case escaped:switch(ch){// ESC [ Control Sequence Introducer ( CSI is 0x9b).
-case'[':this.params=[];this.currentParam=0;this.state=csi;break;// ESC ] Operating System Command ( OSC is 0x9d).
-case']':this.params=[];this.currentParam=0;this.state=osc;break;// ESC P Device Control String ( DCS is 0x90).
-case'P':this.params=[];this.currentParam=0;this.state=dcs;break;// ESC _ Application Program Command ( APC is 0x9f).
-case'_':this.state=ignore;break;// ESC ^ Privacy Message ( PM is 0x9e).
-case'^':this.state=ignore;break;// ESC c Full Reset (RIS).
-case'c':this.reset();break;// ESC E Next Line ( NEL is 0x85).
-// ESC D Index ( IND is 0x84).
-case'E':this.x=0;;case'D':this.index();break;// ESC M Reverse Index ( RI is 0x8d).
-case'M':this.reverseIndex();break;// ESC % Select default/utf-8 character set.
-// @ = default, G = utf-8
-case'%'://this.charset = null;
-this.setgLevel(0);this.setgCharset(0,Terminal.charsets.US);this.state=normal;i++;break;// ESC (,),*,+,-,. Designate G0-G2 Character Set.
-case'(':// <-- this seems to get all the attention
-case')':case'*':case'+':case'-':case'.':switch(ch){case'(':this.gcharset=0;break;case')':this.gcharset=1;break;case'*':this.gcharset=2;break;case'+':this.gcharset=3;break;case'-':this.gcharset=1;break;case'.':this.gcharset=2;break;}this.state=charset;break;// Designate G3 Character Set (VT300).
-// A = ISO Latin-1 Supplemental.
-// Not implemented.
-case'/':this.gcharset=3;this.state=charset;i--;break;// ESC N
-// Single Shift Select of G2 Character Set
-// ( SS2 is 0x8e). This affects next character only.
-case'N':break;// ESC O
-// Single Shift Select of G3 Character Set
-// ( SS3 is 0x8f). This affects next character only.
-case'O':break;// ESC n
-// Invoke the G2 Character Set as GL (LS2).
-case'n':this.setgLevel(2);break;// ESC o
-// Invoke the G3 Character Set as GL (LS3).
-case'o':this.setgLevel(3);break;// ESC |
-// Invoke the G3 Character Set as GR (LS3R).
-case'|':this.setgLevel(3);break;// ESC }
-// Invoke the G2 Character Set as GR (LS2R).
-case'}':this.setgLevel(2);break;// ESC ~
-// Invoke the G1 Character Set as GR (LS1R).
-case'~':this.setgLevel(1);break;// ESC 7 Save Cursor (DECSC).
-case'7':this.saveCursor();this.state=normal;break;// ESC 8 Restore Cursor (DECRC).
-case'8':this.restoreCursor();this.state=normal;break;// ESC # 3 DEC line height/width
-case'#':this.state=normal;i++;break;// ESC H Tab Set (HTS is 0x88).
-case'H':this.tabSet();break;// ESC = Application Keypad (DECKPAM).
-case'=':this.log('Serial port requested application keypad.');this.applicationKeypad=true;this.viewport.syncScrollArea();this.state=normal;break;// ESC > Normal Keypad (DECKPNM).
-case'>':this.log('Switching back to normal keypad.');this.applicationKeypad=false;this.viewport.syncScrollArea();this.state=normal;break;default:this.state=normal;this.error('Unknown ESC control: %s.',ch);break;}break;case charset:switch(ch){case'0':// DEC Special Character and Line Drawing Set.
-cs=Terminal.charsets.SCLD;break;case'A':// UK
-cs=Terminal.charsets.UK;break;case'B':// United States (USASCII).
-cs=Terminal.charsets.US;break;case'4':// Dutch
-cs=Terminal.charsets.Dutch;break;case'C':// Finnish
-case'5':cs=Terminal.charsets.Finnish;break;case'R':// French
-cs=Terminal.charsets.French;break;case'Q':// FrenchCanadian
-cs=Terminal.charsets.FrenchCanadian;break;case'K':// German
-cs=Terminal.charsets.German;break;case'Y':// Italian
-cs=Terminal.charsets.Italian;break;case'E':// NorwegianDanish
-case'6':cs=Terminal.charsets.NorwegianDanish;break;case'Z':// Spanish
-cs=Terminal.charsets.Spanish;break;case'H':// Swedish
-case'7':cs=Terminal.charsets.Swedish;break;case'=':// Swiss
-cs=Terminal.charsets.Swiss;break;case'/':// ISOLatin (actually /A)
-cs=Terminal.charsets.ISOLatin;i++;break;default:// Default
-cs=Terminal.charsets.US;break;}this.setgCharset(this.gcharset,cs);this.gcharset=null;this.state=normal;break;case osc:// OSC Ps ; Pt ST
-// OSC Ps ; Pt BEL
-// Set Text Parameters.
-if(ch==='\x1b'||ch==='\x07'){if(ch==='\x1b')i++;this.params.push(this.currentParam);switch(this.params[0]){case 0:case 1:case 2:if(this.params[1]){this.title=this.params[1];this.handleTitle(this.title);}break;case 3:// set X property
-break;case 4:case 5:// change dynamic colors
-break;case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:case 18:case 19:// change dynamic ui colors
-break;case 46:// change log file
-break;case 50:// dynamic font
-break;case 51:// emacs shell
-break;case 52:// manipulate selection data
-break;case 104:case 105:case 110:case 111:case 112:case 113:case 114:case 115:case 116:case 117:case 118:// reset colors
-break;}this.params=[];this.currentParam=0;this.state=normal;}else{if(!this.params.length){if(ch>='0'&&ch<='9'){this.currentParam=this.currentParam*10+ch.charCodeAt(0)-48;}else if(ch===';'){this.params.push(this.currentParam);this.currentParam='';}}else{this.currentParam+=ch;}}break;case csi:// '?', '>', '!'
-if(ch==='?'||ch==='>'||ch==='!'){this.prefix=ch;break;}// 0 - 9
-if(ch>='0'&&ch<='9'){this.currentParam=this.currentParam*10+ch.charCodeAt(0)-48;break;}// '$', '"', ' ', '\''
-if(ch==='$'||ch==='"'||ch===' '||ch==='\''){this.postfix=ch;break;}this.params.push(this.currentParam);this.currentParam=0;// ';'
-if(ch===';')break;this.state=normal;switch(ch){// CSI Ps A
-// Cursor Up Ps Times (default = 1) (CUU).
-case'A':this.cursorUp(this.params);break;// CSI Ps B
-// Cursor Down Ps Times (default = 1) (CUD).
-case'B':this.cursorDown(this.params);break;// CSI Ps C
-// Cursor Forward Ps Times (default = 1) (CUF).
-case'C':this.cursorForward(this.params);break;// CSI Ps D
-// Cursor Backward Ps Times (default = 1) (CUB).
-case'D':this.cursorBackward(this.params);break;// CSI Ps ; Ps H
-// Cursor Position [row;column] (default = [1,1]) (CUP).
-case'H':this.cursorPos(this.params);break;// CSI Ps J Erase in Display (ED).
-case'J':this.eraseInDisplay(this.params);break;// CSI Ps K Erase in Line (EL).
-case'K':this.eraseInLine(this.params);break;// CSI Pm m Character Attributes (SGR).
-case'm':if(!this.prefix){this.charAttributes(this.params);}break;// CSI Ps n Device Status Report (DSR).
-case'n':if(!this.prefix){this.deviceStatus(this.params);}break;/**
- * Additions
- */// CSI Ps @
-// Insert Ps (Blank) Character(s) (default = 1) (ICH).
-case'@':this.insertChars(this.params);break;// CSI Ps E
-// Cursor Next Line Ps Times (default = 1) (CNL).
-case'E':this.cursorNextLine(this.params);break;// CSI Ps F
-// Cursor Preceding Line Ps Times (default = 1) (CNL).
-case'F':this.cursorPrecedingLine(this.params);break;// CSI Ps G
-// Cursor Character Absolute [column] (default = [row,1]) (CHA).
-case'G':this.cursorCharAbsolute(this.params);break;// CSI Ps L
-// Insert Ps Line(s) (default = 1) (IL).
-case'L':this.insertLines(this.params);break;// CSI Ps M
-// Delete Ps Line(s) (default = 1) (DL).
-case'M':this.deleteLines(this.params);break;// CSI Ps P
-// Delete Ps Character(s) (default = 1) (DCH).
-case'P':this.deleteChars(this.params);break;// CSI Ps X
-// Erase Ps Character(s) (default = 1) (ECH).
-case'X':this.eraseChars(this.params);break;// CSI Pm ` Character Position Absolute
-// [column] (default = [row,1]) (HPA).
-case'`':this.charPosAbsolute(this.params);break;// 141 61 a * HPR -
-// Horizontal Position Relative
-case'a':this.HPositionRelative(this.params);break;// CSI P s c
-// Send Device Attributes (Primary DA).
-// CSI > P s c
-// Send Device Attributes (Secondary DA)
-case'c':this.sendDeviceAttributes(this.params);break;// CSI Pm d
-// Line Position Absolute [row] (default = [1,column]) (VPA).
-case'd':this.linePosAbsolute(this.params);break;// 145 65 e * VPR - Vertical Position Relative
-case'e':this.VPositionRelative(this.params);break;// CSI Ps ; Ps f
-// Horizontal and Vertical Position [row;column] (default =
-// [1,1]) (HVP).
-case'f':this.HVPosition(this.params);break;// CSI Pm h Set Mode (SM).
-// CSI ? Pm h - mouse escape codes, cursor escape codes
-case'h':this.setMode(this.params);break;// CSI Pm l Reset Mode (RM).
-// CSI ? Pm l
-case'l':this.resetMode(this.params);break;// CSI Ps ; Ps r
-// Set Scrolling Region [top;bottom] (default = full size of win-
-// dow) (DECSTBM).
-// CSI ? Pm r
-case'r':this.setScrollRegion(this.params);break;// CSI s
-// Save cursor (ANSI.SYS).
-case's':this.saveCursor(this.params);break;// CSI u
-// Restore cursor (ANSI.SYS).
-case'u':this.restoreCursor(this.params);break;/**
- * Lesser Used
- */// CSI Ps I
-// Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
-case'I':this.cursorForwardTab(this.params);break;// CSI Ps S Scroll up Ps lines (default = 1) (SU).
-case'S':this.scrollUp(this.params);break;// CSI Ps T Scroll down Ps lines (default = 1) (SD).
-// CSI Ps ; Ps ; Ps ; Ps ; Ps T
-// CSI > Ps; Ps T
-case'T':// if (this.prefix === '>') {
-// this.resetTitleModes(this.params);
-// break;
-// }
-// if (this.params.length > 2) {
-// this.initMouseTracking(this.params);
-// break;
-// }
-if(this.params.length<2&&!this.prefix){this.scrollDown(this.params);}break;// CSI Ps Z
-// Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
-case'Z':this.cursorBackwardTab(this.params);break;// CSI Ps b Repeat the preceding graphic character Ps times (REP).
-case'b':this.repeatPrecedingCharacter(this.params);break;// CSI Ps g Tab Clear (TBC).
-case'g':this.tabClear(this.params);break;// CSI Pm i Media Copy (MC).
-// CSI ? Pm i
-// case 'i':
-// this.mediaCopy(this.params);
-// break;
-// CSI Pm m Character Attributes (SGR).
-// CSI > Ps; Ps m
-// case 'm': // duplicate
-// if (this.prefix === '>') {
-// this.setResources(this.params);
-// } else {
-// this.charAttributes(this.params);
-// }
-// break;
-// CSI Ps n Device Status Report (DSR).
-// CSI > Ps n
-// case 'n': // duplicate
-// if (this.prefix === '>') {
-// this.disableModifiers(this.params);
-// } else {
-// this.deviceStatus(this.params);
-// }
-// break;
-// CSI > Ps p Set pointer mode.
-// CSI ! p Soft terminal reset (DECSTR).
-// CSI Ps$ p
-// Request ANSI mode (DECRQM).
-// CSI ? Ps$ p
-// Request DEC private mode (DECRQM).
-// CSI Ps ; Ps " p
-case'p':switch(this.prefix){// case '>':
-// this.setPointerMode(this.params);
-// break;
-case'!':this.softReset(this.params);break;// case '?':
-// if (this.postfix === '$') {
-// this.requestPrivateMode(this.params);
-// }
-// break;
-// default:
-// if (this.postfix === '"') {
-// this.setConformanceLevel(this.params);
-// } else if (this.postfix === '$') {
-// this.requestAnsiMode(this.params);
-// }
-// break;
-}break;// CSI Ps q Load LEDs (DECLL).
-// CSI Ps SP q
-// CSI Ps " q
-// case 'q':
-// if (this.postfix === ' ') {
-// this.setCursorStyle(this.params);
-// break;
-// }
-// if (this.postfix === '"') {
-// this.setCharProtectionAttr(this.params);
-// break;
-// }
-// this.loadLEDs(this.params);
-// break;
-// CSI Ps ; Ps r
-// Set Scrolling Region [top;bottom] (default = full size of win-
-// dow) (DECSTBM).
-// CSI ? Pm r
-// CSI Pt; Pl; Pb; Pr; Ps$ r
-// case 'r': // duplicate
-// if (this.prefix === '?') {
-// this.restorePrivateValues(this.params);
-// } else if (this.postfix === '$') {
-// this.setAttrInRectangle(this.params);
-// } else {
-// this.setScrollRegion(this.params);
-// }
-// break;
-// CSI s Save cursor (ANSI.SYS).
-// CSI ? Pm s
-// case 's': // duplicate
-// if (this.prefix === '?') {
-// this.savePrivateValues(this.params);
-// } else {
-// this.saveCursor(this.params);
-// }
-// break;
-// CSI Ps ; Ps ; Ps t
-// CSI Pt; Pl; Pb; Pr; Ps$ t
-// CSI > Ps; Ps t
-// CSI Ps SP t
-// case 't':
-// if (this.postfix === '$') {
-// this.reverseAttrInRectangle(this.params);
-// } else if (this.postfix === ' ') {
-// this.setWarningBellVolume(this.params);
-// } else {
-// if (this.prefix === '>') {
-// this.setTitleModeFeature(this.params);
-// } else {
-// this.manipulateWindow(this.params);
-// }
-// }
-// break;
-// CSI u Restore cursor (ANSI.SYS).
-// CSI Ps SP u
-// case 'u': // duplicate
-// if (this.postfix === ' ') {
-// this.setMarginBellVolume(this.params);
-// } else {
-// this.restoreCursor(this.params);
-// }
-// break;
-// CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v
-// case 'v':
-// if (this.postfix === '$') {
-// this.copyRectagle(this.params);
-// }
-// break;
-// CSI Pt ; Pl ; Pb ; Pr ' w
-// case 'w':
-// if (this.postfix === '\'') {
-// this.enableFilterRectangle(this.params);
-// }
-// break;
-// CSI Ps x Request Terminal Parameters (DECREQTPARM).
-// CSI Ps x Select Attribute Change Extent (DECSACE).
-// CSI Pc; Pt; Pl; Pb; Pr$ x
-// case 'x':
-// if (this.postfix === '$') {
-// this.fillRectangle(this.params);
-// } else {
-// this.requestParameters(this.params);
-// //this.__(this.params);
-// }
-// break;
-// CSI Ps ; Pu ' z
-// CSI Pt; Pl; Pb; Pr$ z
-// case 'z':
-// if (this.postfix === '\'') {
-// this.enableLocatorReporting(this.params);
-// } else if (this.postfix === '$') {
-// this.eraseRectangle(this.params);
-// }
-// break;
-// CSI Pm ' {
-// CSI Pt; Pl; Pb; Pr$ {
-// case '{':
-// if (this.postfix === '\'') {
-// this.setLocatorEvents(this.params);
-// } else if (this.postfix === '$') {
-// this.selectiveEraseRectangle(this.params);
-// }
-// break;
-// CSI Ps ' |
-// case '|':
-// if (this.postfix === '\'') {
-// this.requestLocatorPosition(this.params);
-// }
-// break;
-// CSI P m SP }
-// Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
-// case '}':
-// if (this.postfix === ' ') {
-// this.insertColumns(this.params);
-// }
-// break;
-// CSI P m SP ~
-// Delete P s Column(s) (default = 1) (DECDC), VT420 and up
-// case '~':
-// if (this.postfix === ' ') {
-// this.deleteColumns(this.params);
-// }
-// break;
-default:this.error('Unknown CSI code: %s.',ch);break;}this.prefix='';this.postfix='';break;case dcs:if(ch==='\x1b'||ch==='\x07'){if(ch==='\x1b')i++;switch(this.prefix){// User-Defined Keys (DECUDK).
-case'':break;// Request Status String (DECRQSS).
-// test: echo -e '\eP$q"p\e\\'
-case'$q':var pt=this.currentParam,valid=false;switch(pt){// DECSCA
-case'"q':pt='0"q';break;// DECSCL
-case'"p':pt='61"p';break;// DECSTBM
-case'r':pt=''+(this.scrollTop+1)+';'+(this.scrollBottom+1)+'r';break;// SGR
-case'm':pt='0m';break;default:this.error('Unknown DCS Pt: %s.',pt);pt='';break;}this.send('\x1bP'+ +valid+'$r'+pt+'\x1b\\');break;// Set Termcap/Terminfo Data (xterm, experimental).
-case'+p':break;// Request Termcap/Terminfo String (xterm, experimental)
-// Regular xterm does not even respond to this sequence.
-// This can cause a small glitch in vim.
-// test: echo -ne '\eP+q6b64\e\\'
-case'+q':var pt=this.currentParam,valid=false;this.send('\x1bP'+ +valid+'+r'+pt+'\x1b\\');break;default:this.error('Unknown DCS prefix: %s.',this.prefix);break;}this.currentParam=0;this.prefix='';this.state=normal;}else if(!this.currentParam){if(!this.prefix&&ch!=='$'&&ch!=='+'){this.currentParam=ch;}else if(this.prefix.length===2){this.currentParam=ch;}else{this.prefix+=ch;}}else{this.currentParam+=ch;}break;case ignore:// For PM and APC.
-if(ch==='\x1b'||ch==='\x07'){if(ch==='\x1b')i++;this.state=normal;}break;}}this.updateRange(this.y);this.refresh(this.refreshStart,this.refreshEnd);};/**
- * Writes text to the terminal, followed by a break line character (\n).
- * @param {string} text The text to write to the terminal.
- */Terminal.prototype.writeln=function(data){this.write(data+'\r\n');};/**
- * Attaches a custom keydown handler which is run before keys are processed, giving consumers of
- * xterm.js ultimate control as to what keys should be processed by the terminal and what keys
- * should not.
- * @param {function} customKeydownHandler The custom KeyboardEvent handler to attach. This is a
- * function that takes a KeyboardEvent, allowing consumers to stop propogation and/or prevent
- * the default action. The function returns whether the event should be processed by xterm.js.
- */Terminal.prototype.attachCustomKeydownHandler=function(customKeydownHandler){this.customKeydownHandler=customKeydownHandler;};/**
- * Handle a keydown event
- * Key Resources:
- * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
- * @param {KeyboardEvent} ev The keydown event to be handled.
- */Terminal.prototype.keyDown=function(ev){// Scroll down to prompt, whenever the user presses a key.
-if(this.ybase!==this.ydisp){this.scrollToBottom();}if(this.customKeydownHandler&&this.customKeydownHandler(ev)===false){return false;}if(!this.compositionHelper.keydown.bind(this.compositionHelper)(ev)){return false;}var self=this;var result=this.evaluateKeyEscapeSequence(ev);if(result.scrollDisp){this.scrollDisp(result.scrollDisp);return this.cancel(ev,true);}if(isThirdLevelShift(this,ev)){return true;}if(result.cancel){// The event is canceled at the end already, is this necessary?
-this.cancel(ev,true);}if(!result.key){return true;}this.emit('keydown',ev);this.emit('key',result.key,ev);this.showCursor();this.handler(result.key);return this.cancel(ev,true);};/**
- * Returns an object that determines how a KeyboardEvent should be handled. The key of the
- * returned value is the new key code to pass to the PTY.
- *
- * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
- * @param {KeyboardEvent} ev The keyboard event to be translated to key escape sequence.
- */Terminal.prototype.evaluateKeyEscapeSequence=function(ev){var result={// Whether to cancel event propogation (NOTE: this may not be needed since the event is
-// canceled at the end of keyDown
-cancel:false,// The new key even to emit
-key:undefined,// The number of characters to scroll, if this is defined it will cancel the event
-scrollDisp:undefined};var modifiers=ev.shiftKey<<0|ev.altKey<<1|ev.ctrlKey<<2|ev.metaKey<<3;switch(ev.keyCode){case 8:// backspace
-if(ev.shiftKey){result.key='\x08';// ^H
-break;}result.key='\x7f';// ^?
-break;case 9:// tab
-if(ev.shiftKey){result.key='\x1b[Z';break;}result.key='\t';result.cancel=true;break;case 13:// return/enter
-result.key='\r';result.cancel=true;break;case 27:// escape
-result.key='\x1b';result.cancel=true;break;case 37:// left-arrow
-if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'D';// HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards
-// http://unix.stackexchange.com/a/108106
-if(result.key=='\x1b[1;3D'){result.key='\x1b[1;5D';}}else if(this.applicationCursor){result.key='\x1bOD';}else{result.key='\x1b[D';}break;case 39:// right-arrow
-if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'C';// HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward
-// http://unix.stackexchange.com/a/108106
-if(result.key=='\x1b[1;3C'){result.key='\x1b[1;5C';}}else if(this.applicationCursor){result.key='\x1bOC';}else{result.key='\x1b[C';}break;case 38:// up-arrow
-if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'A';// HACK: Make Alt + up-arrow behave like Ctrl + up-arrow
-// http://unix.stackexchange.com/a/108106
-if(result.key=='\x1b[1;3A'){result.key='\x1b[1;5A';}}else if(this.applicationCursor){result.key='\x1bOA';}else{result.key='\x1b[A';}break;case 40:// down-arrow
-if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'B';// HACK: Make Alt + down-arrow behave like Ctrl + down-arrow
-// http://unix.stackexchange.com/a/108106
-if(result.key=='\x1b[1;3B'){result.key='\x1b[1;5B';}}else if(this.applicationCursor){result.key='\x1bOB';}else{result.key='\x1b[B';}break;case 45:// insert
-if(!ev.shiftKey&&!ev.ctrlKey){// <Ctrl> or <Shift> + <Insert> are used to
-// copy-paste on some systems.
-result.key='\x1b[2~';}break;case 46:// delete
-if(modifiers){result.key='\x1b[3;'+(modifiers+1)+'~';}else{result.key='\x1b[3~';}break;case 36:// home
-if(modifiers)result.key='\x1b[1;'+(modifiers+1)+'H';else if(this.applicationCursor)result.key='\x1bOH';else result.key='\x1b[H';break;case 35:// end
-if(modifiers)result.key='\x1b[1;'+(modifiers+1)+'F';else if(this.applicationCursor)result.key='\x1bOF';else result.key='\x1b[F';break;case 33:// page up
-if(ev.shiftKey){result.scrollDisp=-(this.rows-1);}else{result.key='\x1b[5~';}break;case 34:// page down
-if(ev.shiftKey){result.scrollDisp=this.rows-1;}else{result.key='\x1b[6~';}break;case 112:// F1-F12
-if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'P';}else{result.key='\x1bOP';}break;case 113:if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'Q';}else{result.key='\x1bOQ';}break;case 114:if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'R';}else{result.key='\x1bOR';}break;case 115:if(modifiers){result.key='\x1b[1;'+(modifiers+1)+'S';}else{result.key='\x1bOS';}break;case 116:if(modifiers){result.key='\x1b[15;'+(modifiers+1)+'~';}else{result.key='\x1b[15~';}break;case 117:if(modifiers){result.key='\x1b[17;'+(modifiers+1)+'~';}else{result.key='\x1b[17~';}break;case 118:if(modifiers){result.key='\x1b[18;'+(modifiers+1)+'~';}else{result.key='\x1b[18~';}break;case 119:if(modifiers){result.key='\x1b[19;'+(modifiers+1)+'~';}else{result.key='\x1b[19~';}break;case 120:if(modifiers){result.key='\x1b[20;'+(modifiers+1)+'~';}else{result.key='\x1b[20~';}break;case 121:if(modifiers){result.key='\x1b[21;'+(modifiers+1)+'~';}else{result.key='\x1b[21~';}break;case 122:if(modifiers){result.key='\x1b[23;'+(modifiers+1)+'~';}else{result.key='\x1b[23~';}break;case 123:if(modifiers){result.key='\x1b[24;'+(modifiers+1)+'~';}else{result.key='\x1b[24~';}break;default:// a-z and space
-if(ev.ctrlKey&&!ev.shiftKey&&!ev.altKey&&!ev.metaKey){if(ev.keyCode>=65&&ev.keyCode<=90){result.key=String.fromCharCode(ev.keyCode-64);}else if(ev.keyCode===32){// NUL
-result.key=String.fromCharCode(0);}else if(ev.keyCode>=51&&ev.keyCode<=55){// escape, file sep, group sep, record sep, unit sep
-result.key=String.fromCharCode(ev.keyCode-51+27);}else if(ev.keyCode===56){// delete
-result.key=String.fromCharCode(127);}else if(ev.keyCode===219){// ^[ - escape
-result.key=String.fromCharCode(27);}else if(ev.keyCode===221){// ^] - group sep
-result.key=String.fromCharCode(29);}}else if(!this.browser.isMac&&ev.altKey&&!ev.ctrlKey&&!ev.metaKey){// On Mac this is a third level shift. Use <Esc> instead.
-if(ev.keyCode>=65&&ev.keyCode<=90){result.key='\x1b'+String.fromCharCode(ev.keyCode+32);}else if(ev.keyCode===192){result.key='\x1b`';}else if(ev.keyCode>=48&&ev.keyCode<=57){result.key='\x1b'+(ev.keyCode-48);}}break;}return result;};/**
- * Set the G level of the terminal
- * @param g
- */Terminal.prototype.setgLevel=function(g){this.glevel=g;this.charset=this.charsets[g];};/**
- * Set the charset for the given G level of the terminal
- * @param g
- * @param charset
- */Terminal.prototype.setgCharset=function(g,charset){this.charsets[g]=charset;if(this.glevel===g){this.charset=charset;}};/**
- * Handle a keypress event.
- * Key Resources:
- * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
- * @param {KeyboardEvent} ev The keypress event to be handled.
- */Terminal.prototype.keyPress=function(ev){var key;this.cancel(ev);if(ev.charCode){key=ev.charCode;}else if(ev.which==null){key=ev.keyCode;}else if(ev.which!==0&&ev.charCode!==0){key=ev.which;}else{return false;}if(!key||(ev.altKey||ev.ctrlKey||ev.metaKey)&&!isThirdLevelShift(this,ev)){return false;}key=String.fromCharCode(key);this.emit('keypress',key,ev);this.emit('key',key,ev);this.showCursor();this.handler(key);return false;};/**
- * Send data for handling to the terminal
- * @param {string} data
- */Terminal.prototype.send=function(data){var self=this;if(!this.queue){setTimeout(function(){self.handler(self.queue);self.queue='';},1);}this.queue+=data;};/**
- * Ring the bell.
- * Note: We could do sweet things with webaudio here
- */Terminal.prototype.bell=function(){if(!this.visualBell)return;var self=this;this.element.style.borderColor='white';setTimeout(function(){self.element.style.borderColor='';},10);if(this.popOnBell)this.focus();};/**
- * Log the current state to the console.
- */Terminal.prototype.log=function(){if(!this.debug)return;if(!this.context.console||!this.context.console.log)return;var args=Array.prototype.slice.call(arguments);this.context.console.log.apply(this.context.console,args);};/**
- * Log the current state as error to the console.
- */Terminal.prototype.error=function(){if(!this.debug)return;if(!this.context.console||!this.context.console.error)return;var args=Array.prototype.slice.call(arguments);this.context.console.error.apply(this.context.console,args);};/**
- * Resizes the terminal.
- *
- * @param {number} x The number of columns to resize to.
- * @param {number} y The number of rows to resize to.
- */Terminal.prototype.resize=function(x,y){var line,el,i,j,ch,addToY;if(x===this.cols&&y===this.rows){return;}if(x<1)x=1;if(y<1)y=1;// resize cols
-j=this.cols;if(j<x){ch=[this.defAttr,' ',1];// does xterm use the default attr?
-i=this.lines.length;while(i--){while(this.lines[i].length<x){this.lines[i].push(ch);}}}else{// (j > x)
-i=this.lines.length;while(i--){while(this.lines[i].length>x){this.lines[i].pop();}}}this.setupStops(j);this.cols=x;// resize rows
-j=this.rows;addToY=0;if(j<y){el=this.element;while(j++<y){// y is rows, not this.y
-if(this.lines.length<y+this.ybase){if(this.ybase>0&&this.lines.length<=this.ybase+this.y+addToY+1){// There is room above the buffer and there are no empty elements below the line,
-// scroll up
-this.ybase--;addToY++;if(this.ydisp>0){// Viewport is at the top of the buffer, must increase downwards
-this.ydisp--;}}else{// Add a blank line if there is no buffer left at the top to scroll to, or if there
-// are blank lines after the cursor
-this.lines.push(this.blankLine());}}if(this.children.length<y){this.insertRow();}}}else{// (j > y)
-while(j-->y){if(this.lines.length>y+this.ybase){if(this.lines.length>this.ybase+this.y+1){// The line is a blank line below the cursor, remove it
-this.lines.pop();}else{// The line is the cursor, scroll down
-this.ybase++;this.ydisp++;}}if(this.children.length>y){el=this.children.shift();if(!el)continue;el.parentNode.removeChild(el);}}}this.rows=y;// Make sure that the cursor stays on screen
-if(this.y>=y){this.y=y-1;}if(addToY){this.y+=addToY;}if(this.x>=x){this.x=x-1;}this.scrollTop=0;this.scrollBottom=y-1;this.refresh(0,this.rows-1);this.normal=null;this.geometry=[this.cols,this.rows];this.emit('resize',{terminal:this,cols:x,rows:y});};/**
- * Updates the range of rows to refresh
- * @param {number} y The number of rows to refresh next.
- */Terminal.prototype.updateRange=function(y){if(y<this.refreshStart)this.refreshStart=y;if(y>this.refreshEnd)this.refreshEnd=y;// if (y > this.refreshEnd) {
-// this.refreshEnd = y;
-// if (y > this.rows - 1) {
-// this.refreshEnd = this.rows - 1;
-// }
-// }
-};/**
- * Set the range of refreshing to the maximum value
- */Terminal.prototype.maxRange=function(){this.refreshStart=0;this.refreshEnd=this.rows-1;};/**
- * Setup the tab stops.
- * @param {number} i
- */Terminal.prototype.setupStops=function(i){if(i!=null){if(!this.tabs[i]){i=this.prevStop(i);}}else{this.tabs={};i=0;}for(;i<this.cols;i+=8){this.tabs[i]=true;}};/**
- * Move the cursor to the previous tab stop from the given position (default is current).
- * @param {number} x The position to move the cursor to the previous tab stop.
- */Terminal.prototype.prevStop=function(x){if(x==null)x=this.x;while(!this.tabs[--x]&&x>0){}return x>=this.cols?this.cols-1:x<0?0:x;};/**
- * Move the cursor one tab stop forward from the given position (default is current).
- * @param {number} x The position to move the cursor one tab stop forward.
- */Terminal.prototype.nextStop=function(x){if(x==null)x=this.x;while(!this.tabs[++x]&&x<this.cols){}return x>=this.cols?this.cols-1:x<0?0:x;};/**
- * Erase in the identified line everything from "x" to the end of the line (right).
- * @param {number} x The column from which to start erasing to the end of the line.
- * @param {number} y The line in which to operate.
- */Terminal.prototype.eraseRight=function(x,y){var line=this.lines[this.ybase+y],ch=[this.eraseAttr(),' ',1];// xterm
-for(;x<this.cols;x++){line[x]=ch;}this.updateRange(y);};/**
- * Erase in the identified line everything from "x" to the start of the line (left).
- * @param {number} x The column from which to start erasing to the start of the line.
- * @param {number} y The line in which to operate.
- */Terminal.prototype.eraseLeft=function(x,y){var line=this.lines[this.ybase+y],ch=[this.eraseAttr(),' ',1];// xterm
-x++;while(x--){line[x]=ch;}this.updateRange(y);};/**
- * Clears the entire buffer, making the prompt line the new first line.
- */Terminal.prototype.clear=function(){if(this.ybase===0&&this.y===0){// Don't clear if it's already clear
-return;}this.lines=[this.lines[this.ybase+this.y]];this.ydisp=0;this.ybase=0;this.y=0;for(var i=1;i<this.rows;i++){this.lines.push(this.blankLine());}this.refresh(0,this.rows-1);this.emit('scroll',this.ydisp);};/**
- * Erase all content in the given line
- * @param {number} y The line to erase all of its contents.
- */Terminal.prototype.eraseLine=function(y){this.eraseRight(0,y);};/**
- * Return the data array of a blank line/
- * @param {number} cur First bunch of data for each "blank" character.
- */Terminal.prototype.blankLine=function(cur){var attr=cur?this.eraseAttr():this.defAttr;var ch=[attr,' ',1]// width defaults to 1 halfwidth character
-,line=[],i=0;for(;i<this.cols;i++){line[i]=ch;}return line;};/**
- * If cur return the back color xterm feature attribute. Else return defAttr.
- * @param {object} cur
- */Terminal.prototype.ch=function(cur){return cur?[this.eraseAttr(),' ',1]:[this.defAttr,' ',1];};/**
- * Evaluate if the current erminal is the given argument.
- * @param {object} term The terminal to evaluate
- */Terminal.prototype.is=function(term){var name=this.termName;return(name+'').indexOf(term)===0;};/**
- * Emit the 'data' event and populate the given data.
- * @param {string} data The data to populate in the event.
- */Terminal.prototype.handler=function(data){this.emit('data',data);};/**
- * Emit the 'title' event and populate the given title.
- * @param {string} title The title to populate in the event.
- */Terminal.prototype.handleTitle=function(title){this.emit('title',title);};/**
- * ESC
- *//**
- * ESC D Index (IND is 0x84).
- */Terminal.prototype.index=function(){this.y++;if(this.y>this.scrollBottom){this.y--;this.scroll();}this.state=normal;};/**
- * ESC M Reverse Index (RI is 0x8d).
- */Terminal.prototype.reverseIndex=function(){var j;this.y--;if(this.y<this.scrollTop){this.y++;// possibly move the code below to term.reverseScroll();
-// test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
-// blankLine(true) is xterm/linux behavior
-this.lines.splice(this.y+this.ybase,0,this.blankLine(true));j=this.rows-1-this.scrollBottom;this.lines.splice(this.rows-1+this.ybase-j+1,1);// this.maxRange();
-this.updateRange(this.scrollTop);this.updateRange(this.scrollBottom);}this.state=normal;};/**
- * ESC c Full Reset (RIS).
- */Terminal.prototype.reset=function(){this.options.rows=this.rows;this.options.cols=this.cols;var customKeydownHandler=this.customKeydownHandler;Terminal.call(this,this.options);this.customKeydownHandler=customKeydownHandler;this.refresh(0,this.rows-1);this.viewport.syncScrollArea();};/**
- * ESC H Tab Set (HTS is 0x88).
- */Terminal.prototype.tabSet=function(){this.tabs[this.x]=true;this.state=normal;};/**
- * CSI
- *//**
- * CSI Ps A
- * Cursor Up Ps Times (default = 1) (CUU).
- */Terminal.prototype.cursorUp=function(params){var param=params[0];if(param<1)param=1;this.y-=param;if(this.y<0)this.y=0;};/**
- * CSI Ps B
- * Cursor Down Ps Times (default = 1) (CUD).
- */Terminal.prototype.cursorDown=function(params){var param=params[0];if(param<1)param=1;this.y+=param;if(this.y>=this.rows){this.y=this.rows-1;}};/**
- * CSI Ps C
- * Cursor Forward Ps Times (default = 1) (CUF).
- */Terminal.prototype.cursorForward=function(params){var param=params[0];if(param<1)param=1;this.x+=param;if(this.x>=this.cols){this.x=this.cols-1;}};/**
- * CSI Ps D
- * Cursor Backward Ps Times (default = 1) (CUB).
- */Terminal.prototype.cursorBackward=function(params){var param=params[0];if(param<1)param=1;this.x-=param;if(this.x<0)this.x=0;};/**
- * CSI Ps ; Ps H
- * Cursor Position [row;column] (default = [1,1]) (CUP).
- */Terminal.prototype.cursorPos=function(params){var row,col;row=params[0]-1;if(params.length>=2){col=params[1]-1;}else{col=0;}if(row<0){row=0;}else if(row>=this.rows){row=this.rows-1;}if(col<0){col=0;}else if(col>=this.cols){col=this.cols-1;}this.x=col;this.y=row;};/**
- * CSI Ps J Erase in Display (ED).
- * Ps = 0 -> Erase Below (default).
- * Ps = 1 -> Erase Above.
- * Ps = 2 -> Erase All.
- * Ps = 3 -> Erase Saved Lines (xterm).
- * CSI ? Ps J
- * Erase in Display (DECSED).
- * Ps = 0 -> Selective Erase Below (default).
- * Ps = 1 -> Selective Erase Above.
- * Ps = 2 -> Selective Erase All.
- */Terminal.prototype.eraseInDisplay=function(params){var j;switch(params[0]){case 0:this.eraseRight(this.x,this.y);j=this.y+1;for(;j<this.rows;j++){this.eraseLine(j);}break;case 1:this.eraseLeft(this.x,this.y);j=this.y;while(j--){this.eraseLine(j);}break;case 2:j=this.rows;while(j--){this.eraseLine(j);}break;case 3:;// no saved lines
-break;}};/**
- * CSI Ps K Erase in Line (EL).
- * Ps = 0 -> Erase to Right (default).
- * Ps = 1 -> Erase to Left.
- * Ps = 2 -> Erase All.
- * CSI ? Ps K
- * Erase in Line (DECSEL).
- * Ps = 0 -> Selective Erase to Right (default).
- * Ps = 1 -> Selective Erase to Left.
- * Ps = 2 -> Selective Erase All.
- */Terminal.prototype.eraseInLine=function(params){switch(params[0]){case 0:this.eraseRight(this.x,this.y);break;case 1:this.eraseLeft(this.x,this.y);break;case 2:this.eraseLine(this.y);break;}};/**
- * CSI Pm m Character Attributes (SGR).
- * Ps = 0 -> Normal (default).
- * Ps = 1 -> Bold.
- * Ps = 4 -> Underlined.
- * Ps = 5 -> Blink (appears as Bold).
- * Ps = 7 -> Inverse.
- * Ps = 8 -> Invisible, i.e., hidden (VT300).
- * Ps = 2 2 -> Normal (neither bold nor faint).
- * Ps = 2 4 -> Not underlined.
- * Ps = 2 5 -> Steady (not blinking).
- * Ps = 2 7 -> Positive (not inverse).
- * Ps = 2 8 -> Visible, i.e., not hidden (VT300).
- * Ps = 3 0 -> Set foreground color to Black.
- * Ps = 3 1 -> Set foreground color to Red.
- * Ps = 3 2 -> Set foreground color to Green.
- * Ps = 3 3 -> Set foreground color to Yellow.
- * Ps = 3 4 -> Set foreground color to Blue.
- * Ps = 3 5 -> Set foreground color to Magenta.
- * Ps = 3 6 -> Set foreground color to Cyan.
- * Ps = 3 7 -> Set foreground color to White.
- * Ps = 3 9 -> Set foreground color to default (original).
- * Ps = 4 0 -> Set background color to Black.
- * Ps = 4 1 -> Set background color to Red.
- * Ps = 4 2 -> Set background color to Green.
- * Ps = 4 3 -> Set background color to Yellow.
- * Ps = 4 4 -> Set background color to Blue.
- * Ps = 4 5 -> Set background color to Magenta.
- * Ps = 4 6 -> Set background color to Cyan.
- * Ps = 4 7 -> Set background color to White.
- * Ps = 4 9 -> Set background color to default (original).
- *
- * If 16-color support is compiled, the following apply. Assume
- * that xterm's resources are set so that the ISO color codes are
- * the first 8 of a set of 16. Then the aixterm colors are the
- * bright versions of the ISO colors:
- * Ps = 9 0 -> Set foreground color to Black.
- * Ps = 9 1 -> Set foreground color to Red.
- * Ps = 9 2 -> Set foreground color to Green.
- * Ps = 9 3 -> Set foreground color to Yellow.
- * Ps = 9 4 -> Set foreground color to Blue.
- * Ps = 9 5 -> Set foreground color to Magenta.
- * Ps = 9 6 -> Set foreground color to Cyan.
- * Ps = 9 7 -> Set foreground color to White.
- * Ps = 1 0 0 -> Set background color to Black.
- * Ps = 1 0 1 -> Set background color to Red.
- * Ps = 1 0 2 -> Set background color to Green.
- * Ps = 1 0 3 -> Set background color to Yellow.
- * Ps = 1 0 4 -> Set background color to Blue.
- * Ps = 1 0 5 -> Set background color to Magenta.
- * Ps = 1 0 6 -> Set background color to Cyan.
- * Ps = 1 0 7 -> Set background color to White.
- *
- * If xterm is compiled with the 16-color support disabled, it
- * supports the following, from rxvt:
- * Ps = 1 0 0 -> Set foreground and background color to
- * default.
- *
- * If 88- or 256-color support is compiled, the following apply.
- * Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second
- * Ps.
- * Ps = 4 8 ; 5 ; Ps -> Set background color to the second
- * Ps.
- */Terminal.prototype.charAttributes=function(params){// Optimize a single SGR0.
-if(params.length===1&&params[0]===0){this.curAttr=this.defAttr;return;}var l=params.length,i=0,flags=this.curAttr>>18,fg=this.curAttr>>9&0x1ff,bg=this.curAttr&0x1ff,p;for(;i<l;i++){p=params[i];if(p>=30&&p<=37){// fg color 8
-fg=p-30;}else if(p>=40&&p<=47){// bg color 8
-bg=p-40;}else if(p>=90&&p<=97){// fg color 16
-p+=8;fg=p-90;}else if(p>=100&&p<=107){// bg color 16
-p+=8;bg=p-100;}else if(p===0){// default
-flags=this.defAttr>>18;fg=this.defAttr>>9&0x1ff;bg=this.defAttr&0x1ff;// flags = 0;
-// fg = 0x1ff;
-// bg = 0x1ff;
-}else if(p===1){// bold text
-flags|=1;}else if(p===4){// underlined text
-flags|=2;}else if(p===5){// blink
-flags|=4;}else if(p===7){// inverse and positive
-// test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
-flags|=8;}else if(p===8){// invisible
-flags|=16;}else if(p===22){// not bold
-flags&=~1;}else if(p===24){// not underlined
-flags&=~2;}else if(p===25){// not blink
-flags&=~4;}else if(p===27){// not inverse
-flags&=~8;}else if(p===28){// not invisible
-flags&=~16;}else if(p===39){// reset fg
-fg=this.defAttr>>9&0x1ff;}else if(p===49){// reset bg
-bg=this.defAttr&0x1ff;}else if(p===38){// fg color 256
-if(params[i+1]===2){i+=2;fg=matchColor(params[i]&0xff,params[i+1]&0xff,params[i+2]&0xff);if(fg===-1)fg=0x1ff;i+=2;}else if(params[i+1]===5){i+=2;p=params[i]&0xff;fg=p;}}else if(p===48){// bg color 256
-if(params[i+1]===2){i+=2;bg=matchColor(params[i]&0xff,params[i+1]&0xff,params[i+2]&0xff);if(bg===-1)bg=0x1ff;i+=2;}else if(params[i+1]===5){i+=2;p=params[i]&0xff;bg=p;}}else if(p===100){// reset fg/bg
-fg=this.defAttr>>9&0x1ff;bg=this.defAttr&0x1ff;}else{this.error('Unknown SGR attribute: %d.',p);}}this.curAttr=flags<<18|fg<<9|bg;};/**
- * CSI Ps n Device Status Report (DSR).
- * Ps = 5 -> Status Report. Result (``OK'') is
- * CSI 0 n
- * Ps = 6 -> Report Cursor Position (CPR) [row;column].
- * Result is
- * CSI r ; c R
- * CSI ? Ps n
- * Device Status Report (DSR, DEC-specific).
- * Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
- * ? r ; c R (assumes page is zero).
- * Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
- * or CSI ? 1 1 n (not ready).
- * Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
- * or CSI ? 2 1 n (locked).
- * Ps = 2 6 -> Report Keyboard status as
- * CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
- * The last two parameters apply to VT400 & up, and denote key-
- * board ready and LK01 respectively.
- * Ps = 5 3 -> Report Locator status as
- * CSI ? 5 3 n Locator available, if compiled-in, or
- * CSI ? 5 0 n No Locator, if not.
- */Terminal.prototype.deviceStatus=function(params){if(!this.prefix){switch(params[0]){case 5:// status report
-this.send('\x1b[0n');break;case 6:// cursor position
-this.send('\x1b['+(this.y+1)+';'+(this.x+1)+'R');break;}}else if(this.prefix==='?'){// modern xterm doesnt seem to
-// respond to any of these except ?6, 6, and 5
-switch(params[0]){case 6:// cursor position
-this.send('\x1b[?'+(this.y+1)+';'+(this.x+1)+'R');break;case 15:// no printer
-// this.send('\x1b[?11n');
-break;case 25:// dont support user defined keys
-// this.send('\x1b[?21n');
-break;case 26:// north american keyboard
-// this.send('\x1b[?27;1;0;0n');
-break;case 53:// no dec locator/mouse
-// this.send('\x1b[?50n');
-break;}}};/**
- * Additions
- *//**
- * CSI Ps @
- * Insert Ps (Blank) Character(s) (default = 1) (ICH).
- */Terminal.prototype.insertChars=function(params){var param,row,j,ch;param=params[0];if(param<1)param=1;row=this.y+this.ybase;j=this.x;ch=[this.eraseAttr(),' ',1];// xterm
-while(param--&&j<this.cols){this.lines[row].splice(j++,0,ch);this.lines[row].pop();}};/**
- * CSI Ps E
- * Cursor Next Line Ps Times (default = 1) (CNL).
- * same as CSI Ps B ?
- */Terminal.prototype.cursorNextLine=function(params){var param=params[0];if(param<1)param=1;this.y+=param;if(this.y>=this.rows){this.y=this.rows-1;}this.x=0;};/**
- * CSI Ps F
- * Cursor Preceding Line Ps Times (default = 1) (CNL).
- * reuse CSI Ps A ?
- */Terminal.prototype.cursorPrecedingLine=function(params){var param=params[0];if(param<1)param=1;this.y-=param;if(this.y<0)this.y=0;this.x=0;};/**
- * CSI Ps G
- * Cursor Character Absolute [column] (default = [row,1]) (CHA).
- */Terminal.prototype.cursorCharAbsolute=function(params){var param=params[0];if(param<1)param=1;this.x=param-1;};/**
- * CSI Ps L
- * Insert Ps Line(s) (default = 1) (IL).
- */Terminal.prototype.insertLines=function(params){var param,row,j;param=params[0];if(param<1)param=1;row=this.y+this.ybase;j=this.rows-1-this.scrollBottom;j=this.rows-1+this.ybase-j+1;while(param--){// test: echo -e '\e[44m\e[1L\e[0m'
-// blankLine(true) - xterm/linux behavior
-this.lines.splice(row,0,this.blankLine(true));this.lines.splice(j,1);}// this.maxRange();
-this.updateRange(this.y);this.updateRange(this.scrollBottom);};/**
- * CSI Ps M
- * Delete Ps Line(s) (default = 1) (DL).
- */Terminal.prototype.deleteLines=function(params){var param,row,j;param=params[0];if(param<1)param=1;row=this.y+this.ybase;j=this.rows-1-this.scrollBottom;j=this.rows-1+this.ybase-j;while(param--){// test: echo -e '\e[44m\e[1M\e[0m'
-// blankLine(true) - xterm/linux behavior
-this.lines.splice(j+1,0,this.blankLine(true));this.lines.splice(row,1);}// this.maxRange();
-this.updateRange(this.y);this.updateRange(this.scrollBottom);};/**
- * CSI Ps P
- * Delete Ps Character(s) (default = 1) (DCH).
- */Terminal.prototype.deleteChars=function(params){var param,row,ch;param=params[0];if(param<1)param=1;row=this.y+this.ybase;ch=[this.eraseAttr(),' ',1];// xterm
-while(param--){this.lines[row].splice(this.x,1);this.lines[row].push(ch);}};/**
- * CSI Ps X
- * Erase Ps Character(s) (default = 1) (ECH).
- */Terminal.prototype.eraseChars=function(params){var param,row,j,ch;param=params[0];if(param<1)param=1;row=this.y+this.ybase;j=this.x;ch=[this.eraseAttr(),' ',1];// xterm
-while(param--&&j<this.cols){this.lines[row][j++]=ch;}};/**
- * CSI Pm ` Character Position Absolute
- * [column] (default = [row,1]) (HPA).
- */Terminal.prototype.charPosAbsolute=function(params){var param=params[0];if(param<1)param=1;this.x=param-1;if(this.x>=this.cols){this.x=this.cols-1;}};/**
- * 141 61 a * HPR -
- * Horizontal Position Relative
- * reuse CSI Ps C ?
- */Terminal.prototype.HPositionRelative=function(params){var param=params[0];if(param<1)param=1;this.x+=param;if(this.x>=this.cols){this.x=this.cols-1;}};/**
- * CSI Ps c Send Device Attributes (Primary DA).
- * Ps = 0 or omitted -> request attributes from terminal. The
- * response depends on the decTerminalID resource setting.
- * -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'')
- * -> CSI ? 1 ; 0 c (``VT101 with No Options'')
- * -> CSI ? 6 c (``VT102'')
- * -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'')
- * The VT100-style response parameters do not mean anything by
- * themselves. VT220 parameters do, telling the host what fea-
- * tures the terminal supports:
- * Ps = 1 -> 132-columns.
- * Ps = 2 -> Printer.
- * Ps = 6 -> Selective erase.
- * Ps = 8 -> User-defined keys.
- * Ps = 9 -> National replacement character sets.
- * Ps = 1 5 -> Technical characters.
- * Ps = 2 2 -> ANSI color, e.g., VT525.
- * Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode).
- * CSI > Ps c
- * Send Device Attributes (Secondary DA).
- * Ps = 0 or omitted -> request the terminal's identification
- * code. The response depends on the decTerminalID resource set-
- * ting. It should apply only to VT220 and up, but xterm extends
- * this to VT100.
- * -> CSI > Pp ; Pv ; Pc c
- * where Pp denotes the terminal type
- * Pp = 0 -> ``VT100''.
- * Pp = 1 -> ``VT220''.
- * and Pv is the firmware version (for xterm, this was originally
- * the XFree86 patch number, starting with 95). In a DEC termi-
- * nal, Pc indicates the ROM cartridge registration number and is
- * always zero.
- * More information:
- * xterm/charproc.c - line 2012, for more information.
- * vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
- */Terminal.prototype.sendDeviceAttributes=function(params){if(params[0]>0)return;if(!this.prefix){if(this.is('xterm')||this.is('rxvt-unicode')||this.is('screen')){this.send('\x1b[?1;2c');}else if(this.is('linux')){this.send('\x1b[?6c');}}else if(this.prefix==='>'){// xterm and urxvt
-// seem to spit this
-// out around ~370 times (?).
-if(this.is('xterm')){this.send('\x1b[>0;276;0c');}else if(this.is('rxvt-unicode')){this.send('\x1b[>85;95;0c');}else if(this.is('linux')){// not supported by linux console.
-// linux console echoes parameters.
-this.send(params[0]+'c');}else if(this.is('screen')){this.send('\x1b[>83;40003;0c');}}};/**
- * CSI Pm d
- * Line Position Absolute [row] (default = [1,column]) (VPA).
- */Terminal.prototype.linePosAbsolute=function(params){var param=params[0];if(param<1)param=1;this.y=param-1;if(this.y>=this.rows){this.y=this.rows-1;}};/**
- * 145 65 e * VPR - Vertical Position Relative
- * reuse CSI Ps B ?
- */Terminal.prototype.VPositionRelative=function(params){var param=params[0];if(param<1)param=1;this.y+=param;if(this.y>=this.rows){this.y=this.rows-1;}};/**
- * CSI Ps ; Ps f
- * Horizontal and Vertical Position [row;column] (default =
- * [1,1]) (HVP).
- */Terminal.prototype.HVPosition=function(params){if(params[0]<1)params[0]=1;if(params[1]<1)params[1]=1;this.y=params[0]-1;if(this.y>=this.rows){this.y=this.rows-1;}this.x=params[1]-1;if(this.x>=this.cols){this.x=this.cols-1;}};/**
- * CSI Pm h Set Mode (SM).
- * Ps = 2 -> Keyboard Action Mode (AM).
- * Ps = 4 -> Insert Mode (IRM).
- * Ps = 1 2 -> Send/receive (SRM).
- * Ps = 2 0 -> Automatic Newline (LNM).
- * CSI ? Pm h
- * DEC Private Mode Set (DECSET).
- * Ps = 1 -> Application Cursor Keys (DECCKM).
- * Ps = 2 -> Designate USASCII for character sets G0-G3
- * (DECANM), and set VT100 mode.
- * Ps = 3 -> 132 Column Mode (DECCOLM).
- * Ps = 4 -> Smooth (Slow) Scroll (DECSCLM).
- * Ps = 5 -> Reverse Video (DECSCNM).
- * Ps = 6 -> Origin Mode (DECOM).
- * Ps = 7 -> Wraparound Mode (DECAWM).
- * Ps = 8 -> Auto-repeat Keys (DECARM).
- * Ps = 9 -> Send Mouse X & Y on button press. See the sec-
- * tion Mouse Tracking.
- * Ps = 1 0 -> Show toolbar (rxvt).
- * Ps = 1 2 -> Start Blinking Cursor (att610).
- * Ps = 1 8 -> Print form feed (DECPFF).
- * Ps = 1 9 -> Set print extent to full screen (DECPEX).
- * Ps = 2 5 -> Show Cursor (DECTCEM).
- * Ps = 3 0 -> Show scrollbar (rxvt).
- * Ps = 3 5 -> Enable font-shifting functions (rxvt).
- * Ps = 3 8 -> Enter Tektronix Mode (DECTEK).
- * Ps = 4 0 -> Allow 80 -> 132 Mode.
- * Ps = 4 1 -> more(1) fix (see curses resource).
- * Ps = 4 2 -> Enable Nation Replacement Character sets (DECN-
- * RCM).
- * Ps = 4 4 -> Turn On Margin Bell.
- * Ps = 4 5 -> Reverse-wraparound Mode.
- * Ps = 4 6 -> Start Logging. This is normally disabled by a
- * compile-time option.
- * Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis-
- * abled by the titeInhibit resource).
- * Ps = 6 6 -> Application keypad (DECNKM).
- * Ps = 6 7 -> Backarrow key sends backspace (DECBKM).
- * Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
- * release. See the section Mouse Tracking.
- * Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
- * Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
- * Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
- * Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
- * Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
- * Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt).
- * Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt).
- * Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit.
- * (enables the eightBitInput resource).
- * Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num-
- * Lock keys. (This enables the numLock resource).
- * Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This
- * enables the metaSendsEscape resource).
- * Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete
- * key.
- * Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This
- * enables the altSendsEscape resource).
- * Ps = 1 0 4 0 -> Keep selection even if not highlighted.
- * (This enables the keepSelection resource).
- * Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables
- * the selectToClipboard resource).
- * Ps = 1 0 4 2 -> Enable Urgency window manager hint when
- * Control-G is received. (This enables the bellIsUrgent
- * resource).
- * Ps = 1 0 4 3 -> Enable raising of the window when Control-G
- * is received. (enables the popOnBell resource).
- * Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be
- * disabled by the titeInhibit resource).
- * Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis-
- * abled by the titeInhibit resource).
- * Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate
- * Screen Buffer, clearing it first. (This may be disabled by
- * the titeInhibit resource). This combines the effects of the 1
- * 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based
- * applications rather than the 4 7 mode.
- * Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode.
- * Ps = 1 0 5 1 -> Set Sun function-key mode.
- * Ps = 1 0 5 2 -> Set HP function-key mode.
- * Ps = 1 0 5 3 -> Set SCO function-key mode.
- * Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6).
- * Ps = 1 0 6 1 -> Set VT220 keyboard emulation.
- * Ps = 2 0 0 4 -> Set bracketed paste mode.
- * Modes:
- * http: *vt100.net/docs/vt220-rm/chapter4.html
- */Terminal.prototype.setMode=function(params){if((typeof params==='undefined'?'undefined':_typeof(params))==='object'){var l=params.length,i=0;for(;i<l;i++){this.setMode(params[i]);}return;}if(!this.prefix){switch(params){case 4:this.insertMode=true;break;case 20://this.convertEol = true;
-break;}}else if(this.prefix==='?'){switch(params){case 1:this.applicationCursor=true;break;case 2:this.setgCharset(0,Terminal.charsets.US);this.setgCharset(1,Terminal.charsets.US);this.setgCharset(2,Terminal.charsets.US);this.setgCharset(3,Terminal.charsets.US);// set VT100 mode here
-break;case 3:// 132 col mode
-this.savedCols=this.cols;this.resize(132,this.rows);break;case 6:this.originMode=true;break;case 7:this.wraparoundMode=true;break;case 12:// this.cursorBlink = true;
-break;case 66:this.log('Serial port requested application keypad.');this.applicationKeypad=true;this.viewport.syncScrollArea();break;case 9:// X10 Mouse
-// no release, no motion, no wheel, no modifiers.
-case 1000:// vt200 mouse
-// no motion.
-// no modifiers, except control on the wheel.
-case 1002:// button event mouse
-case 1003:// any event mouse
-// any event - sends motion events,
-// even if there is no button held down.
-this.x10Mouse=params===9;this.vt200Mouse=params===1000;this.normalMouse=params>1000;this.mouseEvents=true;this.element.style.cursor='default';this.log('Binding to mouse events.');break;case 1004:// send focusin/focusout events
-// focusin: ^[[I
-// focusout: ^[[O
-this.sendFocus=true;break;case 1005:// utf8 ext mode mouse
-this.utfMouse=true;// for wide terminals
-// simply encodes large values as utf8 characters
-break;case 1006:// sgr ext mode mouse
-this.sgrMouse=true;// for wide terminals
-// does not add 32 to fields
-// press: ^[[<b;x;yM
-// release: ^[[<b;x;ym
-break;case 1015:// urxvt ext mode mouse
-this.urxvtMouse=true;// for wide terminals
-// numbers for fields
-// press: ^[[b;x;yM
-// motion: ^[[b;x;yT
-break;case 25:// show cursor
-this.cursorHidden=false;break;case 1049:// alt screen buffer cursor
-//this.saveCursor();
-;// FALL-THROUGH
-case 47:// alt screen buffer
-case 1047:// alt screen buffer
-if(!this.normal){var normal={lines:this.lines,ybase:this.ybase,ydisp:this.ydisp,x:this.x,y:this.y,scrollTop:this.scrollTop,scrollBottom:this.scrollBottom,tabs:this.tabs// XXX save charset(s) here?
-// charset: this.charset,
-// glevel: this.glevel,
-// charsets: this.charsets
-};this.reset();this.normal=normal;this.showCursor();}break;}}};/**
- * CSI Pm l Reset Mode (RM).
- * Ps = 2 -> Keyboard Action Mode (AM).
- * Ps = 4 -> Replace Mode (IRM).
- * Ps = 1 2 -> Send/receive (SRM).
- * Ps = 2 0 -> Normal Linefeed (LNM).
- * CSI ? Pm l
- * DEC Private Mode Reset (DECRST).
- * Ps = 1 -> Normal Cursor Keys (DECCKM).
- * Ps = 2 -> Designate VT52 mode (DECANM).
- * Ps = 3 -> 80 Column Mode (DECCOLM).
- * Ps = 4 -> Jump (Fast) Scroll (DECSCLM).
- * Ps = 5 -> Normal Video (DECSCNM).
- * Ps = 6 -> Normal Cursor Mode (DECOM).
- * Ps = 7 -> No Wraparound Mode (DECAWM).
- * Ps = 8 -> No Auto-repeat Keys (DECARM).
- * Ps = 9 -> Don't send Mouse X & Y on button press.
- * Ps = 1 0 -> Hide toolbar (rxvt).
- * Ps = 1 2 -> Stop Blinking Cursor (att610).
- * Ps = 1 8 -> Don't print form feed (DECPFF).
- * Ps = 1 9 -> Limit print to scrolling region (DECPEX).
- * Ps = 2 5 -> Hide Cursor (DECTCEM).
- * Ps = 3 0 -> Don't show scrollbar (rxvt).
- * Ps = 3 5 -> Disable font-shifting functions (rxvt).
- * Ps = 4 0 -> Disallow 80 -> 132 Mode.
- * Ps = 4 1 -> No more(1) fix (see curses resource).
- * Ps = 4 2 -> Disable Nation Replacement Character sets (DEC-
- * NRCM).
- * Ps = 4 4 -> Turn Off Margin Bell.
- * Ps = 4 5 -> No Reverse-wraparound Mode.
- * Ps = 4 6 -> Stop Logging. (This is normally disabled by a
- * compile-time option).
- * Ps = 4 7 -> Use Normal Screen Buffer.
- * Ps = 6 6 -> Numeric keypad (DECNKM).
- * Ps = 6 7 -> Backarrow key sends delete (DECBKM).
- * Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
- * release. See the section Mouse Tracking.
- * Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
- * Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
- * Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
- * Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
- * Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
- * Ps = 1 0 1 0 -> Don't scroll to bottom on tty output
- * (rxvt).
- * Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt).
- * Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables
- * the eightBitInput resource).
- * Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num-
- * Lock keys. (This disables the numLock resource).
- * Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key.
- * (This disables the metaSendsEscape resource).
- * Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad
- * Delete key.
- * Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key.
- * (This disables the altSendsEscape resource).
- * Ps = 1 0 4 0 -> Do not keep selection when not highlighted.
- * (This disables the keepSelection resource).
- * Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables
- * the selectToClipboard resource).
- * Ps = 1 0 4 2 -> Disable Urgency window manager hint when
- * Control-G is received. (This disables the bellIsUrgent
- * resource).
- * Ps = 1 0 4 3 -> Disable raising of the window when Control-
- * G is received. (This disables the popOnBell resource).
- * Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen
- * first if in the Alternate Screen. (This may be disabled by
- * the titeInhibit resource).
- * Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be
- * disabled by the titeInhibit resource).
- * Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor
- * as in DECRC. (This may be disabled by the titeInhibit
- * resource). This combines the effects of the 1 0 4 7 and 1 0
- * 4 8 modes. Use this with terminfo-based applications rather
- * than the 4 7 mode.
- * Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode.
- * Ps = 1 0 5 1 -> Reset Sun function-key mode.
- * Ps = 1 0 5 2 -> Reset HP function-key mode.
- * Ps = 1 0 5 3 -> Reset SCO function-key mode.
- * Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6).
- * Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.
- * Ps = 2 0 0 4 -> Reset bracketed paste mode.
- */Terminal.prototype.resetMode=function(params){if((typeof params==='undefined'?'undefined':_typeof(params))==='object'){var l=params.length,i=0;for(;i<l;i++){this.resetMode(params[i]);}return;}if(!this.prefix){switch(params){case 4:this.insertMode=false;break;case 20://this.convertEol = false;
-break;}}else if(this.prefix==='?'){switch(params){case 1:this.applicationCursor=false;break;case 3:if(this.cols===132&&this.savedCols){this.resize(this.savedCols,this.rows);}delete this.savedCols;break;case 6:this.originMode=false;break;case 7:this.wraparoundMode=false;break;case 12:// this.cursorBlink = false;
-break;case 66:this.log('Switching back to normal keypad.');this.applicationKeypad=false;this.viewport.syncScrollArea();break;case 9:// X10 Mouse
-case 1000:// vt200 mouse
-case 1002:// button event mouse
-case 1003:// any event mouse
-this.x10Mouse=false;this.vt200Mouse=false;this.normalMouse=false;this.mouseEvents=false;this.element.style.cursor='';break;case 1004:// send focusin/focusout events
-this.sendFocus=false;break;case 1005:// utf8 ext mode mouse
-this.utfMouse=false;break;case 1006:// sgr ext mode mouse
-this.sgrMouse=false;break;case 1015:// urxvt ext mode mouse
-this.urxvtMouse=false;break;case 25:// hide cursor
-this.cursorHidden=true;break;case 1049:// alt screen buffer cursor
-;// FALL-THROUGH
-case 47:// normal screen buffer
-case 1047:// normal screen buffer - clearing it first
-if(this.normal){this.lines=this.normal.lines;this.ybase=this.normal.ybase;this.ydisp=this.normal.ydisp;this.x=this.normal.x;this.y=this.normal.y;this.scrollTop=this.normal.scrollTop;this.scrollBottom=this.normal.scrollBottom;this.tabs=this.normal.tabs;this.normal=null;// if (params === 1049) {
-// this.x = this.savedX;
-// this.y = this.savedY;
-// }
-this.refresh(0,this.rows-1);this.showCursor();}break;}}};/**
- * CSI Ps ; Ps r
- * Set Scrolling Region [top;bottom] (default = full size of win-
- * dow) (DECSTBM).
- * CSI ? Pm r
- */Terminal.prototype.setScrollRegion=function(params){if(this.prefix)return;this.scrollTop=(params[0]||1)-1;this.scrollBottom=(params[1]||this.rows)-1;this.x=0;this.y=0;};/**
- * CSI s
- * Save cursor (ANSI.SYS).
- */Terminal.prototype.saveCursor=function(params){this.savedX=this.x;this.savedY=this.y;};/**
- * CSI u
- * Restore cursor (ANSI.SYS).
- */Terminal.prototype.restoreCursor=function(params){this.x=this.savedX||0;this.y=this.savedY||0;};/**
- * Lesser Used
- *//**
- * CSI Ps I
- * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
- */Terminal.prototype.cursorForwardTab=function(params){var param=params[0]||1;while(param--){this.x=this.nextStop();}};/**
- * CSI Ps S Scroll up Ps lines (default = 1) (SU).
- */Terminal.prototype.scrollUp=function(params){var param=params[0]||1;while(param--){this.lines.splice(this.ybase+this.scrollTop,1);this.lines.splice(this.ybase+this.scrollBottom,0,this.blankLine());}// this.maxRange();
-this.updateRange(this.scrollTop);this.updateRange(this.scrollBottom);};/**
- * CSI Ps T Scroll down Ps lines (default = 1) (SD).
- */Terminal.prototype.scrollDown=function(params){var param=params[0]||1;while(param--){this.lines.splice(this.ybase+this.scrollBottom,1);this.lines.splice(this.ybase+this.scrollTop,0,this.blankLine());}// this.maxRange();
-this.updateRange(this.scrollTop);this.updateRange(this.scrollBottom);};/**
- * CSI Ps ; Ps ; Ps ; Ps ; Ps T
- * Initiate highlight mouse tracking. Parameters are
- * [func;startx;starty;firstrow;lastrow]. See the section Mouse
- * Tracking.
- */Terminal.prototype.initMouseTracking=function(params){// Relevant: DECSET 1001
-};/**
- * CSI > Ps; Ps T
- * Reset one or more features of the title modes to the default
- * value. Normally, "reset" disables the feature. It is possi-
- * ble to disable the ability to reset features by compiling a
- * different default for the title modes into xterm.
- * Ps = 0 -> Do not set window/icon labels using hexadecimal.
- * Ps = 1 -> Do not query window/icon labels using hexadeci-
- * mal.
- * Ps = 2 -> Do not set window/icon labels using UTF-8.
- * Ps = 3 -> Do not query window/icon labels using UTF-8.
- * (See discussion of "Title Modes").
- */Terminal.prototype.resetTitleModes=function(params){;};/**
- * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
- */Terminal.prototype.cursorBackwardTab=function(params){var param=params[0]||1;while(param--){this.x=this.prevStop();}};/**
- * CSI Ps b Repeat the preceding graphic character Ps times (REP).
- */Terminal.prototype.repeatPrecedingCharacter=function(params){var param=params[0]||1,line=this.lines[this.ybase+this.y],ch=line[this.x-1]||[this.defAttr,' ',1];while(param--){line[this.x++]=ch;}};/**
- * CSI Ps g Tab Clear (TBC).
- * Ps = 0 -> Clear Current Column (default).
- * Ps = 3 -> Clear All.
- * Potentially:
- * Ps = 2 -> Clear Stops on Line.
- * http://vt100.net/annarbor/aaa-ug/section6.html
- */Terminal.prototype.tabClear=function(params){var param=params[0];if(param<=0){delete this.tabs[this.x];}else if(param===3){this.tabs={};}};/**
- * CSI Pm i Media Copy (MC).
- * Ps = 0 -> Print screen (default).
- * Ps = 4 -> Turn off printer controller mode.
- * Ps = 5 -> Turn on printer controller mode.
- * CSI ? Pm i
- * Media Copy (MC, DEC-specific).
- * Ps = 1 -> Print line containing cursor.
- * Ps = 4 -> Turn off autoprint mode.
- * Ps = 5 -> Turn on autoprint mode.
- * Ps = 1 0 -> Print composed display, ignores DECPEX.
- * Ps = 1 1 -> Print all pages.
- */Terminal.prototype.mediaCopy=function(params){;};/**
- * CSI > Ps; Ps m
- * Set or reset resource-values used by xterm to decide whether
- * to construct escape sequences holding information about the
- * modifiers pressed with a given key. The first parameter iden-
- * tifies the resource to set/reset. The second parameter is the
- * value to assign to the resource. If the second parameter is
- * omitted, the resource is reset to its initial value.
- * Ps = 1 -> modifyCursorKeys.
- * Ps = 2 -> modifyFunctionKeys.
- * Ps = 4 -> modifyOtherKeys.
- * If no parameters are given, all resources are reset to their
- * initial values.
- */Terminal.prototype.setResources=function(params){;};/**
- * CSI > Ps n
- * Disable modifiers which may be enabled via the CSI > Ps; Ps m
- * sequence. This corresponds to a resource value of "-1", which
- * cannot be set with the other sequence. The parameter identi-
- * fies the resource to be disabled:
- * Ps = 1 -> modifyCursorKeys.
- * Ps = 2 -> modifyFunctionKeys.
- * Ps = 4 -> modifyOtherKeys.
- * If the parameter is omitted, modifyFunctionKeys is disabled.
- * When modifyFunctionKeys is disabled, xterm uses the modifier
- * keys to make an extended sequence of functions rather than
- * adding a parameter to each function key to denote the modi-
- * fiers.
- */Terminal.prototype.disableModifiers=function(params){;};/**
- * CSI > Ps p
- * Set resource value pointerMode. This is used by xterm to
- * decide whether to hide the pointer cursor as the user types.
- * Valid values for the parameter:
- * Ps = 0 -> never hide the pointer.
- * Ps = 1 -> hide if the mouse tracking mode is not enabled.
- * Ps = 2 -> always hide the pointer. If no parameter is
- * given, xterm uses the default, which is 1 .
- */Terminal.prototype.setPointerMode=function(params){;};/**
- * CSI ! p Soft terminal reset (DECSTR).
- * http://vt100.net/docs/vt220-rm/table4-10.html
- */Terminal.prototype.softReset=function(params){this.cursorHidden=false;this.insertMode=false;this.originMode=false;this.wraparoundMode=false;// autowrap
-this.applicationKeypad=false;// ?
-this.viewport.syncScrollArea();this.applicationCursor=false;this.scrollTop=0;this.scrollBottom=this.rows-1;this.curAttr=this.defAttr;this.x=this.y=0;// ?
-this.charset=null;this.glevel=0;// ??
-this.charsets=[null];// ??
-};/**
- * CSI Ps$ p
- * Request ANSI mode (DECRQM). For VT300 and up, reply is
- * CSI Ps; Pm$ y
- * where Ps is the mode number as in RM, and Pm is the mode
- * value:
- * 0 - not recognized
- * 1 - set
- * 2 - reset
- * 3 - permanently set
- * 4 - permanently reset
- */Terminal.prototype.requestAnsiMode=function(params){;};/**
- * CSI ? Ps$ p
- * Request DEC private mode (DECRQM). For VT300 and up, reply is
- * CSI ? Ps; Pm$ p
- * where Ps is the mode number as in DECSET, Pm is the mode value
- * as in the ANSI DECRQM.
- */Terminal.prototype.requestPrivateMode=function(params){;};/**
- * CSI Ps ; Ps " p
- * Set conformance level (DECSCL). Valid values for the first
- * parameter:
- * Ps = 6 1 -> VT100.
- * Ps = 6 2 -> VT200.
- * Ps = 6 3 -> VT300.
- * Valid values for the second parameter:
- * Ps = 0 -> 8-bit controls.
- * Ps = 1 -> 7-bit controls (always set for VT100).
- * Ps = 2 -> 8-bit controls.
- */Terminal.prototype.setConformanceLevel=function(params){;};/**
- * CSI Ps q Load LEDs (DECLL).
- * Ps = 0 -> Clear all LEDS (default).
- * Ps = 1 -> Light Num Lock.
- * Ps = 2 -> Light Caps Lock.
- * Ps = 3 -> Light Scroll Lock.
- * Ps = 2 1 -> Extinguish Num Lock.
- * Ps = 2 2 -> Extinguish Caps Lock.
- * Ps = 2 3 -> Extinguish Scroll Lock.
- */Terminal.prototype.loadLEDs=function(params){;};/**
- * CSI Ps SP q
- * Set cursor style (DECSCUSR, VT520).
- * Ps = 0 -> blinking block.
- * Ps = 1 -> blinking block (default).
- * Ps = 2 -> steady block.
- * Ps = 3 -> blinking underline.
- * Ps = 4 -> steady underline.
- */Terminal.prototype.setCursorStyle=function(params){;};/**
- * CSI Ps " q
- * Select character protection attribute (DECSCA). Valid values
- * for the parameter:
- * Ps = 0 -> DECSED and DECSEL can erase (default).
- * Ps = 1 -> DECSED and DECSEL cannot erase.
- * Ps = 2 -> DECSED and DECSEL can erase.
- */Terminal.prototype.setCharProtectionAttr=function(params){;};/**
- * CSI ? Pm r
- * Restore DEC Private Mode Values. The value of Ps previously
- * saved is restored. Ps values are the same as for DECSET.
- */Terminal.prototype.restorePrivateValues=function(params){;};/**
- * CSI Pt; Pl; Pb; Pr; Ps$ r
- * Change Attributes in Rectangular Area (DECCARA), VT400 and up.
- * Pt; Pl; Pb; Pr denotes the rectangle.
- * Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7.
- * NOTE: xterm doesn't enable this code by default.
- */Terminal.prototype.setAttrInRectangle=function(params){var t=params[0],l=params[1],b=params[2],r=params[3],attr=params[4];var line,i;for(;t<b+1;t++){line=this.lines[this.ybase+t];for(i=l;i<r;i++){line[i]=[attr,line[i][1]];}}// this.maxRange();
-this.updateRange(params[0]);this.updateRange(params[2]);};/**
- * CSI Pc; Pt; Pl; Pb; Pr$ x
- * Fill Rectangular Area (DECFRA), VT420 and up.
- * Pc is the character to use.
- * Pt; Pl; Pb; Pr denotes the rectangle.
- * NOTE: xterm doesn't enable this code by default.
- */Terminal.prototype.fillRectangle=function(params){var ch=params[0],t=params[1],l=params[2],b=params[3],r=params[4];var line,i;for(;t<b+1;t++){line=this.lines[this.ybase+t];for(i=l;i<r;i++){line[i]=[line[i][0],String.fromCharCode(ch)];}}// this.maxRange();
-this.updateRange(params[1]);this.updateRange(params[3]);};/**
- * CSI Ps ; Pu ' z
- * Enable Locator Reporting (DECELR).
- * Valid values for the first parameter:
- * Ps = 0 -> Locator disabled (default).
- * Ps = 1 -> Locator enabled.
- * Ps = 2 -> Locator enabled for one report, then disabled.
- * The second parameter specifies the coordinate unit for locator
- * reports.
- * Valid values for the second parameter:
- * Pu = 0 <- or omitted -> default to character cells.
- * Pu = 1 <- device physical pixels.
- * Pu = 2 <- character cells.
- */Terminal.prototype.enableLocatorReporting=function(params){var val=params[0]>0;//this.mouseEvents = val;
-//this.decLocator = val;
-};/**
- * CSI Pt; Pl; Pb; Pr$ z
- * Erase Rectangular Area (DECERA), VT400 and up.
- * Pt; Pl; Pb; Pr denotes the rectangle.
- * NOTE: xterm doesn't enable this code by default.
- */Terminal.prototype.eraseRectangle=function(params){var t=params[0],l=params[1],b=params[2],r=params[3];var line,i,ch;ch=[this.eraseAttr(),' ',1];// xterm?
-for(;t<b+1;t++){line=this.lines[this.ybase+t];for(i=l;i<r;i++){line[i]=ch;}}// this.maxRange();
-this.updateRange(params[0]);this.updateRange(params[2]);};/**
- * CSI P m SP }
- * Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
- * NOTE: xterm doesn't enable this code by default.
- */Terminal.prototype.insertColumns=function(){var param=params[0],l=this.ybase+this.rows,ch=[this.eraseAttr(),' ',1]// xterm?
-,i;while(param--){for(i=this.ybase;i<l;i++){this.lines[i].splice(this.x+1,0,ch);this.lines[i].pop();}}this.maxRange();};/**
- * CSI P m SP ~
- * Delete P s Column(s) (default = 1) (DECDC), VT420 and up
- * NOTE: xterm doesn't enable this code by default.
- */Terminal.prototype.deleteColumns=function(){var param=params[0],l=this.ybase+this.rows,ch=[this.eraseAttr(),' ',1]// xterm?
-,i;while(param--){for(i=this.ybase;i<l;i++){this.lines[i].splice(this.x,1);this.lines[i].push(ch);}}this.maxRange();};/**
- * Character Sets
- */Terminal.charsets={};// DEC Special Character and Line Drawing Set.
-// http://vt100.net/docs/vt102-ug/table5-13.html
-// A lot of curses apps use this if they see TERM=xterm.
-// testing: echo -e '\e(0a\e(B'
-// The xterm output sometimes seems to conflict with the
-// reference above. xterm seems in line with the reference
-// when running vttest however.
-// The table below now uses xterm's output from vttest.
-Terminal.charsets.SCLD={// (0
-'`':'\u25C6',// 'â—†'
-'a':'\u2592',// 'â–’'
-'b':'\t',// '\t'
-'c':'\f',// '\f'
-'d':'\r',// '\r'
-'e':'\n',// '\n'
-'f':'\xB0',// '°'
-'g':'\xB1',// '±'
-'h':'\u2424',// '\u2424' (NL)
-'i':'\x0B',// '\v'
-'j':'\u2518',// '┘'
-'k':'\u2510',// 'â”'
-'l':'\u250C',// '┌'
-'m':'\u2514',// 'â””'
-'n':'\u253C',// '┼'
-'o':'\u23BA',// '⎺'
-'p':'\u23BB',// '⎻'
-'q':'\u2500',// '─'
-'r':'\u23BC',// '⎼'
-'s':'\u23BD',// '⎽'
-'t':'\u251C',// '├'
-'u':'\u2524',// '┤'
-'v':'\u2534',// 'â”´'
-'w':'\u252C',// '┬'
-'x':'\u2502',// '│'
-'y':'\u2264',// '≤'
-'z':'\u2265',// '≥'
-'{':'\u03C0',// 'Ï€'
-'|':'\u2260',// '≠'
-'}':'\xA3',// '£'
-'~':'\xB7'// '·'
-};Terminal.charsets.UK=null;// (A
-Terminal.charsets.US=null;// (B (USASCII)
-Terminal.charsets.Dutch=null;// (4
-Terminal.charsets.Finnish=null;// (C or (5
-Terminal.charsets.French=null;// (R
-Terminal.charsets.FrenchCanadian=null;// (Q
-Terminal.charsets.German=null;// (K
-Terminal.charsets.Italian=null;// (Y
-Terminal.charsets.NorwegianDanish=null;// (E or (6
-Terminal.charsets.Spanish=null;// (Z
-Terminal.charsets.Swedish=null;// (H or (7
-Terminal.charsets.Swiss=null;// (=
-Terminal.charsets.ISOLatin=null;// /A
-/**
- * Helpers
- */function on(el,type,handler,capture){if(!Array.isArray(el)){el=[el];}el.forEach(function(element){element.addEventListener(type,handler,capture||false);});}function off(el,type,handler,capture){el.removeEventListener(type,handler,capture||false);}function cancel(ev,force){if(!this.cancelEvents&&!force){return;}ev.preventDefault();ev.stopPropagation();return false;}function inherits(child,parent){function f(){this.constructor=child;}f.prototype=parent.prototype;child.prototype=new f();}// if bold is broken, we can't
-// use it in the terminal.
-function isBoldBroken(document){var body=document.getElementsByTagName('body')[0];var el=document.createElement('span');el.innerHTML='hello world';body.appendChild(el);var w1=el.scrollWidth;el.style.fontWeight='bold';var w2=el.scrollWidth;body.removeChild(el);return w1!==w2;}function indexOf(obj,el){var i=obj.length;while(i--){if(obj[i]===el)return i;}return-1;}function isThirdLevelShift(term,ev){var thirdLevelKey=term.browser.isMac&&ev.altKey&&!ev.ctrlKey&&!ev.metaKey||term.browser.isMSWindows&&ev.altKey&&ev.ctrlKey&&!ev.metaKey;if(ev.type=='keypress'){return thirdLevelKey;}// Don't invoke for arrows, pageDown, home, backspace, etc. (on non-keypress events)
-return thirdLevelKey&&(!ev.keyCode||ev.keyCode>47);}function matchColor(r1,g1,b1){var hash=r1<<16|g1<<8|b1;if(matchColor._cache[hash]!=null){return matchColor._cache[hash];}var ldiff=Infinity,li=-1,i=0,c,r2,g2,b2,diff;for(;i<Terminal.vcolors.length;i++){c=Terminal.vcolors[i];r2=c[0];g2=c[1];b2=c[2];diff=matchColor.distance(r1,g1,b1,r2,g2,b2);if(diff===0){li=i;break;}if(diff<ldiff){ldiff=diff;li=i;}}return matchColor._cache[hash]=li;}matchColor._cache={};// http://stackoverflow.com/questions/1633828
-matchColor.distance=function(r1,g1,b1,r2,g2,b2){return Math.pow(30*(r1-r2),2)+Math.pow(59*(g1-g2),2)+Math.pow(11*(b1-b2),2);};function each(obj,iter,con){if(obj.forEach)return obj.forEach(iter,con);for(var i=0;i<obj.length;i++){iter.call(con,obj[i],i,obj);}}function keys(obj){if(Object.keys)return Object.keys(obj);var key,keys=[];for(key in obj){if(Object.prototype.hasOwnProperty.call(obj,key)){keys.push(key);}}return keys;}var wcwidth=function(opts){// extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c
-// combining characters
-var COMBINING=[[0x0300,0x036F],[0x0483,0x0486],[0x0488,0x0489],[0x0591,0x05BD],[0x05BF,0x05BF],[0x05C1,0x05C2],[0x05C4,0x05C5],[0x05C7,0x05C7],[0x0600,0x0603],[0x0610,0x0615],[0x064B,0x065E],[0x0670,0x0670],[0x06D6,0x06E4],[0x06E7,0x06E8],[0x06EA,0x06ED],[0x070F,0x070F],[0x0711,0x0711],[0x0730,0x074A],[0x07A6,0x07B0],[0x07EB,0x07F3],[0x0901,0x0902],[0x093C,0x093C],[0x0941,0x0948],[0x094D,0x094D],[0x0951,0x0954],[0x0962,0x0963],[0x0981,0x0981],[0x09BC,0x09BC],[0x09C1,0x09C4],[0x09CD,0x09CD],[0x09E2,0x09E3],[0x0A01,0x0A02],[0x0A3C,0x0A3C],[0x0A41,0x0A42],[0x0A47,0x0A48],[0x0A4B,0x0A4D],[0x0A70,0x0A71],[0x0A81,0x0A82],[0x0ABC,0x0ABC],[0x0AC1,0x0AC5],[0x0AC7,0x0AC8],[0x0ACD,0x0ACD],[0x0AE2,0x0AE3],[0x0B01,0x0B01],[0x0B3C,0x0B3C],[0x0B3F,0x0B3F],[0x0B41,0x0B43],[0x0B4D,0x0B4D],[0x0B56,0x0B56],[0x0B82,0x0B82],[0x0BC0,0x0BC0],[0x0BCD,0x0BCD],[0x0C3E,0x0C40],[0x0C46,0x0C48],[0x0C4A,0x0C4D],[0x0C55,0x0C56],[0x0CBC,0x0CBC],[0x0CBF,0x0CBF],[0x0CC6,0x0CC6],[0x0CCC,0x0CCD],[0x0CE2,0x0CE3],[0x0D41,0x0D43],[0x0D4D,0x0D4D],[0x0DCA,0x0DCA],[0x0DD2,0x0DD4],[0x0DD6,0x0DD6],[0x0E31,0x0E31],[0x0E34,0x0E3A],[0x0E47,0x0E4E],[0x0EB1,0x0EB1],[0x0EB4,0x0EB9],[0x0EBB,0x0EBC],[0x0EC8,0x0ECD],[0x0F18,0x0F19],[0x0F35,0x0F35],[0x0F37,0x0F37],[0x0F39,0x0F39],[0x0F71,0x0F7E],[0x0F80,0x0F84],[0x0F86,0x0F87],[0x0F90,0x0F97],[0x0F99,0x0FBC],[0x0FC6,0x0FC6],[0x102D,0x1030],[0x1032,0x1032],[0x1036,0x1037],[0x1039,0x1039],[0x1058,0x1059],[0x1160,0x11FF],[0x135F,0x135F],[0x1712,0x1714],[0x1732,0x1734],[0x1752,0x1753],[0x1772,0x1773],[0x17B4,0x17B5],[0x17B7,0x17BD],[0x17C6,0x17C6],[0x17C9,0x17D3],[0x17DD,0x17DD],[0x180B,0x180D],[0x18A9,0x18A9],[0x1920,0x1922],[0x1927,0x1928],[0x1932,0x1932],[0x1939,0x193B],[0x1A17,0x1A18],[0x1B00,0x1B03],[0x1B34,0x1B34],[0x1B36,0x1B3A],[0x1B3C,0x1B3C],[0x1B42,0x1B42],[0x1B6B,0x1B73],[0x1DC0,0x1DCA],[0x1DFE,0x1DFF],[0x200B,0x200F],[0x202A,0x202E],[0x2060,0x2063],[0x206A,0x206F],[0x20D0,0x20EF],[0x302A,0x302F],[0x3099,0x309A],[0xA806,0xA806],[0xA80B,0xA80B],[0xA825,0xA826],[0xFB1E,0xFB1E],[0xFE00,0xFE0F],[0xFE20,0xFE23],[0xFEFF,0xFEFF],[0xFFF9,0xFFFB],[0x10A01,0x10A03],[0x10A05,0x10A06],[0x10A0C,0x10A0F],[0x10A38,0x10A3A],[0x10A3F,0x10A3F],[0x1D167,0x1D169],[0x1D173,0x1D182],[0x1D185,0x1D18B],[0x1D1AA,0x1D1AD],[0x1D242,0x1D244],[0xE0001,0xE0001],[0xE0020,0xE007F],[0xE0100,0xE01EF]];// binary search
-function bisearch(ucs){var min=0;var max=COMBINING.length-1;var mid;if(ucs<COMBINING[0][0]||ucs>COMBINING[max][1])return false;while(max>=min){mid=Math.floor((min+max)/2);if(ucs>COMBINING[mid][1])min=mid+1;else if(ucs<COMBINING[mid][0])max=mid-1;else return true;}return false;}function wcwidth(ucs){// test for 8-bit control characters
-if(ucs===0)return opts.nul;if(ucs<32||ucs>=0x7f&&ucs<0xa0)return opts.control;// binary search in table of non-spacing characters
-if(bisearch(ucs))return 0;// if we arrive here, ucs is not a combining or C0/C1 control character
-return 1+(ucs>=0x1100&&(ucs<=0x115f||// Hangul Jamo init. consonants
-ucs==0x2329||ucs==0x232a||ucs>=0x2e80&&ucs<=0xa4cf&&ucs!=0x303f||// CJK..Yi
-ucs>=0xac00&&ucs<=0xd7a3||// Hangul Syllables
-ucs>=0xf900&&ucs<=0xfaff||// CJK Compat Ideographs
-ucs>=0xfe10&&ucs<=0xfe19||// Vertical forms
-ucs>=0xfe30&&ucs<=0xfe6f||// CJK Compat Forms
-ucs>=0xff00&&ucs<=0xff60||// Fullwidth Forms
-ucs>=0xffe0&&ucs<=0xffe6||ucs>=0x20000&&ucs<=0x2fffd||ucs>=0x30000&&ucs<=0x3fffd));}return wcwidth;}({nul:0,control:0});// configurable options
-/**
- * Expose
- */Terminal.EventEmitter=_EventEmitter.EventEmitter;Terminal.CompositionHelper=_CompositionHelper.CompositionHelper;Terminal.Viewport=_Viewport.Viewport;Terminal.inherits=inherits;/**
- * Adds an event listener to the terminal.
- *
- * @param {string} event The name of the event. TODO: Document all event types
- * @param {function} callback The function to call when the event is triggered.
- */Terminal.on=on;Terminal.off=off;Terminal.cancel=cancel;module.exports=Terminal;
-
-},{"./CompositionHelper.js":1,"./EventEmitter.js":2,"./Viewport.js":3,"./handlers/Clipboard.js":4,"./utils/Browser":5}]},{},[7])(7)
-});
-//# sourceMappingURL=xterm.js.map
diff --git a/vendor/assets/stylesheets/xterm/xterm.css b/vendor/assets/stylesheets/xterm/xterm.css
deleted file mode 100644
index fabc51b0e3d..00000000000
--- a/vendor/assets/stylesheets/xterm/xterm.css
+++ /dev/null
@@ -1,2206 +0,0 @@
-/**
- * xterm.js: xterm, in the browser
- * Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
- * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
- * https://github.com/chjj/term.js
- *
- * 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.
- *
- * Originally forked from (with the author's permission):
- * Fabrice Bellard's javascript vt100 for jslinux:
- * http://bellard.org/jslinux/
- * Copyright (c) 2011 Fabrice Bellard
- * The original design remains. The terminal itself
- * has been extended to include xterm CSI codes, among
- * other features.
- */
-
-/*
- * Default style for xterm.js
- */
-
-.terminal {
- background-color: #000;
- color: #fff;
- font-family: courier-new, courier, monospace;
- font-feature-settings: "liga" 0;
- position: relative;
-}
-
-.terminal.focus,
-.terminal:focus {
- outline: none;
-}
-
-.terminal .xterm-helpers {
- position: absolute;
- top: 0;
-}
-
-.terminal .xterm-helper-textarea {
- /*
- * HACK: to fix IE's blinking cursor
- * Move textarea out of the screen to the far left, so that the cursor is not visible.
- */
- position: absolute;
- opacity: 0;
- left: -9999em;
- top: -9999em;
- width: 0;
- height: 0;
- z-index: -10;
- /** Prevent wrapping so the IME appears against the textarea at the correct position */
- white-space: nowrap;
- overflow: hidden;
- resize: none;
-}
-
-.terminal .terminal-cursor {
- background-color: #fff;
- color: #000;
-}
-
-.terminal:not(.focus) .terminal-cursor {
- outline: 1px solid #fff;
- outline-offset: -1px;
- background-color: transparent;
-}
-
-.terminal.focus .terminal-cursor.blinking {
- animation: blink-cursor 1.2s infinite step-end;
-}
-
-@keyframes blink-cursor {
- 0% {
- background-color: #fff;
- color: #000;
- }
- 50% {
- background-color: transparent;
- color: #FFF;
- }
-}
-
-.terminal .composition-view {
- background: #000;
- color: #FFF;
- display: none;
- position: absolute;
- white-space: nowrap;
- z-index: 1;
-}
-
-.terminal .composition-view.active {
- display: block;
-}
-
-.terminal .xterm-viewport {
- /* On OS X this is required in order for the scroll bar to appear fully opaque */
- background-color: #000;
- overflow-y: scroll;
-}
-
-.terminal .xterm-rows {
- position: absolute;
- left: 0;
- top: 0;
-}
-
-.terminal .xterm-rows > div {
- /* Lines containing spans and text nodes ocassionally wrap despite being the same width (#327) */
- white-space: nowrap;
-}
-
-.terminal .xterm-scroll-area {
- visibility: hidden;
-}
-
-.terminal .xterm-char-measure-element {
- display: inline-block;
- visibility: hidden;
- position: absolute;
- left: -9999em;
-}
-
-/*
- * Determine default colors for xterm.js
- */
-.terminal .xterm-bold {
- font-weight: 600;
-}
-
-.terminal .xterm-underline {
- text-decoration: underline;
-}
-
-.terminal .xterm-blink {
- text-decoration: blink;
-}
-
-.terminal .xterm-hidden {
- visibility: hidden;
-}
-
-.terminal .xterm-color-0 {
- color: #2e3436;
-}
-
-.terminal .xterm-bg-color-0 {
- background-color: #2e3436;
-}
-
-.terminal .xterm-color-1 {
- color: #cc0000;
-}
-
-.terminal .xterm-bg-color-1 {
- background-color: #cc0000;
-}
-
-.terminal .xterm-color-2 {
- color: #4e9a06;
-}
-
-.terminal .xterm-bg-color-2 {
- background-color: #4e9a06;
-}
-
-.terminal .xterm-color-3 {
- color: #c4a000;
-}
-
-.terminal .xterm-bg-color-3 {
- background-color: #c4a000;
-}
-
-.terminal .xterm-color-4 {
- color: #3465a4;
-}
-
-.terminal .xterm-bg-color-4 {
- background-color: #3465a4;
-}
-
-.terminal .xterm-color-5 {
- color: #75507b;
-}
-
-.terminal .xterm-bg-color-5 {
- background-color: #75507b;
-}
-
-.terminal .xterm-color-6 {
- color: #06989a;
-}
-
-.terminal .xterm-bg-color-6 {
- background-color: #06989a;
-}
-
-.terminal .xterm-color-7 {
- color: #d3d7cf;
-}
-
-.terminal .xterm-bg-color-7 {
- background-color: #d3d7cf;
-}
-
-.terminal .xterm-color-8 {
- color: #555753;
-}
-
-.terminal .xterm-bg-color-8 {
- background-color: #555753;
-}
-
-.terminal .xterm-color-9 {
- color: #ef2929;
-}
-
-.terminal .xterm-bg-color-9 {
- background-color: #ef2929;
-}
-
-.terminal .xterm-color-10 {
- color: #8ae234;
-}
-
-.terminal .xterm-bg-color-10 {
- background-color: #8ae234;
-}
-
-.terminal .xterm-color-11 {
- color: #fce94f;
-}
-
-.terminal .xterm-bg-color-11 {
- background-color: #fce94f;
-}
-
-.terminal .xterm-color-12 {
- color: #729fcf;
-}
-
-.terminal .xterm-bg-color-12 {
- background-color: #729fcf;
-}
-
-.terminal .xterm-color-13 {
- color: #ad7fa8;
-}
-
-.terminal .xterm-bg-color-13 {
- background-color: #ad7fa8;
-}
-
-.terminal .xterm-color-14 {
- color: #34e2e2;
-}
-
-.terminal .xterm-bg-color-14 {
- background-color: #34e2e2;
-}
-
-.terminal .xterm-color-15 {
- color: #eeeeec;
-}
-
-.terminal .xterm-bg-color-15 {
- background-color: #eeeeec;
-}
-
-.terminal .xterm-color-16 {
- color: #000000;
-}
-
-.terminal .xterm-bg-color-16 {
- background-color: #000000;
-}
-
-.terminal .xterm-color-17 {
- color: #00005f;
-}
-
-.terminal .xterm-bg-color-17 {
- background-color: #00005f;
-}
-
-.terminal .xterm-color-18 {
- color: #000087;
-}
-
-.terminal .xterm-bg-color-18 {
- background-color: #000087;
-}
-
-.terminal .xterm-color-19 {
- color: #0000af;
-}
-
-.terminal .xterm-bg-color-19 {
- background-color: #0000af;
-}
-
-.terminal .xterm-color-20 {
- color: #0000d7;
-}
-
-.terminal .xterm-bg-color-20 {
- background-color: #0000d7;
-}
-
-.terminal .xterm-color-21 {
- color: #0000ff;
-}
-
-.terminal .xterm-bg-color-21 {
- background-color: #0000ff;
-}
-
-.terminal .xterm-color-22 {
- color: #005f00;
-}
-
-.terminal .xterm-bg-color-22 {
- background-color: #005f00;
-}
-
-.terminal .xterm-color-23 {
- color: #005f5f;
-}
-
-.terminal .xterm-bg-color-23 {
- background-color: #005f5f;
-}
-
-.terminal .xterm-color-24 {
- color: #005f87;
-}
-
-.terminal .xterm-bg-color-24 {
- background-color: #005f87;
-}
-
-.terminal .xterm-color-25 {
- color: #005faf;
-}
-
-.terminal .xterm-bg-color-25 {
- background-color: #005faf;
-}
-
-.terminal .xterm-color-26 {
- color: #005fd7;
-}
-
-.terminal .xterm-bg-color-26 {
- background-color: #005fd7;
-}
-
-.terminal .xterm-color-27 {
- color: #005fff;
-}
-
-.terminal .xterm-bg-color-27 {
- background-color: #005fff;
-}
-
-.terminal .xterm-color-28 {
- color: #008700;
-}
-
-.terminal .xterm-bg-color-28 {
- background-color: #008700;
-}
-
-.terminal .xterm-color-29 {
- color: #00875f;
-}
-
-.terminal .xterm-bg-color-29 {
- background-color: #00875f;
-}
-
-.terminal .xterm-color-30 {
- color: #008787;
-}
-
-.terminal .xterm-bg-color-30 {
- background-color: #008787;
-}
-
-.terminal .xterm-color-31 {
- color: #0087af;
-}
-
-.terminal .xterm-bg-color-31 {
- background-color: #0087af;
-}
-
-.terminal .xterm-color-32 {
- color: #0087d7;
-}
-
-.terminal .xterm-bg-color-32 {
- background-color: #0087d7;
-}
-
-.terminal .xterm-color-33 {
- color: #0087ff;
-}
-
-.terminal .xterm-bg-color-33 {
- background-color: #0087ff;
-}
-
-.terminal .xterm-color-34 {
- color: #00af00;
-}
-
-.terminal .xterm-bg-color-34 {
- background-color: #00af00;
-}
-
-.terminal .xterm-color-35 {
- color: #00af5f;
-}
-
-.terminal .xterm-bg-color-35 {
- background-color: #00af5f;
-}
-
-.terminal .xterm-color-36 {
- color: #00af87;
-}
-
-.terminal .xterm-bg-color-36 {
- background-color: #00af87;
-}
-
-.terminal .xterm-color-37 {
- color: #00afaf;
-}
-
-.terminal .xterm-bg-color-37 {
- background-color: #00afaf;
-}
-
-.terminal .xterm-color-38 {
- color: #00afd7;
-}
-
-.terminal .xterm-bg-color-38 {
- background-color: #00afd7;
-}
-
-.terminal .xterm-color-39 {
- color: #00afff;
-}
-
-.terminal .xterm-bg-color-39 {
- background-color: #00afff;
-}
-
-.terminal .xterm-color-40 {
- color: #00d700;
-}
-
-.terminal .xterm-bg-color-40 {
- background-color: #00d700;
-}
-
-.terminal .xterm-color-41 {
- color: #00d75f;
-}
-
-.terminal .xterm-bg-color-41 {
- background-color: #00d75f;
-}
-
-.terminal .xterm-color-42 {
- color: #00d787;
-}
-
-.terminal .xterm-bg-color-42 {
- background-color: #00d787;
-}
-
-.terminal .xterm-color-43 {
- color: #00d7af;
-}
-
-.terminal .xterm-bg-color-43 {
- background-color: #00d7af;
-}
-
-.terminal .xterm-color-44 {
- color: #00d7d7;
-}
-
-.terminal .xterm-bg-color-44 {
- background-color: #00d7d7;
-}
-
-.terminal .xterm-color-45 {
- color: #00d7ff;
-}
-
-.terminal .xterm-bg-color-45 {
- background-color: #00d7ff;
-}
-
-.terminal .xterm-color-46 {
- color: #00ff00;
-}
-
-.terminal .xterm-bg-color-46 {
- background-color: #00ff00;
-}
-
-.terminal .xterm-color-47 {
- color: #00ff5f;
-}
-
-.terminal .xterm-bg-color-47 {
- background-color: #00ff5f;
-}
-
-.terminal .xterm-color-48 {
- color: #00ff87;
-}
-
-.terminal .xterm-bg-color-48 {
- background-color: #00ff87;
-}
-
-.terminal .xterm-color-49 {
- color: #00ffaf;
-}
-
-.terminal .xterm-bg-color-49 {
- background-color: #00ffaf;
-}
-
-.terminal .xterm-color-50 {
- color: #00ffd7;
-}
-
-.terminal .xterm-bg-color-50 {
- background-color: #00ffd7;
-}
-
-.terminal .xterm-color-51 {
- color: #00ffff;
-}
-
-.terminal .xterm-bg-color-51 {
- background-color: #00ffff;
-}
-
-.terminal .xterm-color-52 {
- color: #5f0000;
-}
-
-.terminal .xterm-bg-color-52 {
- background-color: #5f0000;
-}
-
-.terminal .xterm-color-53 {
- color: #5f005f;
-}
-
-.terminal .xterm-bg-color-53 {
- background-color: #5f005f;
-}
-
-.terminal .xterm-color-54 {
- color: #5f0087;
-}
-
-.terminal .xterm-bg-color-54 {
- background-color: #5f0087;
-}
-
-.terminal .xterm-color-55 {
- color: #5f00af;
-}
-
-.terminal .xterm-bg-color-55 {
- background-color: #5f00af;
-}
-
-.terminal .xterm-color-56 {
- color: #5f00d7;
-}
-
-.terminal .xterm-bg-color-56 {
- background-color: #5f00d7;
-}
-
-.terminal .xterm-color-57 {
- color: #5f00ff;
-}
-
-.terminal .xterm-bg-color-57 {
- background-color: #5f00ff;
-}
-
-.terminal .xterm-color-58 {
- color: #5f5f00;
-}
-
-.terminal .xterm-bg-color-58 {
- background-color: #5f5f00;
-}
-
-.terminal .xterm-color-59 {
- color: #5f5f5f;
-}
-
-.terminal .xterm-bg-color-59 {
- background-color: #5f5f5f;
-}
-
-.terminal .xterm-color-60 {
- color: #5f5f87;
-}
-
-.terminal .xterm-bg-color-60 {
- background-color: #5f5f87;
-}
-
-.terminal .xterm-color-61 {
- color: #5f5faf;
-}
-
-.terminal .xterm-bg-color-61 {
- background-color: #5f5faf;
-}
-
-.terminal .xterm-color-62 {
- color: #5f5fd7;
-}
-
-.terminal .xterm-bg-color-62 {
- background-color: #5f5fd7;
-}
-
-.terminal .xterm-color-63 {
- color: #5f5fff;
-}
-
-.terminal .xterm-bg-color-63 {
- background-color: #5f5fff;
-}
-
-.terminal .xterm-color-64 {
- color: #5f8700;
-}
-
-.terminal .xterm-bg-color-64 {
- background-color: #5f8700;
-}
-
-.terminal .xterm-color-65 {
- color: #5f875f;
-}
-
-.terminal .xterm-bg-color-65 {
- background-color: #5f875f;
-}
-
-.terminal .xterm-color-66 {
- color: #5f8787;
-}
-
-.terminal .xterm-bg-color-66 {
- background-color: #5f8787;
-}
-
-.terminal .xterm-color-67 {
- color: #5f87af;
-}
-
-.terminal .xterm-bg-color-67 {
- background-color: #5f87af;
-}
-
-.terminal .xterm-color-68 {
- color: #5f87d7;
-}
-
-.terminal .xterm-bg-color-68 {
- background-color: #5f87d7;
-}
-
-.terminal .xterm-color-69 {
- color: #5f87ff;
-}
-
-.terminal .xterm-bg-color-69 {
- background-color: #5f87ff;
-}
-
-.terminal .xterm-color-70 {
- color: #5faf00;
-}
-
-.terminal .xterm-bg-color-70 {
- background-color: #5faf00;
-}
-
-.terminal .xterm-color-71 {
- color: #5faf5f;
-}
-
-.terminal .xterm-bg-color-71 {
- background-color: #5faf5f;
-}
-
-.terminal .xterm-color-72 {
- color: #5faf87;
-}
-
-.terminal .xterm-bg-color-72 {
- background-color: #5faf87;
-}
-
-.terminal .xterm-color-73 {
- color: #5fafaf;
-}
-
-.terminal .xterm-bg-color-73 {
- background-color: #5fafaf;
-}
-
-.terminal .xterm-color-74 {
- color: #5fafd7;
-}
-
-.terminal .xterm-bg-color-74 {
- background-color: #5fafd7;
-}
-
-.terminal .xterm-color-75 {
- color: #5fafff;
-}
-
-.terminal .xterm-bg-color-75 {
- background-color: #5fafff;
-}
-
-.terminal .xterm-color-76 {
- color: #5fd700;
-}
-
-.terminal .xterm-bg-color-76 {
- background-color: #5fd700;
-}
-
-.terminal .xterm-color-77 {
- color: #5fd75f;
-}
-
-.terminal .xterm-bg-color-77 {
- background-color: #5fd75f;
-}
-
-.terminal .xterm-color-78 {
- color: #5fd787;
-}
-
-.terminal .xterm-bg-color-78 {
- background-color: #5fd787;
-}
-
-.terminal .xterm-color-79 {
- color: #5fd7af;
-}
-
-.terminal .xterm-bg-color-79 {
- background-color: #5fd7af;
-}
-
-.terminal .xterm-color-80 {
- color: #5fd7d7;
-}
-
-.terminal .xterm-bg-color-80 {
- background-color: #5fd7d7;
-}
-
-.terminal .xterm-color-81 {
- color: #5fd7ff;
-}
-
-.terminal .xterm-bg-color-81 {
- background-color: #5fd7ff;
-}
-
-.terminal .xterm-color-82 {
- color: #5fff00;
-}
-
-.terminal .xterm-bg-color-82 {
- background-color: #5fff00;
-}
-
-.terminal .xterm-color-83 {
- color: #5fff5f;
-}
-
-.terminal .xterm-bg-color-83 {
- background-color: #5fff5f;
-}
-
-.terminal .xterm-color-84 {
- color: #5fff87;
-}
-
-.terminal .xterm-bg-color-84 {
- background-color: #5fff87;
-}
-
-.terminal .xterm-color-85 {
- color: #5fffaf;
-}
-
-.terminal .xterm-bg-color-85 {
- background-color: #5fffaf;
-}
-
-.terminal .xterm-color-86 {
- color: #5fffd7;
-}
-
-.terminal .xterm-bg-color-86 {
- background-color: #5fffd7;
-}
-
-.terminal .xterm-color-87 {
- color: #5fffff;
-}
-
-.terminal .xterm-bg-color-87 {
- background-color: #5fffff;
-}
-
-.terminal .xterm-color-88 {
- color: #870000;
-}
-
-.terminal .xterm-bg-color-88 {
- background-color: #870000;
-}
-
-.terminal .xterm-color-89 {
- color: #87005f;
-}
-
-.terminal .xterm-bg-color-89 {
- background-color: #87005f;
-}
-
-.terminal .xterm-color-90 {
- color: #870087;
-}
-
-.terminal .xterm-bg-color-90 {
- background-color: #870087;
-}
-
-.terminal .xterm-color-91 {
- color: #8700af;
-}
-
-.terminal .xterm-bg-color-91 {
- background-color: #8700af;
-}
-
-.terminal .xterm-color-92 {
- color: #8700d7;
-}
-
-.terminal .xterm-bg-color-92 {
- background-color: #8700d7;
-}
-
-.terminal .xterm-color-93 {
- color: #8700ff;
-}
-
-.terminal .xterm-bg-color-93 {
- background-color: #8700ff;
-}
-
-.terminal .xterm-color-94 {
- color: #875f00;
-}
-
-.terminal .xterm-bg-color-94 {
- background-color: #875f00;
-}
-
-.terminal .xterm-color-95 {
- color: #875f5f;
-}
-
-.terminal .xterm-bg-color-95 {
- background-color: #875f5f;
-}
-
-.terminal .xterm-color-96 {
- color: #875f87;
-}
-
-.terminal .xterm-bg-color-96 {
- background-color: #875f87;
-}
-
-.terminal .xterm-color-97 {
- color: #875faf;
-}
-
-.terminal .xterm-bg-color-97 {
- background-color: #875faf;
-}
-
-.terminal .xterm-color-98 {
- color: #875fd7;
-}
-
-.terminal .xterm-bg-color-98 {
- background-color: #875fd7;
-}
-
-.terminal .xterm-color-99 {
- color: #875fff;
-}
-
-.terminal .xterm-bg-color-99 {
- background-color: #875fff;
-}
-
-.terminal .xterm-color-100 {
- color: #878700;
-}
-
-.terminal .xterm-bg-color-100 {
- background-color: #878700;
-}
-
-.terminal .xterm-color-101 {
- color: #87875f;
-}
-
-.terminal .xterm-bg-color-101 {
- background-color: #87875f;
-}
-
-.terminal .xterm-color-102 {
- color: #878787;
-}
-
-.terminal .xterm-bg-color-102 {
- background-color: #878787;
-}
-
-.terminal .xterm-color-103 {
- color: #8787af;
-}
-
-.terminal .xterm-bg-color-103 {
- background-color: #8787af;
-}
-
-.terminal .xterm-color-104 {
- color: #8787d7;
-}
-
-.terminal .xterm-bg-color-104 {
- background-color: #8787d7;
-}
-
-.terminal .xterm-color-105 {
- color: #8787ff;
-}
-
-.terminal .xterm-bg-color-105 {
- background-color: #8787ff;
-}
-
-.terminal .xterm-color-106 {
- color: #87af00;
-}
-
-.terminal .xterm-bg-color-106 {
- background-color: #87af00;
-}
-
-.terminal .xterm-color-107 {
- color: #87af5f;
-}
-
-.terminal .xterm-bg-color-107 {
- background-color: #87af5f;
-}
-
-.terminal .xterm-color-108 {
- color: #87af87;
-}
-
-.terminal .xterm-bg-color-108 {
- background-color: #87af87;
-}
-
-.terminal .xterm-color-109 {
- color: #87afaf;
-}
-
-.terminal .xterm-bg-color-109 {
- background-color: #87afaf;
-}
-
-.terminal .xterm-color-110 {
- color: #87afd7;
-}
-
-.terminal .xterm-bg-color-110 {
- background-color: #87afd7;
-}
-
-.terminal .xterm-color-111 {
- color: #87afff;
-}
-
-.terminal .xterm-bg-color-111 {
- background-color: #87afff;
-}
-
-.terminal .xterm-color-112 {
- color: #87d700;
-}
-
-.terminal .xterm-bg-color-112 {
- background-color: #87d700;
-}
-
-.terminal .xterm-color-113 {
- color: #87d75f;
-}
-
-.terminal .xterm-bg-color-113 {
- background-color: #87d75f;
-}
-
-.terminal .xterm-color-114 {
- color: #87d787;
-}
-
-.terminal .xterm-bg-color-114 {
- background-color: #87d787;
-}
-
-.terminal .xterm-color-115 {
- color: #87d7af;
-}
-
-.terminal .xterm-bg-color-115 {
- background-color: #87d7af;
-}
-
-.terminal .xterm-color-116 {
- color: #87d7d7;
-}
-
-.terminal .xterm-bg-color-116 {
- background-color: #87d7d7;
-}
-
-.terminal .xterm-color-117 {
- color: #87d7ff;
-}
-
-.terminal .xterm-bg-color-117 {
- background-color: #87d7ff;
-}
-
-.terminal .xterm-color-118 {
- color: #87ff00;
-}
-
-.terminal .xterm-bg-color-118 {
- background-color: #87ff00;
-}
-
-.terminal .xterm-color-119 {
- color: #87ff5f;
-}
-
-.terminal .xterm-bg-color-119 {
- background-color: #87ff5f;
-}
-
-.terminal .xterm-color-120 {
- color: #87ff87;
-}
-
-.terminal .xterm-bg-color-120 {
- background-color: #87ff87;
-}
-
-.terminal .xterm-color-121 {
- color: #87ffaf;
-}
-
-.terminal .xterm-bg-color-121 {
- background-color: #87ffaf;
-}
-
-.terminal .xterm-color-122 {
- color: #87ffd7;
-}
-
-.terminal .xterm-bg-color-122 {
- background-color: #87ffd7;
-}
-
-.terminal .xterm-color-123 {
- color: #87ffff;
-}
-
-.terminal .xterm-bg-color-123 {
- background-color: #87ffff;
-}
-
-.terminal .xterm-color-124 {
- color: #af0000;
-}
-
-.terminal .xterm-bg-color-124 {
- background-color: #af0000;
-}
-
-.terminal .xterm-color-125 {
- color: #af005f;
-}
-
-.terminal .xterm-bg-color-125 {
- background-color: #af005f;
-}
-
-.terminal .xterm-color-126 {
- color: #af0087;
-}
-
-.terminal .xterm-bg-color-126 {
- background-color: #af0087;
-}
-
-.terminal .xterm-color-127 {
- color: #af00af;
-}
-
-.terminal .xterm-bg-color-127 {
- background-color: #af00af;
-}
-
-.terminal .xterm-color-128 {
- color: #af00d7;
-}
-
-.terminal .xterm-bg-color-128 {
- background-color: #af00d7;
-}
-
-.terminal .xterm-color-129 {
- color: #af00ff;
-}
-
-.terminal .xterm-bg-color-129 {
- background-color: #af00ff;
-}
-
-.terminal .xterm-color-130 {
- color: #af5f00;
-}
-
-.terminal .xterm-bg-color-130 {
- background-color: #af5f00;
-}
-
-.terminal .xterm-color-131 {
- color: #af5f5f;
-}
-
-.terminal .xterm-bg-color-131 {
- background-color: #af5f5f;
-}
-
-.terminal .xterm-color-132 {
- color: #af5f87;
-}
-
-.terminal .xterm-bg-color-132 {
- background-color: #af5f87;
-}
-
-.terminal .xterm-color-133 {
- color: #af5faf;
-}
-
-.terminal .xterm-bg-color-133 {
- background-color: #af5faf;
-}
-
-.terminal .xterm-color-134 {
- color: #af5fd7;
-}
-
-.terminal .xterm-bg-color-134 {
- background-color: #af5fd7;
-}
-
-.terminal .xterm-color-135 {
- color: #af5fff;
-}
-
-.terminal .xterm-bg-color-135 {
- background-color: #af5fff;
-}
-
-.terminal .xterm-color-136 {
- color: #af8700;
-}
-
-.terminal .xterm-bg-color-136 {
- background-color: #af8700;
-}
-
-.terminal .xterm-color-137 {
- color: #af875f;
-}
-
-.terminal .xterm-bg-color-137 {
- background-color: #af875f;
-}
-
-.terminal .xterm-color-138 {
- color: #af8787;
-}
-
-.terminal .xterm-bg-color-138 {
- background-color: #af8787;
-}
-
-.terminal .xterm-color-139 {
- color: #af87af;
-}
-
-.terminal .xterm-bg-color-139 {
- background-color: #af87af;
-}
-
-.terminal .xterm-color-140 {
- color: #af87d7;
-}
-
-.terminal .xterm-bg-color-140 {
- background-color: #af87d7;
-}
-
-.terminal .xterm-color-141 {
- color: #af87ff;
-}
-
-.terminal .xterm-bg-color-141 {
- background-color: #af87ff;
-}
-
-.terminal .xterm-color-142 {
- color: #afaf00;
-}
-
-.terminal .xterm-bg-color-142 {
- background-color: #afaf00;
-}
-
-.terminal .xterm-color-143 {
- color: #afaf5f;
-}
-
-.terminal .xterm-bg-color-143 {
- background-color: #afaf5f;
-}
-
-.terminal .xterm-color-144 {
- color: #afaf87;
-}
-
-.terminal .xterm-bg-color-144 {
- background-color: #afaf87;
-}
-
-.terminal .xterm-color-145 {
- color: #afafaf;
-}
-
-.terminal .xterm-bg-color-145 {
- background-color: #afafaf;
-}
-
-.terminal .xterm-color-146 {
- color: #afafd7;
-}
-
-.terminal .xterm-bg-color-146 {
- background-color: #afafd7;
-}
-
-.terminal .xterm-color-147 {
- color: #afafff;
-}
-
-.terminal .xterm-bg-color-147 {
- background-color: #afafff;
-}
-
-.terminal .xterm-color-148 {
- color: #afd700;
-}
-
-.terminal .xterm-bg-color-148 {
- background-color: #afd700;
-}
-
-.terminal .xterm-color-149 {
- color: #afd75f;
-}
-
-.terminal .xterm-bg-color-149 {
- background-color: #afd75f;
-}
-
-.terminal .xterm-color-150 {
- color: #afd787;
-}
-
-.terminal .xterm-bg-color-150 {
- background-color: #afd787;
-}
-
-.terminal .xterm-color-151 {
- color: #afd7af;
-}
-
-.terminal .xterm-bg-color-151 {
- background-color: #afd7af;
-}
-
-.terminal .xterm-color-152 {
- color: #afd7d7;
-}
-
-.terminal .xterm-bg-color-152 {
- background-color: #afd7d7;
-}
-
-.terminal .xterm-color-153 {
- color: #afd7ff;
-}
-
-.terminal .xterm-bg-color-153 {
- background-color: #afd7ff;
-}
-
-.terminal .xterm-color-154 {
- color: #afff00;
-}
-
-.terminal .xterm-bg-color-154 {
- background-color: #afff00;
-}
-
-.terminal .xterm-color-155 {
- color: #afff5f;
-}
-
-.terminal .xterm-bg-color-155 {
- background-color: #afff5f;
-}
-
-.terminal .xterm-color-156 {
- color: #afff87;
-}
-
-.terminal .xterm-bg-color-156 {
- background-color: #afff87;
-}
-
-.terminal .xterm-color-157 {
- color: #afffaf;
-}
-
-.terminal .xterm-bg-color-157 {
- background-color: #afffaf;
-}
-
-.terminal .xterm-color-158 {
- color: #afffd7;
-}
-
-.terminal .xterm-bg-color-158 {
- background-color: #afffd7;
-}
-
-.terminal .xterm-color-159 {
- color: #afffff;
-}
-
-.terminal .xterm-bg-color-159 {
- background-color: #afffff;
-}
-
-.terminal .xterm-color-160 {
- color: #d70000;
-}
-
-.terminal .xterm-bg-color-160 {
- background-color: #d70000;
-}
-
-.terminal .xterm-color-161 {
- color: #d7005f;
-}
-
-.terminal .xterm-bg-color-161 {
- background-color: #d7005f;
-}
-
-.terminal .xterm-color-162 {
- color: #d70087;
-}
-
-.terminal .xterm-bg-color-162 {
- background-color: #d70087;
-}
-
-.terminal .xterm-color-163 {
- color: #d700af;
-}
-
-.terminal .xterm-bg-color-163 {
- background-color: #d700af;
-}
-
-.terminal .xterm-color-164 {
- color: #d700d7;
-}
-
-.terminal .xterm-bg-color-164 {
- background-color: #d700d7;
-}
-
-.terminal .xterm-color-165 {
- color: #d700ff;
-}
-
-.terminal .xterm-bg-color-165 {
- background-color: #d700ff;
-}
-
-.terminal .xterm-color-166 {
- color: #d75f00;
-}
-
-.terminal .xterm-bg-color-166 {
- background-color: #d75f00;
-}
-
-.terminal .xterm-color-167 {
- color: #d75f5f;
-}
-
-.terminal .xterm-bg-color-167 {
- background-color: #d75f5f;
-}
-
-.terminal .xterm-color-168 {
- color: #d75f87;
-}
-
-.terminal .xterm-bg-color-168 {
- background-color: #d75f87;
-}
-
-.terminal .xterm-color-169 {
- color: #d75faf;
-}
-
-.terminal .xterm-bg-color-169 {
- background-color: #d75faf;
-}
-
-.terminal .xterm-color-170 {
- color: #d75fd7;
-}
-
-.terminal .xterm-bg-color-170 {
- background-color: #d75fd7;
-}
-
-.terminal .xterm-color-171 {
- color: #d75fff;
-}
-
-.terminal .xterm-bg-color-171 {
- background-color: #d75fff;
-}
-
-.terminal .xterm-color-172 {
- color: #d78700;
-}
-
-.terminal .xterm-bg-color-172 {
- background-color: #d78700;
-}
-
-.terminal .xterm-color-173 {
- color: #d7875f;
-}
-
-.terminal .xterm-bg-color-173 {
- background-color: #d7875f;
-}
-
-.terminal .xterm-color-174 {
- color: #d78787;
-}
-
-.terminal .xterm-bg-color-174 {
- background-color: #d78787;
-}
-
-.terminal .xterm-color-175 {
- color: #d787af;
-}
-
-.terminal .xterm-bg-color-175 {
- background-color: #d787af;
-}
-
-.terminal .xterm-color-176 {
- color: #d787d7;
-}
-
-.terminal .xterm-bg-color-176 {
- background-color: #d787d7;
-}
-
-.terminal .xterm-color-177 {
- color: #d787ff;
-}
-
-.terminal .xterm-bg-color-177 {
- background-color: #d787ff;
-}
-
-.terminal .xterm-color-178 {
- color: #d7af00;
-}
-
-.terminal .xterm-bg-color-178 {
- background-color: #d7af00;
-}
-
-.terminal .xterm-color-179 {
- color: #d7af5f;
-}
-
-.terminal .xterm-bg-color-179 {
- background-color: #d7af5f;
-}
-
-.terminal .xterm-color-180 {
- color: #d7af87;
-}
-
-.terminal .xterm-bg-color-180 {
- background-color: #d7af87;
-}
-
-.terminal .xterm-color-181 {
- color: #d7afaf;
-}
-
-.terminal .xterm-bg-color-181 {
- background-color: #d7afaf;
-}
-
-.terminal .xterm-color-182 {
- color: #d7afd7;
-}
-
-.terminal .xterm-bg-color-182 {
- background-color: #d7afd7;
-}
-
-.terminal .xterm-color-183 {
- color: #d7afff;
-}
-
-.terminal .xterm-bg-color-183 {
- background-color: #d7afff;
-}
-
-.terminal .xterm-color-184 {
- color: #d7d700;
-}
-
-.terminal .xterm-bg-color-184 {
- background-color: #d7d700;
-}
-
-.terminal .xterm-color-185 {
- color: #d7d75f;
-}
-
-.terminal .xterm-bg-color-185 {
- background-color: #d7d75f;
-}
-
-.terminal .xterm-color-186 {
- color: #d7d787;
-}
-
-.terminal .xterm-bg-color-186 {
- background-color: #d7d787;
-}
-
-.terminal .xterm-color-187 {
- color: #d7d7af;
-}
-
-.terminal .xterm-bg-color-187 {
- background-color: #d7d7af;
-}
-
-.terminal .xterm-color-188 {
- color: #d7d7d7;
-}
-
-.terminal .xterm-bg-color-188 {
- background-color: #d7d7d7;
-}
-
-.terminal .xterm-color-189 {
- color: #d7d7ff;
-}
-
-.terminal .xterm-bg-color-189 {
- background-color: #d7d7ff;
-}
-
-.terminal .xterm-color-190 {
- color: #d7ff00;
-}
-
-.terminal .xterm-bg-color-190 {
- background-color: #d7ff00;
-}
-
-.terminal .xterm-color-191 {
- color: #d7ff5f;
-}
-
-.terminal .xterm-bg-color-191 {
- background-color: #d7ff5f;
-}
-
-.terminal .xterm-color-192 {
- color: #d7ff87;
-}
-
-.terminal .xterm-bg-color-192 {
- background-color: #d7ff87;
-}
-
-.terminal .xterm-color-193 {
- color: #d7ffaf;
-}
-
-.terminal .xterm-bg-color-193 {
- background-color: #d7ffaf;
-}
-
-.terminal .xterm-color-194 {
- color: #d7ffd7;
-}
-
-.terminal .xterm-bg-color-194 {
- background-color: #d7ffd7;
-}
-
-.terminal .xterm-color-195 {
- color: #d7ffff;
-}
-
-.terminal .xterm-bg-color-195 {
- background-color: #d7ffff;
-}
-
-.terminal .xterm-color-196 {
- color: #ff0000;
-}
-
-.terminal .xterm-bg-color-196 {
- background-color: #ff0000;
-}
-
-.terminal .xterm-color-197 {
- color: #ff005f;
-}
-
-.terminal .xterm-bg-color-197 {
- background-color: #ff005f;
-}
-
-.terminal .xterm-color-198 {
- color: #ff0087;
-}
-
-.terminal .xterm-bg-color-198 {
- background-color: #ff0087;
-}
-
-.terminal .xterm-color-199 {
- color: #ff00af;
-}
-
-.terminal .xterm-bg-color-199 {
- background-color: #ff00af;
-}
-
-.terminal .xterm-color-200 {
- color: #ff00d7;
-}
-
-.terminal .xterm-bg-color-200 {
- background-color: #ff00d7;
-}
-
-.terminal .xterm-color-201 {
- color: #ff00ff;
-}
-
-.terminal .xterm-bg-color-201 {
- background-color: #ff00ff;
-}
-
-.terminal .xterm-color-202 {
- color: #ff5f00;
-}
-
-.terminal .xterm-bg-color-202 {
- background-color: #ff5f00;
-}
-
-.terminal .xterm-color-203 {
- color: #ff5f5f;
-}
-
-.terminal .xterm-bg-color-203 {
- background-color: #ff5f5f;
-}
-
-.terminal .xterm-color-204 {
- color: #ff5f87;
-}
-
-.terminal .xterm-bg-color-204 {
- background-color: #ff5f87;
-}
-
-.terminal .xterm-color-205 {
- color: #ff5faf;
-}
-
-.terminal .xterm-bg-color-205 {
- background-color: #ff5faf;
-}
-
-.terminal .xterm-color-206 {
- color: #ff5fd7;
-}
-
-.terminal .xterm-bg-color-206 {
- background-color: #ff5fd7;
-}
-
-.terminal .xterm-color-207 {
- color: #ff5fff;
-}
-
-.terminal .xterm-bg-color-207 {
- background-color: #ff5fff;
-}
-
-.terminal .xterm-color-208 {
- color: #ff8700;
-}
-
-.terminal .xterm-bg-color-208 {
- background-color: #ff8700;
-}
-
-.terminal .xterm-color-209 {
- color: #ff875f;
-}
-
-.terminal .xterm-bg-color-209 {
- background-color: #ff875f;
-}
-
-.terminal .xterm-color-210 {
- color: #ff8787;
-}
-
-.terminal .xterm-bg-color-210 {
- background-color: #ff8787;
-}
-
-.terminal .xterm-color-211 {
- color: #ff87af;
-}
-
-.terminal .xterm-bg-color-211 {
- background-color: #ff87af;
-}
-
-.terminal .xterm-color-212 {
- color: #ff87d7;
-}
-
-.terminal .xterm-bg-color-212 {
- background-color: #ff87d7;
-}
-
-.terminal .xterm-color-213 {
- color: #ff87ff;
-}
-
-.terminal .xterm-bg-color-213 {
- background-color: #ff87ff;
-}
-
-.terminal .xterm-color-214 {
- color: #ffaf00;
-}
-
-.terminal .xterm-bg-color-214 {
- background-color: #ffaf00;
-}
-
-.terminal .xterm-color-215 {
- color: #ffaf5f;
-}
-
-.terminal .xterm-bg-color-215 {
- background-color: #ffaf5f;
-}
-
-.terminal .xterm-color-216 {
- color: #ffaf87;
-}
-
-.terminal .xterm-bg-color-216 {
- background-color: #ffaf87;
-}
-
-.terminal .xterm-color-217 {
- color: #ffafaf;
-}
-
-.terminal .xterm-bg-color-217 {
- background-color: #ffafaf;
-}
-
-.terminal .xterm-color-218 {
- color: #ffafd7;
-}
-
-.terminal .xterm-bg-color-218 {
- background-color: #ffafd7;
-}
-
-.terminal .xterm-color-219 {
- color: #ffafff;
-}
-
-.terminal .xterm-bg-color-219 {
- background-color: #ffafff;
-}
-
-.terminal .xterm-color-220 {
- color: #ffd700;
-}
-
-.terminal .xterm-bg-color-220 {
- background-color: #ffd700;
-}
-
-.terminal .xterm-color-221 {
- color: #ffd75f;
-}
-
-.terminal .xterm-bg-color-221 {
- background-color: #ffd75f;
-}
-
-.terminal .xterm-color-222 {
- color: #ffd787;
-}
-
-.terminal .xterm-bg-color-222 {
- background-color: #ffd787;
-}
-
-.terminal .xterm-color-223 {
- color: #ffd7af;
-}
-
-.terminal .xterm-bg-color-223 {
- background-color: #ffd7af;
-}
-
-.terminal .xterm-color-224 {
- color: #ffd7d7;
-}
-
-.terminal .xterm-bg-color-224 {
- background-color: #ffd7d7;
-}
-
-.terminal .xterm-color-225 {
- color: #ffd7ff;
-}
-
-.terminal .xterm-bg-color-225 {
- background-color: #ffd7ff;
-}
-
-.terminal .xterm-color-226 {
- color: #ffff00;
-}
-
-.terminal .xterm-bg-color-226 {
- background-color: #ffff00;
-}
-
-.terminal .xterm-color-227 {
- color: #ffff5f;
-}
-
-.terminal .xterm-bg-color-227 {
- background-color: #ffff5f;
-}
-
-.terminal .xterm-color-228 {
- color: #ffff87;
-}
-
-.terminal .xterm-bg-color-228 {
- background-color: #ffff87;
-}
-
-.terminal .xterm-color-229 {
- color: #ffffaf;
-}
-
-.terminal .xterm-bg-color-229 {
- background-color: #ffffaf;
-}
-
-.terminal .xterm-color-230 {
- color: #ffffd7;
-}
-
-.terminal .xterm-bg-color-230 {
- background-color: #ffffd7;
-}
-
-.terminal .xterm-color-231 {
- color: #ffffff;
-}
-
-.terminal .xterm-bg-color-231 {
- background-color: #ffffff;
-}
-
-.terminal .xterm-color-232 {
- color: #080808;
-}
-
-.terminal .xterm-bg-color-232 {
- background-color: #080808;
-}
-
-.terminal .xterm-color-233 {
- color: #121212;
-}
-
-.terminal .xterm-bg-color-233 {
- background-color: #121212;
-}
-
-.terminal .xterm-color-234 {
- color: #1c1c1c;
-}
-
-.terminal .xterm-bg-color-234 {
- background-color: #1c1c1c;
-}
-
-.terminal .xterm-color-235 {
- color: #262626;
-}
-
-.terminal .xterm-bg-color-235 {
- background-color: #262626;
-}
-
-.terminal .xterm-color-236 {
- color: #303030;
-}
-
-.terminal .xterm-bg-color-236 {
- background-color: #303030;
-}
-
-.terminal .xterm-color-237 {
- color: #3a3a3a;
-}
-
-.terminal .xterm-bg-color-237 {
- background-color: #3a3a3a;
-}
-
-.terminal .xterm-color-238 {
- color: #444444;
-}
-
-.terminal .xterm-bg-color-238 {
- background-color: #444444;
-}
-
-.terminal .xterm-color-239 {
- color: #4e4e4e;
-}
-
-.terminal .xterm-bg-color-239 {
- background-color: #4e4e4e;
-}
-
-.terminal .xterm-color-240 {
- color: #585858;
-}
-
-.terminal .xterm-bg-color-240 {
- background-color: #585858;
-}
-
-.terminal .xterm-color-241 {
- color: #626262;
-}
-
-.terminal .xterm-bg-color-241 {
- background-color: #626262;
-}
-
-.terminal .xterm-color-242 {
- color: #6c6c6c;
-}
-
-.terminal .xterm-bg-color-242 {
- background-color: #6c6c6c;
-}
-
-.terminal .xterm-color-243 {
- color: #767676;
-}
-
-.terminal .xterm-bg-color-243 {
- background-color: #767676;
-}
-
-.terminal .xterm-color-244 {
- color: #808080;
-}
-
-.terminal .xterm-bg-color-244 {
- background-color: #808080;
-}
-
-.terminal .xterm-color-245 {
- color: #8a8a8a;
-}
-
-.terminal .xterm-bg-color-245 {
- background-color: #8a8a8a;
-}
-
-.terminal .xterm-color-246 {
- color: #949494;
-}
-
-.terminal .xterm-bg-color-246 {
- background-color: #949494;
-}
-
-.terminal .xterm-color-247 {
- color: #9e9e9e;
-}
-
-.terminal .xterm-bg-color-247 {
- background-color: #9e9e9e;
-}
-
-.terminal .xterm-color-248 {
- color: #a8a8a8;
-}
-
-.terminal .xterm-bg-color-248 {
- background-color: #a8a8a8;
-}
-
-.terminal .xterm-color-249 {
- color: #b2b2b2;
-}
-
-.terminal .xterm-bg-color-249 {
- background-color: #b2b2b2;
-}
-
-.terminal .xterm-color-250 {
- color: #bcbcbc;
-}
-
-.terminal .xterm-bg-color-250 {
- background-color: #bcbcbc;
-}
-
-.terminal .xterm-color-251 {
- color: #c6c6c6;
-}
-
-.terminal .xterm-bg-color-251 {
- background-color: #c6c6c6;
-}
-
-.terminal .xterm-color-252 {
- color: #d0d0d0;
-}
-
-.terminal .xterm-bg-color-252 {
- background-color: #d0d0d0;
-}
-
-.terminal .xterm-color-253 {
- color: #dadada;
-}
-
-.terminal .xterm-bg-color-253 {
- background-color: #dadada;
-}
-
-.terminal .xterm-color-254 {
- color: #e4e4e4;
-}
-
-.terminal .xterm-bg-color-254 {
- background-color: #e4e4e4;
-}
-
-.terminal .xterm-color-255 {
- color: #eeeeee;
-}
-
-.terminal .xterm-bg-color-255 {
- background-color: #eeeeee;
-}
diff --git a/vendor/gitignore/Autotools.gitignore b/vendor/gitignore/Autotools.gitignore
index ffa6ecc3f9b..f4f545c9ca4 100644
--- a/vendor/gitignore/Autotools.gitignore
+++ b/vendor/gitignore/Autotools.gitignore
@@ -9,13 +9,15 @@ Makefile.in
# http://www.gnu.org/software/autoconf
-/autom4te.cache
+autom4te.cache
/autoscan.log
/autoscan-*.log
/aclocal.m4
/compile
/config.guess
/config.h.in
+/config.log
+/config.status
/config.sub
/configure
/configure.scan
@@ -39,4 +41,3 @@ m4/ltoptions.m4
m4/ltsugar.m4
m4/ltversion.m4
m4/lt~obsolete.m4
-autom4te.cache
diff --git a/vendor/gitignore/CraftCMS.gitignore b/vendor/gitignore/CraftCMS.gitignore
index a70d4772c46..0d81b397e35 100644
--- a/vendor/gitignore/CraftCMS.gitignore
+++ b/vendor/gitignore/CraftCMS.gitignore
@@ -1,3 +1,4 @@
-# Craft Storage (cache) [http://buildwithcraft.com/help/craft-storage-gitignore]
+# Craft 2 Storage (https://craftcms.com/support/craft-storage-gitignore)
+# not necessary for Craft 3 (https://github.com/craftcms/craft/issues/26)
/craft/storage/*
-!/craft/storage/logo/* \ No newline at end of file
+!/craft/storage/rebrand
diff --git a/vendor/gitignore/Delphi.gitignore b/vendor/gitignore/Delphi.gitignore
index 19864c6bbef..000ee5f104b 100644
--- a/vendor/gitignore/Delphi.gitignore
+++ b/vendor/gitignore/Delphi.gitignore
@@ -20,7 +20,7 @@
# Deployment Manager configuration file for your project. Added in Delphi XE2.
# Uncomment this if it is not mobile development and you do not use remote debug feature.
#*.deployproj
-#
+#
# C++ object files produced when C/C++ Output file generation is configured.
# Uncomment this if you are not using external objects (zlib library for example).
#*.obj
diff --git a/vendor/gitignore/Eagle.gitignore b/vendor/gitignore/Eagle.gitignore
index 9afc324d6ae..28f0b9715e6 100644
--- a/vendor/gitignore/Eagle.gitignore
+++ b/vendor/gitignore/Eagle.gitignore
@@ -35,7 +35,6 @@ eagle.epf
*.gpi
*.pls
*.ger
-*.gpi
*.xln
*.drd
diff --git a/vendor/gitignore/GWT.gitignore b/vendor/gitignore/GWT.gitignore
index 07704e54bbc..a01e7fcd921 100644
--- a/vendor/gitignore/GWT.gitignore
+++ b/vendor/gitignore/GWT.gitignore
@@ -18,9 +18,6 @@ war/WEB-INF/classes/
#compilation logs
.gwt/
-#caching for already compiled files
-gwt-unitCache/
-
#gwt junit compilation files
www-test/
diff --git a/vendor/gitignore/Global/Backup.gitignore b/vendor/gitignore/Global/Backup.gitignore
new file mode 100644
index 00000000000..825ce52db53
--- /dev/null
+++ b/vendor/gitignore/Global/Backup.gitignore
@@ -0,0 +1,5 @@
+*.bak
+*.gho
+*.ori
+*.orig
+*.tmp
diff --git a/vendor/gitignore/Global/CodeKit.gitignore b/vendor/gitignore/Global/CodeKit.gitignore
index bd9e67fcca2..09b84126cea 100644
--- a/vendor/gitignore/Global/CodeKit.gitignore
+++ b/vendor/gitignore/Global/CodeKit.gitignore
@@ -1,3 +1,4 @@
# General CodeKit files to ignore
config.codekit
+config.codekit3
/min
diff --git a/vendor/gitignore/Global/Eclipse.gitignore b/vendor/gitignore/Global/Eclipse.gitignore
index 0eb8a5e8571..a65649a9ed2 100644
--- a/vendor/gitignore/Global/Eclipse.gitignore
+++ b/vendor/gitignore/Global/Eclipse.gitignore
@@ -23,7 +23,7 @@ local.properties
# CDT-specific (C/C++ Development Tooling)
.cproject
-# CDT- autotools
+# CDT- autotools
.autotools
# Java annotation processor (APT)
@@ -47,6 +47,9 @@ local.properties
# Code Recommenders
.recommenders/
+# Annotation Processing
+.apt_generated/
+
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
index 4d5117a1d9d..0d95b087f19 100644
--- a/vendor/gitignore/Global/JetBrains.gitignore
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -4,6 +4,7 @@
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
@@ -20,9 +21,16 @@
.idea/**/gradle.xml
.idea/**/libraries
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+
# CMake
-cmake-build-debug/
-cmake-build-release/
+cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
diff --git a/vendor/gitignore/Global/Matlab.gitignore b/vendor/gitignore/Global/Matlab.gitignore
index d87a6bdbeeb..46a83d635ba 100644
--- a/vendor/gitignore/Global/Matlab.gitignore
+++ b/vendor/gitignore/Global/Matlab.gitignore
@@ -7,17 +7,20 @@
# Compiled MEX binaries (all platforms)
*.mex*
-# Packaged app and toolbox files
-*.mlappinstall
-*.mltbx
-
-# Generated helpsearch folders
-helpsearch*/
+# Packaged app and toolbox files
+*.mlappinstall
+*.mltbx
+
+# Generated helpsearch folders
+helpsearch*/
# Simulink code generation folders
slprj/
sccprj/
+# Matlab code generation folders
+codegen/
+
# Simulink autosave extension
*.autosave
diff --git a/vendor/gitignore/Global/Patch.gitignore b/vendor/gitignore/Global/Patch.gitignore
new file mode 100644
index 00000000000..6ffab9ad295
--- /dev/null
+++ b/vendor/gitignore/Global/Patch.gitignore
@@ -0,0 +1,2 @@
+*.orig
+*.rej
diff --git a/vendor/gitignore/Global/SynopsysVCS.gitignore b/vendor/gitignore/Global/SynopsysVCS.gitignore
index eed2432fb78..ad751f6bd75 100644
--- a/vendor/gitignore/Global/SynopsysVCS.gitignore
+++ b/vendor/gitignore/Global/SynopsysVCS.gitignore
@@ -4,8 +4,8 @@
*.evcd
*.fsdb
-# Default name of the simulation executable. A different name can be
-# specified with this switch (the associated daidir database name is
+# Default name of the simulation executable. A different name can be
+# specified with this switch (the associated daidir database name is
# also taken from here): -o <path>/<filename>
simv
@@ -13,7 +13,7 @@ simv
simv.daidir/
simv.db.dir/
-# Infrastructure necessary to co-simulate SystemC models with
+# Infrastructure necessary to co-simulate SystemC models with
# Verilog/VHDL models. An alternate directory may be specified with this
# switch: -Mdir=<directory_path>
csrc/
@@ -22,7 +22,7 @@ csrc/
# used to write all messages from simulation: -l <filename>
*.log
-# Coverage results (generated with urg) and database location. The
+# Coverage results (generated with urg) and database location. The
# following switch can also be used: urg -dir <coverage_directory>.vdb
simv.vdb/
urgReport/
diff --git a/vendor/gitignore/Global/Vim.gitignore b/vendor/gitignore/Global/Vim.gitignore
index 19cfe22f583..741518ffd24 100644
--- a/vendor/gitignore/Global/Vim.gitignore
+++ b/vendor/gitignore/Global/Vim.gitignore
@@ -1,7 +1,8 @@
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
-[._]s[a-v][a-z]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
[._]sw[a-p]
# Session
diff --git a/vendor/gitignore/LabVIEW.gitignore b/vendor/gitignore/LabVIEW.gitignore
index 122450865cf..31619f59814 100644
--- a/vendor/gitignore/LabVIEW.gitignore
+++ b/vendor/gitignore/LabVIEW.gitignore
@@ -14,3 +14,4 @@
# Metadata
*.aliases
*.lvlps
+.cache/
diff --git a/vendor/gitignore/Laravel.gitignore b/vendor/gitignore/Laravel.gitignore
index a4854bef534..67e2146f2bc 100644
--- a/vendor/gitignore/Laravel.gitignore
+++ b/vendor/gitignore/Laravel.gitignore
@@ -1,6 +1,7 @@
vendor/
node_modules/
npm-debug.log
+yarn-error.log
# Laravel 4 specific
bootstrap/compiled.php
@@ -10,11 +11,7 @@ app/storage/
public/storage
public/hot
storage/*.key
-.env.*.php
-.env.php
.env
Homestead.yaml
Homestead.json
-
-# Rocketeer PHP task runner and deployment package. https://github.com/rocketeers/rocketeer
-.rocketeer/
+/.vagrant
diff --git a/vendor/gitignore/Maven.gitignore b/vendor/gitignore/Maven.gitignore
index 5f2dbe11df9..e8d57d08088 100644
--- a/vendor/gitignore/Maven.gitignore
+++ b/vendor/gitignore/Maven.gitignore
@@ -7,6 +7,4 @@ release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
-
-# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
-!/.mvn/wrapper/maven-wrapper.jar
+.mvn/wrapper/maven-wrapper.jar
diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore
index 4454ba1b5bf..3a4c8581b3a 100644
--- a/vendor/gitignore/Node.gitignore
+++ b/vendor/gitignore/Node.gitignore
@@ -57,9 +57,15 @@ typings/
# dotenv environment variables file
.env
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
# next.js build output
.next
+# nuxt.js build output
+.nuxt
+
# vuepress build output
.vuepress/dist
diff --git a/vendor/gitignore/Objective-C.gitignore b/vendor/gitignore/Objective-C.gitignore
index 86de6aa3f5f..a0bd6b453a8 100644
--- a/vendor/gitignore/Objective-C.gitignore
+++ b/vendor/gitignore/Objective-C.gitignore
@@ -35,6 +35,9 @@ xcuserdata/
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
+#
+# Add this line if you want to avoid checking in source code from the Xcode workspace
+# *.xcworkspace
# Carthage
#
diff --git a/vendor/gitignore/Perl6.gitignore b/vendor/gitignore/Perl6.gitignore
new file mode 100644
index 00000000000..7b2c018a562
--- /dev/null
+++ b/vendor/gitignore/Perl6.gitignore
@@ -0,0 +1,7 @@
+# Gitignore for Perl 6 (http://www.perl6.org)
+# As part of https://github.com/github/gitignore
+
+# precompiled files
+.precomp
+lib/.precomp
+
diff --git a/vendor/gitignore/Swift.gitignore b/vendor/gitignore/Swift.gitignore
index 312d1f652c6..b8e04d98e33 100644
--- a/vendor/gitignore/Swift.gitignore
+++ b/vendor/gitignore/Swift.gitignore
@@ -47,6 +47,9 @@ playground.xcworkspace
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
+#
+# Add this line if you want to avoid checking in source code from the Xcode workspace
+# *.xcworkspace
# Carthage
#
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
index 3d12d3f90ad..79a66f9ebfa 100644
--- a/vendor/gitignore/TeX.gitignore
+++ b/vendor/gitignore/TeX.gitignore
@@ -226,6 +226,9 @@ TSWLatexianTemp*
# Texpad
.texpadtmp
+# LyX
+*.lyx~
+
# Kile
*.backup
@@ -241,6 +244,3 @@ TSWLatexianTemp*
# standalone packages
*.sta
-
-# generated if using elsarticle.cls
-*.spl
diff --git a/vendor/gitignore/Typo3.gitignore b/vendor/gitignore/Typo3.gitignore
index cb024fefe99..200c2a2bf79 100644
--- a/vendor/gitignore/Typo3.gitignore
+++ b/vendor/gitignore/Typo3.gitignore
@@ -8,12 +8,15 @@
/typo3conf/temp_CACHED*
/typo3conf/temp_fieldInfo.php
/typo3conf/deprecation_*.log
-/typo3conf/AdditionalConfiguration.php
+/typo3conf/ENABLE_INSTALL_TOOL
+/typo3conf/realurl_autoconf.php
+/FIRST_INSTALL
# Ignore system folders, you should have them symlinked.
# If not comment out the following entries.
/typo3
/typo3_src
/typo3_src-*
+/Packages
/.htaccess
/index.php
# Ignore temp directory.
diff --git a/vendor/gitignore/Umbraco.gitignore b/vendor/gitignore/Umbraco.gitignore
index 10fc2b4d825..cd90af3071a 100644
--- a/vendor/gitignore/Umbraco.gitignore
+++ b/vendor/gitignore/Umbraco.gitignore
@@ -19,7 +19,7 @@
!**/App_Data/[Pp]ackages/*
!**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages/*
-# ImageProcessor DiskCache
+# ImageProcessor DiskCache
**/App_Data/cache/
# Ignore the Models Builder models out of date flag
diff --git a/vendor/gitignore/UnrealEngine.gitignore b/vendor/gitignore/UnrealEngine.gitignore
index 1daca8b50d9..6582eaf9a11 100644
--- a/vendor/gitignore/UnrealEngine.gitignore
+++ b/vendor/gitignore/UnrealEngine.gitignore
@@ -1,9 +1,6 @@
# Visual Studio 2015 user specific files
.vs/
-# Visual Studio 2015 database file
-*.VC.db
-
# Compiled Object files
*.slo
*.lo
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index 1e9abf78d69..94b41b913fb 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -59,7 +59,7 @@ StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
-*_i.h
+*_h.h
*.ilk
*.meta
*.obj
@@ -220,7 +220,7 @@ ClientBin/
*.publishsettings
orleans.codegen.cs
-# Including strong name files can present a security risk
+# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
@@ -316,7 +316,7 @@ __pycache__/
# OpenCover UI analysis results
OpenCover/
-# Azure Stream Analytics local run output
+# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
@@ -325,5 +325,8 @@ ASALocalRun/
# NVidia Nsight GPU debugger configuration file
*.nvuser
-# MFractors (Xamarin productivity tool) working folder
+# MFractors (Xamarin productivity tool) working folder
.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index 0d58a00482a..b698248bc38 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -105,11 +105,14 @@ code_quality:
- code_quality
artifacts:
paths: [gl-code-quality-report.json]
+ only:
+ - branches
except:
variables:
- $CODE_QUALITY_DISABLED
license_management:
+ stage: test
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
@@ -121,6 +124,8 @@ license_management:
- license_management
artifacts:
paths: [gl-license-management-report.json]
+ only:
+ - branches
except:
variables:
- $LICENSE_MANAGEMENT_DISABLED
@@ -161,6 +166,8 @@ sast:
- sast
artifacts:
paths: [gl-sast-report.json]
+ only:
+ - branches
except:
variables:
- $SAST_DISABLED
@@ -178,6 +185,8 @@ dependency_scanning:
- dependency_scanning
artifacts:
paths: [gl-dependency-scanning-report.json]
+ only:
+ - branches
except:
variables:
- $DEPENDENCY_SCANNING_DISABLED
@@ -195,6 +204,8 @@ container_scanning:
- container_scanning
artifacts:
paths: [gl-container-scanning-report.json]
+ only:
+ - branches
except:
variables:
- $CONTAINER_SCANNING_DISABLED
@@ -365,6 +376,7 @@ production_manual:
kubernetes: active
variables:
- $STAGING_ENABLED
+ - $CANARY_ENABLED
except:
variables:
- $INCREMENTAL_ROLLOUT_ENABLED
@@ -665,6 +677,7 @@ rollout 100%:
auto_chart=${AUTO_DEVOPS_CHART:-gitlab/auto-deploy-app}
auto_chart_name=$(basename $auto_chart)
auto_chart_name=${auto_chart_name%.tgz}
+ auto_chart_name=${auto_chart_name%.tar.gz}
else
auto_chart="chart"
auto_chart_name="chart"
diff --git a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml b/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml
index d5ee7ed2c13..5f9c9b2c965 100644
--- a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml
@@ -17,7 +17,7 @@
variables:
# This will supress any download for dependencies and plugins or upload messages which would clutter the console log.
# `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
- MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
+ MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
# As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used
# when running from the command line.
# `installAtEnd` and `deployAtEnd` are only effective with recent version of the corresponding plugins.
diff --git a/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml b/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml
index ff7bdd32239..93cb31f48c0 100644
--- a/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml
@@ -1,6 +1,6 @@
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/ruby/tags/
-image: "ruby:2.4"
+image: "ruby:2.5"
# Pick zero or more services to be used on all builds.
# Only needed when using a docker container to run your tests in.
diff --git a/vendor/jupyter/values.yaml b/vendor/jupyter/values.yaml
index 90817de0f1b..4ea5b44c59c 100644
--- a/vendor/jupyter/values.yaml
+++ b/vendor/jupyter/values.yaml
@@ -4,6 +4,7 @@ rbac:
hub:
extraEnv:
JUPYTER_ENABLE_LAB: 1
+ SINGLEUSER_IMAGE: 'registry.gitlab.com/gitlab-org/jupyterhub-user-image:latest'
extraConfig: |
c.KubeSpawner.cmd = ['jupyter-labhub']
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index dada0da0b31..a462daf3067 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -7,35 +7,37 @@
@babel/template,7.0.0-beta.44,MIT
@babel/traverse,7.0.0-beta.44,MIT
@babel/types,7.0.0-beta.44,MIT
-@gitlab-org/gitlab-svgs,1.23.0,SEE LICENSE IN LICENSE
+@gitlab-org/gitlab-svgs,1.27.0,SEE LICENSE IN LICENSE
+@gitlab-org/gitlab-ui,1.0.5,UNKNOWN
@sindresorhus/is,0.7.0,MIT
@types/jquery,2.0.48,MIT
@vue/component-compiler-utils,1.2.1,MIT
-@webassemblyjs/ast,1.5.10,MIT
-@webassemblyjs/floating-point-hex-parser,1.5.10,MIT
-@webassemblyjs/helper-api-error,1.5.10,MIT
-@webassemblyjs/helper-buffer,1.5.10,MIT
-@webassemblyjs/helper-code-frame,1.5.10,MIT
-@webassemblyjs/helper-fsm,1.5.10,ISC
-@webassemblyjs/helper-module-context,1.5.10,MIT
-@webassemblyjs/helper-wasm-bytecode,1.5.10,MIT
-@webassemblyjs/helper-wasm-section,1.5.10,MIT
-@webassemblyjs/ieee754,1.5.10,Unknown
-@webassemblyjs/leb128,1.5.10,Apache 2.0
-@webassemblyjs/utf8,1.5.10,MIT
-@webassemblyjs/wasm-edit,1.5.10,MIT
-@webassemblyjs/wasm-gen,1.5.10,MIT
-@webassemblyjs/wasm-opt,1.5.10,MIT
-@webassemblyjs/wasm-parser,1.5.10,MIT
-@webassemblyjs/wast-parser,1.5.10,MIT
-@webassemblyjs/wast-printer,1.5.10,MIT
+@webassemblyjs/ast,1.5.13,MIT
+@webassemblyjs/floating-point-hex-parser,1.5.13,MIT
+@webassemblyjs/helper-api-error,1.5.13,MIT
+@webassemblyjs/helper-buffer,1.5.13,MIT
+@webassemblyjs/helper-code-frame,1.5.13,MIT
+@webassemblyjs/helper-fsm,1.5.13,ISC
+@webassemblyjs/helper-module-context,1.5.13,MIT
+@webassemblyjs/helper-wasm-bytecode,1.5.13,MIT
+@webassemblyjs/helper-wasm-section,1.5.13,MIT
+@webassemblyjs/ieee754,1.5.13,MIT
+@webassemblyjs/leb128,1.5.13,MIT
+@webassemblyjs/utf8,1.5.13,MIT
+@webassemblyjs/wasm-edit,1.5.13,MIT
+@webassemblyjs/wasm-gen,1.5.13,MIT
+@webassemblyjs/wasm-opt,1.5.13,MIT
+@webassemblyjs/wasm-parser,1.5.13,MIT
+@webassemblyjs/wast-parser,1.5.13,MIT
+@webassemblyjs/wast-printer,1.5.13,MIT
RedCloth,4.3.2,MIT
abbrev,1.0.9,ISC
abbrev,1.1.1,ISC
accepts,1.3.4,MIT
ace-rails-ap,4.1.2,MIT
acorn,3.3.0,MIT
-acorn,5.5.3,MIT
+acorn,5.6.2,MIT
+acorn,5.7.1,MIT
acorn-dynamic-import,3.0.0,MIT
acorn-jsx,3.0.1,MIT
actionmailer,4.2.10,MIT
@@ -50,33 +52,29 @@ addressable,2.5.2,Apache 2.0
addressparser,1.0.1,MIT
aes_key_wrap,1.0.1,MIT
after,0.8.2,MIT
-agent-base,2.1.1,MIT
-ajv,4.11.8,MIT
+agent-base,4.2.1,MIT
ajv,5.5.2,MIT
ajv,6.1.1,MIT
ajv-keywords,2.1.1,MIT
ajv-keywords,3.1.0,MIT
akismet,2.0.0,MIT
align-text,0.1.4,MIT
-allocations,1.0.5,MIT
-alphanum-sort,1.0.2,MIT
amdefine,1.0.1,BSD-3-Clause OR MIT
amqplib,0.5.2,MIT
ansi-align,2.0.0,ISC
+ansi-escapes,1.4.0,MIT
ansi-escapes,3.0.0,MIT
ansi-html,0.0.7,Apache 2.0
ansi-regex,2.1.1,MIT
ansi-regex,3.0.0,MIT
ansi-styles,2.2.1,MIT
ansi-styles,3.2.1,MIT
-anymatch,1.3.2,ISC
anymatch,2.0.0,ISC
append-transform,0.4.0,MIT
aproba,1.2.0,ISC
are-we-there-yet,1.1.4,ISC
arel,6.0.4,MIT
argparse,1.0.9,MIT
-arr-diff,2.0.0,MIT
arr-diff,4.0.0,MIT
arr-flatten,1.1.0,MIT
arr-union,3.1.0,MIT
@@ -104,8 +102,8 @@ asset_sync,2.4.0,MIT
assign-symbols,1.0.0,MIT
ast-types,0.11.3,MIT
async,1.5.2,MIT
-async,2.1.5,MIT
async,2.6.0,MIT
+async,2.6.1,MIT
async-each,1.0.1,MIT
async-limiter,1.0.0,MIT
asynckit,0.4.0,MIT
@@ -113,7 +111,6 @@ atob,2.0.3,(MIT OR Apache-2.0)
atomic,1.1.99,Apache 2.0
attr_encrypted,3.1.0,MIT
attr_required,1.0.0,MIT
-autoprefixer,6.7.7,MIT
autosize,4.0.0,MIT
aws-sign2,0.6.0,Apache 2.0
aws-sign2,0.7.0,Apache 2.0
@@ -140,7 +137,7 @@ babel-helper-regex,6.26.0,MIT
babel-helper-remap-async-to-generator,6.24.1,MIT
babel-helper-replace-supers,6.24.1,MIT
babel-helpers,6.24.1,MIT
-babel-loader,7.1.4,MIT
+babel-loader,7.1.5,MIT
babel-messages,6.23.0,MIT
babel-plugin-check-es2015-constants,6.22.0,MIT
babel-plugin-istanbul,4.1.6,New BSD
@@ -184,6 +181,7 @@ babel-plugin-transform-exponentiation-operator,6.24.1,MIT
babel-plugin-transform-object-rest-spread,6.23.0,MIT
babel-plugin-transform-regenerator,6.26.0,MIT
babel-plugin-transform-strict-mode,6.24.1,MIT
+babel-polyfill,6.23.0,MIT
babel-preset-es2015,6.24.1,MIT
babel-preset-es2016,6.24.1,MIT
babel-preset-es2017,6.24.1,MIT
@@ -199,7 +197,6 @@ babosa,1.0.2,MIT
babylon,6.18.0,MIT
babylon,7.0.0-beta.44,MIT
backo2,1.0.2,MIT
-balanced-match,0.4.2,MIT
balanced-match,1.0.0,MIT
base,0.11.2,MIT
base32,0.3.2,MIT
@@ -208,19 +205,19 @@ base64-js,1.2.3,MIT
base64id,1.0.0,MIT
batch,0.6.1,MIT
batch-loader,1.2.1,MIT
-bcrypt,3.1.11,MIT
+bcrypt,3.1.12,MIT
bcrypt-pbkdf,1.0.1,New BSD
bcrypt_pbkdf,1.0.0,MIT
better-assert,1.0.2,MIT
bfj-node4,5.2.1,MIT
big.js,3.1.3,MIT
binary-extensions,1.11.0,MIT
+binaryextensions,2.1.1,MIT
bindata,2.4.3,ruby
-bitsyntax,0.0.4,Unknown
+bitsyntax,0.0.4,UNKNOWN
bl,1.1.2,MIT
blackst0ne-mermaid,7.1.0-fixed,MIT
blob,0.0.4,MIT*
-block-stream,0.0.9,ISC
bluebird,3.5.1,MIT
bn.js,4.11.8,MIT
body-parser,1.18.2,MIT
@@ -229,11 +226,12 @@ boom,2.10.1,New BSD
boom,4.3.1,New BSD
boom,5.2.0,New BSD
bootstrap,4.1.1,MIT
+bootstrap,4.1.2,MIT
+bootstrap-vue,2.0.0-rc.11,MIT
bootstrap_form,2.7.0,MIT
boxen,1.3.0,MIT
brace-expansion,1.1.11,MIT
braces,0.1.5,MIT
-braces,1.8.5,MIT
braces,2.3.1,MIT
brorand,1.1.0,MIT
browser,2.2.0,MIT
@@ -243,7 +241,6 @@ browserify-des,1.0.0,MIT
browserify-rsa,4.0.1,MIT
browserify-sign,4.0.4,ISC
browserify-zlib,0.2.0,MIT
-browserslist,1.7.7,MIT
buffer,4.9.1,MIT
buffer-from,1.0.0,MIT
buffer-indexof,1.1.0,MIT
@@ -266,10 +263,8 @@ camelcase,1.2.1,MIT
camelcase,2.1.1,MIT
camelcase,4.1.0,MIT
camelcase-keys,2.1.0,MIT
-caniuse-api,1.6.1,MIT
-caniuse-db,1.0.30000649,CC-BY-4.0
capture-stack-trace,1.0.0,MIT
-carrierwave,1.2.1,MIT
+carrierwave,1.2.3,MIT
caseless,0.11.0,Apache 2.0
caseless,0.12.0,Apache 2.0
cause,0.1,MIT
@@ -277,22 +272,22 @@ center-align,0.1.3,MIT
chalk,1.1.3,MIT
chalk,2.4.1,MIT
chardet,0.4.2,MIT
+chardet,0.5.0,MIT
charenc,0.0.2,New BSD
charlock_holmes,0.7.6,MIT
chart.js,1.0.2,MIT
check-types,7.3.0,MIT
-chokidar,1.7.0,MIT
chokidar,2.0.2,MIT
+chokidar,2.0.4,MIT
chownr,1.0.1,ISC
-chrome-trace-event,0.1.2,MIT
+chrome-trace-event,1.0.0,MIT
chronic,0.10.2,MIT
chronic_duration,0.10.6,MIT
chunky_png,1.3.5,MIT
cipher-base,1.0.4,MIT
circular-json,0.3.3,MIT
-circular-json,0.5.1,MIT
+circular-json,0.5.5,MIT
citrus,3.0.2,MIT
-clap,1.1.3,MIT
class-utils,0.3.6,MIT
classlist-polyfill,1.2.0,Unlicense
cli-boxes,1.0.0,MIT
@@ -301,19 +296,16 @@ cli-width,2.1.0,ISC
clipboard,1.7.1,MIT
cliui,2.1.0,ISC
cliui,4.0.0,ISC
-clone,1.0.3,MIT
clone-response,1.0.2,MIT
-co,3.0.6,MIT
co,4.6.0,MIT
-coa,1.0.1,MIT
code-point-at,1.1.0,MIT
+codesandbox-api,0.0.18,MIT
+codesandbox-import-util-types,1.2.11,UNKNOWN
+codesandbox-import-utils,1.2.11,UNKNOWN
coercible,1.0.0,MIT
collection-visit,1.0.0,MIT
-color,0.11.4,MIT
color-convert,1.9.1,MIT
color-name,1.1.2,MIT
-color-string,0.3.0,MIT
-colormin,1.1.2,MIT
colors,1.1.2,MIT
combine-lists,1.0.1,MIT
combined-stream,1.0.6,MIT
@@ -364,28 +356,36 @@ cryptiles,2.0.5,New BSD
cryptiles,3.1.2,New BSD
crypto-browserify,3.12.0,MIT
crypto-random-string,1.0.0,MIT
-css-color-names,0.0.4,MIT
-css-loader,0.28.11,MIT
+css-loader,1.0.0,MIT
css-selector-tokenizer,0.7.0,MIT
css_parser,1.5.0,MIT
cssesc,0.1.0,MIT
-cssnano,3.10.0,MIT
-csso,2.3.2,MIT
currently-unhandled,0.4.1,MIT
custom-event,1.0.1,MIT
cyclist,0.2.2,MIT*
d3,3.5.17,New BSD
+d3,4.12.2,New BSD
d3-array,1.2.1,New BSD
d3-axis,1.0.8,New BSD
d3-brush,1.0.4,New BSD
+d3-chord,1.0.4,New BSD
d3-collection,1.0.4,New BSD
d3-color,1.0.3,New BSD
d3-dispatch,1.0.3,New BSD
d3-drag,1.2.1,New BSD
+d3-dsv,1.0.8,New BSD
d3-ease,1.0.3,New BSD
+d3-force,1.1.0,New BSD
d3-format,1.2.1,New BSD
+d3-geo,1.9.1,New BSD
+d3-hierarchy,1.1.5,New BSD
d3-interpolate,1.1.6,New BSD
d3-path,1.0.5,New BSD
+d3-polygon,1.0.3,New BSD
+d3-quadtree,1.0.3,New BSD
+d3-queue,3.0.7,New BSD
+d3-random,1.1.0,New BSD
+d3-request,1.0.6,New BSD
d3-scale,1.0.7,New BSD
d3-selection,1.2.0,New BSD
d3-shape,1.2.0,New BSD
@@ -393,14 +393,16 @@ d3-time,1.0.8,New BSD
d3-time-format,2.1.1,New BSD
d3-timer,1.0.7,New BSD
d3-transition,1.1.1,New BSD
+d3-voronoi,1.1.2,New BSD
+d3-zoom,1.7.1,New BSD
dagre-d3-renderer,0.4.24,MIT
dagre-layout,0.8.0,MIT
dashdash,1.14.1,MIT
data-uri-to-buffer,1.2.0,MIT
date-format,1.2.0,MIT
date-now,0.1.4,MIT
+dateformat,3.0.3,MIT
de-indent,1.0.2,MIT
-debug,2.2.0,MIT
debug,2.6.8,MIT
debug,2.6.9,MIT
debug,3.1.0,MIT
@@ -420,7 +422,6 @@ define-properties,1.1.2,MIT
define-property,0.2.5,MIT
define-property,1.0.0,MIT
define-property,2.0.2,MIT
-defined,1.0.0,MIT
degenerator,1.0.4,MIT
del,2.2.2,MIT
del,3.0.0,MIT
@@ -428,6 +429,7 @@ delayed-stream,1.0.0,MIT
delegate,3.1.2,MIT
delegates,1.0.0,MIT
depd,1.1.1,MIT
+depd,1.1.2,MIT
des.js,1.0.0,MIT
descendants_tracker,0.0.4,MIT
destroy,1.0.4,MIT
@@ -457,7 +459,7 @@ domelementtype,1.3.0,Simplified BSD
domhandler,2.4.1,Simplified BSD
domutils,1.6.2,Simplified BSD
doorkeeper,4.3.2,MIT
-doorkeeper-openid_connect,1.4.0,MIT
+doorkeeper-openid_connect,1.5.0,MIT
dot-prop,4.2.0,MIT
double-ended-queue,2.1.0-0,MIT
dropzone,4.2.0,MIT
@@ -467,14 +469,15 @@ duplexer3,0.1.4,New BSD
duplexify,3.5.3,MIT
ecc-jsbn,0.1.1,MIT
ed25519,1.2.4,MIT
+editions,1.3.4,MIT
ee-first,1.1.1,MIT
ejs,2.5.9,Apache 2.0
-electron-to-chromium,1.3.3,ISC
elliptic,6.4.0,MIT
email_reply_trimmer,0.1.6,MIT
emoji-unicode-version,0.2.1,MIT
emojis-list,2.1.0,MIT
encodeurl,1.0.2,MIT
+encoding,0.1.12,MIT
encryptor,3.0.0,MIT
end-of-stream,1.4.1,MIT
engine.io,3.1.5,MIT
@@ -482,6 +485,7 @@ engine.io-client,3.1.5,MIT
engine.io-parser,2.1.2,MIT
enhanced-resolve,0.9.1,MIT
enhanced-resolve,4.0.0,MIT
+enhanced-resolve,4.1.0,MIT
ent,2.2.0,MIT
entities,1.1.1,Simplified BSD
equalizer,0.0.11,MIT
@@ -492,6 +496,8 @@ erubis,2.7.0,MIT
es-abstract,1.10.0,MIT
es-to-primitive,1.1.1,MIT
es6-promise,3.0.2,MIT
+es6-promise,4.2.4,MIT
+es6-promisify,5.0.0,MIT
escape-html,1.0.3,MIT
escape-string-regexp,1.0.5,MIT
escape_utils,1.1.1,MIT
@@ -507,7 +513,7 @@ eslint-plugin-html,4.0.3,ISC
eslint-plugin-import,2.12.0,MIT
eslint-plugin-jasmine,2.2.0,MIT
eslint-plugin-promise,3.8.0,ISC
-eslint-plugin-vue,4.0.1,MIT
+eslint-plugin-vue,4.5.0,MIT
eslint-restricted-globals,0.1.1,MIT
eslint-scope,3.7.1,Simplified BSD
eslint-visitor-keys,1.0.0,Apache 2.0
@@ -515,10 +521,9 @@ espree,3.5.4,Simplified BSD
esprima,2.7.3,Simplified BSD
esprima,3.1.3,Simplified BSD
esprima,4.0.0,Simplified BSD
-esquery,1.0.0,New BSD
-esrecurse,4.1.0,Simplified BSD
+esquery,1.0.1,New BSD
+esrecurse,4.2.1,Simplified BSD
estraverse,1.9.3,Simplified BSD
-estraverse,4.1.1,Simplified BSD
estraverse,4.2.0,Simplified BSD
esutils,2.0.2,Simplified BSD
et-orbi,1.0.3,MIT
@@ -533,10 +538,8 @@ excon,0.62.0,MIT
execa,0.7.0,MIT
execjs,2.6.0,MIT
expand-braces,0.1.2,MIT
-expand-brackets,0.1.5,MIT
expand-brackets,2.1.4,MIT
expand-range,0.1.1,MIT
-expand-range,1.8.2,MIT
exports-loader,0.7.0,MIT
express,4.16.2,MIT
expression_parser,0.9.0,MIT
@@ -544,7 +547,7 @@ extend,3.0.1,MIT
extend-shallow,2.0.1,MIT
extend-shallow,3.0.2,MIT
external-editor,2.2.0,MIT
-extglob,0.3.2,MIT
+external-editor,3.0.0,MIT
extglob,2.0.4,MIT
extsprintf,1.3.0,MIT
extsprintf,1.4.0,MIT
@@ -564,10 +567,8 @@ figures,2.0.0,MIT
file-entry-cache,2.0.0,MIT
file-loader,1.1.11,MIT
file-uri-to-path,1.0.0,MIT
-filename-regex,2.0.1,MIT
fileset,2.0.3,MIT
filesize,3.6.0,New BSD
-fill-range,2.2.3,MIT
fill-range,4.0.0,MIT
finalhandler,1.1.0,MIT
find-cache-dir,1.0.0,MIT
@@ -575,7 +576,6 @@ find-root,1.1.0,MIT
find-up,1.1.2,MIT
find-up,2.1.0,MIT
flat-cache,1.2.2,MIT
-flatten,1.0.2,MIT
flipper,0.13.0,MIT
flipper-active_record,0.13.0,MIT
flipper-active_support_cache_store,0.13.0,MIT
@@ -594,24 +594,22 @@ follow-redirects,1.0.0,MIT
follow-redirects,1.2.6,MIT
font-awesome-rails,4.7.0.1,"MIT,SIL Open Font License"
for-in,1.0.2,MIT
-for-own,0.1.5,MIT
foreach,2.0.5,MIT
forever-agent,0.6.1,Apache 2.0
form-data,2.0.0,MIT
-form-data,2.1.4,MIT
form-data,2.3.2,MIT
formatador,0.2.5,MIT
+formdata-polyfill,3.0.11,MIT
forwarded,0.1.2,MIT
fragment-cache,0.2.1,MIT
fresh,0.5.2,MIT
from,0.1.7,MIT
from2,2.3.0,MIT
fs-access,1.0.1,MIT
+fs-minipass,1.2.5,ISC
fs-write-stream-atomic,1.0.10,ISC
fs.realpath,1.0.0,ISC
-fsevents,1.1.3,MIT
-fstream,1.0.11,ISC
-fstream-ignore,1.0.5,ISC
+fsevents,1.2.4,MIT
ftp,0.3.10,MIT
function-bind,1.1.1,MIT
functional-red-black-tree,1.0.1,MIT
@@ -624,25 +622,23 @@ generate-object-property,1.2.0,MIT
get-caller-file,1.0.2,ISC
get-stdin,4.0.1,MIT
get-stream,3.0.0,MIT
-get-uri,2.0.1,MIT
+get-uri,2.0.2,MIT
get-value,2.0.6,MIT
get_process_mem,0.2.0,MIT
getpass,0.1.7,MIT
gettext_i18n_rails,1.8.0,MIT
gettext_i18n_rails_js,1.3.0,MIT
-gitaly-proto,0.100.0,MIT
+gitaly-proto,0.112.0,MIT
github-linguist,5.3.3,MIT
github-markup,1.7.0,MIT
gitlab-flowdock-git-hook,1.0.1,MIT
-gitlab-gollum-lib,4.2.7.2,MIT
-gitlab-gollum-rugged_adapter,0.4.4,MIT
+gitlab-gollum-lib,4.2.7.5,MIT
+gitlab-gollum-rugged_adapter,0.4.4.1,MIT
gitlab-grit,2.8.2,MIT
-gitlab-markup,1.6.3,MIT
+gitlab-markup,1.6.4,MIT
gitlab_omniauth-ldap,2.0.4,MIT
glob,5.0.15,ISC
glob,7.1.2,ISC
-glob-base,0.3.0,MIT
-glob-parent,2.0.0,ISC
glob-parent,3.1.0,ISC
global-dirs,0.1.1,MIT
global-modules-path,2.1.0,Apache 2.0
@@ -664,20 +660,19 @@ gpgme,2.0.13,LGPL-2.1+
graceful-fs,4.1.11,ISC
grape,1.0.3,MIT
grape-entity,0.7.1,MIT
-grape-path-helpers,1.0.4,MIT
+grape-path-helpers,1.0.6,MIT
grape_logging,1.7.0,MIT
graphiql-rails,1.4.10,MIT
graphlib,2.1.1,MIT
graphql,1.8.1,MIT
grpc,1.11.0,Apache 2.0
gzip-size,4.1.0,MIT
-hamlit,2.6.1,MIT
+hamlit,2.8.8,MIT
handle-thing,1.2.5,MIT
handlebars,4.0.6,MIT
-har-schema,1.0.5,ISC
+hangouts-chat,0.0.5,MIT
har-schema,2.0.0,ISC
har-validator,2.0.6,ISC
-har-validator,4.2.1,ISC
har-validator,5.0.3,ISC
has,1.0.1,MIT
has-ansi,2.0.0,MIT
@@ -710,9 +705,8 @@ hoek,4.2.1,New BSD
home-or-tmp,2.0.0,MIT
hosted-git-info,2.2.0,ISC
hpack.js,2.1.6,MIT
-html-comment-regex,1.1.1,MIT
html-entities,1.2.0,MIT
-html-pipeline,2.7.1,MIT
+html-pipeline,2.8.4,MIT
html2text,0.2.0,MIT
htmlentities,4.3.4,MIT
htmlparser2,3.9.2,MIT
@@ -721,9 +715,10 @@ http-cache-semantics,3.8.1,Simplified BSD
http-cookie,1.0.3,MIT
http-deceiver,1.2.7,MIT
http-errors,1.6.2,MIT
+http-errors,1.6.3,MIT
http-form_data,1.0.3,MIT
http-proxy,1.16.2,MIT
-http-proxy-agent,1.0.0,MIT
+http-proxy-agent,2.1.0,MIT
http-proxy-middleware,0.18.0,MIT
http-signature,1.1.1,MIT
http-signature,1.2.0,MIT
@@ -733,19 +728,20 @@ httpclient,2.8.3,ruby
httpntlm,1.6.1,MIT
httpreq,0.4.24,MIT
https-browserify,1.0.0,MIT
-https-proxy-agent,1.0.0,MIT
+https-proxy-agent,2.2.1,MIT
i18n,0.9.5,MIT
icalendar,2.4.1,ruby
ice_nine,0.11.2,MIT
iconv-lite,0.4.15,MIT
iconv-lite,0.4.19,MIT
+iconv-lite,0.4.23,MIT
icss-replace-symbols,1.1.0,ISC
icss-utils,2.1.0,ISC
ieee754,1.1.11,New BSD
-ieee754,1.1.8,New BSD
iferr,0.1.5,MIT
ignore,3.3.8,MIT
ignore-by-default,1.0.1,ISC
+ignore-walk,3.0.1,ISC
immediate,3.0.6,MIT
import-lazy,2.1.0,MIT
import-local,1.0.0,MIT
@@ -754,25 +750,24 @@ imurmurhash,0.1.4,MIT
indent-string,2.1.0,MIT
indexes-of,1.0.1,MIT
indexof,0.0.1,MIT*
-inflection,1.10.0,MIT
+inflection,1.12.0,MIT
inflection,1.3.8,MIT
inflight,1.0.6,ISC
influxdb,0.2.3,MIT
inherits,2.0.1,ISC
inherits,2.0.3,ISC
ini,1.3.5,ISC
+inquirer,3.0.6,MIT
inquirer,3.3.0,MIT
-inquirer,5.2.0,MIT
+inquirer,6.0.0,MIT
internal-ip,1.2.0,MIT
interpret,1.1.0,MIT
into-stream,3.1.0,MIT
invariant,2.2.2,New BSD
invert-kv,1.0.0,MIT
-ip,1.0.1,MIT
ip,1.1.5,MIT
ipaddr.js,1.6.0,MIT
ipaddress,0.8.3,MIT
-is-absolute-url,2.1.0,MIT
is-accessor-descriptor,0.1.6,MIT
is-accessor-descriptor,1.0.0,MIT
is-arrayish,0.2.1,MIT
@@ -785,16 +780,12 @@ is-data-descriptor,1.0.0,MIT
is-date-object,1.0.1,MIT
is-descriptor,0.1.6,MIT
is-descriptor,1.0.2,MIT
-is-dotfile,1.0.3,MIT
-is-equal-shallow,0.1.3,MIT
is-extendable,0.1.1,MIT
is-extendable,1.0.1,MIT
-is-extglob,1.0.0,MIT
is-extglob,2.1.1,MIT
is-finite,1.0.2,MIT
is-fullwidth-code-point,1.0.0,MIT
is-fullwidth-code-point,2.0.0,MIT
-is-glob,2.0.1,MIT
is-glob,3.1.0,MIT
is-glob,4.0.0,MIT
is-installed-globally,0.1.0,MIT
@@ -802,7 +793,6 @@ is-my-ip-valid,1.0.0,MIT
is-my-json-valid,2.17.2,MIT
is-npm,1.0.0,MIT
is-number,0.1.1,MIT
-is-number,2.1.0,MIT
is-number,3.0.0,MIT
is-number,4.0.0,MIT
is-obj,1.0.1,MIT
@@ -813,8 +803,6 @@ is-path-in-cwd,1.0.0,MIT
is-path-inside,1.0.0,MIT
is-plain-obj,1.1.0,MIT
is-plain-object,2.0.4,MIT
-is-posix-bracket,0.1.1,MIT
-is-primitive,2.0.0,MIT
is-promise,2.1.0,MIT
is-property,1.0.2,MIT
is-redirect,1.0.0,MIT
@@ -822,7 +810,6 @@ is-regex,1.0.4,MIT
is-resolvable,1.0.0,MIT
is-retry-allowed,1.1.0,MIT
is-stream,1.1.0,MIT
-is-svg,2.1.0,MIT
is-symbol,1.0.1,MIT
is-typedarray,1.0.0,MIT
is-utf8,0.2.1,MIT
@@ -845,8 +832,10 @@ istanbul-lib-instrument,1.10.1,New BSD
istanbul-lib-report,1.1.2,New BSD
istanbul-lib-source-maps,1.2.2,New BSD
istanbul-reports,1.1.3,New BSD
+istextorbinary,2.2.1,MIT
isurl,1.0.0,MIT
jasmine-core,2.9.0,MIT
+jasmine-diff,0.1.3,MIT
jasmine-jquery,2.1.1,MIT
jed,1.1.1,MIT
jira-ruby,1.4.1,MIT
@@ -854,27 +843,23 @@ jquery,3.3.1,MIT
jquery-atwho-rails,1.3.2,MIT
jquery-ujs,1.2.2,MIT
jquery.waitforimages,2.2.0,MIT
-js-base64,2.1.9,New BSD
js-cookie,2.1.3,MIT
js-tokens,3.0.2,MIT
js-yaml,3.11.0,MIT
-js-yaml,3.7.0,MIT
jsbn,0.1.1,MIT
jsesc,0.5.0,MIT
jsesc,1.3.0,MIT
jsesc,2.5.1,MIT
json,1.8.6,ruby
json-buffer,3.0.0,MIT
-json-jwt,1.9.2,MIT
+json-jwt,1.9.4,MIT
json-parse-better-errors,1.0.2,MIT
json-schema,0.2.3,BSD
json-schema-traverse,0.3.1,MIT
-json-stable-stringify,1.0.1,MIT
json-stable-stringify-without-jsonify,1.0.1,MIT
json-stringify-safe,5.0.1,ISC
json3,3.3.2,MIT
json5,0.5.1,MIT
-jsonify,0.0.0,Public Domain
jsonpointer,4.0.1,MIT
jsprim,1.4.1,MIT
jszip,3.1.3,(MIT OR GPL-3.0)
@@ -884,13 +869,13 @@ kaminari,1.0.1,MIT
kaminari-actionview,1.0.1,MIT
kaminari-activerecord,1.0.1,MIT
kaminari-core,1.0.1,MIT
-karma,2.0.2,MIT
+karma,2.0.4,MIT
karma-chrome-launcher,2.2.0,MIT
karma-coverage-istanbul-reporter,1.4.2,MIT
-karma-jasmine,1.1.1,MIT
+karma-jasmine,1.1.2,MIT
karma-mocha-reporter,2.2.5,MIT
karma-sourcemap-loader,0.3.7,MIT
-karma-webpack,3.0.0,MIT
+karma-webpack,4.0.0-beta.0,MIT
katex,0.8.3,MIT
keyv,3.0.0,MIT
kgio,2.10.0,LGPL-2.1+
@@ -904,7 +889,6 @@ latest-version,3.1.0,MIT
lazy-cache,1.0.4,MIT
lazy-cache,2.0.2,MIT
lcid,1.0.0,MIT
-leb,0.3.0,Apache 2.0
levn,0.3.0,MIT
libbase64,0.1.0,MIT
libmime,3.0.0,MIT
@@ -922,43 +906,44 @@ lodash,4.17.10,MIT
lodash,4.17.4,MIT
lodash.camelcase,4.3.0,MIT
lodash.clonedeep,4.5.0,MIT
+lodash.debounce,4.0.8,MIT
lodash.escaperegexp,4.1.2,MIT
+lodash.get,4.4.2,MIT
+lodash.isequal,4.5.0,MIT
lodash.kebabcase,4.1.1,MIT
-lodash.memoize,4.1.2,MIT
lodash.mergewith,4.6.0,MIT
lodash.snakecase,4.1.1,MIT
-lodash.uniq,4.5.0,MIT
+lodash.startcase,4.4.0,MIT
lodash.upperfirst,4.3.1,MIT
log-symbols,2.2.0,MIT
-log4js,2.5.3,Apache 2.0
+log4js,2.11.0,Apache 2.0
logging,2.2.2,MIT
loggly,1.1.1,MIT
loglevel,1.4.1,MIT
loglevelnext,1.0.3,MIT
lograge,0.10.0,MIT
long,3.2.0,Apache 2.0
+long,4.0.0,Apache 2.0
longest,1.0.1,MIT
loofah,2.2.2,MIT
loose-envify,1.3.1,MIT
loud-rejection,1.6.0,MIT
lowercase-keys,1.0.0,MIT
lru-cache,2.2.4,MIT
-lru-cache,2.6.5,ISC
lru-cache,4.1.3,ISC
-macaddress,0.2.8,MIT
+lz-string,1.4.4,WTFPL
mail,2.7.0,MIT
mail_room,0.9.1,MIT
mailcomposer,4.0.1,MIT
-mailgun-js,0.7.15,MIT
+mailgun-js,0.18.1,MIT
make-dir,1.2.0,MIT
mamacro,0.0.3,MIT
map-cache,0.2.2,MIT
map-obj,1.0.1,MIT
-map-stream,0.1.0,Unknown
+map-stream,0.1.0,UNKNOWN
map-visit,1.0.0,MIT
marked,0.3.12,MIT
match-at,0.1.1,MIT
-math-expression-evaluator,1.2.16,MIT
md5.js,1.3.4,MIT
media-typer,0.3.0,MIT
mem,1.1.0,MIT
@@ -970,7 +955,6 @@ merge-descriptors,1.0.1,MIT
merge-source-map,1.1.0,MIT
method_source,0.8.2,MIT
methods,1.1.2,MIT
-micromatch,2.3.11,MIT
micromatch,3.1.10,MIT
miller-rabin,4.0.1,MIT
mime,1.4.1,MIT
@@ -992,17 +976,19 @@ minimatch,3.0.4,ISC
minimist,0.0.10,MIT
minimist,0.0.8,MIT
minimist,1.2.0,MIT
+minipass,2.3.3,ISC
+minizlib,1.1.0,MIT
mississippi,2.0.0,Simplified BSD
mixin-deep,1.3.1,MIT
mkdirp,0.5.1,MIT
moment,2.19.2,MIT
monaco-editor,0.13.1,MIT
-monaco-editor-webpack-plugin,1.2.1,MIT
+monaco-editor-webpack-plugin,1.4.0,MIT
mousetrap,1.4.6,Apache 2.0
mousetrap-rails,1.4.6,"MIT,Apache"
move-concurrently,1.0.1,ISC
-ms,0.7.1,MIT
ms,2.0.0,MIT
+msgpack,1.2.4,Apache 2.0
multi_json,1.13.1,MIT
multi_xml,0.6.0,MIT
multicast-dns,6.1.1,MIT
@@ -1012,9 +998,10 @@ mustermann,1.0.2,MIT
mustermann-grape,1.0.0,MIT
mute-stream,0.0.7,ISC
mysql2,0.4.10,MIT
-nan,2.8.0,MIT
+nan,2.10.0,MIT
nanomatch,1.2.9,MIT
natural-compare,1.4.0,MIT
+needle,2.2.1,MIT
negotiator,0.6.1,MIT
neo-async,2.5.0,MIT
net-ldap,0.16.0,MIT
@@ -1022,9 +1009,10 @@ net-ssh,5.0.1,MIT
netmask,1.0.6,MIT
netrc,0.11.0,MIT
nice-try,1.0.4,MIT
+node-fetch,1.6.3,MIT
node-forge,0.6.33,New BSD
node-libs-browser,2.1.0,MIT
-node-pre-gyp,0.6.39,New BSD
+node-pre-gyp,0.10.0,New BSD
node-uuid,1.4.8,MIT
nodemailer,2.7.2,MIT
nodemailer-direct-transport,3.3.2,MIT
@@ -1033,20 +1021,20 @@ nodemailer-shared,1.1.0,MIT
nodemailer-smtp-pool,2.8.2,MIT
nodemailer-smtp-transport,2.7.2,MIT
nodemailer-wellknown,0.1.10,MIT
-nodemon,1.17.3,MIT
-nokogiri,1.8.2,MIT
+nodemon,1.18.2,MIT
+nokogiri,1.8.4,MIT
+nokogumbo,1.5.0,Apache 2.0
nopt,1.0.10,MIT
nopt,3.0.6,ISC
nopt,4.0.1,ISC
normalize-package-data,2.4.0,Simplified BSD
normalize-path,2.1.1,MIT
-normalize-range,0.1.2,MIT
-normalize-url,1.9.1,MIT
normalize-url,2.0.1,MIT
+npm-bundled,1.0.3,ISC
+npm-packlist,1.1.10,ISC
npm-run-path,2.0.2,MIT
npmlog,4.1.2,ISC
null-check,1.0.0,MIT
-num2fraction,1.2.2,MIT
number-is-nan,1.0.1,MIT
numerizer,0.1.1,MIT
oauth,0.5.4,MIT
@@ -1057,7 +1045,6 @@ object-component,0.0.3,MIT*
object-copy,0.1.0,MIT
object-keys,1.0.11,MIT
object-visit,1.0.1,MIT
-object.omit,2.0.1,MIT
object.pick,1.3.0,MIT
obuf,1.1.1,MIT
octokit,4.9.0,MIT
@@ -1076,14 +1063,16 @@ omniauth-oauth,1.1.0,MIT
omniauth-oauth2,1.5.0,MIT
omniauth-oauth2-generic,0.2.2,MIT
omniauth-saml,1.10.0,MIT
-omniauth-shibboleth,1.2.1,MIT
+omniauth-shibboleth,1.3.0,MIT
omniauth-twitter,1.4.0,MIT
omniauth_crowd,2.2.3,MIT
on-finished,2.3.0,MIT
on-headers,1.0.1,MIT
once,1.4.0,ISC
onetime,2.0.1,MIT
+opencollective,1.0.3,MIT
opener,1.4.3,(WTFPL OR MIT)
+opn,4.0.2,MIT
opn,5.2.0,MIT
optimist,0.6.1,MIT
optionator,0.8.2,MIT
@@ -1104,13 +1093,12 @@ p-locate,2.0.0,MIT
p-map,1.1.1,MIT
p-timeout,2.0.1,MIT
p-try,1.0.0,MIT
-pac-proxy-agent,1.1.0,MIT
-pac-resolver,2.0.0,MIT
+pac-proxy-agent,2.0.2,MIT
+pac-resolver,3.0.0,MIT
package-json,4.0.1,MIT
pako,1.0.6,(MIT AND Zlib)
parallel-transform,1.1.0,MIT
parse-asn1,5.1.0,ISC
-parse-glob,3.0.4,MIT
parse-json,2.2.0,MIT
parseqs,0.0.5,MIT
parseuri,0.0.5,MIT
@@ -1137,7 +1125,6 @@ peek-pg,1.3.0,MIT
peek-rblineprof,0.2.0,MIT
peek-redis,1.2.0,MIT
peek-sidekiq,1.0.3,MIT
-performance-now,0.2.0,MIT
performance-now,2.1.0,MIT
pg,0.18.4,"BSD,ruby,GPL"
pify,2.3.0,MIT
@@ -1153,48 +1140,19 @@ popper.js,1.14.3,MIT
portfinder,1.0.13,MIT
posix-character-classes,0.1.1,MIT
posix-spawn,0.3.13,MIT
-postcss,5.2.16,MIT
postcss,6.0.22,MIT
-postcss-calc,5.3.1,MIT
-postcss-colormin,2.2.2,MIT
-postcss-convert-values,2.6.1,MIT
-postcss-discard-comments,2.0.4,MIT
-postcss-discard-duplicates,2.1.0,MIT
-postcss-discard-empty,2.1.0,MIT
-postcss-discard-overridden,0.1.1,MIT
-postcss-discard-unused,2.2.3,MIT
-postcss-filter-plugins,2.0.2,MIT
-postcss-merge-idents,2.1.7,MIT
-postcss-merge-longhand,2.0.2,MIT
-postcss-merge-rules,2.1.2,MIT
-postcss-message-helpers,2.0.0,MIT
-postcss-minify-font-values,1.0.5,MIT
-postcss-minify-gradients,1.0.5,MIT
-postcss-minify-params,1.2.2,MIT
-postcss-minify-selectors,2.1.1,MIT
+postcss,6.0.23,MIT
postcss-modules-extract-imports,1.2.0,ISC
postcss-modules-local-by-default,1.2.0,MIT
postcss-modules-scope,1.1.0,ISC
postcss-modules-values,1.3.0,ISC
-postcss-normalize-charset,1.1.1,MIT
-postcss-normalize-url,3.0.8,MIT
-postcss-ordered-values,2.2.3,MIT
-postcss-reduce-idents,2.4.0,MIT
-postcss-reduce-initial,1.0.1,MIT
-postcss-reduce-transforms,1.0.4,MIT
-postcss-selector-parser,2.2.3,MIT
postcss-selector-parser,3.1.1,MIT
-postcss-svgo,2.1.6,MIT
-postcss-unique-selectors,2.0.2,MIT
postcss-value-parser,3.3.0,MIT
-postcss-zindex,2.2.0,MIT
prelude-ls,1.1.2,MIT
premailer,1.10.4,New BSD
premailer-rails,1.9.7,MIT
prepend-http,1.0.4,MIT
prepend-http,2.0.0,MIT
-preserve,0.2.0,MIT
-prettier,1.11.1,MIT
prettier,1.12.1,MIT
prismjs,1.6.0,MIT
private,0.1.8,MIT
@@ -1202,10 +1160,12 @@ process,0.11.10,MIT
process-nextick-args,1.0.7,MIT
process-nextick-args,2.0.0,MIT
progress,2.0.0,MIT
-prometheus-client-mmap,0.9.3,Apache 2.0
+prometheus-client-mmap,0.9.4,Apache 2.0
promise-inflight,1.0.1,ISC
+promisify-call,2.0.4,MIT
proxy-addr,2.0.3,MIT
-proxy-agent,2.0.0,MIT
+proxy-agent,3.0.1,MIT
+proxy-from-env,1.0.0,MIT
prr,0.0.0,MIT
prr,1.0.1,MIT
ps-tree,1.1.0,MIT
@@ -1218,13 +1178,9 @@ pumpify,1.4.0,MIT
punycode,1.3.2,MIT
punycode,1.4.1,MIT
pyu-ruby-sasl,0.0.3.3,MIT
-q,1.4.1,MIT
-q,1.5.0,MIT
qjobs,1.2.0,MIT
qs,6.2.3,New BSD
-qs,6.4.0,New BSD
qs,6.5.1,New BSD
-query-string,4.3.2,MIT
query-string,5.1.1,MIT
querystring,0.2.0,MIT
querystring-es3,0.2.1,MIT
@@ -1247,16 +1203,17 @@ railties,4.2.10,MIT
rainbow,2.2.2,MIT
raindrops,0.18.0,LGPL-2.1+
rake,12.3.1,MIT
-randomatic,1.1.7,MIT
randombytes,2.0.6,MIT
randomfill,1.0.4,MIT
range-parser,1.2.0,MIT
raphael,2.2.7,MIT
raven-js,3.22.1,Simplified BSD
raw-body,2.3.2,MIT
+raw-body,2.3.3,MIT
raw-loader,0.5.1,MIT
rb-fsevent,0.10.2,MIT
rb-inotify,0.9.10,MIT
+rbtrace,0.4.10,MIT
rc,1.2.5,(BSD-2-Clause OR MIT OR Apache-2.0)
rdoc,6.0.4,ruby
re2,1.1.1,New BSD
@@ -1283,12 +1240,10 @@ redis-parser,2.6.0,MIT
redis-rack,2.0.4,MIT
redis-rails,5.0.2,MIT
redis-store,1.4.1,MIT
-reduce-css-calc,1.3.0,MIT
-reduce-function-call,1.0.2,MIT
regenerate,1.3.2,MIT
+regenerator-runtime,0.10.5,MIT
regenerator-runtime,0.11.0,MIT
regenerator-transform,0.10.1,BSD
-regex-cache,0.4.4,MIT
regex-not,1.0.2,MIT
regexpu-core,1.0.0,MIT
regexpu-core,2.0.0,MIT
@@ -1303,11 +1258,9 @@ repeat-string,1.6.1,MIT
repeating,2.0.1,MIT
representable,3.0.4,MIT
request,2.75.0,Apache 2.0
-request,2.81.0,Apache 2.0
request,2.83.0,Apache 2.0
request_store,1.3.1,MIT
requestretry,1.13.0,MIT
-require-all,2.2.0,MIT
require-directory,2.1.1,MIT
require-main-filename,1.0.1,ISC
require-uncached,1.0.3,MIT
@@ -1329,7 +1282,7 @@ rimraf,2.6.2,ISC
rinku,2.0.0,ISC
ripemd160,2.0.1,MIT
rotp,2.1.2,MIT
-rouge,3.1.1,MIT
+rouge,3.2.0,MIT
rqrcode,0.7.0,MIT
rqrcode-rails3,0.1.7,MIT
ruby-enum,0.7.2,MIT
@@ -1341,31 +1294,33 @@ ruby_parser,3.9.0,MIT
rubyntlm,0.6.2,MIT
rubypants,0.2.0,BSD
rufus-scheduler,3.4.0,MIT
-rugged,0.27.1,MIT
+rugged,0.27.2,MIT
run-async,2.3.0,MIT
run-queue,1.0.3,ISC
+rw,1.3.3,New BSD
+rx,4.1.0,Apache 2.0
rx-lite,4.0.8,Apache 2.0
rx-lite-aggregates,4.0.8,Apache 2.0
-rxjs,5.5.10,Apache 2.0
+rxjs,6.2.1,Apache 2.0
safe-buffer,5.1.1,MIT
+safe-buffer,5.1.2,MIT
safe-regex,1.1.0,MIT
safe_yaml,1.0.4,MIT
-sanitize,2.1.0,MIT
+safer-buffer,2.1.2,MIT
+sanitize,4.6.6,MIT
sanitize-html,1.16.3,MIT
sass,3.5.5,MIT
sass-listen,4.0.0,MIT
sass-rails,5.0.6,MIT
sawyer,0.8.1,MIT
-sax,1.2.2,ISC
+sax,1.2.4,ISC
schema-utils,0.4.5,MIT
-securecompare,1.0.0,MIT
seed-fu,2.3.7,MIT
select,1.1.2,MIT
select-hose,2.0.0,MIT
select2,3.5.2-browserify,Apache*
select2-rails,3.5.9.3,MIT
selfsigned,1.10.1,MIT
-semver,5.0.3,ISC
semver,5.5.0,ISC
semver-diff,2.1.0,MIT
send,0.16.1,MIT
@@ -1397,6 +1352,8 @@ slack-notifier,1.5.1,MIT
slash,1.0.0,MIT
slice-ansi,1.0.0,MIT
smart-buffer,1.1.15,MIT
+smart-buffer,4.0.1,MIT
+smooshpack,0.0.48,SEE LICENSE.MD IN ROOT
smtp-connection,2.12.0,MIT
snapdragon,0.8.1,MIT
snapdragon-node,2.1.1,MIT
@@ -1411,8 +1368,9 @@ sockjs,0.3.19,MIT
sockjs-client,1.1.4,MIT
socks,1.1.10,MIT
socks,1.1.9,MIT
-socks-proxy-agent,2.1.1,MIT
-sort-keys,1.1.2,MIT
+socks,2.2.1,MIT
+socks-proxy-agent,3.0.1,MIT
+socks-proxy-agent,4.0.1,MIT
sort-keys,2.0.0,MIT
sortablejs,1.7.0,MIT
source-list-map,2.0.0,MIT
@@ -1433,7 +1391,7 @@ spdy-transport,2.0.20,MIT
split,0.3.3,MIT
split-string,3.1.0,MIT
sprintf-js,1.0.3,New BSD
-sprockets,3.7.1,MIT
+sprockets,3.7.2,MIT
sprockets-rails,3.2.1,MIT
sql.js,0.4.0,MIT
srcset,1.0.0,MIT
@@ -1446,6 +1404,7 @@ state_machines-activerecord,0.5.1,MIT
static-extend,0.1.2,MIT
statuses,1.3.1,MIT
statuses,1.4.0,MIT
+statuses,1.5.0,MIT
stickyfilljs,2.0.5,MIT
stream-browserify,2.0.1,MIT
stream-combiner,0.0.4,MIT
@@ -1473,19 +1432,17 @@ supports-color,2.0.0,MIT
supports-color,3.2.3,MIT
supports-color,5.4.0,MIT
svg4everybody,2.1.9,CC0-1.0
-svgo,0.7.2,MIT
-symbol-observable,1.0.1,MIT
sys-filesystem,1.1.6,Artistic 2.0
table,4.0.2,New BSD
tapable,0.1.10,MIT
tapable,1.0.0,MIT
-tar,2.2.1,ISC
-tar-pack,3.4.1,Simplified BSD
-temple,0.7.7,MIT
+tar,4.4.4,ISC
+temple,0.8.0,MIT
term-size,1.2.0,MIT
test-exclude,4.2.1,ISC
text,1.3.1,MIT
text-table,0.2.0,MIT
+textextensions,2.2.0,MIT
thor,0.19.4,MIT
thread_safe,0.3.6,Apache 2.0
three,0.84.0,MIT
@@ -1495,7 +1452,7 @@ through,2.3.8,MIT
through2,2.0.3,MIT
thunkify,2.1.2,MIT
thunky,0.1.0,MIT*
-tilt,2.0.6,MIT
+tilt,2.0.8,MIT
timeago.js,3.0.2,MIT
timed-out,4.0.1,MIT
timers-browserify,2.0.10,MIT
@@ -1516,9 +1473,11 @@ tough-cookie,2.3.3,New BSD
traverse,0.6.6,MIT
trim-newlines,1.0.0,MIT
trim-right,1.0.1,MIT
+trollop,2.1.3,MIT
truncato,0.7.10,MIT
tryer,1.0.0,MIT
tryit,1.0.3,MIT
+tslib,1.9.3,Apache 2.0
tsscmp,1.0.5,MIT
tty-browserify,0.0.0,MIT
tunnel-agent,0.4.3,Apache 2.0
@@ -1535,7 +1494,6 @@ uglify-es,3.3.9,Simplified BSD
uglify-js,2.8.29,Simplified BSD
uglify-to-browserify,1.0.2,MIT
uglifyjs-webpack-plugin,1.2.5,MIT
-uid-number,0.0.6,ISC
ultron,1.1.1,MIT
undefsafe,2.0.2,MIT
underscore,1.7.0,MIT
@@ -1546,8 +1504,6 @@ unicorn,5.1.0,ruby
unicorn-worker-killer,0.4.4,ruby
union-value,1.0.0,MIT
uniq,1.0.1,MIT
-uniqid,4.1.1,MIT
-uniqs,2.0.0,MIT
unique-filename,1.1.0,ISC
unique-slug,2.0.0,ISC
unique-string,1.0.0,MIT
@@ -1555,10 +1511,10 @@ unpipe,1.0.0,MIT
unset-value,1.0.0,MIT
unzip-response,2.0.1,MIT
upath,1.0.5,MIT
+upath,1.1.0,MIT
update-notifier,2.3.0,Simplified BSD
urix,0.1.0,MIT
url,0.11.0,MIT
-url-join,2.0.5,MIT
url-join,4.0.0,MIT
url-loader,1.0.1,MIT
url-parse,1.0.5,MIT
@@ -1566,7 +1522,6 @@ url-parse,1.1.9,MIT
url-parse-lax,1.0.0,MIT
url-parse-lax,3.0.0,MIT
url-to-options,1.0.1,MIT
-url_safe_base64,0.2.2,MIT
use,2.0.2,MIT
useragent,2.2.1,MIT
util,0.10.3,MIT
@@ -1579,7 +1534,6 @@ validate-npm-package-license,3.0.1,Apache 2.0
validates_hostname,1.0.6,MIT
vary,1.1.1,MIT
vary,1.1.2,MIT
-vendors,1.0.1,MIT
verror,1.10.0,MIT
version_sorter,2.1.0,MIT
virtus,1.0.5,MIT
@@ -1589,8 +1543,9 @@ vmstat,2.3.0,MIT
void-elements,2.0.1,MIT
vue,2.5.16,MIT
vue-eslint-parser,2.0.3,MIT
+vue-functional-data-merge,2.0.6,MIT
vue-hot-reload-api,2.3.0,MIT
-vue-loader,15.2.0,MIT
+vue-loader,15.2.4,MIT
vue-resource,1.5.0,MIT
vue-router,3.0.1,MIT
vue-style-loader,4.1.0,MIT
@@ -1601,10 +1556,9 @@ vuex,3.0.1,MIT
warden,1.2.7,MIT
watchpack,1.5.0,MIT
wbuf,1.7.2,MIT
-webpack,4.11.1,MIT
-webpack-bundle-analyzer,2.11.1,MIT
-webpack-cli,3.0.2,MIT
-webpack-dev-middleware,2.0.6,MIT
+webpack,4.16.0,MIT
+webpack-bundle-analyzer,2.13.1,MIT
+webpack-cli,3.0.8,MIT
webpack-dev-middleware,3.1.3,MIT
webpack-dev-server,3.1.4,MIT
webpack-log,1.2.0,MIT
@@ -1614,13 +1568,13 @@ webpack-stats-plugin,0.2.1,MIT
websocket-driver,0.6.5,MIT
websocket-extensions,0.1.1,MIT
when,3.7.8,MIT
-whet.extend,0.9.9,MIT
which,1.3.0,ISC
which-module,2.0.0,ISC
wide-align,1.1.2,ISC
widest-line,2.0.0,MIT
wikicloth,0.8.1,MIT
window-size,0.1.0,MIT
+with-callback,1.0.2,MIT
wordwrap,0.0.2,MIT
wordwrap,0.0.3,MIT
wordwrap,1.0.0,MIT
@@ -1634,12 +1588,15 @@ ws,3.3.3,MIT
ws,4.0.0,MIT
xdg-basedir,3.0.0,MIT
xml-simple,1.1.5,ruby
+xmlhttprequest,1.8.0,MIT
xmlhttprequest-ssl,1.5.5,MIT
xregexp,2.0.0,MIT
xtend,4.0.1,MIT
+xterm,3.5.0,MIT
y18n,3.2.1,ISC
y18n,4.0.0,ISC
yallist,2.1.2,ISC
+yallist,3.0.2,ISC
yargs,11.0.0,MIT
yargs,11.1.0,MIT
yargs,3.10.0,MIT
diff --git a/yarn.lock b/yarn.lock
index ef7fa659d6e..c1e9d0ab73e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -78,9 +78,17 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
-"@gitlab-org/gitlab-svgs@^1.24.0":
- version "1.24.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.24.0.tgz#3b2b58c5a1d58ce784f486d648bd87cbbb06cedc"
+"@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.27.0":
+ version "1.27.0"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.27.0.tgz#638e70399ebd59e503732177316bb9a18bf7a13f"
+
+"@gitlab-org/gitlab-ui@1.0.5":
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.0.5.tgz#a64b402650494115c8b494a44b72c2d6fbf33fff"
+ dependencies:
+ "@gitlab-org/gitlab-svgs" "^1.23.0"
+ bootstrap-vue "^2.0.0-rc.11"
+ vue "^2.5.16"
"@sindresorhus/is@^0.7.0":
version "0.7.0"
@@ -104,139 +112,140 @@
source-map "^0.5.6"
vue-template-es2015-compiler "^1.6.0"
-"@webassemblyjs/ast@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.10.tgz#7f1e81149ca4e103c9e7cc321ea0dcb83a392512"
+"@webassemblyjs/ast@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25"
dependencies:
- "@webassemblyjs/helper-module-context" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/wast-parser" "1.5.10"
+ "@webassemblyjs/helper-module-context" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/wast-parser" "1.5.13"
debug "^3.1.0"
mamacro "^0.0.3"
-"@webassemblyjs/floating-point-hex-parser@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.10.tgz#ae48705fd58927df62023f114520b8215330ff86"
+"@webassemblyjs/floating-point-hex-parser@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz#29ce0baa97411f70e8cce68ce9c0f9d819a4e298"
-"@webassemblyjs/helper-api-error@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.10.tgz#0baf9453ce2fd8db58f0fdb4fb2852557c71d5a7"
+"@webassemblyjs/helper-api-error@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz#e49b051d67ee19a56e29b9aa8bd949b5b4442a59"
-"@webassemblyjs/helper-buffer@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.10.tgz#abee4284161e9cd6ba7619785ca277bfcb8052ce"
+"@webassemblyjs/helper-buffer@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz#873bb0a1b46449231137c1262ddfd05695195a1e"
dependencies:
debug "^3.1.0"
-"@webassemblyjs/helper-code-frame@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.10.tgz#4e23c05431665f16322104580af7c06253d4b4e0"
+"@webassemblyjs/helper-code-frame@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz#1bd2181b6a0be14e004f0fe9f5a660d265362b58"
dependencies:
- "@webassemblyjs/wast-printer" "1.5.10"
+ "@webassemblyjs/wast-printer" "1.5.13"
-"@webassemblyjs/helper-fsm@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.10.tgz#490bab613ea255a9272b764826d3cc9d15170676"
+"@webassemblyjs/helper-fsm@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz#cdf3d9d33005d543a5c5e5adaabf679ffa8db924"
-"@webassemblyjs/helper-module-context@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.10.tgz#6fca93585228bf33e6da076d0a1373db1fdd6580"
+"@webassemblyjs/helper-module-context@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz#dc29ddfb51ed657655286f94a5d72d8a489147c5"
dependencies:
+ debug "^3.1.0"
mamacro "^0.0.3"
-"@webassemblyjs/helper-wasm-bytecode@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.10.tgz#90f6da93c7a186bfb2f587de442982ff533c4b44"
+"@webassemblyjs/helper-wasm-bytecode@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz#03245817f0a762382e61733146f5773def15a747"
-"@webassemblyjs/helper-wasm-section@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.10.tgz#d64292a19f7f357c49719461065efdf7ec975d66"
+"@webassemblyjs/helper-wasm-section@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz#efc76f44a10d3073b584b43c38a179df173d5c7d"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-buffer" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/wasm-gen" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-buffer" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/wasm-gen" "1.5.13"
debug "^3.1.0"
-"@webassemblyjs/ieee754@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.10.tgz#257cad440dd6c8a339402d31e035ba2e38e9c245"
+"@webassemblyjs/ieee754@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz#573e97c8c12e4eebb316ca5fde0203ddd90b0364"
dependencies:
ieee754 "^1.1.11"
-"@webassemblyjs/leb128@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.10.tgz#a8e4fe5f4b16daadb241fcc44d9735e9f27b05a3"
+"@webassemblyjs/leb128@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.13.tgz#ab52ebab9cec283c1c1897ac1da833a04a3f4cee"
dependencies:
- leb "^0.3.0"
+ long "4.0.0"
-"@webassemblyjs/utf8@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.10.tgz#0b3b6bc86b7619c5dc7b2789db6665aa35689983"
+"@webassemblyjs/utf8@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.13.tgz#6b53d2cd861cf94fa99c1f12779dde692fbc2469"
-"@webassemblyjs/wasm-edit@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.10.tgz#0fe80f19e57f669eab1caa8c1faf9690b259d5b9"
+"@webassemblyjs/wasm-edit@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz#c9cef5664c245cf11b3b3a73110c9155831724a8"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-buffer" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/helper-wasm-section" "1.5.10"
- "@webassemblyjs/wasm-gen" "1.5.10"
- "@webassemblyjs/wasm-opt" "1.5.10"
- "@webassemblyjs/wasm-parser" "1.5.10"
- "@webassemblyjs/wast-printer" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-buffer" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/helper-wasm-section" "1.5.13"
+ "@webassemblyjs/wasm-gen" "1.5.13"
+ "@webassemblyjs/wasm-opt" "1.5.13"
+ "@webassemblyjs/wasm-parser" "1.5.13"
+ "@webassemblyjs/wast-printer" "1.5.13"
debug "^3.1.0"
-"@webassemblyjs/wasm-gen@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.10.tgz#8b29ddd3651259408ae5d5c816a011fb3f3f3584"
+"@webassemblyjs/wasm-gen@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz#8e6ea113c4b432fa66540189e79b16d7a140700e"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/ieee754" "1.5.10"
- "@webassemblyjs/leb128" "1.5.10"
- "@webassemblyjs/utf8" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/ieee754" "1.5.13"
+ "@webassemblyjs/leb128" "1.5.13"
+ "@webassemblyjs/utf8" "1.5.13"
-"@webassemblyjs/wasm-opt@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.10.tgz#569e45ab1b2bf0a7706cdf6d1b51d1188e9e4c7b"
+"@webassemblyjs/wasm-opt@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz#147aad7717a7ee4211c36b21a5f4c30dddf33138"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-buffer" "1.5.10"
- "@webassemblyjs/wasm-gen" "1.5.10"
- "@webassemblyjs/wasm-parser" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-buffer" "1.5.13"
+ "@webassemblyjs/wasm-gen" "1.5.13"
+ "@webassemblyjs/wasm-parser" "1.5.13"
debug "^3.1.0"
-"@webassemblyjs/wasm-parser@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.10.tgz#3e1017e49f833f46b840db7cf9d194d4f00037ff"
- dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-api-error" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/ieee754" "1.5.10"
- "@webassemblyjs/leb128" "1.5.10"
- "@webassemblyjs/wasm-parser" "1.5.10"
-
-"@webassemblyjs/wast-parser@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.10.tgz#1a3235926483c985a00ee8ebca856ffda9544934"
- dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/floating-point-hex-parser" "1.5.10"
- "@webassemblyjs/helper-api-error" "1.5.10"
- "@webassemblyjs/helper-code-frame" "1.5.10"
- "@webassemblyjs/helper-fsm" "1.5.10"
+"@webassemblyjs/wasm-parser@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz#6f46516c5bb23904fbdf58009233c2dd8a54c72f"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-api-error" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/ieee754" "1.5.13"
+ "@webassemblyjs/leb128" "1.5.13"
+ "@webassemblyjs/utf8" "1.5.13"
+
+"@webassemblyjs/wast-parser@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz#5727a705d397ae6a3ae99d7f5460acf2ec646eea"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/floating-point-hex-parser" "1.5.13"
+ "@webassemblyjs/helper-api-error" "1.5.13"
+ "@webassemblyjs/helper-code-frame" "1.5.13"
+ "@webassemblyjs/helper-fsm" "1.5.13"
long "^3.2.0"
mamacro "^0.0.3"
-"@webassemblyjs/wast-printer@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.10.tgz#adb38831ba45efd0a5c7971b666e179b64f68bba"
+"@webassemblyjs/wast-printer@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz#bb34d528c14b4f579e7ec11e793ec50ad7cd7c95"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/wast-parser" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/wast-parser" "1.5.13"
long "^3.2.0"
abbrev@1:
@@ -274,6 +283,10 @@ acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0:
version "5.6.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.2.tgz#b1da1d7be2ac1b4a327fb9eab851702c5045b4e7"
+acorn@^5.6.2:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
+
addressparser@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746"
@@ -282,12 +295,11 @@ after@0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
-agent-base@2:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7"
+agent-base@4, agent-base@^4.1.0, agent-base@^4.2.0, agent-base@~4.2.0:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
dependencies:
- extend "~3.0.0"
- semver "~5.0.1"
+ es6-promisify "^5.0.0"
ajv-keywords@^2.1.0:
version "2.1.1"
@@ -322,10 +334,6 @@ align-text@^0.1.1, align-text@^0.1.3:
longest "^1.0.1"
repeat-string "^1.5.2"
-alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
-
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
@@ -346,6 +354,10 @@ ansi-align@^2.0.0:
dependencies:
string-width "^2.0.0"
+ansi-escapes@^1.1.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+
ansi-escapes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92"
@@ -372,13 +384,6 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-anymatch@^1.3.0:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a"
- dependencies:
- micromatch "^2.1.5"
- normalize-path "^2.0.0"
-
anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -409,17 +414,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
-arr-diff@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
- dependencies:
- arr-flatten "^1.0.1"
-
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
-arr-flatten@^1.0.1, arr-flatten@^1.1.0:
+arr-flatten@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
@@ -532,11 +531,11 @@ async@^2.0.0, async@^2.1.4:
dependencies:
lodash "^4.14.0"
-async@~2.1.2:
- version "2.1.5"
- resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc"
+async@~2.6.0:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
dependencies:
- lodash "^4.14.0"
+ lodash "^4.17.10"
asynckit@^0.4.0:
version "0.4.0"
@@ -546,17 +545,6 @@ atob@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d"
-autoprefixer@^6.3.1:
- version "6.7.7"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
- dependencies:
- browserslist "^1.7.6"
- caniuse-db "^1.0.30000634"
- normalize-range "^0.1.2"
- num2fraction "^1.2.2"
- postcss "^5.2.16"
- postcss-value-parser "^3.2.3"
-
autosize@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.0.tgz#7a0599b1ba84d73bd7589b0d9da3870152c69237"
@@ -766,9 +754,9 @@ babel-helpers@^6.24.1:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
-babel-loader@^7.1.4:
- version "7.1.4"
- resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.4.tgz#e3463938bd4e6d55d1c174c5485d406a188ed015"
+babel-loader@^7.1.5:
+ version "7.1.5"
+ resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.5.tgz#e3ee0cd7394aa557e013b02d3e492bfd07aa6d68"
dependencies:
find-cache-dir "^1.0.0"
loader-utils "^1.0.2"
@@ -1069,6 +1057,14 @@ babel-plugin-transform-strict-mode@^6.24.1:
babel-runtime "^6.22.0"
babel-types "^6.24.1"
+babel-polyfill@6.23.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d"
+ dependencies:
+ babel-runtime "^6.22.0"
+ core-js "^2.4.0"
+ regenerator-runtime "^0.10.0"
+
babel-preset-es2015@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939"
@@ -1202,10 +1198,6 @@ backo2@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
-balanced-match@^0.4.2:
- version "0.4.2"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
-
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
@@ -1266,6 +1258,10 @@ binary-extensions@^1.0.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
+binaryextensions@2:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935"
+
bitsyntax@~0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82"
@@ -1345,6 +1341,21 @@ boom@5.x.x:
dependencies:
hoek "4.x.x"
+bootstrap-vue@^2.0.0-rc.11:
+ version "2.0.0-rc.11"
+ resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.0.0-rc.11.tgz#47aaa6d2a8d390477de75e636d8ea652b1d03f59"
+ dependencies:
+ bootstrap "^4.1.1"
+ lodash.get "^4.4.2"
+ lodash.startcase "^4.4.0"
+ opencollective "^1.0.3"
+ popper.js "^1.12.9"
+ vue-functional-data-merge "^2.0.5"
+
+bootstrap@^4.1.1:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.2.tgz#aee2a93472e61c471fc79fb475531dcbc87de326"
+
bootstrap@~4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.1.tgz#3aec85000fa619085da8d2e4983dfd67cf2114cb"
@@ -1374,14 +1385,6 @@ braces@^0.1.2:
dependencies:
expand-range "^0.1.0"
-braces@^1.8.2:
- version "1.8.5"
- resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
- dependencies:
- expand-range "^1.8.1"
- preserve "^0.2.0"
- repeat-element "^1.1.2"
-
braces@^2.3.0, braces@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb"
@@ -1455,13 +1458,6 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
-browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
- version "1.7.7"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
- dependencies:
- caniuse-db "^1.0.30000639"
- electron-to-chromium "^1.2.7"
-
buffer-from@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
@@ -1600,19 +1596,6 @@ camelcase@^4.0.0, camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
-caniuse-api@^1.5.2:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
- dependencies:
- browserslist "^1.3.6"
- caniuse-db "^1.0.30000529"
- lodash.memoize "^4.1.2"
- lodash.uniq "^4.5.0"
-
-caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
- version "1.0.30000649"
- resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000649.tgz#1ee1754a6df235450c8b7cd15e0ebf507221a86a"
-
capture-stack-trace@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
@@ -1632,7 +1615,7 @@ center-align@^0.1.1:
align-text "^0.1.3"
lazy-cache "^1.0.3"
-chalk@^1.1.1, chalk@^1.1.3:
+chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
dependencies:
@@ -1654,6 +1637,10 @@ chardet@^0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
+chardet@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029"
+
"charenc@>= 0.0.1":
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
@@ -1666,24 +1653,27 @@ check-types@^7.3.0:
version "7.3.0"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.3.0.tgz#468f571a4435c24248f5fd0cb0e8d87c3c341e7d"
-chokidar@^1.4.1:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
+chokidar@^2.0.0, chokidar@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7"
dependencies:
- anymatch "^1.3.0"
+ anymatch "^2.0.0"
async-each "^1.0.0"
- glob-parent "^2.0.0"
+ braces "^2.3.0"
+ glob-parent "^3.1.0"
inherits "^2.0.1"
is-binary-path "^1.0.0"
- is-glob "^2.0.0"
+ is-glob "^4.0.0"
+ normalize-path "^2.1.1"
path-is-absolute "^1.0.0"
readdirp "^2.0.0"
+ upath "^1.0.0"
optionalDependencies:
fsevents "^1.0.0"
-chokidar@^2.0.0, chokidar@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7"
+chokidar@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26"
dependencies:
anymatch "^2.0.0"
async-each "^1.0.0"
@@ -1692,20 +1682,23 @@ chokidar@^2.0.0, chokidar@^2.0.2:
inherits "^2.0.1"
is-binary-path "^1.0.0"
is-glob "^4.0.0"
+ lodash.debounce "^4.0.8"
normalize-path "^2.1.1"
path-is-absolute "^1.0.0"
readdirp "^2.0.0"
- upath "^1.0.0"
+ upath "^1.0.5"
optionalDependencies:
- fsevents "^1.0.0"
+ fsevents "^1.2.2"
chownr@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
-chrome-trace-event@^0.1.1:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-0.1.2.tgz#90f36885d5345a50621332f0717b595883d5d982"
+chrome-trace-event@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48"
+ dependencies:
+ tslib "^1.9.0"
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
@@ -1718,15 +1711,9 @@ circular-json@^0.3.1:
version "0.3.3"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
-circular-json@^0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.1.tgz#b8942a09e535863dc21b04417a91971e1d9cd91f"
-
-clap@^1.0.9:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.3.tgz#b3bd36e93dd4cbfb395a3c26896352445265c05b"
- dependencies:
- chalk "^1.1.3"
+circular-json@^0.5.4:
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.5.tgz#64182ef359042d37cd8e767fc9de878b1e9447d3"
class-utils@^0.3.5:
version "0.3.6"
@@ -1785,28 +1772,30 @@ clone-response@1.0.2:
dependencies:
mimic-response "^1.0.0"
-clone@^1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f"
-
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
-co@~3.0.6:
- version "3.0.6"
- resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda"
-
-coa@~1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.1.tgz#7f959346cfc8719e3f7233cd6852854a7c67d8a3"
- dependencies:
- q "^1.1.2"
-
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+codesandbox-api@^0.0.18:
+ version "0.0.18"
+ resolved "https://registry.yarnpkg.com/codesandbox-api/-/codesandbox-api-0.0.18.tgz#56b96b37533f80d20c21861e5e477d3557e613ca"
+
+codesandbox-import-util-types@^1.2.11:
+ version "1.2.11"
+ resolved "https://registry.yarnpkg.com/codesandbox-import-util-types/-/codesandbox-import-util-types-1.2.11.tgz#68e812f21d6b309e9a52eec5cf027c3e63b4c703"
+
+codesandbox-import-utils@^1.2.3:
+ version "1.2.11"
+ resolved "https://registry.yarnpkg.com/codesandbox-import-utils/-/codesandbox-import-utils-1.2.11.tgz#b88423a4a7c785175c784c84e87f5950820280e1"
+ dependencies:
+ codesandbox-import-util-types "^1.2.11"
+ istextorbinary "^2.2.1"
+ lz-string "^1.4.4"
+
collection-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -1814,39 +1803,17 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"
-color-convert@^1.3.0, color-convert@^1.9.0:
+color-convert@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
dependencies:
color-name "^1.1.1"
-color-name@^1.0.0, color-name@^1.1.1:
+color-name@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.2.tgz#5c8ab72b64bd2215d617ae9559ebb148475cf98d"
-color-string@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
- dependencies:
- color-name "^1.0.0"
-
-color@^0.11.0:
- version "0.11.4"
- resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
- dependencies:
- clone "^1.0.2"
- color-convert "^1.3.0"
- color-string "^0.3.0"
-
-colormin@^1.0.5:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
- dependencies:
- color "^0.11.0"
- css-color-names "0.0.4"
- has "^1.0.1"
-
-colors@^1.1.0, colors@~1.1.2:
+colors@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
@@ -1862,7 +1829,7 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
dependencies:
delayed-stream "~1.0.0"
-commander@^2.13.0, commander@^2.15.1, commander@^2.9.0:
+commander@2, commander@^2.13.0, commander@^2.15.1, commander@^2.9.0:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
@@ -2115,22 +2082,16 @@ crypto-random-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
-css-color-names@0.0.4:
- version "0.0.4"
- resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
-
-css-loader@^0.28.11:
- version "0.28.11"
- resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.11.tgz#c3f9864a700be2711bb5a2462b2389b1a392dab7"
+css-loader@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.0.tgz#9f46aaa5ca41dbe31860e3b62b8e23c42916bf56"
dependencies:
babel-code-frame "^6.26.0"
css-selector-tokenizer "^0.7.0"
- cssnano "^3.10.0"
icss-utils "^2.1.0"
loader-utils "^1.0.2"
lodash.camelcase "^4.3.0"
- object-assign "^4.1.1"
- postcss "^5.0.6"
+ postcss "^6.0.23"
postcss-modules-extract-imports "^1.2.0"
postcss-modules-local-by-default "^1.2.0"
postcss-modules-scope "^1.1.0"
@@ -2150,50 +2111,6 @@ cssesc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
-cssnano@^3.10.0:
- version "3.10.0"
- resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
- dependencies:
- autoprefixer "^6.3.1"
- decamelize "^1.1.2"
- defined "^1.0.0"
- has "^1.0.1"
- object-assign "^4.0.1"
- postcss "^5.0.14"
- postcss-calc "^5.2.0"
- postcss-colormin "^2.1.8"
- postcss-convert-values "^2.3.4"
- postcss-discard-comments "^2.0.4"
- postcss-discard-duplicates "^2.0.1"
- postcss-discard-empty "^2.0.1"
- postcss-discard-overridden "^0.1.1"
- postcss-discard-unused "^2.2.1"
- postcss-filter-plugins "^2.0.0"
- postcss-merge-idents "^2.1.5"
- postcss-merge-longhand "^2.0.1"
- postcss-merge-rules "^2.0.3"
- postcss-minify-font-values "^1.0.2"
- postcss-minify-gradients "^1.0.1"
- postcss-minify-params "^1.0.4"
- postcss-minify-selectors "^2.0.4"
- postcss-normalize-charset "^1.1.0"
- postcss-normalize-url "^3.0.7"
- postcss-ordered-values "^2.1.0"
- postcss-reduce-idents "^2.2.2"
- postcss-reduce-initial "^1.0.0"
- postcss-reduce-transforms "^1.0.3"
- postcss-svgo "^2.1.1"
- postcss-unique-selectors "^2.0.2"
- postcss-value-parser "^3.2.3"
- postcss-zindex "^2.0.1"
-
-csso@~2.3.1:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85"
- dependencies:
- clap "^1.0.9"
- source-map "^0.5.3"
-
currently-unhandled@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
@@ -2208,15 +2125,15 @@ cyclist@~0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
-d3-array@^1.2.0, d3-array@^1.2.1:
+d3-array@1, d3-array@1.2.1, d3-array@^1.2.0, d3-array@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc"
-d3-axis@^1.0.8:
+d3-axis@1.0.8, d3-axis@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.8.tgz#31a705a0b535e65759de14173a31933137f18efa"
-d3-brush@^1.0.4:
+d3-brush@1.0.4, d3-brush@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.0.4.tgz#00c2f238019f24f6c0a194a26d41a1530ffe7bc4"
dependencies:
@@ -2226,44 +2143,103 @@ d3-brush@^1.0.4:
d3-selection "1"
d3-transition "1"
-d3-collection@1:
+d3-chord@1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.4.tgz#7dec4f0ba886f713fe111c45f763414f6f74ca2c"
+ dependencies:
+ d3-array "1"
+ d3-path "1"
+
+d3-collection@1, d3-collection@1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2"
-d3-color@1:
+d3-color@1, d3-color@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.3.tgz#bc7643fca8e53a8347e2fbdaffa236796b58509b"
-d3-dispatch@1:
+d3-dispatch@1, d3-dispatch@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.3.tgz#46e1491eaa9b58c358fce5be4e8bed626e7871f8"
-d3-drag@1:
+d3-drag@1, d3-drag@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.1.tgz#df8dd4c502fb490fc7462046a8ad98a5c479282d"
dependencies:
d3-dispatch "1"
d3-selection "1"
-d3-ease@1:
+d3-dsv@1, d3-dsv@1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.8.tgz#907e240d57b386618dc56468bacfe76bf19764ae"
+ dependencies:
+ commander "2"
+ iconv-lite "0.4"
+ rw "1"
+
+d3-ease@1, d3-ease@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.3.tgz#68bfbc349338a380c44d8acc4fbc3304aa2d8c0e"
-d3-format@1:
+d3-force@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.1.0.tgz#cebf3c694f1078fcc3d4daf8e567b2fbd70d4ea3"
+ dependencies:
+ d3-collection "1"
+ d3-dispatch "1"
+ d3-quadtree "1"
+ d3-timer "1"
+
+d3-format@1, d3-format@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.1.tgz#4e19ecdb081a341dafaf5f555ee956bcfdbf167f"
-d3-interpolate@1:
+d3-geo@1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356"
+ dependencies:
+ d3-array "1"
+
+d3-hierarchy@1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz#a1c845c42f84a206bcf1c01c01098ea4ddaa7a26"
+
+d3-interpolate@1, d3-interpolate@1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.6.tgz#2cf395ae2381804df08aa1bf766b7f97b5f68fb6"
dependencies:
d3-color "1"
-d3-path@1:
+d3-path@1, d3-path@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.5.tgz#241eb1849bd9e9e8021c0d0a799f8a0e8e441764"
-d3-scale@^1.0.7:
+d3-polygon@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.3.tgz#16888e9026460933f2b179652ad378224d382c62"
+
+d3-quadtree@1, d3-quadtree@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.3.tgz#ac7987e3e23fe805a990f28e1b50d38fcb822438"
+
+d3-queue@3.0.7:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/d3-queue/-/d3-queue-3.0.7.tgz#c93a2e54b417c0959129d7d73f6cf7d4292e7618"
+
+d3-random@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.0.tgz#6642e506c6fa3a648595d2b2469788a8d12529d3"
+
+d3-request@1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/d3-request/-/d3-request-1.0.6.tgz#a1044a9ef4ec28c824171c9379fae6d79474b19f"
+ dependencies:
+ d3-collection "1"
+ d3-dispatch "1"
+ d3-dsv "1"
+ xmlhttprequest "1"
+
+d3-scale@1.0.7, d3-scale@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.7.tgz#fa90324b3ea8a776422bd0472afab0b252a0945d"
dependencies:
@@ -2275,31 +2251,31 @@ d3-scale@^1.0.7:
d3-time "1"
d3-time-format "2"
-d3-selection@1, d3-selection@^1.1.0, d3-selection@^1.2.0:
+d3-selection@1, d3-selection@1.2.0, d3-selection@^1.1.0, d3-selection@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.2.0.tgz#1b8ec1c7cedadfb691f2ba20a4a3cfbeb71bbc88"
-d3-shape@^1.2.0:
+d3-shape@1.2.0, d3-shape@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.0.tgz#45d01538f064bafd05ea3d6d2cb748fd8c41f777"
dependencies:
d3-path "1"
-d3-time-format@2, d3-time-format@^2.1.1:
+d3-time-format@2, d3-time-format@2.1.1, d3-time-format@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.1.tgz#85b7cdfbc9ffca187f14d3c456ffda268081bb31"
dependencies:
d3-time "1"
-d3-time@1, d3-time@^1.0.8:
+d3-time@1, d3-time@1.0.8, d3-time@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.8.tgz#dbd2d6007bf416fe67a76d17947b784bffea1e84"
-d3-timer@1:
+d3-timer@1, d3-timer@1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.7.tgz#df9650ca587f6c96607ff4e60cc38229e8dd8531"
-d3-transition@1:
+d3-transition@1, d3-transition@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.1.1.tgz#d8ef89c3b848735b060e54a39b32aaebaa421039"
dependencies:
@@ -2310,10 +2286,59 @@ d3-transition@1:
d3-selection "^1.1.0"
d3-timer "1"
+d3-voronoi@1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c"
+
+d3-zoom@1.7.1:
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.7.1.tgz#02f43b3c3e2db54f364582d7e4a236ccc5506b63"
+ dependencies:
+ d3-dispatch "1"
+ d3-drag "1"
+ d3-interpolate "1"
+ d3-selection "1"
+ d3-transition "1"
+
d3@3.5.17:
version "3.5.17"
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
+d3@4.12.2:
+ version "4.12.2"
+ resolved "https://registry.yarnpkg.com/d3/-/d3-4.12.2.tgz#12f775564c6a9de229f63db03446e2cb7bb56c8f"
+ dependencies:
+ d3-array "1.2.1"
+ d3-axis "1.0.8"
+ d3-brush "1.0.4"
+ d3-chord "1.0.4"
+ d3-collection "1.0.4"
+ d3-color "1.0.3"
+ d3-dispatch "1.0.3"
+ d3-drag "1.2.1"
+ d3-dsv "1.0.8"
+ d3-ease "1.0.3"
+ d3-force "1.1.0"
+ d3-format "1.2.1"
+ d3-geo "1.9.1"
+ d3-hierarchy "1.1.5"
+ d3-interpolate "1.1.6"
+ d3-path "1.0.5"
+ d3-polygon "1.0.3"
+ d3-quadtree "1.0.3"
+ d3-queue "3.0.7"
+ d3-random "1.1.0"
+ d3-request "1.0.6"
+ d3-scale "1.0.7"
+ d3-selection "1.2.0"
+ d3-shape "1.2.0"
+ d3-time "1.0.8"
+ d3-time-format "2.1.1"
+ d3-timer "1.0.7"
+ d3-transition "1.1.1"
+ d3-voronoi "1.1.2"
+ d3-zoom "1.7.1"
+
dagre-d3-renderer@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.4.24.tgz#b36ce2fe4ea20de43e7698627c6ede2a9f15ec45"
@@ -2348,6 +2373,10 @@ date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
+dateformat@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
+
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
@@ -2364,18 +2393,12 @@ debug@2.6.8:
dependencies:
ms "2.0.0"
-debug@^3.0.1, debug@^3.1.0, debug@~3.1.0:
+debug@3.1.0, debug@^3.0.1, debug@^3.1.0, debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
-debug@~2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
- dependencies:
- ms "0.7.1"
-
decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -2438,11 +2461,7 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"
-defined@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
-
-degenerator@~1.0.2:
+degenerator@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095"
dependencies:
@@ -2489,6 +2508,10 @@ depd@1.1.1, depd@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
+depd@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+
des.js@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
@@ -2518,7 +2541,7 @@ di@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
-diff@^3.4.0:
+diff@^3.2.0, diff@^3.4.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@@ -2642,6 +2665,10 @@ ecc-jsbn@~0.1.1:
dependencies:
jsbn "~0.1.0"
+editions@^1.3.3:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b"
+
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -2650,10 +2677,6 @@ ejs@^2.5.7:
version "2.5.9"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5"
-electron-to-chromium@^1.2.7:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.3.tgz#651eb63fe89f39db70ffc8dbd5d9b66958bc6a0e"
-
elliptic@^6.0.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
@@ -2678,6 +2701,12 @@ encodeurl@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+encoding@^0.1.11:
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+ dependencies:
+ iconv-lite "~0.4.13"
+
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@@ -2731,6 +2760,14 @@ enhanced-resolve@^4.0.0:
memory-fs "^0.4.0"
tapable "^1.0.0"
+enhanced-resolve@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f"
+ dependencies:
+ graceful-fs "^4.1.2"
+ memory-fs "^0.4.0"
+ tapable "^1.0.0"
+
enhanced-resolve@~0.9.0:
version "0.9.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e"
@@ -2783,10 +2820,20 @@ es-to-primitive@^1.1.1:
is-date-object "^1.0.1"
is-symbol "^1.0.1"
+es6-promise@^4.0.3:
+ version "4.2.4"
+ resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
+
es6-promise@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
+es6-promisify@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
+ dependencies:
+ es6-promise "^4.0.3"
+
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -2960,7 +3007,7 @@ espree@^3.5.2:
acorn "^5.5.0"
acorn-jsx "^3.0.0"
-esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1:
+esprima@2.7.x, esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
@@ -3057,12 +3104,6 @@ expand-braces@^0.1.1:
array-unique "^0.2.1"
braces "^0.1.2"
-expand-brackets@^0.1.4:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
- dependencies:
- is-posix-bracket "^0.1.0"
-
expand-brackets@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@@ -3082,12 +3123,6 @@ expand-range@^0.1.0:
is-number "^0.1.1"
repeat-string "^0.2.2"
-expand-range@^1.8.1:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
- dependencies:
- fill-range "^2.1.0"
-
exports-loader@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5"
@@ -3147,7 +3182,7 @@ extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
-external-editor@^2.0.4, external-editor@^2.1.0:
+external-editor@^2.0.1, external-editor@^2.0.4:
version "2.2.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
dependencies:
@@ -3155,11 +3190,13 @@ external-editor@^2.0.4, external-editor@^2.1.0:
iconv-lite "^0.4.17"
tmp "^0.0.33"
-extglob@^0.3.1:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
+external-editor@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.0.tgz#dc35c48c6f98a30ca27a20e9687d7f3c77704bb6"
dependencies:
- is-extglob "^1.0.0"
+ chardet "^0.5.0"
+ iconv-lite "^0.4.22"
+ tmp "^0.0.33"
extglob@^2.0.4:
version "2.0.4"
@@ -3234,10 +3271,6 @@ file-uri-to-path@1:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
-filename-regex@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
-
fileset@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0"
@@ -3249,16 +3282,6 @@ filesize@^3.5.11:
version "3.6.0"
resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.0.tgz#22d079615624bb6fd3c04026120628a41b3f4efa"
-fill-range@^2.1.0:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723"
- dependencies:
- is-number "^2.1.0"
- isobject "^2.0.0"
- randomatic "^1.1.3"
- repeat-element "^1.1.2"
- repeat-string "^1.5.2"
-
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
@@ -3314,10 +3337,6 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"
-flatten@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
-
flush-write-stream@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417"
@@ -3337,16 +3356,10 @@ follow-redirects@^1.2.5:
dependencies:
debug "^3.1.0"
-for-in@^1.0.1, for-in@^1.0.2:
+for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
-for-own@^0.1.4:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
- dependencies:
- for-in "^1.0.1"
-
foreach@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
@@ -3363,15 +3376,7 @@ form-data@~2.0.0:
combined-stream "^1.0.5"
mime-types "^2.1.11"
-form-data@~2.1.1:
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.5"
- mime-types "^2.1.12"
-
-form-data@~2.3.1:
+form-data@~2.3.0, form-data@~2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
dependencies:
@@ -3379,6 +3384,10 @@ form-data@~2.3.1:
combined-stream "1.0.6"
mime-types "^2.1.12"
+formdata-polyfill@^3.0.11:
+ version "3.0.11"
+ resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-3.0.11.tgz#c82b4b4bea3356c0a6752219e54ce1edb2a7fb5b"
+
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@@ -3429,7 +3438,7 @@ fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
-fsevents@^1.0.0:
+fsevents@^1.0.0, fsevents@^1.2.2:
version "1.2.4"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426"
dependencies:
@@ -3490,9 +3499,9 @@ get-stream@3.0.0, get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
-get-uri@2:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.1.tgz#dbdcacacd8c608a38316869368117697a1631c59"
+get-uri@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.2.tgz#5c795e71326f6ca1286f2fc82575cd2bab2af578"
dependencies:
data-uri-to-buffer "1"
debug "2"
@@ -3511,19 +3520,6 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
-glob-base@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
- dependencies:
- glob-parent "^2.0.0"
- is-glob "^2.0.0"
-
-glob-parent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
- dependencies:
- is-glob "^2.0.0"
-
glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@@ -3846,10 +3842,6 @@ hpack.js@^2.1.6:
readable-stream "^2.0.1"
wbuf "^1.1.0"
-html-comment-regex@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
-
html-entities@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2"
@@ -3882,13 +3874,21 @@ http-errors@1.6.2, http-errors@~1.6.1, http-errors@~1.6.2:
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
-http-proxy-agent@1:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a"
+http-errors@1.6.3:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
dependencies:
- agent-base "2"
- debug "2"
- extend "3"
+ depd "~1.1.2"
+ inherits "2.0.3"
+ setprototypeof "1.1.0"
+ statuses ">= 1.4.0 < 2"
+
+http-proxy-agent@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
+ dependencies:
+ agent-base "4"
+ debug "3.1.0"
http-proxy-middleware@~0.18.0:
version "0.18.0"
@@ -3937,13 +3937,18 @@ https-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
-https-proxy-agent@1:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6"
+https-proxy-agent@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies:
- agent-base "2"
- debug "2"
- extend "3"
+ agent-base "^4.1.0"
+ debug "^3.1.0"
+
+iconv-lite@0.4, iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
+ version "0.4.23"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
iconv-lite@0.4.15:
version "0.4.15"
@@ -3953,12 +3958,6 @@ iconv-lite@0.4.19, iconv-lite@^0.4.17:
version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
-iconv-lite@^0.4.4:
- version "0.4.23"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
- dependencies:
- safer-buffer ">= 2.1.2 < 3"
-
icss-replace-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
@@ -4031,9 +4030,9 @@ indexof@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
-inflection@~1.10.0:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f"
+inflection@~1.12.0:
+ version "1.12.0"
+ resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.12.0.tgz#a200935656d6f5f6bc4dc7502e1aecb703228416"
inflection@~1.3.0:
version "1.3.8"
@@ -4058,6 +4057,24 @@ ini@^1.3.4, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+inquirer@3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.6.tgz#e04aaa9d05b7a3cb9b0f407d04375f0447190347"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ chalk "^1.0.0"
+ cli-cursor "^2.1.0"
+ cli-width "^2.0.0"
+ external-editor "^2.0.1"
+ figures "^2.0.0"
+ lodash "^4.3.0"
+ mute-stream "0.0.7"
+ run-async "^2.2.0"
+ rx "^4.1.0"
+ string-width "^2.0.0"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
inquirer@^3.0.6:
version "3.3.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
@@ -4077,20 +4094,20 @@ inquirer@^3.0.6:
strip-ansi "^4.0.0"
through "^2.3.6"
-inquirer@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726"
+inquirer@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.0.0.tgz#e8c20303ddc15bbfc2c12a6213710ccd9e1413d8"
dependencies:
ansi-escapes "^3.0.0"
chalk "^2.0.0"
cli-cursor "^2.1.0"
cli-width "^2.0.0"
- external-editor "^2.1.0"
+ external-editor "^3.0.0"
figures "^2.0.0"
lodash "^4.3.0"
mute-stream "0.0.7"
run-async "^2.2.0"
- rxjs "^5.5.2"
+ rxjs "^6.1.0"
string-width "^2.1.0"
strip-ansi "^4.0.0"
through "^2.3.6"
@@ -4122,10 +4139,6 @@ invert-kv@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
-ip@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590"
-
ip@^1.1.0, ip@^1.1.2, ip@^1.1.4, ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
@@ -4134,10 +4147,6 @@ ipaddr.js@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b"
-is-absolute-url@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
-
is-accessor-descriptor@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
@@ -4206,16 +4215,6 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2:
is-data-descriptor "^1.0.0"
kind-of "^6.0.2"
-is-dotfile@^1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
-
-is-equal-shallow@^0.1.3:
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
- dependencies:
- is-primitive "^2.0.0"
-
is-extendable@^0.1.0, is-extendable@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
@@ -4226,10 +4225,6 @@ is-extendable@^1.0.1:
dependencies:
is-plain-object "^2.0.4"
-is-extglob@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
-
is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -4250,12 +4245,6 @@ is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
-is-glob@^2.0.0, is-glob@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
- dependencies:
- is-extglob "^1.0.0"
-
is-glob@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
@@ -4297,12 +4286,6 @@ is-number@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806"
-is-number@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
- dependencies:
- kind-of "^3.0.2"
-
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@@ -4353,14 +4336,6 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies:
isobject "^3.0.1"
-is-posix-bracket@^0.1.0:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
-
-is-primitive@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
-
is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
@@ -4389,16 +4364,10 @@ is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
-is-stream@^1.0.0, is-stream@^1.1.0:
+is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
-is-svg@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
- dependencies:
- html-comment-regex "^1.1.0"
-
is-symbol@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
@@ -4539,6 +4508,14 @@ istanbul@^0.4.5:
which "^1.1.1"
wordwrap "^1.0.0"
+istextorbinary@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.2.1.tgz#a5231a08ef6dd22b268d0895084cf8d58b5bec53"
+ dependencies:
+ binaryextensions "2"
+ editions "^1.3.3"
+ textextensions "2"
+
isurl@^1.0.0-alpha5:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
@@ -4550,6 +4527,12 @@ jasmine-core@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.9.0.tgz#bfbb56defcd30789adec5a3fbba8504233289c72"
+jasmine-diff@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/jasmine-diff/-/jasmine-diff-0.1.3.tgz#93ccc2dcc41028c5ddd4606558074839f2deeaa8"
+ dependencies:
+ diff "^3.2.0"
+
jasmine-jquery@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jasmine-jquery/-/jasmine-jquery-2.1.1.tgz#d4095e646944a26763235769ab018d9f30f0d47b"
@@ -4572,10 +4555,6 @@ jquery.waitforimages@^2.2.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
-js-base64@^2.1.9:
- version "2.1.9"
- resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
-
js-cookie@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.1.3.tgz#48071625217ac9ecfab8c343a13d42ec09ff0526"
@@ -4591,13 +4570,6 @@ js-yaml@3.x, js-yaml@^3.7.0, js-yaml@^3.9.1:
argparse "^1.0.7"
esprima "^4.0.0"
-js-yaml@~3.7.0:
- version "3.7.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
- dependencies:
- argparse "^1.0.7"
- esprima "^2.6.0"
-
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -4687,9 +4659,9 @@ karma-coverage-istanbul-reporter@^1.4.2:
istanbul-api "^1.1.14"
minimatch "^3.0.4"
-karma-jasmine@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.1.tgz#6fe840e75a11600c9d91e84b33c458e1c46a3529"
+karma-jasmine@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.2.tgz#394f2b25ffb4a644b9ada6f22d443e2fd08886c3"
karma-mocha-reporter@^2.2.5:
version "2.2.5"
@@ -4705,24 +4677,24 @@ karma-sourcemap-loader@^0.3.7:
dependencies:
graceful-fs "^4.1.2"
-karma-webpack@3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-3.0.0.tgz#bf009c5b73c667c11c015717e9e520f581317c44"
+karma-webpack@^4.0.0-beta.0:
+ version "4.0.0-beta.0"
+ resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-4.0.0-beta.0.tgz#2b386df6c364f588f896ffbdae57c2e51513d1ba"
dependencies:
async "^2.0.0"
babel-runtime "^6.0.0"
loader-utils "^1.0.0"
lodash "^4.0.0"
source-map "^0.5.6"
- webpack-dev-middleware "^2.0.6"
+ webpack-dev-middleware "^3.0.1"
-karma@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.2.tgz#4d2db9402850a66551fa784b0164fb0824ed8c4b"
+karma@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.4.tgz#b399785f57e9bab1d3c4384db33fef4dec8ae349"
dependencies:
bluebird "^3.3.0"
body-parser "^1.16.1"
- chokidar "^1.4.1"
+ chokidar "^2.0.3"
colors "^1.1.0"
combine-lists "^1.0.0"
connect "^3.6.0"
@@ -4735,7 +4707,7 @@ karma@^2.0.2:
http-proxy "^1.13.0"
isbinaryfile "^3.0.0"
lodash "^4.17.4"
- log4js "^2.3.9"
+ log4js "^2.5.3"
mime "^1.3.4"
minimatch "^3.0.2"
optimist "^0.6.1"
@@ -4806,10 +4778,6 @@ lcid@^1.0.0:
dependencies:
invert-kv "^1.0.0"
-leb@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/leb/-/leb-0.3.0.tgz#32bee9fad168328d6aea8522d833f4180eed1da3"
-
levn@^0.3.0, levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
@@ -4885,18 +4853,26 @@ lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+lodash.debounce@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+
lodash.escaperegexp@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
+lodash.get@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+
+lodash.isequal@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+
lodash.kebabcase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
-lodash.memoize@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
-
lodash.mergewith@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
@@ -4905,9 +4881,9 @@ lodash.snakecase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
-lodash.uniq@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+lodash.startcase@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8"
lodash.upperfirst@4.3.1:
version "4.3.1"
@@ -4917,7 +4893,7 @@ lodash@4.17.4:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
-lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
+lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
@@ -4927,21 +4903,21 @@ log-symbols@^2.1.0:
dependencies:
chalk "^2.0.1"
-log4js@^2.3.9:
- version "2.5.3"
- resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.3.tgz#38bb7bde5e9c1c181bd75e8bc128c5cd0409caf1"
+log4js@^2.5.3:
+ version "2.11.0"
+ resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.11.0.tgz#bf3902eff65c6923d9ce9cfbd2db54160e34005a"
dependencies:
- circular-json "^0.5.1"
+ circular-json "^0.5.4"
date-format "^1.2.0"
debug "^3.1.0"
- semver "^5.3.0"
- streamroller "^0.7.0"
+ semver "^5.5.0"
+ streamroller "0.7.0"
optionalDependencies:
amqplib "^0.5.2"
axios "^0.15.3"
hipchat-notifier "^1.1.0"
loggly "^1.1.0"
- mailgun-js "^0.7.0"
+ mailgun-js "^0.18.0"
nodemailer "^2.5.0"
redis "^2.7.1"
slack-node "~0.2.0"
@@ -4962,6 +4938,10 @@ loglevelnext@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.3.tgz#0f69277e73bbbf2cd61b94d82313216bf87ac66e"
+long@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
+
long@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
@@ -4998,13 +4978,9 @@ lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2:
pseudomap "^1.0.2"
yallist "^2.1.2"
-lru-cache@~2.6.5:
- version "2.6.5"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5"
-
-macaddress@^0.2.8:
- version "0.2.8"
- resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
+lz-string@^1.4.4:
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
mailcomposer@4.0.1:
version "4.0.1"
@@ -5013,18 +4989,18 @@ mailcomposer@4.0.1:
buildmail "4.0.1"
libmime "3.0.0"
-mailgun-js@^0.7.0:
- version "0.7.15"
- resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.7.15.tgz#ee366a20dac64c3c15c03d6c1b3e0ed795252abb"
+mailgun-js@^0.18.0:
+ version "0.18.1"
+ resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.18.1.tgz#ee39aa18d7bb598a5ce9ede84afb681defc8a6b0"
dependencies:
- async "~2.1.2"
- debug "~2.2.0"
- form-data "~2.1.1"
- inflection "~1.10.0"
+ async "~2.6.0"
+ debug "~3.1.0"
+ form-data "~2.3.0"
+ inflection "~1.12.0"
is-stream "^1.1.0"
path-proxy "~1.0.0"
- proxy-agent "~2.0.0"
- q "~1.4.0"
+ promisify-call "^2.0.2"
+ proxy-agent "~3.0.0"
tsscmp "~1.0.0"
make-dir@^1.0.0:
@@ -5063,10 +5039,6 @@ match-at@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/match-at/-/match-at-0.1.1.tgz#25d040d291777704d5e6556bbb79230ec2de0540"
-math-expression-evaluator@^1.2.14:
- version "1.2.16"
- resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9"
-
md5.js@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d"
@@ -5124,24 +5096,6 @@ methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
-micromatch@^2.1.5:
- version "2.3.11"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
- dependencies:
- arr-diff "^2.0.0"
- array-unique "^0.2.1"
- braces "^1.8.2"
- expand-brackets "^0.1.4"
- extglob "^0.3.1"
- filename-regex "^2.0.0"
- is-extglob "^1.0.0"
- is-glob "^2.0.1"
- kind-of "^3.0.2"
- normalize-path "^2.0.1"
- object.omit "^2.0.0"
- parse-glob "^3.0.4"
- regex-cache "^0.4.2"
-
micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
@@ -5215,7 +5169,7 @@ minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@^1.1.3, minimist@^1.2.0:
+minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -5258,7 +5212,7 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
-mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
+mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
@@ -5268,9 +5222,9 @@ moment@2.x, moment@^2.18.1:
version "2.19.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe"
-monaco-editor-webpack-plugin@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.2.1.tgz#577ed091420f422bb8f0ff3a8899dd82344da56d"
+monaco-editor-webpack-plugin@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.4.0.tgz#7324258ab3695464cfe3bc12edb2e8c55b80d92f"
monaco-editor@0.13.1:
version "0.13.1"
@@ -5291,10 +5245,6 @@ move-concurrently@^1.0.1:
rimraf "^2.5.4"
run-queue "^1.0.3"
-ms@0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
-
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -5355,7 +5305,7 @@ neo-async@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f"
-netmask@~1.0.4:
+netmask@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
@@ -5363,6 +5313,13 @@ nice-try@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
+node-fetch@1.6.3:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04"
+ dependencies:
+ encoding "^0.1.11"
+ is-stream "^1.0.1"
+
node-forge@0.6.33:
version "0.6.33"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc"
@@ -5463,9 +5420,9 @@ nodemailer@^2.5.0:
nodemailer-smtp-transport "2.7.2"
socks "1.1.9"
-nodemon@^1.17.3:
- version "1.17.3"
- resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.17.3.tgz#3b0bbc2ee05ccb43b1aef15ba05c63c7bc9b8530"
+nodemon@^1.18.2:
+ version "1.18.2"
+ resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.2.tgz#36b89c790da70c4f270e2cc0718723131bc04abb"
dependencies:
chokidar "^2.0.2"
debug "^3.1.0"
@@ -5506,16 +5463,12 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
-normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1:
+normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
dependencies:
remove-trailing-separator "^1.0.1"
-normalize-range@^0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
-
normalize-url@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
@@ -5524,15 +5477,6 @@ normalize-url@2.0.1:
query-string "^5.0.1"
sort-keys "^2.0.0"
-normalize-url@^1.4.0:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
- dependencies:
- object-assign "^4.0.1"
- prepend-http "^1.0.0"
- query-string "^4.1.0"
- sort-keys "^1.0.0"
-
npm-bundled@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308"
@@ -5563,10 +5507,6 @@ null-check@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd"
-num2fraction@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
-
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -5575,7 +5515,7 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
-object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -5601,13 +5541,6 @@ object-visit@^1.0.0:
dependencies:
isobject "^3.0.0"
-object.omit@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
- dependencies:
- for-own "^0.1.4"
- is-extendable "^0.1.1"
-
object.pick@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
@@ -5640,10 +5573,28 @@ onetime@^2.0.0:
dependencies:
mimic-fn "^1.0.0"
+opencollective@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/opencollective/-/opencollective-1.0.3.tgz#aee6372bc28144583690c3ca8daecfc120dd0ef1"
+ dependencies:
+ babel-polyfill "6.23.0"
+ chalk "1.1.3"
+ inquirer "3.0.6"
+ minimist "1.2.0"
+ node-fetch "1.6.3"
+ opn "4.0.2"
+
opener@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
+opn@4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95"
+ dependencies:
+ object-assign "^4.0.1"
+ pinkie-promise "^2.0.0"
+
opn@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225"
@@ -5739,29 +5690,28 @@ p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
-pac-proxy-agent@1:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d"
+pac-proxy-agent@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-2.0.2.tgz#90d9f6730ab0f4d2607dcdcd4d3d641aa26c3896"
dependencies:
- agent-base "2"
- debug "2"
- extend "3"
- get-uri "2"
- http-proxy-agent "1"
- https-proxy-agent "1"
- pac-resolver "~2.0.0"
- raw-body "2"
- socks-proxy-agent "2"
-
-pac-resolver@~2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd"
+ agent-base "^4.2.0"
+ debug "^3.1.0"
+ get-uri "^2.0.0"
+ http-proxy-agent "^2.1.0"
+ https-proxy-agent "^2.2.1"
+ pac-resolver "^3.0.0"
+ raw-body "^2.2.0"
+ socks-proxy-agent "^3.0.0"
+
+pac-resolver@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26"
dependencies:
- co "~3.0.6"
- degenerator "~1.0.2"
- ip "1.0.1"
- netmask "~1.0.4"
- thunkify "~2.1.1"
+ co "^4.6.0"
+ degenerator "^1.0.4"
+ ip "^1.1.5"
+ netmask "^1.0.6"
+ thunkify "^2.1.2"
package-json@^4.0.0:
version "4.0.1"
@@ -5794,15 +5744,6 @@ parse-asn1@^5.0.0:
evp_bytestokey "^1.0.0"
pbkdf2 "^3.0.3"
-parse-glob@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
- dependencies:
- glob-base "^0.3.0"
- is-dotfile "^1.0.0"
- is-extglob "^1.0.0"
- is-glob "^2.0.0"
-
parse-json@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
@@ -5947,7 +5888,7 @@ pluralize@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
-popper.js@^1.14.3:
+popper.js@^1.12.9, popper.js@^1.14.3:
version "1.14.3"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.3.tgz#1438f98d046acf7b4d78cd502bf418ac64d4f095"
@@ -5963,128 +5904,6 @@ posix-character-classes@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
-postcss-calc@^5.2.0:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
- dependencies:
- postcss "^5.0.2"
- postcss-message-helpers "^2.0.0"
- reduce-css-calc "^1.2.6"
-
-postcss-colormin@^2.1.8:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b"
- dependencies:
- colormin "^1.0.5"
- postcss "^5.0.13"
- postcss-value-parser "^3.2.3"
-
-postcss-convert-values@^2.3.4:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d"
- dependencies:
- postcss "^5.0.11"
- postcss-value-parser "^3.1.2"
-
-postcss-discard-comments@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
- dependencies:
- postcss "^5.0.14"
-
-postcss-discard-duplicates@^2.0.1:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932"
- dependencies:
- postcss "^5.0.4"
-
-postcss-discard-empty@^2.0.1:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5"
- dependencies:
- postcss "^5.0.14"
-
-postcss-discard-overridden@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58"
- dependencies:
- postcss "^5.0.16"
-
-postcss-discard-unused@^2.2.1:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433"
- dependencies:
- postcss "^5.0.14"
- uniqs "^2.0.0"
-
-postcss-filter-plugins@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c"
- dependencies:
- postcss "^5.0.4"
- uniqid "^4.0.0"
-
-postcss-merge-idents@^2.1.5:
- version "2.1.7"
- resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
- dependencies:
- has "^1.0.1"
- postcss "^5.0.10"
- postcss-value-parser "^3.1.1"
-
-postcss-merge-longhand@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658"
- dependencies:
- postcss "^5.0.4"
-
-postcss-merge-rules@^2.0.3:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721"
- dependencies:
- browserslist "^1.5.2"
- caniuse-api "^1.5.2"
- postcss "^5.0.4"
- postcss-selector-parser "^2.2.2"
- vendors "^1.0.0"
-
-postcss-message-helpers@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e"
-
-postcss-minify-font-values@^1.0.2:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69"
- dependencies:
- object-assign "^4.0.1"
- postcss "^5.0.4"
- postcss-value-parser "^3.0.2"
-
-postcss-minify-gradients@^1.0.1:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1"
- dependencies:
- postcss "^5.0.12"
- postcss-value-parser "^3.3.0"
-
-postcss-minify-params@^1.0.4:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3"
- dependencies:
- alphanum-sort "^1.0.1"
- postcss "^5.0.2"
- postcss-value-parser "^3.0.2"
- uniqs "^2.0.0"
-
-postcss-minify-selectors@^2.0.4:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf"
- dependencies:
- alphanum-sort "^1.0.2"
- has "^1.0.1"
- postcss "^5.0.14"
- postcss-selector-parser "^2.0.0"
-
postcss-modules-extract-imports@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85"
@@ -6112,57 +5931,6 @@ postcss-modules-values@^1.3.0:
icss-replace-symbols "^1.1.0"
postcss "^6.0.1"
-postcss-normalize-charset@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1"
- dependencies:
- postcss "^5.0.5"
-
-postcss-normalize-url@^3.0.7:
- version "3.0.8"
- resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222"
- dependencies:
- is-absolute-url "^2.0.0"
- normalize-url "^1.4.0"
- postcss "^5.0.14"
- postcss-value-parser "^3.2.3"
-
-postcss-ordered-values@^2.1.0:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d"
- dependencies:
- postcss "^5.0.4"
- postcss-value-parser "^3.0.1"
-
-postcss-reduce-idents@^2.2.2:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3"
- dependencies:
- postcss "^5.0.4"
- postcss-value-parser "^3.0.2"
-
-postcss-reduce-initial@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea"
- dependencies:
- postcss "^5.0.4"
-
-postcss-reduce-transforms@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1"
- dependencies:
- has "^1.0.1"
- postcss "^5.0.8"
- postcss-value-parser "^3.0.1"
-
-postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
- dependencies:
- flatten "^1.0.2"
- indexes-of "^1.0.1"
- uniq "^1.0.1"
-
postcss-selector-parser@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865"
@@ -6171,44 +5939,10 @@ postcss-selector-parser@^3.1.1:
indexes-of "^1.0.1"
uniq "^1.0.1"
-postcss-svgo@^2.1.1:
- version "2.1.6"
- resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
- dependencies:
- is-svg "^2.0.0"
- postcss "^5.0.14"
- postcss-value-parser "^3.2.3"
- svgo "^0.7.0"
-
-postcss-unique-selectors@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d"
- dependencies:
- alphanum-sort "^1.0.1"
- postcss "^5.0.4"
- uniqs "^2.0.0"
-
-postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
+postcss-value-parser@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
-postcss-zindex@^2.0.1:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22"
- dependencies:
- has "^1.0.1"
- postcss "^5.0.4"
- uniqs "^2.0.0"
-
-postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16:
- version "5.2.16"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.16.tgz#732b3100000f9ff8379a48a53839ed097376ad57"
- dependencies:
- chalk "^1.1.3"
- js-base64 "^2.1.9"
- source-map "^0.5.6"
- supports-color "^3.2.3"
-
postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.20:
version "6.0.22"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.22.tgz#e23b78314905c3b90cbd61702121e7a78848f2a3"
@@ -6217,11 +5951,19 @@ postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.20:
source-map "^0.6.1"
supports-color "^5.4.0"
+postcss@^6.0.23:
+ version "6.0.23"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
+ dependencies:
+ chalk "^2.4.1"
+ source-map "^0.6.1"
+ supports-color "^5.4.0"
+
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
-prepend-http@^1.0.0, prepend-http@^1.0.1:
+prepend-http@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
@@ -6229,10 +5971,6 @@ prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
-preserve@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
-
prettier@1.12.1, prettier@^1.11.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
@@ -6267,6 +6005,12 @@ promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
+promisify-call@^2.0.2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/promisify-call/-/promisify-call-2.0.4.tgz#d48c2d45652ccccd52801ddecbd533a6d4bd5fba"
+ dependencies:
+ with-callback "^1.0.2"
+
proxy-addr@~2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
@@ -6274,18 +6018,22 @@ proxy-addr@~2.0.2:
forwarded "~0.1.2"
ipaddr.js "1.6.0"
-proxy-agent@~2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499"
+proxy-agent@~3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.0.1.tgz#4fb7b61b1476d0fe8e3a3384d90e2460bbded3f9"
dependencies:
- agent-base "2"
- debug "2"
- extend "3"
- http-proxy-agent "1"
- https-proxy-agent "1"
- lru-cache "~2.6.5"
- pac-proxy-agent "1"
- socks-proxy-agent "2"
+ agent-base "^4.2.0"
+ debug "^3.1.0"
+ http-proxy-agent "^2.1.0"
+ https-proxy-agent "^2.2.1"
+ lru-cache "^4.1.2"
+ pac-proxy-agent "^2.0.1"
+ proxy-from-env "^1.0.0"
+ socks-proxy-agent "^4.0.1"
+
+proxy-from-env@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
prr@~0.0.0:
version "0.0.0"
@@ -6344,14 +6092,6 @@ punycode@1.4.1, punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
-q@^1.1.2:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1"
-
-q@~1.4.0:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
-
qjobs@^1.1.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071"
@@ -6364,13 +6104,6 @@ qs@~6.2.0:
version "6.2.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe"
-query-string@^4.1.0:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.2.tgz#ec0fd765f58a50031a3968c2431386f8947a5cdd"
- dependencies:
- object-assign "^4.1.0"
- strict-uri-encode "^1.0.0"
-
query-string@^5.0.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
@@ -6395,13 +6128,6 @@ querystringify@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb"
-randomatic@^1.1.3:
- version "1.1.7"
- resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
- dependencies:
- is-number "^3.0.0"
- kind-of "^4.0.0"
-
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80"
@@ -6429,7 +6155,7 @@ raven-js@^3.22.1:
version "3.22.1"
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.1.tgz#1117f00dfefaa427ef6e1a7d50bbb1fb998a24da"
-raw-body@2, raw-body@2.3.2:
+raw-body@2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
dependencies:
@@ -6438,6 +6164,15 @@ raw-body@2, raw-body@2.3.2:
iconv-lite "0.4.19"
unpipe "1.0.0"
+raw-body@^2.2.0:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
+ dependencies:
+ bytes "3.0.0"
+ http-errors "1.6.3"
+ iconv-lite "0.4.23"
+ unpipe "1.0.0"
+
raw-loader@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
@@ -6557,24 +6292,14 @@ redis@^2.7.1:
redis-commands "^1.2.0"
redis-parser "^2.6.0"
-reduce-css-calc@^1.2.6:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
- dependencies:
- balanced-match "^0.4.2"
- math-expression-evaluator "^1.2.14"
- reduce-function-call "^1.0.1"
-
-reduce-function-call@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99"
- dependencies:
- balanced-match "^0.4.2"
-
regenerate@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
+regenerator-runtime@^0.10.0:
+ version "0.10.5"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+
regenerator-runtime@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
@@ -6587,12 +6312,6 @@ regenerator-transform@^0.10.0:
babel-types "^6.19.0"
private "^0.1.6"
-regex-cache@^0.4.2:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
- dependencies:
- is-equal-shallow "^0.1.3"
-
regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
@@ -6818,6 +6537,10 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
+rw@1:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
+
rx-lite-aggregates@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
@@ -6828,11 +6551,15 @@ rx-lite@*, rx-lite@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
-rxjs@^5.5.2:
- version "5.5.10"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.10.tgz#fde02d7a614f6c8683d0d1957827f492e09db045"
+rx@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
+
+rxjs@^6.1.0:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.1.tgz#246cebec189a6cbc143a3ef9f62d6f4c91813ca1"
dependencies:
- symbol-observable "1.0.1"
+ tslib "^1.9.0"
safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
@@ -6868,10 +6595,6 @@ sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
-sax@~1.2.1:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828"
-
schema-utils@^0.4.0, schema-utils@^0.4.2, schema-utils@^0.4.3, schema-utils@^0.4.4, schema-utils@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e"
@@ -6907,10 +6630,6 @@ semver-diff@^2.0.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
-semver@~5.0.1:
- version "5.0.3"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
-
send@0.16.1:
version "0.16.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3"
@@ -7046,6 +6765,18 @@ smart-buffer@^1.0.13, smart-buffer@^1.0.4:
version "1.1.15"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16"
+smart-buffer@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3"
+
+smooshpack@^0.0.48:
+ version "0.0.48"
+ resolved "https://registry.yarnpkg.com/smooshpack/-/smooshpack-0.0.48.tgz#6fbeaaf59226a1fe500f56aa17185eed377d2823"
+ dependencies:
+ codesandbox-api "^0.0.18"
+ codesandbox-import-utils "^1.2.3"
+ lodash.isequal "^4.5.0"
+
smtp-connection@2.12.0:
version "2.12.0"
resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.12.0.tgz#d76ef9127cb23c2259edb1e8349c2e8d5e2d74c1"
@@ -7151,13 +6882,19 @@ sockjs@0.3.19:
faye-websocket "^0.10.0"
uuid "^3.0.1"
-socks-proxy-agent@2:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3"
+socks-proxy-agent@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz#2eae7cf8e2a82d34565761539a7f9718c5617659"
dependencies:
- agent-base "2"
- extend "3"
- socks "~1.1.5"
+ agent-base "^4.1.0"
+ socks "^1.1.10"
+
+socks-proxy-agent@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473"
+ dependencies:
+ agent-base "~4.2.0"
+ socks "~2.2.0"
socks@1.1.9:
version "1.1.9"
@@ -7166,18 +6903,19 @@ socks@1.1.9:
ip "^1.1.2"
smart-buffer "^1.0.4"
-socks@~1.1.5:
+socks@^1.1.10:
version "1.1.10"
resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a"
dependencies:
ip "^1.1.4"
smart-buffer "^1.0.13"
-sort-keys@^1.0.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
+socks@~2.2.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/socks/-/socks-2.2.1.tgz#68ad678b3642fbc5d99c64c165bc561eab0215f9"
dependencies:
- is-plain-obj "^1.0.0"
+ ip "^1.1.5"
+ smart-buffer "^4.0.1"
sort-keys@^2.0.0:
version "2.0.0"
@@ -7336,6 +7074,10 @@ static-extend@^0.1.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
+"statuses@>= 1.4.0 < 2":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+
statuses@~1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
@@ -7378,7 +7120,7 @@ stream-shift@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
-streamroller@^0.7.0:
+streamroller@0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b"
dependencies:
@@ -7473,7 +7215,7 @@ supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
-supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3:
+supports-color@^3.1.0, supports-color@^3.1.2:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
dependencies:
@@ -7489,22 +7231,6 @@ svg4everybody@2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/svg4everybody/-/svg4everybody-2.1.9.tgz#5bd9f6defc133859a044646d4743fabc28db7e2d"
-svgo@^0.7.0:
- version "0.7.2"
- resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
- dependencies:
- coa "~1.0.1"
- colors "~1.1.2"
- csso "~2.3.1"
- js-yaml "~3.7.0"
- mkdirp "~0.5.1"
- sax "~1.2.1"
- whet.extend "~0.9.9"
-
-symbol-observable@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
-
table@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
@@ -7556,6 +7282,10 @@ text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+textextensions@2:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286"
+
three-orbit-controls@^82.1.0:
version "82.1.0"
resolved "https://registry.yarnpkg.com/three-orbit-controls/-/three-orbit-controls-82.1.0.tgz#11a7f33d0a20ecec98f098b37780f6537374fab4"
@@ -7579,7 +7309,7 @@ through@2, through@^2.3.6, through@~2.3, through@~2.3.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
-thunkify@~2.1.1:
+thunkify@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d"
@@ -7687,6 +7417,10 @@ tryit@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
+tslib@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
+
tsscmp@~1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97"
@@ -7790,16 +7524,6 @@ uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
-uniqid@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1"
- dependencies:
- macaddress "^0.2.8"
-
-uniqs@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
-
unique-filename@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3"
@@ -7837,6 +7561,10 @@ upath@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.5.tgz#02cab9ecebe95bbec6d5fc2566325725ab6d1a73"
+upath@^1.0.5:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd"
+
update-notifier@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451"
@@ -7855,10 +7583,6 @@ urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
-url-join@^2.0.2:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728"
-
url-join@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.0.tgz#4d3340e807d3773bda9991f8305acdcc2a665d2a"
@@ -7964,10 +7688,6 @@ vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
-vendors@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
-
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
@@ -8001,13 +7721,17 @@ vue-eslint-parser@^2.0.3:
esquery "^1.0.0"
lodash "^4.17.4"
+vue-functional-data-merge@^2.0.5:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/vue-functional-data-merge/-/vue-functional-data-merge-2.0.6.tgz#f08055adfb92458debcf2ad10c3aa712277f7fc2"
+
vue-hot-reload-api@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926"
-vue-loader@^15.2.0:
- version "15.2.0"
- resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.2.0.tgz#5a8138e490a1040942d2f10ae68fa72b5a923364"
+vue-loader@^15.2.4:
+ version "15.2.4"
+ resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.2.4.tgz#a7b923123d3cf87230a8ff54a1c16d31a6c5dbb4"
dependencies:
"@vue/component-compiler-utils" "^1.2.1"
hash-sum "^1.0.2"
@@ -8069,9 +7793,9 @@ wbuf@^1.1.0, wbuf@^1.7.2:
dependencies:
minimalistic-assert "^1.0.0"
-webpack-bundle-analyzer@^2.11.1:
- version "2.11.1"
- resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.11.1.tgz#b9fbfb6a32c0a8c1c3237223e90890796b950ab9"
+webpack-bundle-analyzer@^2.13.1:
+ version "2.13.1"
+ resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz#07d2176c6e86c3cdce4c23e56fae2a7b6b4ad526"
dependencies:
acorn "^5.3.0"
bfj-node4 "^5.2.0"
@@ -8086,23 +7810,23 @@ webpack-bundle-analyzer@^2.11.1:
opener "^1.4.3"
ws "^4.0.0"
-webpack-cli@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.0.2.tgz#e48c5662aff8ed5aac3db5f82f51d7f32e50459e"
+webpack-cli@^3.0.8:
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.0.8.tgz#90eddcf04a4bfc31aa8c0edc4c76785bc4f1ccd9"
dependencies:
chalk "^2.4.1"
cross-spawn "^6.0.5"
enhanced-resolve "^4.0.0"
global-modules-path "^2.1.0"
import-local "^1.0.0"
- inquirer "^5.2.0"
+ inquirer "^6.0.0"
interpret "^1.1.0"
loader-utils "^1.1.0"
supports-color "^5.4.0"
v8-compile-cache "^2.0.0"
yargs "^11.1.0"
-webpack-dev-middleware@3.1.3:
+webpack-dev-middleware@3.1.3, webpack-dev-middleware@^3.0.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz#8b32aa43da9ae79368c1bf1183f2b6cf5e1f39ed"
dependencies:
@@ -8114,18 +7838,6 @@ webpack-dev-middleware@3.1.3:
url-join "^4.0.0"
webpack-log "^1.0.1"
-webpack-dev-middleware@^2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz#a51692801e8310844ef3e3790e1eacfe52326fd4"
- dependencies:
- loud-rejection "^1.6.0"
- memory-fs "~0.4.1"
- mime "^2.1.0"
- path-is-absolute "^1.0.0"
- range-parser "^1.0.3"
- url-join "^2.0.2"
- webpack-log "^1.0.1"
-
webpack-dev-server@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.4.tgz#9a08d13c4addd1e3b6d8ace116e86715094ad5b4"
@@ -8179,21 +7891,21 @@ webpack-stats-plugin@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.2.1.tgz#1f5bac13fc25d62cbb5fd0ff646757dc802b8595"
-webpack@^4.11.1:
- version "4.11.1"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.11.1.tgz#1aa0b936f7ae93a52cf38d2ad0d0f46dcf3c2723"
+webpack@^4.16.0:
+ version "4.16.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.0.tgz#660dae90890e55b8ed17c6f9d17bebb01dab5b4c"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-module-context" "1.5.10"
- "@webassemblyjs/wasm-edit" "1.5.10"
- "@webassemblyjs/wasm-opt" "1.5.10"
- "@webassemblyjs/wasm-parser" "1.5.10"
- acorn "^5.0.0"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-module-context" "1.5.13"
+ "@webassemblyjs/wasm-edit" "1.5.13"
+ "@webassemblyjs/wasm-opt" "1.5.13"
+ "@webassemblyjs/wasm-parser" "1.5.13"
+ acorn "^5.6.2"
acorn-dynamic-import "^3.0.0"
ajv "^6.1.0"
ajv-keywords "^3.1.0"
- chrome-trace-event "^0.1.1"
- enhanced-resolve "^4.0.0"
+ chrome-trace-event "^1.0.0"
+ enhanced-resolve "^4.1.0"
eslint-scope "^3.7.1"
json-parse-better-errors "^1.0.2"
loader-runner "^2.3.0"
@@ -8223,10 +7935,6 @@ when@^3.7.7:
version "3.7.8"
resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82"
-whet.extend@~0.9.9:
- version "0.9.9"
- resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
-
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
@@ -8253,6 +7961,10 @@ window-size@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
+with-callback@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/with-callback/-/with-callback-1.0.2.tgz#a09629b9a920028d721404fb435bdcff5c91bc21"
+
wordwrap@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
@@ -8328,6 +8040,10 @@ xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
+xmlhttprequest@1:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
+
xregexp@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
@@ -8336,6 +8052,10 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
+xterm@^3.5.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.5.0.tgz#ba3f464bc5730c9d259ebe62131862224db9ddcc"
+
y18n@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"